Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 7 additions & 4 deletions backend/src/ee/routes/v1/pam-session-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { PolicyRulesResponseSchema } from "@app/ee/services/pam-account-policy";
import { KubernetesSessionCredentialsSchema } from "@app/ee/services/pam-resource/kubernetes/kubernetes-resource-schemas";
import { MongoDBSessionCredentialsSchema } from "@app/ee/services/pam-resource/mongodb/mongodb-resource-schemas";
import { MsSQLSessionCredentialsSchema } from "@app/ee/services/pam-resource/mssql/mssql-resource-schemas";
import { MySQLSessionCredentialsSchema } from "@app/ee/services/pam-resource/mysql/mysql-resource-schemas";
import { OracleSessionCredentialsSchema } from "@app/ee/services/pam-resource/oracle/oracle-resource-schemas";
import { PostgresSessionCredentialsSchema } from "@app/ee/services/pam-resource/postgres/postgres-resource-schemas";
Expand All @@ -26,15 +27,17 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";

// Schemas with distinguishing fields must precede simpler ones — Zod strips unrecognized keys on first match.
const SessionCredentialsSchema = z.union([
MsSQLSessionCredentialsSchema,
SSHSessionCredentialsSchema,
WindowsSessionCredentialsSchema,
KubernetesSessionCredentialsSchema,
MongoDBSessionCredentialsSchema,
PostgresSessionCredentialsSchema,
MySQLSessionCredentialsSchema,
OracleSessionCredentialsSchema,
MongoDBSessionCredentialsSchema,
KubernetesSessionCredentialsSchema,
RedisSessionCredentialsSchema,
WindowsSessionCredentialsSchema
RedisSessionCredentialsSchema
]);

export const registerPamSessionRouter = async (server: FastifyZodProvider) => {
Expand Down
16 changes: 12 additions & 4 deletions backend/src/ee/services/pam-account/pam-account-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { PamDomainType } from "../pam-domain/pam-domain-enums";
import { PAM_DOMAIN_FACTORY_MAP } from "../pam-domain/pam-domain-factory";
import { TPamProjectRecordingConfigDALFactory } from "../pam-project-recording-config/pam-project-recording-config-dal";
import { TPamProjectRecordingConfigServiceFactory } from "../pam-project-recording-config/pam-project-recording-config-service";
import { MsSqlAuthMethod } from "../pam-resource/mssql/mssql-resource-enums";
import { TPamResourceDALFactory } from "../pam-resource/pam-resource-dal";
import { PamResource } from "../pam-resource/pam-resource-enums";
import { TPamResourceRotationRulesDALFactory } from "../pam-resource/pam-resource-rotation-rules-dal";
Expand Down Expand Up @@ -1499,11 +1500,18 @@ export const pamAccountServiceFactory = ({
};
}

const credentials: Record<string, unknown> = {
...decryptedResource.connectionDetails,
...decryptedAccount.credentials
};

// Old MSSQL accounts pre-date the authMethod field — default to sql-login
if (decryptedResource.resourceType === PamResource.MsSQL && !("authMethod" in credentials)) {
credentials.authMethod = MsSqlAuthMethod.SqlLogin;
}

return {
credentials: {
...decryptedResource.connectionDetails,
...decryptedAccount.credentials
},
credentials,
policyRules,
projectId: project.id,
account,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum MsSqlAuthMethod {
SqlLogin = "sql-login",
Ntlm = "ntlm"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,29 @@ import {
BaseUpdateGatewayPamResourceSchema,
BaseUpdatePamAccountSchema
} from "../pam-resource-schemas";
import {
BaseSqlAccountCredentialsSchema,
BaseSqlResourceConnectionDetailsSchema
} from "../shared/sql/sql-resource-schemas";
import { BaseSqlResourceConnectionDetailsSchema } from "../shared/sql/sql-resource-schemas";
import { MsSqlAuthMethod } from "./mssql-resource-enums";

export { MsSqlAuthMethod };

// Resources
export const MsSQLResourceConnectionDetailsSchema = BaseSqlResourceConnectionDetailsSchema;
export const MsSQLAccountCredentialsSchema = BaseSqlAccountCredentialsSchema;

const MsSQLSqlLoginCredentialsSchema = z.object({
authMethod: z.literal(MsSqlAuthMethod.SqlLogin).default(MsSqlAuthMethod.SqlLogin),
username: z.string().trim().min(1).max(63),
password: z.string().trim().min(1).max(256)
});

const MsSQLNtlmCredentialsSchema = z.object({
authMethod: z.literal(MsSqlAuthMethod.Ntlm),
username: z.string().trim().min(1).max(63),
Comment thread
saifsmailbox98 marked this conversation as resolved.
password: z.string().trim().min(1).max(256),
domain: z.string().trim().min(1, "Domain is required for NTLM authentication").max(255)
});

// z.union so old accounts without authMethod fall through to the sql-login .default()
export const MsSQLAccountCredentialsSchema = z.union([MsSQLNtlmCredentialsSchema, MsSQLSqlLoginCredentialsSchema]);

const BaseMsSQLResourceSchema = BasePamResourceSchema.extend({ resourceType: z.literal(PamResource.MsSQL) });

Expand All @@ -26,13 +41,14 @@ export const MsSQLResourceSchema = BaseMsSQLResourceSchema.extend({
rotationAccountCredentials: MsSQLAccountCredentialsSchema.nullable().optional()
});

const SanitizedMsSQLCredentialsSchema = z.union([
z.object({ authMethod: z.literal(MsSqlAuthMethod.Ntlm), username: z.string(), domain: z.string() }),
z.object({ authMethod: z.literal(MsSqlAuthMethod.SqlLogin).default(MsSqlAuthMethod.SqlLogin), username: z.string() })
]);

export const SanitizedMsSQLResourceSchema = BaseMsSQLResourceSchema.extend({
connectionDetails: MsSQLResourceConnectionDetailsSchema,
rotationAccountCredentials: MsSQLAccountCredentialsSchema.pick({
username: true
})
.nullable()
.optional()
rotationAccountCredentials: SanitizedMsSQLCredentialsSchema.nullable().optional()
});

export const MsSQLResourceListItemSchema = z.object({
Expand Down Expand Up @@ -65,10 +81,17 @@ export const UpdateMsSQLAccountSchema = BaseUpdatePamAccountSchema.extend({

export const SanitizedMsSQLAccountWithResourceSchema = BasePamAccountSchemaWithResource.extend({
parentType: z.literal(PamResource.MsSQL),
credentials: MsSQLAccountCredentialsSchema.pick({
username: true
})
credentials: SanitizedMsSQLCredentialsSchema
});

// Strict variant (no .default()) — prevents cross-resource false matches in SessionCredentialsSchema
const MsSQLStrictSqlLoginCredentialsSchema = z.object({
authMethod: z.literal(MsSqlAuthMethod.SqlLogin),
username: z.string().trim().min(1).max(63),
password: z.string().trim().min(1).max(256)
});

// Sessions
export const MsSQLSessionCredentialsSchema = MsSQLResourceConnectionDetailsSchema.and(MsSQLAccountCredentialsSchema);
export const MsSQLSessionCredentialsSchema = z.union([
MsSQLResourceConnectionDetailsSchema.and(MsSQLNtlmCredentialsSchema),
MsSQLResourceConnectionDetailsSchema.and(MsSQLStrictSqlLoginCredentialsSchema)
]);
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const makeSqlConnection = (
resourceType: PamResource;
username?: string;
password?: string;
authMethod?: string; // MSSQL-only: "ntlm" triggers Windows auth via Tedious
domain?: string; // MSSQL-only: AD domain for NTLM authentication
}
): SqlResourceConnection => {
const { connectionDetails, resourceType, username, password } = config;
Expand Down Expand Up @@ -191,22 +193,35 @@ const makeSqlConnection = (
encrypt: true,
trustServerCertificate: !sslRejectUnauthorized,
cryptoCredentialsDetails: sslCertificate ? { ca: sslCertificate } : {},
// serverName tells tedious to use this hostname for TLS SNI and certificate validation
// instead of the server/host value used for the TCP connection
serverName: host
}
: { encrypt: false };

const isNtlm = config.authMethod === "ntlm";

if (isNtlm && !config.domain) {
throw new BadRequestError({ message: "Domain is required for NTLM authentication" });
}

const client = knex({
client: "mssql",
connection: {
server: "localhost",
port: proxyPort,
user: actualUsername,
password: actualPassword,
database: connectionDetails.database,
requestTimeout: EXTERNAL_REQUEST_TIMEOUT,
// mssqlOptions is passed to tedious driver
// Knex MSSQL dialect maps these flat fields into Tedious's authentication object
...(isNtlm
? {
type: "ntlm",
userName: actualUsername,
password: actualPassword,
domain: config.domain
}
: {
user: actualUsername,
password: actualPassword
}),
// ref: https://github.com/knex/knex/blob/b6507a7129d2b9fafebf5f831494431e64c6a8a0/lib/dialects/mssql/index.js#L66
options: mssqlOptions
}
Expand Down Expand Up @@ -253,6 +268,8 @@ export const executeWithGateway = async <T>(
gatewayId: string;
username?: string;
password?: string;
authMethod?: string; // MSSQL-only: "ntlm" triggers Windows auth via Tedious
domain?: string; // MSSQL-only: AD domain for NTLM authentication
},
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
operation: (connection: SqlResourceConnection) => Promise<T>
Expand Down Expand Up @@ -333,7 +350,9 @@ export const sqlResourceFactory: TPamResourceFactory<
gatewayId,
resourceType,
username: credentials.username,
password: credentials.password
password: credentials.password,
authMethod: "authMethod" in credentials ? credentials.authMethod : undefined,
domain: "domain" in credentials ? credentials.domain : undefined
},
gatewayV2Service,
async (client) => {
Comment thread
saifsmailbox98 marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ sequenceDiagram

1. **Gateway**: An Infisical Gateway deployed in your network that can reach the MsSQL server. The Gateway handles secure communication between users and your MsSQL instance.

2. **Authentication**: Credentials (username/password) are stored securely in Infisical and used by the Gateway to authenticate with MsSQL on behalf of the user.
2. **Authentication**: Credentials are stored securely in Infisical and used by the Gateway to authenticate with MsSQL on behalf of the user. Both SQL Server Authentication (username/password) and Windows Authentication (NTLM) are supported.

3. **Local Proxy**: The Infisical CLI starts a local proxy on your machine that intercepts MsSQL connections and routes them securely through the Gateway to your MsSQL instance.

Expand All @@ -63,7 +63,7 @@ Infisical tracks:
Before configuring MsSQL access in Infisical PAM, you need:

1. **Infisical Gateway** - A Gateway deployed in your network with access to the MsSQL server
2. **MsSQL Credentials** - Username and password for the MsSQL instance
2. **MsSQL Credentials** - SQL Server credentials (username/password) or Windows domain credentials (domain/username/password) for the MsSQL instance
3. **Infisical CLI** - The Infisical CLI installed on user machines

<Warning>
Expand Down Expand Up @@ -126,12 +126,22 @@ A PAM Account represents a specific set of credentials that users can request ac
An optional description for this account.
</ParamField>

<ParamField path="Authentication Method" type="string" required>
Choose how the account authenticates with SQL Server:
- **SQL Server Authentication** — standard username and password
- **Windows Authentication (NTLM)** — authenticates using Active Directory domain credentials
</ParamField>

<ParamField path="Domain" type="string">
The Active Directory domain name (e.g., `CORP`). Only required when using Windows Authentication (NTLM).
</ParamField>

<ParamField path="Username" type="string" required>
The MsSQL username.
The MsSQL or domain username.
</ParamField>

<ParamField path="Password" type="string" required>
The MsSQL password.
The MsSQL or domain password.
</ParamField>

<ParamField path="Require MFA for Access" type="boolean">
Expand Down
24 changes: 22 additions & 2 deletions frontend/src/hooks/api/pam/types/mssql-resource.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
import { PamResourceType } from "../enums";
import { TBaseSqlConnectionDetails, TBaseSqlCredentials } from "./shared/sql-resource";
import { TBaseSqlConnectionDetails } from "./shared/sql-resource";
import { TBasePamAccount } from "./base-account";
import { TBasePamResource } from "./base-resource";

export enum MsSqlAuthMethod {
SqlLogin = "sql-login",
Ntlm = "ntlm"
}

export type TMsSQLSqlLoginCredentials = {
authMethod: MsSqlAuthMethod.SqlLogin;
username: string;
password: string;
};

export type TMsSQLNtlmCredentials = {
authMethod: MsSqlAuthMethod.Ntlm;
username: string;
password: string;
domain: string;
};

export type TMsSQLCredentials = TMsSQLSqlLoginCredentials | TMsSQLNtlmCredentials;

// Resources
export type TMsSQLResource = TBasePamResource & { resourceType: PamResourceType.MsSQL } & {
connectionDetails: TBaseSqlConnectionDetails;
};

// Accounts
export type TMsSQLAccount = TBasePamAccount & {
credentials: TBaseSqlCredentials;
credentials: TMsSQLCredentials;
};
Loading
Loading