11import { AbstractStartedContainer , GenericContainer , StartedTestContainer , Wait } from "testcontainers" ;
22
33const POSTGRES_PORT = 5432 ;
4+ const POSTGRES_SSL_PATH = "/tmp/testcontainers-node/postgres" ;
5+ const POSTGRES_SSL_CA_CERT_PATH = `${ POSTGRES_SSL_PATH } /ca_cert.pem` ;
6+ const POSTGRES_SSL_CERT_PATH = `${ POSTGRES_SSL_PATH } /server.crt` ;
7+ const POSTGRES_SSL_KEY_PATH = `${ POSTGRES_SSL_PATH } /server.key` ;
8+ const POSTGRES_SSL_ENTRYPOINT_PATH = "/usr/local/bin/docker-entrypoint-ssl.sh" ;
9+ const POSTGRES_SSL_ENTRYPOINT_CONTENT = `#!/bin/sh
10+ set -eu
11+
12+ puid="$(id -u postgres)"
13+ pgid="$(id -g postgres)"
14+
15+ if [ -z "$puid" ] || [ -z "$pgid" ]; then
16+ echo "Unable to determine postgres uid/gid for SSL key material ownership"
17+ exit 1
18+ fi
19+
20+ for file in "${ POSTGRES_SSL_CA_CERT_PATH } " "${ POSTGRES_SSL_CERT_PATH } " "${ POSTGRES_SSL_KEY_PATH } "; do
21+ if [ -f "$file" ]; then
22+ chown "$puid:$pgid" "$file"
23+ chmod 600 "$file"
24+ fi
25+ done
26+
27+ exec /usr/local/bin/docker-entrypoint.sh "$@"
28+ ` ;
29+
30+ type PostgreSqlSslConfig = {
31+ caCertPath ?: string ;
32+ } ;
433
534export class PostgreSqlContainer extends GenericContainer {
635 private database = "test" ;
736 private username = "test" ;
837 private password = "test" ;
38+ private sslConfig ?: PostgreSqlSslConfig ;
939
1040 constructor ( image : string ) {
1141 super ( image ) ;
@@ -29,12 +59,63 @@ export class PostgreSqlContainer extends GenericContainer {
2959 return this ;
3060 }
3161
62+ public withSSL ( certFile : string , keyFile : string , caCertFile ?: string ) : this {
63+ if ( ! certFile ) throw new Error ( "SSL certificate file should not be empty." ) ;
64+ if ( ! keyFile ) throw new Error ( "SSL key file should not be empty." ) ;
65+ if ( caCertFile !== undefined && ! caCertFile ) throw new Error ( "SSL CA certificate file should not be empty." ) ;
66+
67+ const filesToCopy = [
68+ { source : certFile , target : POSTGRES_SSL_CERT_PATH , mode : 0o600 } ,
69+ { source : keyFile , target : POSTGRES_SSL_KEY_PATH , mode : 0o600 } ,
70+ ] ;
71+
72+ if ( caCertFile ) {
73+ filesToCopy . push ( { source : caCertFile , target : POSTGRES_SSL_CA_CERT_PATH , mode : 0o600 } ) ;
74+ }
75+
76+ this . sslConfig = {
77+ caCertPath : caCertFile ? POSTGRES_SSL_CA_CERT_PATH : undefined ,
78+ } ;
79+
80+ this . withCopyFilesToContainer ( filesToCopy )
81+ . withCopyContentToContainer ( [
82+ {
83+ content : POSTGRES_SSL_ENTRYPOINT_CONTENT ,
84+ target : POSTGRES_SSL_ENTRYPOINT_PATH ,
85+ mode : 0o700 ,
86+ } ,
87+ ] )
88+ . withEntrypoint ( [ "sh" , POSTGRES_SSL_ENTRYPOINT_PATH ] ) ;
89+
90+ return this ;
91+ }
92+
93+ public withSSLCert ( caCertFile : string , certFile : string , keyFile : string ) : this {
94+ if ( ! caCertFile ) throw new Error ( "SSL CA certificate file should not be empty." ) ;
95+ return this . withSSL ( certFile , keyFile , caCertFile ) ;
96+ }
97+
3298 public override async start ( ) : Promise < StartedPostgreSqlContainer > {
3399 this . withEnvironment ( {
34100 POSTGRES_DB : this . database ,
35101 POSTGRES_USER : this . username ,
36102 POSTGRES_PASSWORD : this . password ,
37103 } ) ;
104+ if ( this . sslConfig ) {
105+ const command = this . createOpts . Cmd ;
106+ if ( ! command || command [ 0 ] === "postgres" ) {
107+ this . withCommand ( [
108+ ...( command ?? [ "postgres" ] ) ,
109+ "-c" ,
110+ "ssl=on" ,
111+ "-c" ,
112+ `ssl_cert_file=${ POSTGRES_SSL_CERT_PATH } ` ,
113+ "-c" ,
114+ `ssl_key_file=${ POSTGRES_SSL_KEY_PATH } ` ,
115+ ...( this . sslConfig . caCertPath ? [ "-c" , `ssl_ca_file=${ this . sslConfig . caCertPath } ` ] : [ ] ) ,
116+ ] ) ;
117+ }
118+ }
38119 if ( ! this . healthCheck ) {
39120 this . withHealthCheck ( {
40121 test : [
0 commit comments