Skip to content

Commit

Permalink
feat: support CAS-based instances
Browse files Browse the repository at this point in the history
  • Loading branch information
jackwotherspoon committed Sep 27, 2024
1 parent e93696a commit faedae5
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/cloud-sql-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export class CloudSQLInstance {
public port = 3307;
public privateKey?: string;
public serverCaCert?: SslCert;
public serverCaMode?: string | null | undefined;
public dnsName?: string | null | undefined;

constructor({
ipType,
Expand Down Expand Up @@ -193,6 +195,8 @@ export class CloudSQLInstance {
const host = selectIpAddress(metadata.ipAddresses, this.ipType);
const privateKey = rsaKeys.privateKey;
const serverCaCert = metadata.serverCaCert;
this.serverCaMode = metadata.serverCaMode;
this.dnsName = metadata.dnsName;

const currentValues = {
ephemeralCert: this.ephemeralCert,
Expand Down
4 changes: 4 additions & 0 deletions src/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ export class Connector {
port,
privateKey,
serverCaCert,
serverCaMode,
dnsName,
} = cloudSqlInstance;

if (
Expand All @@ -223,6 +225,8 @@ export class Connector {
port,
privateKey,
serverCaCert,
serverCaMode,
dnsName,
});
tlsSocket.once('error', async () => {
await cloudSqlInstance.forceRefresh();
Expand Down
19 changes: 17 additions & 2 deletions src/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,19 @@ interface SocketOptions {
instanceInfo: InstanceConnectionInfo;
privateKey: string;
serverCaCert: SslCert;
serverCaMode?: string | null | undefined;
dnsName?: string | null | undefined;
}

export function validateCertificate(instanceInfo: InstanceConnectionInfo) {
export function validateCertificate(
instanceInfo: InstanceConnectionInfo,
serverCaMode?: string | null | undefined,
dnsName?: string | null | undefined
) {
return (hostname: string, cert: tls.PeerCertificate): Error | undefined => {
if (serverCaMode == 'GOOGLE_MANAGED_CAS_CA') {
return tls.checkServerIdentity(dnsName, cert);
}
if (!cert || !cert.subject) {
return new CloudSQLConnectorError({
message: 'No certificate to verify',
Expand All @@ -54,6 +63,8 @@ export function getSocket({
instanceInfo,
privateKey,
serverCaCert,
serverCaMode,
dnsName,
}: SocketOptions): tls.TLSSocket {
const socketOpts = {
host,
Expand All @@ -64,7 +75,11 @@ export function getSocket({
key: privateKey,
minVersion: 'TLSv1.3',
}),
checkServerIdentity: validateCertificate(instanceInfo),
checkServerIdentity: validateCertificate(
instanceInfo,
serverCaMode,
dnsName
),
};
const tlsSocket = tls.connect(socketOpts);
tlsSocket.setKeepAlive(true, DEFAULT_KEEP_ALIVE_DELAY_MS);
Expand Down
4 changes: 4 additions & 0 deletions src/sqladmin-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {AuthTypes} from './auth-types';
export interface InstanceMetadata {
ipAddresses: IpAddresses;
serverCaCert: SslCert;
serverCaMode?: string | null | undefined;
dnsName?: string | null | undefined;
}

interface RequestBody {
Expand Down Expand Up @@ -216,6 +218,8 @@ export class SQLAdminFetcher {
cert: serverCaCert.cert,
expirationTime: serverCaCert.expirationTime,
},
serverCaMode: res.data.serverCaMode,
dnsName: res.data.dnsName,
};
}

Expand Down
26 changes: 26 additions & 0 deletions system-test/pg-connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,29 @@ t.test('open IAM connection and retrieves standard pg tables', async t => {
await client.end();
connector.close();
});

t.test(
'open connection to CAS-based CA instance and retrieves standard pg tables',
async t => {
const connector = new Connector();
const clientOpts = await connector.getOptions({
instanceConnectionName: String(process.env.POSTGRES_CAS_CONNECTION_NAME),
});
const client = new Client({
...clientOpts,
user: String(process.env.POSTGRES_USER),
password: String(process.env.POSTGRES_CAS_PASS),
database: String(process.env.POSTGRES_DB),
});
client.connect();

const {
rows: [result],
} = await client.query('SELECT NOW();');
const returnedDate = result['now'];
t.ok(returnedDate.getTime(), 'should have valid returned date object');

await client.end();
connector.close();
}
);
3 changes: 3 additions & 0 deletions test/sqladmin-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ const mockSQLAdminGetInstanceMetadata = (
pscEnabled: true,
region: regionId,
serverCaCert: serverCaCertResponse(instanceId),
serverCaMode: 'GOOGLE_MANAGED_INTERNAL_CA',
...overrides,
},
});
Expand Down Expand Up @@ -189,6 +190,8 @@ t.test('getInstanceMetadata', async t => {
cert: '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----',
expirationTime: '2033-01-06T10:00:00.232Z',
},
serverCaMode: 'GOOGLE_MANAGED_INTERNAL_CA',
dnsName: 'abcde.12345.us-central1.sql.goog',
},
'should return expected instance metadata object'
);
Expand Down

0 comments on commit faedae5

Please sign in to comment.