mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-09-14 11:06:15 +05:00
Merge pull request #5214 from Dokploy/fix/issue-4658-domain-validation-multi-ip
fix: domain validation false negative on servers with multiple IPs
This commit is contained in:
commit
4b2de31b1c
@ -191,8 +191,7 @@ export const ShowDomains = ({ id, type }: Props) => {
|
||||
try {
|
||||
const result = await validateDomain({
|
||||
domain: host,
|
||||
serverIp:
|
||||
application?.server?.ipAddress?.toString() || ip?.toString() || "",
|
||||
serverId: application?.serverId ?? undefined,
|
||||
});
|
||||
|
||||
setValidationStates((prev) => ({
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
findPreviewDeploymentById,
|
||||
findServerById,
|
||||
generateTraefikMeDomain,
|
||||
getServerIpCandidates,
|
||||
getWebServerSettings,
|
||||
manageDomain,
|
||||
removeDomain,
|
||||
@ -248,10 +249,21 @@ export const domainRouter = createTRPCRouter({
|
||||
.input(
|
||||
z.object({
|
||||
domain: z.string(),
|
||||
serverIp: z.string().optional(),
|
||||
serverId: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
return validateDomain(input.domain, input.serverIp);
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
if (input.serverId) {
|
||||
const server = await findServerById(input.serverId);
|
||||
if (server.organizationId !== ctx.session.activeOrganizationId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to access this server",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const expectedIps = await getServerIpCandidates(input.serverId);
|
||||
return validateDomain(input.domain, expectedIps);
|
||||
}),
|
||||
});
|
||||
|
||||
@ -3,7 +3,9 @@ import { promisify } from "node:util";
|
||||
import { db } from "@dokploy/server/db";
|
||||
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
||||
import { generateRandomDomain } from "@dokploy/server/templates";
|
||||
import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync";
|
||||
import { manageDomain } from "@dokploy/server/utils/traefik/domain";
|
||||
import { getPublicIpWithFallback } from "@dokploy/server/wss/utils";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import type { z } from "zod";
|
||||
@ -154,7 +156,7 @@ const resolveDns = promisify(dns.resolve4);
|
||||
|
||||
export const validateDomain = async (
|
||||
domain: string,
|
||||
expectedIp?: string,
|
||||
expectedIps?: string[],
|
||||
): Promise<{
|
||||
isValid: boolean;
|
||||
resolvedIp?: string;
|
||||
@ -186,13 +188,13 @@ export const validateDomain = async (
|
||||
};
|
||||
}
|
||||
|
||||
// If we have an expected IP, validate against it
|
||||
if (expectedIp) {
|
||||
if (expectedIps && expectedIps.length > 0) {
|
||||
const isValid = resolvedIps.some((ip) => expectedIps.includes(ip));
|
||||
return {
|
||||
isValid: resolvedIps.includes(expectedIp),
|
||||
isValid,
|
||||
resolvedIp: resolvedIps.join(", "),
|
||||
error: !resolvedIps.includes(expectedIp)
|
||||
? `Domain resolves to ${resolvedIps.join(", ")} but should point to ${expectedIp}`
|
||||
error: !isValid
|
||||
? `Domain resolves to ${resolvedIps.join(", ")} but should point to ${expectedIps.join(" or ")}`
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
@ -210,3 +212,47 @@ export const validateDomain = async (
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const getServerIpCandidates = async (
|
||||
serverId?: string | null,
|
||||
): Promise<string[]> => {
|
||||
const candidates = new Set<string>();
|
||||
|
||||
if (serverId) {
|
||||
const server = await findServerById(serverId);
|
||||
if (server.ipAddress) {
|
||||
candidates.add(server.ipAddress);
|
||||
}
|
||||
|
||||
const publicIp = await withTimeout(
|
||||
execAsyncRemote(
|
||||
serverId,
|
||||
"curl -s -m 5 https://ifconfig.me || curl -s -m 5 https://icanhazip.com",
|
||||
),
|
||||
7000,
|
||||
);
|
||||
const detectedIp = publicIp?.stdout?.trim();
|
||||
if (detectedIp) {
|
||||
candidates.add(detectedIp);
|
||||
}
|
||||
} else {
|
||||
const settings = await getWebServerSettings();
|
||||
if (settings?.serverIp) {
|
||||
candidates.add(settings.serverIp);
|
||||
}
|
||||
|
||||
const publicIp = await withTimeout(getPublicIpWithFallback(), 7000);
|
||||
if (publicIp) {
|
||||
candidates.add(publicIp);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(candidates);
|
||||
};
|
||||
|
||||
const withTimeout = <T>(promise: Promise<T>, ms: number): Promise<T | null> => {
|
||||
return Promise.race([
|
||||
promise,
|
||||
new Promise<null>((resolve) => setTimeout(() => resolve(null), ms)),
|
||||
]).catch(() => null);
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user