mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #4512 from Infisical/fix/address-azure-sql-server-dynamic-secret-integration
fix: address sql server dynamic secret integration
This commit is contained in:
@@ -165,6 +165,7 @@ export const DynamicSecretSqlDBSchema = z.object({
|
|||||||
revocationStatement: z.string().trim(),
|
revocationStatement: z.string().trim(),
|
||||||
renewStatement: z.string().trim().optional(),
|
renewStatement: z.string().trim().optional(),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
|
sslEnabled: z.boolean().optional(),
|
||||||
gatewayId: z.string().nullable().optional()
|
gatewayId: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
|
import RE2 from "re2";
|
||||||
import knex from "knex";
|
import knex from "knex";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -150,12 +151,30 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
return { ...providerInputs, hostIp };
|
return { ...providerInputs, hostIp };
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema> & { hostIp: string }) => {
|
const $getClient = async (
|
||||||
|
providerInputs: z.infer<typeof DynamicSecretSqlDBSchema> & { hostIp: string; originalHost: string }
|
||||||
|
) => {
|
||||||
const ssl = providerInputs.ca
|
const ssl = providerInputs.ca
|
||||||
? { rejectUnauthorized: false, ca: providerInputs.ca, servername: providerInputs.host }
|
? { rejectUnauthorized: false, ca: providerInputs.ca, servername: providerInputs.host }
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const isMsSQLClient = providerInputs.client === SqlProviders.MsSQL;
|
const isMsSQLClient = providerInputs.client === SqlProviders.MsSQL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We route through the gateway by setting connection.host = "localhost".
|
||||||
|
Azure SQL identifies the logical server from the TDS login name when the host
|
||||||
|
isn’t the Azure FQDN. Therefore, when using the gateway, ensure username is
|
||||||
|
"user@<azure-server-name>" so Azure opens the correct logical server.
|
||||||
|
Direct connections to the Azure FQDN usually don’t require this suffix.
|
||||||
|
*/
|
||||||
|
const isAzureSql = isMsSQLClient && new RE2(/\.database\.windows\.net$/i).test(providerInputs.originalHost);
|
||||||
|
const azureServerLabel =
|
||||||
|
isAzureSql && providerInputs.gatewayId ? providerInputs.originalHost?.split(".")[0] : undefined;
|
||||||
|
const effectiveUser =
|
||||||
|
isAzureSql && !providerInputs.username.includes("@") && azureServerLabel
|
||||||
|
? `${providerInputs.username}@${azureServerLabel}`
|
||||||
|
: providerInputs.username;
|
||||||
|
|
||||||
const db = knex({
|
const db = knex({
|
||||||
client: providerInputs.client,
|
client: providerInputs.client,
|
||||||
connection: {
|
connection: {
|
||||||
@@ -165,7 +184,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
providerInputs.client === SqlProviders.Postgres && !providerInputs.gatewayId
|
providerInputs.client === SqlProviders.Postgres && !providerInputs.gatewayId
|
||||||
? providerInputs.hostIp
|
? providerInputs.hostIp
|
||||||
: providerInputs.host,
|
: providerInputs.host,
|
||||||
user: providerInputs.username,
|
user: effectiveUser,
|
||||||
password: providerInputs.password,
|
password: providerInputs.password,
|
||||||
ssl,
|
ssl,
|
||||||
// @ts-expect-error this is because of knexjs type signature issue. This is directly passed to driver
|
// @ts-expect-error this is because of knexjs type signature issue. This is directly passed to driver
|
||||||
@@ -173,6 +192,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
// https://github.com/tediousjs/tedious/blob/ebb023ed90969a7ec0e4b036533ad52739d921f7/test/config.ci.ts#L19
|
// https://github.com/tediousjs/tedious/blob/ebb023ed90969a7ec0e4b036533ad52739d921f7/test/config.ci.ts#L19
|
||||||
options: isMsSQLClient
|
options: isMsSQLClient
|
||||||
? {
|
? {
|
||||||
|
...(providerInputs.sslEnabled !== undefined ? { encrypt: providerInputs.sslEnabled } : {}),
|
||||||
trustServerCertificate: !providerInputs.ca,
|
trustServerCertificate: !providerInputs.ca,
|
||||||
cryptoCredentialsDetails: providerInputs.ca ? { ca: providerInputs.ca } : {}
|
cryptoCredentialsDetails: providerInputs.ca ? { ca: providerInputs.ca } : {}
|
||||||
}
|
}
|
||||||
@@ -215,7 +235,13 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
let isConnected = false;
|
let isConnected = false;
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host, hostIp: providerInputs.hostIp });
|
const db = await $getClient({
|
||||||
|
...providerInputs,
|
||||||
|
port,
|
||||||
|
host,
|
||||||
|
hostIp: providerInputs.hostIp,
|
||||||
|
originalHost: providerInputs.host
|
||||||
|
});
|
||||||
// oracle needs from keyword
|
// oracle needs from keyword
|
||||||
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
|
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
|
||||||
|
|
||||||
@@ -256,7 +282,12 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
|
|
||||||
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host });
|
const db = await $getClient({
|
||||||
|
...providerInputs,
|
||||||
|
port,
|
||||||
|
host,
|
||||||
|
originalHost: providerInputs.host
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
|
|
||||||
@@ -299,7 +330,12 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
const username = entityId;
|
const username = entityId;
|
||||||
const { database } = providerInputs;
|
const { database } = providerInputs;
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host });
|
const db = await $getClient({
|
||||||
|
...providerInputs,
|
||||||
|
port,
|
||||||
|
host,
|
||||||
|
originalHost: providerInputs.host
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username, database });
|
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username, database });
|
||||||
const queries = revokeStatement.toString().split(";").filter(Boolean);
|
const queries = revokeStatement.toString().split(";").filter(Boolean);
|
||||||
@@ -334,7 +370,12 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
if (!providerInputs.renewStatement) return { entityId };
|
if (!providerInputs.renewStatement) return { entityId };
|
||||||
|
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host });
|
const db = await $getClient({
|
||||||
|
...providerInputs,
|
||||||
|
port,
|
||||||
|
host,
|
||||||
|
originalHost: providerInputs.host
|
||||||
|
});
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
const { database } = providerInputs;
|
const { database } = providerInputs;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
SecretInput,
|
SecretInput,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
|
Switch,
|
||||||
TextArea,
|
TextArea,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
@@ -66,6 +67,7 @@ const formSchema = z.object({
|
|||||||
creationStatement: z.string().min(1),
|
creationStatement: z.string().min(1),
|
||||||
revocationStatement: z.string().min(1),
|
revocationStatement: z.string().min(1),
|
||||||
renewStatement: z.string().optional(),
|
renewStatement: z.string().optional(),
|
||||||
|
sslEnabled: z.boolean().optional(),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
gatewayId: z.string().optional()
|
gatewayId: z.string().optional()
|
||||||
}),
|
}),
|
||||||
@@ -200,6 +202,7 @@ export const SqlDatabaseInputForm = ({
|
|||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
const selectedClient = watch("provider.client");
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({
|
const handleCreateDynamicSecret = async ({
|
||||||
name,
|
name,
|
||||||
@@ -458,13 +461,34 @@ export const SqlDatabaseInputForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
{selectedClient === SqlProviders.MsSQL && (
|
||||||
|
<div className="mb-2 mt-2">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="provider.sslEnabled"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl isError={Boolean(error?.message)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
|
||||||
|
id="sql-ds-ssl-enabled"
|
||||||
|
thumbClassName="bg-mineshaft-800"
|
||||||
|
isChecked={value}
|
||||||
|
onCheckedChange={onChange}
|
||||||
|
>
|
||||||
|
Encrypt Connection (SSL)
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="provider.ca"
|
name="provider.ca"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
isOptional
|
isOptional
|
||||||
label="CA(SSL)"
|
label="CA (SSL)"
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
SecretInput,
|
SecretInput,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
|
Switch,
|
||||||
TextArea,
|
TextArea,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
@@ -63,6 +64,7 @@ const formSchema = z.object({
|
|||||||
creationStatement: z.string().min(1),
|
creationStatement: z.string().min(1),
|
||||||
revocationStatement: z.string().min(1),
|
revocationStatement: z.string().min(1),
|
||||||
renewStatement: z.string().optional(),
|
renewStatement: z.string().optional(),
|
||||||
|
sslEnabled: z.boolean().optional(),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
gatewayId: z.string().optional().nullable()
|
gatewayId: z.string().optional().nullable()
|
||||||
})
|
})
|
||||||
@@ -151,6 +153,7 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
const selectedClient = watch("inputs.client");
|
||||||
|
|
||||||
const updateDynamicSecret = useUpdateDynamicSecret();
|
const updateDynamicSecret = useUpdateDynamicSecret();
|
||||||
const selectedGatewayId = watch("inputs.gatewayId");
|
const selectedGatewayId = watch("inputs.gatewayId");
|
||||||
@@ -407,13 +410,34 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
{selectedClient === SqlProviders.MsSQL && (
|
||||||
|
<div className="mb-2 mt-2">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="inputs.sslEnabled"
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl isError={Boolean(error?.message)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
|
||||||
|
id="sql-ds-ssl-enabled"
|
||||||
|
thumbClassName="bg-mineshaft-800"
|
||||||
|
isChecked={Boolean(value)}
|
||||||
|
onCheckedChange={onChange}
|
||||||
|
>
|
||||||
|
Encrypt Connection (SSL)
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="inputs.ca"
|
name="inputs.ca"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
isOptional
|
isOptional
|
||||||
label="CA(SSL)"
|
label="CA (SSL)"
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user