Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
92bd782
Add PowerDNS Connection Service
Apr 1, 2026
fa3018b
Implement Connection Router
Apr 1, 2026
f6a6710
Implement PowerDNS API Calls
Apr 1, 2026
796bb7f
Add PowerDNS integration image
Apr 1, 2026
3e068ff
Add IP address validation to PowerDNS connection credentials
Apr 1, 2026
3dddfe0
Add IP address validation to PowerDNS record operations
Apr 1, 2026
b1a56f6
Remove unused parameter 'value' from PowerDNS TXT record deletion fun…
Apr 1, 2026
38ac731
Add validation for PowerDNS app connection in AcmeCertificateAuthorit…
Apr 1, 2026
cbf8679
Add PowerDNS integration documentation and connection setup instructions
Apr 1, 2026
68c03a7
Remove unused 'value' parameter from PowerDNS TXT record deletion log…
Apr 1, 2026
4c69d91
Increase DNS propagation maximum retries from 5 to 30
Apr 1, 2026
74850e4
Add error handling for DNS propagation failure in waitForDnsPropagati…
Apr 1, 2026
0a1490f
Update backend/src/services/certificate-authority/acme/acme-certifica…
JustinBack Apr 2, 2026
a81d241
Remove duplicate delay call in waitForDnsPropagation function
Apr 2, 2026
d36e0c1
Merge branch 'main' into main
JustinBack Apr 7, 2026
c01d4dc
Merge branch 'main' into main
JustinBack Apr 14, 2026
73be340
Fix syntax error in app connection routers
JustinBack Apr 14, 2026
f3173c6
Update app-connection-enums.ts
JustinBack Apr 14, 2026
bf8937b
Update app-connection-fns.ts
JustinBack Apr 14, 2026
86b34a0
Fix formatting of PowerDNS in app connection options
JustinBack Apr 14, 2026
63f57b9
Fix formatting for PowerDNS and NetScaler entries
JustinBack Apr 14, 2026
f097d8e
Update app-connection-service.ts
JustinBack Apr 14, 2026
adfa4ef
Update appConnections.ts
JustinBack Apr 14, 2026
c6f7331
Fix formatting of TPowerDNSConnection type
JustinBack Apr 14, 2026
f00b472
Update enums.ts
JustinBack Apr 14, 2026
af523d4
Update app-connection-router.ts
JustinBack Apr 14, 2026
d53f81a
Fix formatting in app-connection-routers index
JustinBack Apr 14, 2026
b8c6939
Merge branch 'Infisical:main' into main
JustinBack Apr 14, 2026
e534326
Merge branch 'Infisical:main' into main
JustinBack Apr 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ import {
DNSMadeEasyConnectionListItemSchema,
SanitizedDNSMadeEasyConnectionSchema
} from "@app/services/app-connection/dns-made-easy/dns-made-easy-connection-schema";
import {
PowerDNSConnectionListItemSchema,
SanitizedPowerDNSConnectionSchema
} from "@app/services/app-connection/powerdns/powerdns-connection-schema";
import {
ExternalInfisicalConnectionListItemSchema,
SanitizedExternalInfisicalConnectionSchema
Expand Down Expand Up @@ -228,6 +232,7 @@ const SanitizedAppConnectionSchema = z.union([
...SanitizedAzureEntraIdConnectionSchema.options,
...SanitizedVenafiConnectionSchema.options,
...SanitizedExternalInfisicalConnectionSchema.options,
...SanitizedPowerDNSConnectionSchema.options,
...SanitizedNetScalerConnectionSchema.options
]);

Expand Down Expand Up @@ -286,6 +291,7 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
AzureEntraIdConnectionListItemSchema,
VenafiConnectionListItemSchema,
ExternalInfisicalConnectionListItemSchema,
PowerDNSConnectionListItemSchema,
NetScalerConnectionListItemSchema,
AnthropicConnectionListItemSchema
]);
Expand Down
2 changes: 2 additions & 0 deletions backend/src/server/routes/v1/app-connection-routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { registerDatabricksConnectionRouter } from "./databricks-connection-rout
import { registerDbtConnectionRouter } from "./dbt-connection-router";
import { registerDigitalOceanConnectionRouter } from "./digital-ocean-connection-router";
import { registerDNSMadeEasyConnectionRouter } from "./dns-made-easy-connection-router";
import { registerPowerDNSConnectionRouter } from "./powerdns-connection-router";
import { registerExternalInfisicalConnectionRouter } from "./external-infisical-connection-router";
import { registerFlyioConnectionRouter } from "./flyio-connection-router";
import { registerGcpConnectionRouter } from "./gcp-connection-router";
Expand Down Expand Up @@ -115,6 +116,7 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
[AppConnection.AzureEntraId]: registerAzureEntraIdConnectionRouter,
[AppConnection.Venafi]: registerVenafiConnectionRouter,
[AppConnection.ExternalInfisical]: registerExternalInfisicalConnectionRouter,
[AppConnection.PowerDNS]: registerPowerDNSConnectionRouter,
[AppConnection.NetScaler]: registerNetScalerConnectionRouter,
[AppConnection.Anthropic]: registerAnthropicConnectionRouter
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
CreatePowerDNSConnectionSchema,
SanitizedPowerDNSConnectionSchema,
UpdatePowerDNSConnectionSchema
} from "@app/services/app-connection/powerdns/powerdns-connection-schema";

import { registerAppConnectionEndpoints } from "./app-connection-endpoints";

export const registerPowerDNSConnectionRouter = async (server: FastifyZodProvider) => {
registerAppConnectionEndpoints({
app: AppConnection.PowerDNS,
server,
sanitizedResponseSchema: SanitizedPowerDNSConnectionSchema,
createSchema: CreatePowerDNSConnectionSchema,
updateSchema: UpdatePowerDNSConnectionSchema
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export enum AppConnection {
AzureEntraId = "azure-entra-id",
Venafi = "venafi",
ExternalInfisical = "external-infisical",
PowerDNS = "powerdns",
NetScaler = "netscaler",
Anthropic = "anthropic"
}
Expand Down
12 changes: 11 additions & 1 deletion backend/src/services/app-connection/app-connection-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ import {
getDNSMadeEasyConnectionListItem,
validateDNSMadeEasyConnectionCredentials
} from "./dns-made-easy/dns-made-easy-connection-fns";
import { PowerDNSConnectionMethod } from "./powerdns/powerdns-connection-enum";
import {
getPowerDNSConnectionListItem,
validatePowerDNSConnectionCredentials
} from "./powerdns/powerdns-connection-fns";
import {
ExternalInfisicalConnectionMethod,
getExternalInfisicalConnectionListItem,
Expand Down Expand Up @@ -232,6 +237,7 @@ const PKI_APP_CONNECTIONS = [
AppConnection.DNSMadeEasy,
AppConnection.AzureDNS,
AppConnection.Venafi,
AppConnection.PowerDNS,
AppConnection.NetScaler
];

Expand Down Expand Up @@ -292,6 +298,7 @@ export const listAppConnectionOptions = (projectType?: ProjectType) => {
getAzureEntraIdConnectionListItem(),
getVenafiConnectionListItem(),
getExternalInfisicalConnectionListItem(),
getPowerDNSConnectionListItem(),
getNetScalerConnectionListItem()
]
.filter((option) => {
Expand Down Expand Up @@ -443,7 +450,8 @@ export const validateAppConnectionCredentials = async (
validateExternalInfisicalConnectionCredentials(
config as TExternalInfisicalConnectionConfig,
deps.identityUaDAL
)) as TAppConnectionCredentialsValidator
)) as TAppConnectionCredentialsValidator,
[AppConnection.PowerDNS]: validatePowerDNSConnectionCredentials as TAppConnectionCredentialsValidator
};

return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection, gatewayService, gatewayV2Service);
Expand Down Expand Up @@ -538,6 +546,8 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
return "Basic Auth";
case ExternalInfisicalConnectionMethod.MachineIdentityUniversalAuth:
return "Machine Identity - Universal Auth";
case PowerDNSConnectionMethod.APIKey:
return "API Key";
default:
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Unhandled App Connection Method: ${method}`);
Expand Down
2 changes: 2 additions & 0 deletions backend/src/services/app-connection/app-connection-maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
[AppConnection.AzureEntraId]: "Azure Entra ID",
[AppConnection.Venafi]: "Venafi TLS Protect Cloud",
[AppConnection.ExternalInfisical]: "Infisical",
[AppConnection.PowerDNS]: "PowerDNS",
[AppConnection.NetScaler]: "NetScaler",
[AppConnection.Anthropic]: "Anthropic"
};
Expand Down Expand Up @@ -114,6 +115,7 @@ export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanTyp
[AppConnection.AzureEntraId]: AppConnectionPlanType.Regular,
[AppConnection.Venafi]: AppConnectionPlanType.Regular,
[AppConnection.ExternalInfisical]: AppConnectionPlanType.Regular,
[AppConnection.PowerDNS]: AppConnectionPlanType.Regular,
[AppConnection.NetScaler]: AppConnectionPlanType.Regular,
[AppConnection.Anthropic]: AppConnectionPlanType.Regular
};
4 changes: 4 additions & 0 deletions backend/src/services/app-connection/app-connection-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ import { ValidateDigitalOceanConnectionCredentialsSchema } from "./digital-ocean
import { digitalOceanAppPlatformConnectionService } from "./digital-ocean/digital-ocean-connection-service";
import { ValidateDNSMadeEasyConnectionCredentialsSchema } from "./dns-made-easy/dns-made-easy-connection-schema";
import { dnsMadeEasyConnectionService } from "./dns-made-easy/dns-made-easy-connection-service";
import { ValidatePowerDNSConnectionCredentialsSchema } from "./powerdns/powerdns-connection-schema";
import { powerDnsConnectionService } from "./powerdns/powerdns-connection-service";
import { ValidateExternalInfisicalConnectionCredentialsSchema } from "./external-infisical";
import { externalInfisicalConnectionService } from "./external-infisical/external-infisical-connection-service";
import { ValidateFlyioConnectionCredentialsSchema } from "./flyio";
Expand Down Expand Up @@ -218,6 +220,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
[AppConnection.AzureEntraId]: ValidateAzureEntraIdConnectionCredentialsSchema,
[AppConnection.Venafi]: ValidateVenafiConnectionCredentialsSchema,
[AppConnection.ExternalInfisical]: ValidateExternalInfisicalConnectionCredentialsSchema,
[AppConnection.PowerDNS]: ValidatePowerDNSConnectionCredentialsSchema,
[AppConnection.NetScaler]: ValidateNetScalerConnectionCredentialsSchema,
[AppConnection.Anthropic]: ValidateAnthropicConnectionCredentialsSchema
};
Expand Down Expand Up @@ -1081,6 +1084,7 @@ export const appConnectionServiceFactory = ({
venafi: venafiConnectionService(connectAppConnectionById),
azureAdcs: azureAdcsConnectionService(connectAppConnectionById),
dnsMadeEasy: dnsMadeEasyConnectionService(connectAppConnectionById),
powerDns: powerDnsConnectionService(),
azureDns: azureDnsConnectionService(connectAppConnectionById),
zabbix: zabbixConnectionService(connectAppConnectionById),
railway: railwayConnectionService(connectAppConnectionById),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum PowerDNSConnectionMethod {
APIKey = "api-key"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { AxiosError } from "axios";

import { request } from "@app/lib/config/request";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";


import { PowerDNSConnectionMethod } from "./powerdns-connection-enum";
import { TPowerDNSConnectionConfig } from "./powerdns-connection-types";

export const getPowerDNSConnectionListItem = () => {
return {
name: "PowerDNS" as const,
app: AppConnection.PowerDNS as const,
methods: Object.values(PowerDNSConnectionMethod) as [PowerDNSConnectionMethod.APIKey]
};
};

export const validatePowerDNSConnectionCredentials = async (config: TPowerDNSConnectionConfig) => {
if (config.method !== PowerDNSConnectionMethod.APIKey) {
throw new BadRequestError({ message: "Unsupported PowerDNS connection method" });
}

const { apiKey, baseUrl } = config.credentials;

try {

await blockLocalAndPrivateIpAddresses(baseUrl);


// Use /servers/localhost/zones as the validation endpoint — it is supported by both
// direct PowerDNS Server and PowerDNS-Admin proxy configurations.
const resp = await request.get(`${baseUrl}/servers/localhost/zones`, {
headers: {
"x-api-key": apiKey,
Accept: "application/json"
}
});
Comment thread
JustinBack marked this conversation as resolved.

if (resp.status !== 200) {
throw new BadRequestError({
message: "Unable to validate connection: Invalid API credentials provided."
});
}
} catch (error: unknown) {
if (error instanceof BadRequestError) {
throw error;
}
if (error instanceof AxiosError) {
throw new BadRequestError({
message: `Failed to validate credentials: ${
(error.response?.data as { error?: string })?.error || error.message || "Unknown error"
}`
});
}
logger.error(error, "Error validating PowerDNS connection credentials");
throw new BadRequestError({
message: "Unable to validate connection: verify credentials and base URL"
});
}

return config.credentials;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import z from "zod";

import { AppConnections } from "@app/lib/api-docs";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
BaseAppConnectionSchema,
GenericCreateAppConnectionFieldsSchema,
GenericUpdateAppConnectionFieldsSchema
} from "@app/services/app-connection/app-connection-schemas";

import { APP_CONNECTION_NAME_MAP } from "../app-connection-maps";
import { PowerDNSConnectionMethod } from "./powerdns-connection-enum";

export const PowerDNSConnectionApiKeyCredentialsSchema = z.object({
apiKey: z.string().trim().min(1, "API key required").max(256, "API key cannot exceed 256 characters"),
baseUrl: z
.string()
.trim()
.url("Base URL must be a valid URL")
.min(1, "Base URL required")
.max(1024, "Base URL cannot exceed 1024 characters")
});

const BasePowerDNSConnectionSchema = BaseAppConnectionSchema.extend({
app: z.literal(AppConnection.PowerDNS)
});

export const PowerDNSConnectionSchema = BasePowerDNSConnectionSchema.extend({
method: z.literal(PowerDNSConnectionMethod.APIKey),
credentials: PowerDNSConnectionApiKeyCredentialsSchema
});

export const SanitizedPowerDNSConnectionSchema = z.discriminatedUnion("method", [
BasePowerDNSConnectionSchema.extend({
method: z.literal(PowerDNSConnectionMethod.APIKey),
credentials: PowerDNSConnectionApiKeyCredentialsSchema.pick({ baseUrl: true })
}).describe(JSON.stringify({ title: `${APP_CONNECTION_NAME_MAP[AppConnection.PowerDNS]} (API Key)` }))
]);

export const ValidatePowerDNSConnectionCredentialsSchema = z.discriminatedUnion("method", [
z.object({
method: z
.literal(PowerDNSConnectionMethod.APIKey)
.describe(AppConnections.CREATE(AppConnection.PowerDNS).method),
credentials: PowerDNSConnectionApiKeyCredentialsSchema.describe(
AppConnections.CREATE(AppConnection.PowerDNS).credentials
)
})
]);

export const CreatePowerDNSConnectionSchema = ValidatePowerDNSConnectionCredentialsSchema.and(
GenericCreateAppConnectionFieldsSchema(AppConnection.PowerDNS)
);

export const UpdatePowerDNSConnectionSchema = z
.object({
credentials: PowerDNSConnectionApiKeyCredentialsSchema.optional().describe(
AppConnections.UPDATE(AppConnection.PowerDNS).credentials
)
})
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.PowerDNS));

export const PowerDNSConnectionListItemSchema = z
.object({
name: z.literal("PowerDNS"),
app: z.literal(AppConnection.PowerDNS),
methods: z.nativeEnum(PowerDNSConnectionMethod).array()
})
.describe(JSON.stringify({ title: APP_CONNECTION_NAME_MAP[AppConnection.PowerDNS] }));
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const powerDnsConnectionService = () => {
return {};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import z from "zod";

import { DiscriminativePick } from "@app/lib/types";

import { AppConnection } from "../app-connection-enums";
import {
CreatePowerDNSConnectionSchema,
PowerDNSConnectionSchema,
ValidatePowerDNSConnectionCredentialsSchema
} from "./powerdns-connection-schema";

export type TPowerDNSConnection = z.infer<typeof PowerDNSConnectionSchema>;

export type TPowerDNSConnectionInput = z.infer<typeof CreatePowerDNSConnectionSchema> & {
app: AppConnection.PowerDNS;
};

export type TValidatePowerDNSConnectionCredentialsSchema = typeof ValidatePowerDNSConnectionCredentialsSchema;

export type TPowerDNSConnectionConfig = DiscriminativePick<
TPowerDNSConnectionInput,
"method" | "app" | "credentials"
> & {
orgId: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export enum AcmeDnsProvider {
Route53 = "route53",
Cloudflare = "cloudflare",
DNSMadeEasy = "dns-made-easy",
AzureDNS = "azure-dns"
AzureDNS = "azure-dns",
PowerDNS = "powerdns"
}
Loading