From daa8cf2921d07f27a57cdb8d24faba19b5a6a526 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 8 Apr 2024 14:42:44 -0400 Subject: [PATCH 01/42] SSL for postgres --- modules/postgres/options.go | 23 + modules/postgres/postgres.go | 34 +- modules/postgres/postgres_test.go | 52 +- modules/postgres/testdata/certs/server_ca.pem | 20 + .../postgres/testdata/certs/server_cert.pem | 21 + .../postgres/testdata/certs/server_key.pem | 27 + .../postgres/testdata/my-postgres-ssl.conf | 691 ++++++++++++++++++ 7 files changed, 862 insertions(+), 6 deletions(-) create mode 100644 modules/postgres/options.go create mode 100644 modules/postgres/testdata/certs/server_ca.pem create mode 100644 modules/postgres/testdata/certs/server_cert.pem create mode 100644 modules/postgres/testdata/certs/server_key.pem create mode 100644 modules/postgres/testdata/my-postgres-ssl.conf diff --git a/modules/postgres/options.go b/modules/postgres/options.go new file mode 100644 index 0000000000..6e983b67af --- /dev/null +++ b/modules/postgres/options.go @@ -0,0 +1,23 @@ +package postgres + +type SSLVerificationMode string + +const ( + SSLVerificationModeNone SSLVerificationMode = "disable" + SSLVerificationModeRequire SSLVerificationMode = "require" +) + +type SSLSettings struct { + // Path to the CA certificate file + CACertFile string + // Path to the client certificate file + CertFile string + // Path to the key file + KeyFile string + // Verification mode + VerificationMode SSLVerificationMode + // Fail if no certificate is provided + FailIfNoCert bool + // Depth of certificate chain verification + VerificationDepth int +} diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index cb3d01694b..e0eb6a66d3 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" ) const ( @@ -26,10 +27,9 @@ type PostgresContainer struct { snapshotName string } - // MustConnectionString panics if the address cannot be determined. func (c *PostgresContainer) MustConnectionString(ctx context.Context, args ...string) string { - addr, err := c.ConnectionString(ctx,args...) + addr, err := c.ConnectionString(ctx, args...) if err != nil { panic(err) } @@ -170,6 +170,36 @@ func WithSnapshotName(name string) SnapshotOption { } } +func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOption { + const postgresCaCertPath = "/tmp/data/ca_cert.pem" + const postgresCertPath = "/tmp/data/server.crt" + const postgresKeyPath = "/tmp/data/server.key" + + const defaultPermission = 0o640 + + return func(req *testcontainers.GenericContainerRequest) { + req.Files = append(req.Files, testcontainers.ContainerFile{ + HostFilePath: sslSettings.CACertFile, + ContainerFilePath: postgresCaCertPath, + FileMode: defaultPermission, + }) + req.Files = append(req.Files, testcontainers.ContainerFile{ + HostFilePath: sslSettings.CertFile, + ContainerFilePath: postgresCertPath, + FileMode: defaultPermission, + }) + req.Files = append(req.Files, testcontainers.ContainerFile{ + HostFilePath: sslSettings.KeyFile, + ContainerFilePath: postgresKeyPath, + FileMode: defaultPermission, + }) + + // TODO: Can we detect TLS by port. I don't think so... + // Probably use logs + req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) + } +} + // Snapshot takes a snapshot of the current state of the database as a template, which can then be restored using // the Restore method. By default, the snapshot will be created under a database called migrated_template, you can // customize the snapshot name with the options. diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 2a74e147f6..5e0eaa6e37 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -87,12 +87,12 @@ func TestPostgres(t *testing.T) { connStr, err := container.ConnectionString(ctx, "sslmode=disable", "application_name=test") // } require.NoError(t, err) - - mustConnStr := container.MustConnectionString(ctx,"sslmode=disable", "application_name=test") - if mustConnStr!=connStr{ + + mustConnStr := container.MustConnectionString(ctx, "sslmode=disable", "application_name=test") + if mustConnStr != connStr { t.Errorf("ConnectionString was not equal to MustConnectionString") } - + // Ensure connection string is using generic format id, err := container.MappedPort(ctx, "5432/tcp") require.NoError(t, err) @@ -188,6 +188,50 @@ func TestWithConfigFile(t *testing.T) { defer db.Close() } +func TestWithSSLEnabledConfigFile(t *testing.T) { + ctx := context.Background() + + sslSettings := postgres.SSLSettings{ + CACertFile: filepath.Join("testdata", "certs", "server_ca.pem"), + CertFile: filepath.Join("testdata", "certs", "server_cert.pem"), + KeyFile: filepath.Join("testdata", "certs", "server_key.pem"), + VerificationMode: postgres.SSLVerificationModeRequire, + FailIfNoCert: true, + VerificationDepth: 1, + } + + container, err := postgres.RunContainer(ctx, + postgres.WithConfigFile(filepath.Join("testdata", "my-postgres-ssl.conf")), + postgres.WithDatabase(dbname), + postgres.WithUsername(user), + postgres.WithPassword(password), + testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), + postgres.WithSSLSettings(sslSettings), + ) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { + if err := container.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) + + // explicitly set sslmode=disable because the container is not configured to use TLS + connStr, err := container.ConnectionString(ctx, "sslmode=require") + require.NoError(t, err) + + db, err := sql.Open("postgres", connStr) + require.NoError(t, err) + assert.NotNil(t, db) + defer db.Close() + + result, err := db.Exec("SELECT * FROM testdb;") + require.NoError(t, err) + assert.NotNil(t, result) +} + func TestWithInitScript(t *testing.T) { ctx := context.Background() diff --git a/modules/postgres/testdata/certs/server_ca.pem b/modules/postgres/testdata/certs/server_ca.pem new file mode 100644 index 0000000000..f22df82909 --- /dev/null +++ b/modules/postgres/testdata/certs/server_ca.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRzCCAi+gAwIBAgIJAJJIMzvZuRzlMA0GCSqGSIb3DQEBCwUAMDExIDAeBgNV +BAMMF1RMU0dlblNlbGZTaWduZWR0Um9vdENBMQ0wCwYDVQQHDAQkJCQkMCAXDTE5 +MDUwMjA3MjI0OVoYDzIxMTkwNDA4MDcyMjQ5WjAxMSAwHgYDVQQDDBdUTFNHZW5T +ZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKko8FmfzrLHyZckvdR1oiSZf80m0t66TMqtLat1Oxjh +CjsxvswwJ/m2I5dM48hwZ+0b2ufkvaudLPq/8jDGyONVfjMGlbe1YlmQMDC7YWdI +XM1nCWAZIKaOHwIkfswuVBAdBVYV4Polu6wjVt5edEpl/IWEpPicXjLOY1Fw3q67 +5tP2Mmo6TJg5YqgB4fH4SmajtP3j+H4puQ8ZPIs26mInEgfCyrMWey/oQX8qqMph +pKMEJYE7DHawriFraOooJadJYojbY5H27nmJe8yXURb3wSQSaKnFZL25cmVm2kue +/lw+n+a2wLdHdU4cmghCURalhcXUNZe7UbdRZ9e9r2cCAwEAAaNgMF4wCwYDVR0P +BAQDAgEGMB0GA1UdDgQWBBSZiNur/XHsqSfdWnB1NPi/ql5+tzAfBgNVHSMEGDAW +gBSZiNur/XHsqSfdWnB1NPi/ql5+tzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQAar/db/T7izD4pyh2titl7Dkgp2iTditfgqRlU0yVGiiB6rLmY +sYE2QAuFhgqyRLPcjVV8F39iRJHQ17SGT8e2iAaUTnbQj0AiskKjonF9+quKuVbr +TpYHk+guS0Jn2rU6HK8WQeYZOh3WdLTu4ArXkxywgwVssQQ9JmpTd9YEYePWfs7i +WZB6AQyL9CD3z1j4i1G4ft6pB1Ps5XjznqMZ2//7AUpoRTrettWqorPWwudQ9yna +B4S6KtvpnxUQSeHJW6Q4NvTrOsvHEOCa6OtwYbWmLf+qbpPb8oHt9UF3ze2PJopB +QzsQop1+gPudG0DX0SgyuQT+SsFjYlDazZdZ +-----END CERTIFICATE----- diff --git a/modules/postgres/testdata/certs/server_cert.pem b/modules/postgres/testdata/certs/server_cert.pem new file mode 100644 index 0000000000..8a78318338 --- /dev/null +++ b/modules/postgres/testdata/certs/server_cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDajCCAlKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH +ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAgFw0xOTA1MDIwNzIy +NDlaGA8yMTE5MDQwODA3MjI0OVowIzEQMA4GA1UEAwwHQzY1U1RUMjEPMA0GA1UE +CgwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0QXKtb +KVeEuCmZGcZAlAlTBC8E/G3UuX6qKwTR1xEOvUWeBH1n0WeXXGd/p/y6P4lRBeWN +BZ9KcvIlNDeDMy05NfxnO1vnJk9E8/0xwMiY1LJdMHzIzhmrrqXo0u3DT8MmoNR6 +7CTcnG21gi1GrjW8a747yFF0xfukEc6FkyVqLsjtCkHPwrc/sBHVS3aivNWGkJzA +eBXBdWJAg3ZC6T9U+Y8cndWQrpYMJvek1IewlyDSspHZDFmM1OwVwypnMt4fGgaX +5IlUMnNgKmisOSuI529rxLF+mvYIQLRl5bP+1/c9JD5MZ5krA3SrjdwRFS3sQXC3 +nuHqJofFXNkbXQIDAQABo4GYMIGVMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMCYGA1UdEQQfMB2CB0M2NVNUVDKCB0M2NVNUVDKC +CWxvY2FsaG9zdDAdBgNVHQ4EFgQURq22sa46tA0SGHhEm9jxGP9aDrswHwYDVR0j +BBgwFoAUmYjbq/1x7Kkn3VpwdTT4v6pefrcwDQYJKoZIhvcNAQELBQADggEBAKUP +7RgmJyMVoHxg46F1fjWVhlF4BbQuEtB8mC+4G4e68lDU/TPAbmB3aj91oQDgBiTd +R2O7U6tyitxxrU2r7rFAHGhFHeyCQ3yZMwydO2V3Nm2Ywzdyk8er4yghjg9FS8tH +egDGDDod3l1yrAbHHuXmzDjnAFwHwRkm5cYUz00/IuZ3sQZ70XofL3KXNj1tAtfK +PSpdSAxSTO99ofjVKjlyywQSZKNbXfqD5DGz8e0rmqPfZ+3zi75E5nEuJ3UI2wXg +LuI4j6FIzNQyei/FdSynktcIm+hefQEyex4cho4C8RYB2S5S8RWrnP9jOzsaQFHn +bHXf7dKwRfA6/u8JmtQ= +-----END CERTIFICATE----- diff --git a/modules/postgres/testdata/certs/server_key.pem b/modules/postgres/testdata/certs/server_key.pem new file mode 100644 index 0000000000..dfbfb6db7e --- /dev/null +++ b/modules/postgres/testdata/certs/server_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqR0QXKtbKVeEuCmZGcZAlAlTBC8E/G3UuX6qKwTR1xEOvUWe +BH1n0WeXXGd/p/y6P4lRBeWNBZ9KcvIlNDeDMy05NfxnO1vnJk9E8/0xwMiY1LJd +MHzIzhmrrqXo0u3DT8MmoNR67CTcnG21gi1GrjW8a747yFF0xfukEc6FkyVqLsjt +CkHPwrc/sBHVS3aivNWGkJzAeBXBdWJAg3ZC6T9U+Y8cndWQrpYMJvek1IewlyDS +spHZDFmM1OwVwypnMt4fGgaX5IlUMnNgKmisOSuI529rxLF+mvYIQLRl5bP+1/c9 +JD5MZ5krA3SrjdwRFS3sQXC3nuHqJofFXNkbXQIDAQABAoIBAA0dxvYZCEIFmrKZ +71jzanDQ5FJvvyhA8H3OmC4r+oZ+uTDu5FmezF2OdkvhbyI9VMi2wsT9T9m+yAxw +QXhyUce3WzeXsv4Em8H55fQykBhOtqPQja/EDeMGVK2ACrXJYRufnDBfKoWEOmQb +kjddgZzjaBDHOWXJA5CTet8ysGOAJBTxyzU69k5Vj9B5abG9CofNzGOFF+Uleff5 +ip3sz7JpDXCex3oEs98veco6+8i/MZNo3BnwB5J+P+2MFFKONfPwuNyKAWBza2/X +66Lk3xXBjLJJ+Ww16jkqueTXEq6GCFXavNfdL9aonth5V5YYR/cj+2u2LM1oj9cJ +bp0xbvUCgYEA2Svq1DyR9cfTwrbc/0J2JfrjavClzDYU2oeO2fSU85WEEjJguaja +17Vdo/UsJtiUiSq4UhI1n0haaIpTBCeF2tHGXVEYZ7ZBi1zzdWbWlDxFmi+rcE57 +ytx5w+iLE366tQEMa/Jn3bly54pG5JZAr9TXkpg9sMbzWZri2ocyU/cCgYEAx1l/ +9X9C/OruDp/MhhmVwKfw/X2+RhZRuv0pPcpJu7/gIoLgaxNj41XSeLqLYMlisaRk +GFU17GFXtfRGE1a3z+jj8UPTP2sHk3w8m0yI+pgWgsvG0TJ0B+XsRfpVxFiIoaEs +3AsBaGR+hrRY1dpaJ9Cu3J9mEeToTpbCzPzVDksCgYEAzwSvWNvYY4u2UFHSvz2S +tMfBzCpUUiNno50/ToN5De4ENPhy/eh5nNDVz7qh+PHSPiNMC2gyV4E4NZlOY5Jt +Zdc8ma35brvtJTVZGxwKBsqhqsYwTeFy3kFnjZn6IX5X6r1yIuCzpEfowdEtnS+h +wDtLuAGKJR6x0UP1Zk0ka6cCgYBGE6I1rJzhx7wTi/0bjtbjuKWwlolSnfnxH5ll +zTyKMXMa7qLxQQm2Gq84HWtthJ2bEMzW+O1RwQ5SOiKAHdXT0mx+nXcfLgKlx+CO +PyNP5DLVm8iyNWgwdpTOLKgFs5GkL8JTP9Mo3VrVA4TO+EkFAgjWKXp6A9vd9IVa +Be7nbQKBgAVtFKuf9nbCMfN+W1gN0vlW2lwxCTa4w0KHgIlGIIvnYVuixSgu9fGt +uylQcQirEjqrdzdVF9L2BQ37ZcLaGh1LoCmx8XVCX/HhbwW2RP798P3Z1P7htm16 +ha5OfuPjHvoZklbYJo6EORJZQehS2VP63pjdnmUeMHPFzrPUevI5 +-----END RSA PRIVATE KEY----- diff --git a/modules/postgres/testdata/my-postgres-ssl.conf b/modules/postgres/testdata/my-postgres-ssl.conf new file mode 100644 index 0000000000..35adc25d4e --- /dev/null +++ b/modules/postgres/testdata/my-postgres-ssl.conf @@ -0,0 +1,691 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: B = bytes Time units: ms = milliseconds +# kB = kilobytes s = seconds +# MB = megabytes min = minutes +# GB = gigabytes h = hours +# TB = terabytes d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/tmp' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP Keepalives - +# see "man 7 tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = md5 # md5 or scram-sha-256 +#db_user_namespace = off + +# GSSAPI using Kerberos +#krb_server_keyfile = '' +#krb_caseins_users = off + +# - SSL - + +ssl = on +ssl_ca_file = '/tmp/data/ca_cert.pem"' +ssl_cert_file = '/tmp/data/server.crt' +#ssl_crl_file = '' +ssl_key_file = '/tmp/data/server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +#shared_buffers = 32MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#max_stack_depth = 2MB # min 100kB +#dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # use none to disable dynamic shared memory + # (change requires restart) + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kB, or -1 for no limit + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 25 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 10 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 0 # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 # (change requires restart) +#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers +#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers +#parallel_leader_participation = on +#max_parallel_workers = 8 # maximum number of max_worker_processes that + # can be used in parallel operations +#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate + # (change requires restart) +#backend_flush_after = 0 # measured in pages, 0 disables + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux and FreeBSD) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_compression = off # enable compression of full-page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#max_wal_size = 1GB +#min_wal_size = 80MB +#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 0 # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_command = '' # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a logfile segment switch after this + # number of seconds; 0 disables + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the master and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#wal_keep_segments = 0 # in logfile segments; 0 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables + +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Master Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a master server. + +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from master + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_bitmapscan = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_parallel_hash = on +#enable_partition_pruning = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#force_parallel_mode = off +#jit = off # allow JIT compilation + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (win32): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %p = process ID + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +#log_timezone = 'GMT' + +#------------------------------------------------------------------------------ +# PROCESS TITLE +#------------------------------------------------------------------------------ + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Query and Index Statistics Collector - + +#track_activities = on +#track_counts = on +#track_io_timing = off +#track_functions = none # none, pl, all +#track_activity_query_size = 1024 # (change requires restart) +#stats_temp_directory = 'pg_stat_tmp' + + +# - Monitoring - + +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off +#log_statement_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_tablespace = '' # a tablespace name, '' uses the default +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_min_age = 50000000 +#vacuum_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples + # before index cleanup, 0 always performs + # index cleanup +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_fuzzy_search_limit = 0 +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +#datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +#timezone = 'GMT' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 0 # min -15, max 3 +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +#lc_messages = 'C' # locale for system error message + # strings +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting + +# default configuration for text search +#default_text_search_config = 'pg_catalog.simple' + +# - Shared Library Preloading - + +#shared_preload_libraries = '' # (change requires restart) +#local_preload_libraries = '' +#session_preload_libraries = '' +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#default_with_oids = off +#escape_string_warning = on +#lo_compat_privileges = off +#operator_precedence_warning = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here From 53e213546117c7d763902b9d1c8364e63dac33b9 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 8 Apr 2024 17:08:00 -0400 Subject: [PATCH 02/42] Add entrypoint wrapper --- modules/postgres/options.go | 2 ++ modules/postgres/postgres.go | 12 ++++++++++-- modules/postgres/postgres_test.go | 1 + modules/postgres/testdata/docker-entrypoint-ssl.bash | 10 ++++++++++ modules/postgres/testdata/my-postgres-ssl.conf | 4 ++-- 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 modules/postgres/testdata/docker-entrypoint-ssl.bash diff --git a/modules/postgres/options.go b/modules/postgres/options.go index 6e983b67af..3c0c4bfa70 100644 --- a/modules/postgres/options.go +++ b/modules/postgres/options.go @@ -14,6 +14,8 @@ type SSLSettings struct { CertFile string // Path to the key file KeyFile string + // + Entrypoint string // Verification mode VerificationMode SSLVerificationMode // Fail if no certificate is provided diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index e0eb6a66d3..63747e4ce2 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -172,10 +172,11 @@ func WithSnapshotName(name string) SnapshotOption { func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOption { const postgresCaCertPath = "/tmp/data/ca_cert.pem" - const postgresCertPath = "/tmp/data/server.crt" + const postgresCertPath = "/tmp/data/server.cert" const postgresKeyPath = "/tmp/data/server.key" + const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" - const defaultPermission = 0o640 + const defaultPermission = 0o600 return func(req *testcontainers.GenericContainerRequest) { req.Files = append(req.Files, testcontainers.ContainerFile{ @@ -193,6 +194,13 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt ContainerFilePath: postgresKeyPath, FileMode: defaultPermission, }) + req.Files = append(req.Files, testcontainers.ContainerFile{ + HostFilePath: sslSettings.Entrypoint, + ContainerFilePath: entrypointPath, + FileMode: 0o666, + }) + + req.Entrypoint = []string{"sh", "/usr/local/bin/docker-entrypoint-ssl.bash"} // TODO: Can we detect TLS by port. I don't think so... // Probably use logs diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 5e0eaa6e37..19d4884d75 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -195,6 +195,7 @@ func TestWithSSLEnabledConfigFile(t *testing.T) { CACertFile: filepath.Join("testdata", "certs", "server_ca.pem"), CertFile: filepath.Join("testdata", "certs", "server_cert.pem"), KeyFile: filepath.Join("testdata", "certs", "server_key.pem"), + Entrypoint: filepath.Join("testdata", "docker-entrypoint-ssl.bash"), VerificationMode: postgres.SSLVerificationModeRequire, FailIfNoCert: true, VerificationDepth: 1, diff --git a/modules/postgres/testdata/docker-entrypoint-ssl.bash b/modules/postgres/testdata/docker-entrypoint-ssl.bash new file mode 100644 index 0000000000..33ea352cef --- /dev/null +++ b/modules/postgres/testdata/docker-entrypoint-ssl.bash @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -Eeo pipefail + +# Hardcoded user id from pg11-alpine image +chown 70:70 /tmp/data/ca_cert.pem +chown 70:70 /tmp/data/server.cert +chown 70:70 /tmp/data/server.key + +ls -lah /tmp/data +/usr/local/bin/docker-entrypoint.sh "$@" \ No newline at end of file diff --git a/modules/postgres/testdata/my-postgres-ssl.conf b/modules/postgres/testdata/my-postgres-ssl.conf index 35adc25d4e..0aa6f94739 100644 --- a/modules/postgres/testdata/my-postgres-ssl.conf +++ b/modules/postgres/testdata/my-postgres-ssl.conf @@ -96,8 +96,8 @@ listen_addresses = '*' # - SSL - ssl = on -ssl_ca_file = '/tmp/data/ca_cert.pem"' -ssl_cert_file = '/tmp/data/server.crt' +ssl_ca_file = '/tmp/data/ca_cert.pem' +ssl_cert_file = '/tmp/data/server.cert' #ssl_crl_file = '' ssl_key_file = '/tmp/data/server.key' #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers From bd3360dca35ecc297d101f881d14fdcf10e89024 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 8 Apr 2024 17:10:33 -0400 Subject: [PATCH 03/42] Add in init so we can test ssl+init path --- modules/postgres/postgres_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 19d4884d75..93fa61a56a 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -203,6 +203,7 @@ func TestWithSSLEnabledConfigFile(t *testing.T) { container, err := postgres.RunContainer(ctx, postgres.WithConfigFile(filepath.Join("testdata", "my-postgres-ssl.conf")), + postgres.WithInitScripts(filepath.Join("testdata", "init-user-db.sh")), postgres.WithDatabase(dbname), postgres.WithUsername(user), postgres.WithPassword(password), From c3fd69bffeae60c7b92a872c4e29200ac5feacc0 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 8 Apr 2024 17:17:01 -0400 Subject: [PATCH 04/42] Remove unused fields from options --- modules/postgres/options.go | 8 +------- modules/postgres/postgres_test.go | 11 ++++------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/modules/postgres/options.go b/modules/postgres/options.go index 3c0c4bfa70..1516c41d3f 100644 --- a/modules/postgres/options.go +++ b/modules/postgres/options.go @@ -14,12 +14,6 @@ type SSLSettings struct { CertFile string // Path to the key file KeyFile string - // + // Entrypoint used to override and set SSL ownership Entrypoint string - // Verification mode - VerificationMode SSLVerificationMode - // Fail if no certificate is provided - FailIfNoCert bool - // Depth of certificate chain verification - VerificationDepth int } diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 93fa61a56a..099617354f 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -192,13 +192,10 @@ func TestWithSSLEnabledConfigFile(t *testing.T) { ctx := context.Background() sslSettings := postgres.SSLSettings{ - CACertFile: filepath.Join("testdata", "certs", "server_ca.pem"), - CertFile: filepath.Join("testdata", "certs", "server_cert.pem"), - KeyFile: filepath.Join("testdata", "certs", "server_key.pem"), - Entrypoint: filepath.Join("testdata", "docker-entrypoint-ssl.bash"), - VerificationMode: postgres.SSLVerificationModeRequire, - FailIfNoCert: true, - VerificationDepth: 1, + CACertFile: filepath.Join("testdata", "certs", "server_ca.pem"), + CertFile: filepath.Join("testdata", "certs", "server_cert.pem"), + KeyFile: filepath.Join("testdata", "certs", "server_key.pem"), + Entrypoint: filepath.Join("testdata", "docker-entrypoint-ssl.bash"), } container, err := postgres.RunContainer(ctx, From 445a575c34712b9869727d396e1724f13fc88051 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 8 Apr 2024 17:17:48 -0400 Subject: [PATCH 05/42] Remove unused consts --- modules/postgres/options.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/postgres/options.go b/modules/postgres/options.go index 1516c41d3f..33b9455a5b 100644 --- a/modules/postgres/options.go +++ b/modules/postgres/options.go @@ -2,11 +2,6 @@ package postgres type SSLVerificationMode string -const ( - SSLVerificationModeNone SSLVerificationMode = "disable" - SSLVerificationModeRequire SSLVerificationMode = "require" -) - type SSLSettings struct { // Path to the CA certificate file CACertFile string From 8814e8c22b7030abf7110829aa9f12f789134024 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Tue, 9 Apr 2024 10:15:54 -0400 Subject: [PATCH 06/42] Separate entrypoint from ssl --- modules/postgres/options.go | 2 -- modules/postgres/postgres.go | 23 +++++++++++++------ modules/postgres/postgres_test.go | 3 +-- .../testdata/docker-entrypoint-ssl.bash | 21 +++++++++++++---- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/modules/postgres/options.go b/modules/postgres/options.go index 33b9455a5b..3cedfa80d2 100644 --- a/modules/postgres/options.go +++ b/modules/postgres/options.go @@ -9,6 +9,4 @@ type SSLSettings struct { CertFile string // Path to the key file KeyFile string - // Entrypoint used to override and set SSL ownership - Entrypoint string } diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 63747e4ce2..bab78ea902 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -174,7 +174,6 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt const postgresCaCertPath = "/tmp/data/ca_cert.pem" const postgresCertPath = "/tmp/data/server.cert" const postgresKeyPath = "/tmp/data/server.key" - const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" const defaultPermission = 0o600 @@ -194,18 +193,28 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt ContainerFilePath: postgresKeyPath, FileMode: defaultPermission, }) + + // TODO: Can we detect TLS by port. I don't think so... + // Probably use logs + req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) + } +} + +func WithEntrypoint(hostEntrypointPath string) testcontainers.CustomizeRequestOption { + + const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" + + return func(req *testcontainers.GenericContainerRequest) { + req.Files = append(req.Files, testcontainers.ContainerFile{ - HostFilePath: sslSettings.Entrypoint, + HostFilePath: hostEntrypointPath, ContainerFilePath: entrypointPath, FileMode: 0o666, }) - req.Entrypoint = []string{"sh", "/usr/local/bin/docker-entrypoint-ssl.bash"} - - // TODO: Can we detect TLS by port. I don't think so... - // Probably use logs - req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) + req.Entrypoint = []string{"sh", entrypointPath} } + } // Snapshot takes a snapshot of the current state of the database as a template, which can then be restored using diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 099617354f..9dde750103 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -195,7 +195,6 @@ func TestWithSSLEnabledConfigFile(t *testing.T) { CACertFile: filepath.Join("testdata", "certs", "server_ca.pem"), CertFile: filepath.Join("testdata", "certs", "server_cert.pem"), KeyFile: filepath.Join("testdata", "certs", "server_key.pem"), - Entrypoint: filepath.Join("testdata", "docker-entrypoint-ssl.bash"), } container, err := postgres.RunContainer(ctx, @@ -206,6 +205,7 @@ func TestWithSSLEnabledConfigFile(t *testing.T) { postgres.WithPassword(password), testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), postgres.WithSSLSettings(sslSettings), + postgres.WithEntrypoint(filepath.Join("testdata", "docker-entrypoint-ssl.bash")), ) if err != nil { t.Fatal(err) @@ -217,7 +217,6 @@ func TestWithSSLEnabledConfigFile(t *testing.T) { } }) - // explicitly set sslmode=disable because the container is not configured to use TLS connStr, err := container.ConnectionString(ctx, "sslmode=require") require.NoError(t, err) diff --git a/modules/postgres/testdata/docker-entrypoint-ssl.bash b/modules/postgres/testdata/docker-entrypoint-ssl.bash index 33ea352cef..50a0bd51e0 100644 --- a/modules/postgres/testdata/docker-entrypoint-ssl.bash +++ b/modules/postgres/testdata/docker-entrypoint-ssl.bash @@ -1,10 +1,23 @@ #!/usr/bin/env bash set -Eeo pipefail -# Hardcoded user id from pg11-alpine image -chown 70:70 /tmp/data/ca_cert.pem -chown 70:70 /tmp/data/server.cert -chown 70:70 /tmp/data/server.key + +pUID=$(id -u postgres) +pGID=$(id -g postgres) + +if [ -z "$pUID" ] +then + exit 1 +fi + +if [ -z "$pGID" ] +then + exit 1 +fi + +chown "$pUID":"$pGID" /tmp/data/ca_cert.pem +chown "$pUID":"$pGID" /tmp/data/server.cert +chown "$pUID":"$pGID" /tmp/data/server.key ls -lah /tmp/data /usr/local/bin/docker-entrypoint.sh "$@" \ No newline at end of file From 2aecc1bf0c73c0af69f2870ee80d02bcea748031 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Thu, 11 Apr 2024 15:14:41 -0400 Subject: [PATCH 07/42] Use external cert generation --- modules/postgres/go.mod | 1 + modules/postgres/go.sum | 2 + modules/postgres/postgres_test.go | 62 +++++++++++++++++-- modules/postgres/testdata/certs/server_ca.pem | 20 ------ .../postgres/testdata/certs/server_cert.pem | 21 ------- .../postgres/testdata/certs/server_key.pem | 27 -------- 6 files changed, 60 insertions(+), 73 deletions(-) delete mode 100644 modules/postgres/testdata/certs/server_ca.pem delete mode 100644 modules/postgres/testdata/certs/server_cert.pem delete mode 100644 modules/postgres/testdata/certs/server_key.pem diff --git a/modules/postgres/go.mod b/modules/postgres/go.mod index c24255482a..9b9dc277b4 100644 --- a/modules/postgres/go.mod +++ b/modules/postgres/go.mod @@ -6,6 +6,7 @@ require ( github.com/docker/go-connections v0.5.0 github.com/jackc/pgx/v5 v5.5.4 github.com/lib/pq v1.10.9 + github.com/mdelapenya/tlscert v0.0.0-20240411155228-ea0834bcfa84 github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.29.1 diff --git a/modules/postgres/go.sum b/modules/postgres/go.sum index 07b01ce51b..7a4ad1c988 100644 --- a/modules/postgres/go.sum +++ b/modules/postgres/go.sum @@ -75,6 +75,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mdelapenya/tlscert v0.0.0-20240411155228-ea0834bcfa84 h1:6ZdtWuke1JydwlgIMwmC1IMTGXgl0wUWvFGvHSgv78Y= +github.com/mdelapenya/tlscert v0.0.0-20240411155228-ea0834bcfa84/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 9dde750103..4951b73e7b 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "os" "path/filepath" "testing" "time" @@ -12,6 +13,8 @@ import ( "github.com/docker/go-connections/nat" "github.com/jackc/pgx/v5" _ "github.com/lib/pq" + "github.com/mdelapenya/tlscert" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -26,6 +29,59 @@ const ( password = "password" ) +func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, error) { + + tmpDir := t.TempDir() + certsDir := tmpDir + "/certs" + + if err := os.MkdirAll(certsDir, 0755); err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { + os.RemoveAll(tmpDir) + }) + + caCert := tlscert.SelfSignedFromRequest(tlscert.Request{ + Host: "localhost", + Name: "ca-cert", + ParentDir: certsDir, + }) + + if caCert == nil { + + return caCert, nil, errors.New("Unable to create CA Authority") + } + + cert := tlscert.SelfSignedFromRequest(tlscert.Request{ + Host: "localhost", + Name: "client-cert", + Parent: caCert, + ParentDir: certsDir, + }) + if cert == nil { + return caCert, cert, errors.New("Unable to create Server Certificates") + } + + return caCert, cert, nil + +} + +func createSSLSettings(t *testing.T) postgres.SSLSettings { + + caCert, serverCerts, err := createSSLCerts(t) + if err != nil { + t.Fatal(err) + } + + return postgres.SSLSettings{ + CACertFile: caCert.CertPath, + CertFile: serverCerts.CertPath, + KeyFile: serverCerts.KeyPath, + } + +} + func TestPostgres(t *testing.T) { ctx := context.Background() @@ -191,11 +247,7 @@ func TestWithConfigFile(t *testing.T) { func TestWithSSLEnabledConfigFile(t *testing.T) { ctx := context.Background() - sslSettings := postgres.SSLSettings{ - CACertFile: filepath.Join("testdata", "certs", "server_ca.pem"), - CertFile: filepath.Join("testdata", "certs", "server_cert.pem"), - KeyFile: filepath.Join("testdata", "certs", "server_key.pem"), - } + sslSettings := createSSLSettings(t) container, err := postgres.RunContainer(ctx, postgres.WithConfigFile(filepath.Join("testdata", "my-postgres-ssl.conf")), diff --git a/modules/postgres/testdata/certs/server_ca.pem b/modules/postgres/testdata/certs/server_ca.pem deleted file mode 100644 index f22df82909..0000000000 --- a/modules/postgres/testdata/certs/server_ca.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDRzCCAi+gAwIBAgIJAJJIMzvZuRzlMA0GCSqGSIb3DQEBCwUAMDExIDAeBgNV -BAMMF1RMU0dlblNlbGZTaWduZWR0Um9vdENBMQ0wCwYDVQQHDAQkJCQkMCAXDTE5 -MDUwMjA3MjI0OVoYDzIxMTkwNDA4MDcyMjQ5WjAxMSAwHgYDVQQDDBdUTFNHZW5T -ZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAKko8FmfzrLHyZckvdR1oiSZf80m0t66TMqtLat1Oxjh -CjsxvswwJ/m2I5dM48hwZ+0b2ufkvaudLPq/8jDGyONVfjMGlbe1YlmQMDC7YWdI -XM1nCWAZIKaOHwIkfswuVBAdBVYV4Polu6wjVt5edEpl/IWEpPicXjLOY1Fw3q67 -5tP2Mmo6TJg5YqgB4fH4SmajtP3j+H4puQ8ZPIs26mInEgfCyrMWey/oQX8qqMph -pKMEJYE7DHawriFraOooJadJYojbY5H27nmJe8yXURb3wSQSaKnFZL25cmVm2kue -/lw+n+a2wLdHdU4cmghCURalhcXUNZe7UbdRZ9e9r2cCAwEAAaNgMF4wCwYDVR0P -BAQDAgEGMB0GA1UdDgQWBBSZiNur/XHsqSfdWnB1NPi/ql5+tzAfBgNVHSMEGDAW -gBSZiNur/XHsqSfdWnB1NPi/ql5+tzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 -DQEBCwUAA4IBAQAar/db/T7izD4pyh2titl7Dkgp2iTditfgqRlU0yVGiiB6rLmY -sYE2QAuFhgqyRLPcjVV8F39iRJHQ17SGT8e2iAaUTnbQj0AiskKjonF9+quKuVbr -TpYHk+guS0Jn2rU6HK8WQeYZOh3WdLTu4ArXkxywgwVssQQ9JmpTd9YEYePWfs7i -WZB6AQyL9CD3z1j4i1G4ft6pB1Ps5XjznqMZ2//7AUpoRTrettWqorPWwudQ9yna -B4S6KtvpnxUQSeHJW6Q4NvTrOsvHEOCa6OtwYbWmLf+qbpPb8oHt9UF3ze2PJopB -QzsQop1+gPudG0DX0SgyuQT+SsFjYlDazZdZ ------END CERTIFICATE----- diff --git a/modules/postgres/testdata/certs/server_cert.pem b/modules/postgres/testdata/certs/server_cert.pem deleted file mode 100644 index 8a78318338..0000000000 --- a/modules/postgres/testdata/certs/server_cert.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDajCCAlKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH -ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAgFw0xOTA1MDIwNzIy -NDlaGA8yMTE5MDQwODA3MjI0OVowIzEQMA4GA1UEAwwHQzY1U1RUMjEPMA0GA1UE -CgwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0QXKtb -KVeEuCmZGcZAlAlTBC8E/G3UuX6qKwTR1xEOvUWeBH1n0WeXXGd/p/y6P4lRBeWN -BZ9KcvIlNDeDMy05NfxnO1vnJk9E8/0xwMiY1LJdMHzIzhmrrqXo0u3DT8MmoNR6 -7CTcnG21gi1GrjW8a747yFF0xfukEc6FkyVqLsjtCkHPwrc/sBHVS3aivNWGkJzA -eBXBdWJAg3ZC6T9U+Y8cndWQrpYMJvek1IewlyDSspHZDFmM1OwVwypnMt4fGgaX -5IlUMnNgKmisOSuI529rxLF+mvYIQLRl5bP+1/c9JD5MZ5krA3SrjdwRFS3sQXC3 -nuHqJofFXNkbXQIDAQABo4GYMIGVMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMG -A1UdJQQMMAoGCCsGAQUFBwMBMCYGA1UdEQQfMB2CB0M2NVNUVDKCB0M2NVNUVDKC -CWxvY2FsaG9zdDAdBgNVHQ4EFgQURq22sa46tA0SGHhEm9jxGP9aDrswHwYDVR0j -BBgwFoAUmYjbq/1x7Kkn3VpwdTT4v6pefrcwDQYJKoZIhvcNAQELBQADggEBAKUP -7RgmJyMVoHxg46F1fjWVhlF4BbQuEtB8mC+4G4e68lDU/TPAbmB3aj91oQDgBiTd -R2O7U6tyitxxrU2r7rFAHGhFHeyCQ3yZMwydO2V3Nm2Ywzdyk8er4yghjg9FS8tH -egDGDDod3l1yrAbHHuXmzDjnAFwHwRkm5cYUz00/IuZ3sQZ70XofL3KXNj1tAtfK -PSpdSAxSTO99ofjVKjlyywQSZKNbXfqD5DGz8e0rmqPfZ+3zi75E5nEuJ3UI2wXg -LuI4j6FIzNQyei/FdSynktcIm+hefQEyex4cho4C8RYB2S5S8RWrnP9jOzsaQFHn -bHXf7dKwRfA6/u8JmtQ= ------END CERTIFICATE----- diff --git a/modules/postgres/testdata/certs/server_key.pem b/modules/postgres/testdata/certs/server_key.pem deleted file mode 100644 index dfbfb6db7e..0000000000 --- a/modules/postgres/testdata/certs/server_key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAqR0QXKtbKVeEuCmZGcZAlAlTBC8E/G3UuX6qKwTR1xEOvUWe -BH1n0WeXXGd/p/y6P4lRBeWNBZ9KcvIlNDeDMy05NfxnO1vnJk9E8/0xwMiY1LJd -MHzIzhmrrqXo0u3DT8MmoNR67CTcnG21gi1GrjW8a747yFF0xfukEc6FkyVqLsjt -CkHPwrc/sBHVS3aivNWGkJzAeBXBdWJAg3ZC6T9U+Y8cndWQrpYMJvek1IewlyDS -spHZDFmM1OwVwypnMt4fGgaX5IlUMnNgKmisOSuI529rxLF+mvYIQLRl5bP+1/c9 -JD5MZ5krA3SrjdwRFS3sQXC3nuHqJofFXNkbXQIDAQABAoIBAA0dxvYZCEIFmrKZ -71jzanDQ5FJvvyhA8H3OmC4r+oZ+uTDu5FmezF2OdkvhbyI9VMi2wsT9T9m+yAxw -QXhyUce3WzeXsv4Em8H55fQykBhOtqPQja/EDeMGVK2ACrXJYRufnDBfKoWEOmQb -kjddgZzjaBDHOWXJA5CTet8ysGOAJBTxyzU69k5Vj9B5abG9CofNzGOFF+Uleff5 -ip3sz7JpDXCex3oEs98veco6+8i/MZNo3BnwB5J+P+2MFFKONfPwuNyKAWBza2/X -66Lk3xXBjLJJ+Ww16jkqueTXEq6GCFXavNfdL9aonth5V5YYR/cj+2u2LM1oj9cJ -bp0xbvUCgYEA2Svq1DyR9cfTwrbc/0J2JfrjavClzDYU2oeO2fSU85WEEjJguaja -17Vdo/UsJtiUiSq4UhI1n0haaIpTBCeF2tHGXVEYZ7ZBi1zzdWbWlDxFmi+rcE57 -ytx5w+iLE366tQEMa/Jn3bly54pG5JZAr9TXkpg9sMbzWZri2ocyU/cCgYEAx1l/ -9X9C/OruDp/MhhmVwKfw/X2+RhZRuv0pPcpJu7/gIoLgaxNj41XSeLqLYMlisaRk -GFU17GFXtfRGE1a3z+jj8UPTP2sHk3w8m0yI+pgWgsvG0TJ0B+XsRfpVxFiIoaEs -3AsBaGR+hrRY1dpaJ9Cu3J9mEeToTpbCzPzVDksCgYEAzwSvWNvYY4u2UFHSvz2S -tMfBzCpUUiNno50/ToN5De4ENPhy/eh5nNDVz7qh+PHSPiNMC2gyV4E4NZlOY5Jt -Zdc8ma35brvtJTVZGxwKBsqhqsYwTeFy3kFnjZn6IX5X6r1yIuCzpEfowdEtnS+h -wDtLuAGKJR6x0UP1Zk0ka6cCgYBGE6I1rJzhx7wTi/0bjtbjuKWwlolSnfnxH5ll -zTyKMXMa7qLxQQm2Gq84HWtthJ2bEMzW+O1RwQ5SOiKAHdXT0mx+nXcfLgKlx+CO -PyNP5DLVm8iyNWgwdpTOLKgFs5GkL8JTP9Mo3VrVA4TO+EkFAgjWKXp6A9vd9IVa -Be7nbQKBgAVtFKuf9nbCMfN+W1gN0vlW2lwxCTa4w0KHgIlGIIvnYVuixSgu9fGt -uylQcQirEjqrdzdVF9L2BQ37ZcLaGh1LoCmx8XVCX/HhbwW2RP798P3Z1P7htm16 -ha5OfuPjHvoZklbYJo6EORJZQehS2VP63pjdnmUeMHPFzrPUevI5 ------END RSA PRIVATE KEY----- From eac665dd4cee0b8df7385b160d4aa160a675ba33 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Fri, 12 Apr 2024 09:14:50 -0400 Subject: [PATCH 08/42] Make entrypoint not-optional --- modules/postgres/go.mod | 2 +- modules/postgres/go.sum | 4 +-- modules/postgres/postgres.go | 45 +++++++++++++++++++++++++++++++ modules/postgres/postgres_test.go | 1 - 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/modules/postgres/go.mod b/modules/postgres/go.mod index 9b9dc277b4..1c4e979d03 100644 --- a/modules/postgres/go.mod +++ b/modules/postgres/go.mod @@ -6,7 +6,7 @@ require ( github.com/docker/go-connections v0.5.0 github.com/jackc/pgx/v5 v5.5.4 github.com/lib/pq v1.10.9 - github.com/mdelapenya/tlscert v0.0.0-20240411155228-ea0834bcfa84 + github.com/mdelapenya/tlscert v0.1.0 github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.29.1 diff --git a/modules/postgres/go.sum b/modules/postgres/go.sum index 7a4ad1c988..e36f3f54db 100644 --- a/modules/postgres/go.sum +++ b/modules/postgres/go.sum @@ -75,8 +75,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mdelapenya/tlscert v0.0.0-20240411155228-ea0834bcfa84 h1:6ZdtWuke1JydwlgIMwmC1IMTGXgl0wUWvFGvHSgv78Y= -github.com/mdelapenya/tlscert v0.0.0-20240411155228-ea0834bcfa84/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= +github.com/mdelapenya/tlscert v0.1.0 h1:YTpF579PYUX475eOL+6zyEO3ngLTOUWck78NBuJVXaM= +github.com/mdelapenya/tlscert v0.1.0/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index bab78ea902..246e8b89c0 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -197,9 +197,54 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt // TODO: Can we detect TLS by port. I don't think so... // Probably use logs req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) + + internalEntrypoint(req) } } +func internalEntrypoint(req *testcontainers.GenericContainerRequest) { + + const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" + + entrypoint := ` + #!/usr/bin/env bash + set -Eeo pipefail + + + pUID=$(id -u postgres) + pGID=$(id -g postgres) + + if [ -z "$pUID" ] + then + exit 1 + fi + + if [ -z "$pGID" ] + then + exit 1 + fi + + chown "$pUID":"$pGID" /tmp/data/ca_cert.pem + chown "$pUID":"$pGID" /tmp/data/server.cert + chown "$pUID":"$pGID" /tmp/data/server.key + + ls -lah /tmp/data + /usr/local/bin/docker-entrypoint.sh "$@" + + ` + + reader := strings.NewReader(entrypoint) + + req.Files = append(req.Files, testcontainers.ContainerFile{ + Reader: reader, + ContainerFilePath: entrypointPath, + FileMode: 0o666, + }) + + req.Entrypoint = []string{"sh", entrypointPath} + +} + func WithEntrypoint(hostEntrypointPath string) testcontainers.CustomizeRequestOption { const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 4951b73e7b..23b032130d 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -257,7 +257,6 @@ func TestWithSSLEnabledConfigFile(t *testing.T) { postgres.WithPassword(password), testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), postgres.WithSSLSettings(sslSettings), - postgres.WithEntrypoint(filepath.Join("testdata", "docker-entrypoint-ssl.bash")), ) if err != nil { t.Fatal(err) From 302472cff945949c4629b58703ad201331d5535c Mon Sep 17 00:00:00 2001 From: bstrausser Date: Fri, 12 Apr 2024 09:19:16 -0400 Subject: [PATCH 09/42] Add docstring --- modules/postgres/postgres.go | 6 +++--- modules/postgres/testdata/docker-entrypoint-ssl.bash | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 246e8b89c0..3c0c2d39ad 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -170,6 +170,9 @@ func WithSnapshotName(name string) SnapshotOption { } } +// WithSSLSettings configures the Postgres server to run with the provided CA Chain +// This will not function if the corresponding postgres conf is not correctly configured. +// Namely the paths below must match what is set in the conf file func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOption { const postgresCaCertPath = "/tmp/data/ca_cert.pem" const postgresCertPath = "/tmp/data/server.cert" @@ -194,8 +197,6 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt FileMode: defaultPermission, }) - // TODO: Can we detect TLS by port. I don't think so... - // Probably use logs req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) internalEntrypoint(req) @@ -228,7 +229,6 @@ func internalEntrypoint(req *testcontainers.GenericContainerRequest) { chown "$pUID":"$pGID" /tmp/data/server.cert chown "$pUID":"$pGID" /tmp/data/server.key - ls -lah /tmp/data /usr/local/bin/docker-entrypoint.sh "$@" ` diff --git a/modules/postgres/testdata/docker-entrypoint-ssl.bash b/modules/postgres/testdata/docker-entrypoint-ssl.bash index 50a0bd51e0..386dc0ebcc 100644 --- a/modules/postgres/testdata/docker-entrypoint-ssl.bash +++ b/modules/postgres/testdata/docker-entrypoint-ssl.bash @@ -19,5 +19,4 @@ chown "$pUID":"$pGID" /tmp/data/ca_cert.pem chown "$pUID":"$pGID" /tmp/data/server.cert chown "$pUID":"$pGID" /tmp/data/server.key -ls -lah /tmp/data /usr/local/bin/docker-entrypoint.sh "$@" \ No newline at end of file From 3099790c37cafebb131bc8196e31b41b0b9de4e8 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Wed, 24 Apr 2024 08:56:35 -0400 Subject: [PATCH 10/42] Spaces to tab in entrypoint --- modules/postgres/postgres.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 3c0c2d39ad..21179b9304 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -217,12 +217,12 @@ func internalEntrypoint(req *testcontainers.GenericContainerRequest) { if [ -z "$pUID" ] then - exit 1 + exit 1 fi if [ -z "$pGID" ] then - exit 1 + exit 1 fi chown "$pUID":"$pGID" /tmp/data/ca_cert.pem From e067d0f53b3a00746ff49e086bcbed7da930bcca Mon Sep 17 00:00:00 2001 From: bstrausser Date: Wed, 24 Apr 2024 09:14:15 -0400 Subject: [PATCH 11/42] Add postgres ssl docs --- docs/modules/postgres.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 47cb9115dc..3806fc1b4c 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -69,6 +69,27 @@ In the case you have a custom config file for Postgres, it's possible to copy th !!!tip For information on what is available to configure, see the [PostgreSQL docs](https://www.postgresql.org/docs/14/runtime-config.html) for the specific version of PostgreSQL that you are running. +#### SSL Configuration + +If you would like to use SSL with the container you can use the `WithSSLSettings` this function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/data/ca_cert.pem`, `tmp/data/server.cert` and `/tmp/data/server.key` + + If you use this function you must also provide a custom conf via `WithConfigFile`. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following + ``` + ssl = on +ssl_ca_file = '/tmp/data/ca_cert.pem' +ssl_cert_file = '/tmp/data/server.cert' +ssl_key_file = '/tmp/data/server.key' + ``` + + +This function assumes the postgres user in the container is `postgres` + +There is no current support for mutual authentication. + +The `SSLSettings` function will modify the container `entrypoint`. This is done so that key material copied over to the container is chowned by `postgres`. All other container arguments will be passed through to the original container entrypoint. + +- Not available until the next release of testcontainers-go + ### Container Methods #### ConnectionString From 2c7f62113246147c28a1439f599464ea0b147561 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Wed, 24 Apr 2024 09:28:31 -0400 Subject: [PATCH 12/42] Remove WithEntrypoint --- modules/postgres/postgres.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 21179b9304..bf69d6812f 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -245,23 +245,6 @@ func internalEntrypoint(req *testcontainers.GenericContainerRequest) { } -func WithEntrypoint(hostEntrypointPath string) testcontainers.CustomizeRequestOption { - - const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" - - return func(req *testcontainers.GenericContainerRequest) { - - req.Files = append(req.Files, testcontainers.ContainerFile{ - HostFilePath: hostEntrypointPath, - ContainerFilePath: entrypointPath, - FileMode: 0o666, - }) - - req.Entrypoint = []string{"sh", entrypointPath} - } - -} - // Snapshot takes a snapshot of the current state of the database as a template, which can then be restored using // the Restore method. By default, the snapshot will be created under a database called migrated_template, you can // customize the snapshot name with the options. From b9cd59b6ae4d5377dd53959cb69c56501a001379 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Wed, 24 Apr 2024 12:09:51 -0400 Subject: [PATCH 13/42] Update docs/modules/postgres.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- docs/modules/postgres.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 3806fc1b4c..59a706a8ad 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -88,8 +88,6 @@ There is no current support for mutual authentication. The `SSLSettings` function will modify the container `entrypoint`. This is done so that key material copied over to the container is chowned by `postgres`. All other container arguments will be passed through to the original container entrypoint. -- Not available until the next release of testcontainers-go - ### Container Methods #### ConnectionString From ef017c9498b68c254d135ce1c86d31ef5690d08a Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Wed, 24 Apr 2024 12:10:33 -0400 Subject: [PATCH 14/42] Update docs/modules/postgres.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- docs/modules/postgres.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 59a706a8ad..a40513b324 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -71,6 +71,8 @@ In the case you have a custom config file for Postgres, it's possible to copy th #### SSL Configuration +- Not available until the next release of testcontainers-go :material-tag: main + If you would like to use SSL with the container you can use the `WithSSLSettings` this function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/data/ca_cert.pem`, `tmp/data/server.cert` and `/tmp/data/server.key` If you use this function you must also provide a custom conf via `WithConfigFile`. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following From fd3d3e5f5e80e68dae242ab756f206b027ad589c Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Wed, 24 Apr 2024 12:10:50 -0400 Subject: [PATCH 15/42] Update docs/modules/postgres.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- docs/modules/postgres.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index a40513b324..90aff8f185 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -73,7 +73,7 @@ In the case you have a custom config file for Postgres, it's possible to copy th - Not available until the next release of testcontainers-go :material-tag: main -If you would like to use SSL with the container you can use the `WithSSLSettings` this function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/data/ca_cert.pem`, `tmp/data/server.cert` and `/tmp/data/server.key` +If you would like to use SSL with the container you can use the `WithSSLSettings`. This function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/data/ca_cert.pem`, `tmp/data/server.cert` and `/tmp/data/server.key` If you use this function you must also provide a custom conf via `WithConfigFile`. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following ``` From cd1b63fbacc65227e9f775861f05a4e74488d777 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Wed, 24 Apr 2024 12:11:07 -0400 Subject: [PATCH 16/42] Update modules/postgres/postgres_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- modules/postgres/postgres_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 23b032130d..ef62a92ec2 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -68,7 +68,6 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e } func createSSLSettings(t *testing.T) postgres.SSLSettings { - caCert, serverCerts, err := createSSLCerts(t) if err != nil { t.Fatal(err) From c7eebeb2e3dfd0704a0793caef30596f6eb5dba6 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Wed, 24 Apr 2024 12:12:53 -0400 Subject: [PATCH 17/42] Update modules/postgres/postgres_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- modules/postgres/postgres_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index ef62a92ec2..afe731ee99 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -30,7 +30,6 @@ const ( ) func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, error) { - tmpDir := t.TempDir() certsDir := tmpDir + "/certs" From a36eafaec5ae6df467e2f7f2a4ae9569a92a810a Mon Sep 17 00:00:00 2001 From: bstrausser Date: Wed, 24 Apr 2024 16:36:19 -0400 Subject: [PATCH 18/42] Embed resources + Use custom conf automatically --- docs/modules/postgres.md | 7 +- modules/postgres/postgres.go | 51 ++++++------ modules/postgres/postgres_test.go | 5 +- .../postgres/resources/customEntrypoint.sh | 22 +++++ modules/postgres/resources/postgres-ssl.conf | 80 +++++++++++++++++++ 5 files changed, 134 insertions(+), 31 deletions(-) create mode 100644 modules/postgres/resources/customEntrypoint.sh create mode 100644 modules/postgres/resources/postgres-ssl.conf diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 90aff8f185..7ae807d14c 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -66,6 +66,8 @@ An example of a `*.sh` script that creates a user and database is shown below: In the case you have a custom config file for Postgres, it's possible to copy that file into the container before it's started, using the `WithConfigFile(cfgPath string)` function. +This function can be used `WithSSLSettings` but requires your configuration correctly sets the SSL properties. See the below section for more information. + !!!tip For information on what is available to configure, see the [PostgreSQL docs](https://www.postgresql.org/docs/14/runtime-config.html) for the specific version of PostgreSQL that you are running. @@ -75,7 +77,10 @@ In the case you have a custom config file for Postgres, it's possible to copy th If you would like to use SSL with the container you can use the `WithSSLSettings`. This function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/data/ca_cert.pem`, `tmp/data/server.cert` and `/tmp/data/server.key` - If you use this function you must also provide a custom conf via `WithConfigFile`. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following +This function will inject a custom postgres configuration file that enables SSL and correctly sets the paths on the key material. + + +If you use this function in conjuction with `WithConfigFile` your custom conf must set the require ssl fields. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following ``` ssl = on ssl_ca_file = '/tmp/data/ca_cert.pem' diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index bf69d6812f..a71480bc1e 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -7,6 +7,8 @@ import ( "path/filepath" "strings" + _ "embed" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) @@ -18,6 +20,13 @@ const ( defaultSnapshotName = "migrated_template" ) +var ( + //go:embed resources/postgres-ssl.conf + embeddedConf string + //go:embed resources/customEntrypoint.sh + embeddedCustomEntrypoint string +) + // PostgresContainer represents the postgres container type used in the module type PostgresContainer struct { testcontainers.Container @@ -200,40 +209,27 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) internalEntrypoint(req) + overwriteConfForSSL(req) } } -func internalEntrypoint(req *testcontainers.GenericContainerRequest) { - - const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" - - entrypoint := ` - #!/usr/bin/env bash - set -Eeo pipefail - +func overwriteConfForSSL(req *testcontainers.GenericContainerRequest) { + const confPath = "/etc/postgresql.conf" - pUID=$(id -u postgres) - pGID=$(id -g postgres) - - if [ -z "$pUID" ] - then - exit 1 - fi - - if [ -z "$pGID" ] - then - exit 1 - fi + cfgFile := testcontainers.ContainerFile{ + ContainerFilePath: confPath, + FileMode: 0o755, + Reader: strings.NewReader(embeddedConf), + } - chown "$pUID":"$pGID" /tmp/data/ca_cert.pem - chown "$pUID":"$pGID" /tmp/data/server.cert - chown "$pUID":"$pGID" /tmp/data/server.key + req.Files = append(req.Files, cfgFile) + req.Cmd = append(req.Cmd, "-c", "config_file=/etc/postgresql.conf") +} - /usr/local/bin/docker-entrypoint.sh "$@" - - ` +func internalEntrypoint(req *testcontainers.GenericContainerRequest) { + const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" - reader := strings.NewReader(entrypoint) + reader := strings.NewReader(embeddedCustomEntrypoint) req.Files = append(req.Files, testcontainers.ContainerFile{ Reader: reader, @@ -242,7 +238,6 @@ func internalEntrypoint(req *testcontainers.GenericContainerRequest) { }) req.Entrypoint = []string{"sh", entrypointPath} - } // Snapshot takes a snapshot of the current state of the database as a template, which can then be restored using diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index afe731ee99..ade48c92a1 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -30,6 +30,7 @@ const ( ) func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, error) { + tmpDir := t.TempDir() certsDir := tmpDir + "/certs" @@ -67,6 +68,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e } func createSSLSettings(t *testing.T) postgres.SSLSettings { + caCert, serverCerts, err := createSSLCerts(t) if err != nil { t.Fatal(err) @@ -242,13 +244,12 @@ func TestWithConfigFile(t *testing.T) { defer db.Close() } -func TestWithSSLEnabledConfigFile(t *testing.T) { +func TestWithSSL(t *testing.T) { ctx := context.Background() sslSettings := createSSLSettings(t) container, err := postgres.RunContainer(ctx, - postgres.WithConfigFile(filepath.Join("testdata", "my-postgres-ssl.conf")), postgres.WithInitScripts(filepath.Join("testdata", "init-user-db.sh")), postgres.WithDatabase(dbname), postgres.WithUsername(user), diff --git a/modules/postgres/resources/customEntrypoint.sh b/modules/postgres/resources/customEntrypoint.sh new file mode 100644 index 0000000000..8b0102df6b --- /dev/null +++ b/modules/postgres/resources/customEntrypoint.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -Eeo pipefail + + +pUID=$(id -u postgres) +pGID=$(id -g postgres) + +if [ -z "$pUID" ] +then + exit 1 +fi + +if [ -z "$pGID" ] +then + exit 1 +fi + +chown "$pUID":"$pGID" /tmp/data/ca_cert.pem +chown "$pUID":"$pGID" /tmp/data/server.cert +chown "$pUID":"$pGID" /tmp/data/server.key + +/usr/local/bin/docker-entrypoint.sh "$@" \ No newline at end of file diff --git a/modules/postgres/resources/postgres-ssl.conf b/modules/postgres/resources/postgres-ssl.conf new file mode 100644 index 0000000000..51f92d5621 --- /dev/null +++ b/modules/postgres/resources/postgres-ssl.conf @@ -0,0 +1,80 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: B = bytes Time units: ms = milliseconds +# kB = kilobytes s = seconds +# MB = megabytes min = minutes +# GB = gigabytes h = hours +# TB = terabytes d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) + +# - SSL - + +ssl = on +ssl_ca_file = '/tmp/data/ca_cert.pem' +ssl_cert_file = '/tmp/data/server.cert' +#ssl_crl_file = '' +ssl_key_file = '/tmp/data/server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + From 7ea323a40b4c0b4288f4aeec6a5cab77b80839ed Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Fri, 26 Apr 2024 11:04:27 -0400 Subject: [PATCH 19/42] Update docs/modules/postgres.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- docs/modules/postgres.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 7ae807d14c..5d08fe3a9b 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -79,7 +79,6 @@ If you would like to use SSL with the container you can use the `WithSSLSettings This function will inject a custom postgres configuration file that enables SSL and correctly sets the paths on the key material. - If you use this function in conjuction with `WithConfigFile` your custom conf must set the require ssl fields. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following ``` ssl = on From 0557c9de83b8ea4595dffbc638d86a6e4b9d6798 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Fri, 26 Apr 2024 11:04:35 -0400 Subject: [PATCH 20/42] Update docs/modules/postgres.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- docs/modules/postgres.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 5d08fe3a9b..804803fde1 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -87,7 +87,6 @@ ssl_cert_file = '/tmp/data/server.cert' ssl_key_file = '/tmp/data/server.key' ``` - This function assumes the postgres user in the container is `postgres` There is no current support for mutual authentication. From 5d0fa1efd9f09c96b2644db106d9480f17179fc9 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Fri, 26 Apr 2024 11:04:45 -0400 Subject: [PATCH 21/42] Update docs/modules/postgres.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- docs/modules/postgres.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 804803fde1..dd89d10d06 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -79,7 +79,8 @@ If you would like to use SSL with the container you can use the `WithSSLSettings This function will inject a custom postgres configuration file that enables SSL and correctly sets the paths on the key material. -If you use this function in conjuction with `WithConfigFile` your custom conf must set the require ssl fields. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following +If you use this function in conjuction with `WithConfigFile` your custom conf must set the require ssl fields. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following: + ``` ssl = on ssl_ca_file = '/tmp/data/ca_cert.pem' From 7805e1f51c0b6cc1315ff376513fe777d068bc23 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Fri, 26 Apr 2024 11:04:56 -0400 Subject: [PATCH 22/42] Update modules/postgres/postgres_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- modules/postgres/postgres_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index ade48c92a1..2097b7184b 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -79,7 +79,6 @@ func createSSLSettings(t *testing.T) postgres.SSLSettings { CertFile: serverCerts.CertPath, KeyFile: serverCerts.KeyPath, } - } func TestPostgres(t *testing.T) { From c80fa83442e88c3cc8ab95b71e29f6d9cb15b37b Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Fri, 26 Apr 2024 11:05:02 -0400 Subject: [PATCH 23/42] Update modules/postgres/postgres_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- modules/postgres/postgres_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 2097b7184b..6390b11127 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -68,7 +68,6 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e } func createSSLSettings(t *testing.T) postgres.SSLSettings { - caCert, serverCerts, err := createSSLCerts(t) if err != nil { t.Fatal(err) From e73f9a5040a4d942a120bf187f7e92d9edff138c Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Fri, 26 Apr 2024 11:05:11 -0400 Subject: [PATCH 24/42] Update modules/postgres/postgres_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- modules/postgres/postgres_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 6390b11127..e3ccddfb39 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -30,7 +30,6 @@ const ( ) func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, error) { - tmpDir := t.TempDir() certsDir := tmpDir + "/certs" From 1be6a31e46eb06257bba28295e6cac28ac2e4a7a Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Fri, 26 Apr 2024 11:05:22 -0400 Subject: [PATCH 25/42] Update modules/postgres/postgres_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- modules/postgres/postgres_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index e3ccddfb39..4ba0c05293 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -63,7 +63,6 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e } return caCert, cert, nil - } func createSSLSettings(t *testing.T) postgres.SSLSettings { From 4e6c6d77fc9b36e6adb2adb392f627e564f22530 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Fri, 26 Apr 2024 11:29:27 -0400 Subject: [PATCH 26/42] Revert to use passed in conf --- modules/postgres/postgres.go | 16 - modules/postgres/postgres_test.go | 1 + .../testdata/docker-entrypoint-ssl.bash | 22 - .../postgres/testdata/my-postgres-ssl.conf | 691 ------------------ .../{resources => testdata}/postgres-ssl.conf | 0 5 files changed, 1 insertion(+), 729 deletions(-) delete mode 100644 modules/postgres/testdata/docker-entrypoint-ssl.bash delete mode 100644 modules/postgres/testdata/my-postgres-ssl.conf rename modules/postgres/{resources => testdata}/postgres-ssl.conf (100%) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index a71480bc1e..362f38399f 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -21,8 +21,6 @@ const ( ) var ( - //go:embed resources/postgres-ssl.conf - embeddedConf string //go:embed resources/customEntrypoint.sh embeddedCustomEntrypoint string ) @@ -209,23 +207,9 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) internalEntrypoint(req) - overwriteConfForSSL(req) } } -func overwriteConfForSSL(req *testcontainers.GenericContainerRequest) { - const confPath = "/etc/postgresql.conf" - - cfgFile := testcontainers.ContainerFile{ - ContainerFilePath: confPath, - FileMode: 0o755, - Reader: strings.NewReader(embeddedConf), - } - - req.Files = append(req.Files, cfgFile) - req.Cmd = append(req.Cmd, "-c", "config_file=/etc/postgresql.conf") -} - func internalEntrypoint(req *testcontainers.GenericContainerRequest) { const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 4ba0c05293..a0e6133019 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -246,6 +246,7 @@ func TestWithSSL(t *testing.T) { sslSettings := createSSLSettings(t) container, err := postgres.RunContainer(ctx, + postgres.WithConfigFile(filepath.Join("testdata", "postgres-ssl.conf")), postgres.WithInitScripts(filepath.Join("testdata", "init-user-db.sh")), postgres.WithDatabase(dbname), postgres.WithUsername(user), diff --git a/modules/postgres/testdata/docker-entrypoint-ssl.bash b/modules/postgres/testdata/docker-entrypoint-ssl.bash deleted file mode 100644 index 386dc0ebcc..0000000000 --- a/modules/postgres/testdata/docker-entrypoint-ssl.bash +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -Eeo pipefail - - -pUID=$(id -u postgres) -pGID=$(id -g postgres) - -if [ -z "$pUID" ] -then - exit 1 -fi - -if [ -z "$pGID" ] -then - exit 1 -fi - -chown "$pUID":"$pGID" /tmp/data/ca_cert.pem -chown "$pUID":"$pGID" /tmp/data/server.cert -chown "$pUID":"$pGID" /tmp/data/server.key - -/usr/local/bin/docker-entrypoint.sh "$@" \ No newline at end of file diff --git a/modules/postgres/testdata/my-postgres-ssl.conf b/modules/postgres/testdata/my-postgres-ssl.conf deleted file mode 100644 index 0aa6f94739..0000000000 --- a/modules/postgres/testdata/my-postgres-ssl.conf +++ /dev/null @@ -1,691 +0,0 @@ -# ----------------------------- -# PostgreSQL configuration file -# ----------------------------- -# -# This file consists of lines of the form: -# -# name = value -# -# (The "=" is optional.) Whitespace may be used. Comments are introduced with -# "#" anywhere on a line. The complete list of parameter names and allowed -# values can be found in the PostgreSQL documentation. -# -# The commented-out settings shown in this file represent the default values. -# Re-commenting a setting is NOT sufficient to revert it to the default value; -# you need to reload the server. -# -# This file is read on server startup and when the server receives a SIGHUP -# signal. If you edit the file on a running system, you have to SIGHUP the -# server for the changes to take effect, run "pg_ctl reload", or execute -# "SELECT pg_reload_conf()". Some parameters, which are marked below, -# require a server shutdown and restart to take effect. -# -# Any parameter can also be given as a command-line option to the server, e.g., -# "postgres -c log_connections=on". Some parameters can be changed at run time -# with the "SET" SQL command. -# -# Memory units: B = bytes Time units: ms = milliseconds -# kB = kilobytes s = seconds -# MB = megabytes min = minutes -# GB = gigabytes h = hours -# TB = terabytes d = days - - -#------------------------------------------------------------------------------ -# FILE LOCATIONS -#------------------------------------------------------------------------------ - -# The default values of these variables are driven from the -D command-line -# option or PGDATA environment variable, represented here as ConfigDir. - -#data_directory = 'ConfigDir' # use data in another directory - # (change requires restart) -#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file - # (change requires restart) -#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file - # (change requires restart) - -# If external_pid_file is not explicitly set, no extra PID file is written. -#external_pid_file = '' # write an extra PID file - # (change requires restart) - - -#------------------------------------------------------------------------------ -# CONNECTIONS AND AUTHENTICATION -#------------------------------------------------------------------------------ - -# - Connection Settings - - -listen_addresses = '*' - # comma-separated list of addresses; - # defaults to 'localhost'; use '*' for all - # (change requires restart) -#port = 5432 # (change requires restart) -#max_connections = 100 # (change requires restart) -#superuser_reserved_connections = 3 # (change requires restart) -#unix_socket_directories = '/tmp' # comma-separated list of directories - # (change requires restart) -#unix_socket_group = '' # (change requires restart) -#unix_socket_permissions = 0777 # begin with 0 to use octal notation - # (change requires restart) -#bonjour = off # advertise server via Bonjour - # (change requires restart) -#bonjour_name = '' # defaults to the computer name - # (change requires restart) - -# - TCP Keepalives - -# see "man 7 tcp" for details - -#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; - # 0 selects the system default -#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; - # 0 selects the system default -#tcp_keepalives_count = 0 # TCP_KEEPCNT; - # 0 selects the system default - -# - Authentication - - -#authentication_timeout = 1min # 1s-600s -#password_encryption = md5 # md5 or scram-sha-256 -#db_user_namespace = off - -# GSSAPI using Kerberos -#krb_server_keyfile = '' -#krb_caseins_users = off - -# - SSL - - -ssl = on -ssl_ca_file = '/tmp/data/ca_cert.pem' -ssl_cert_file = '/tmp/data/server.cert' -#ssl_crl_file = '' -ssl_key_file = '/tmp/data/server.key' -#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers -#ssl_prefer_server_ciphers = on -#ssl_ecdh_curve = 'prime256v1' -#ssl_dh_params_file = '' -#ssl_passphrase_command = '' -#ssl_passphrase_command_supports_reload = off - - -#------------------------------------------------------------------------------ -# RESOURCE USAGE (except WAL) -#------------------------------------------------------------------------------ - -# - Memory - - -#shared_buffers = 32MB # min 128kB - # (change requires restart) -#huge_pages = try # on, off, or try - # (change requires restart) -#temp_buffers = 8MB # min 800kB -#max_prepared_transactions = 0 # zero disables the feature - # (change requires restart) -# Caution: it is not advisable to set max_prepared_transactions nonzero unless -# you actively intend to use prepared transactions. -#work_mem = 4MB # min 64kB -#maintenance_work_mem = 64MB # min 1MB -#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem -#max_stack_depth = 2MB # min 100kB -#dynamic_shared_memory_type = posix # the default is the first option - # supported by the operating system: - # posix - # sysv - # windows - # mmap - # use none to disable dynamic shared memory - # (change requires restart) - -# - Disk - - -#temp_file_limit = -1 # limits per-process temp file space - # in kB, or -1 for no limit - -# - Kernel Resources - - -#max_files_per_process = 1000 # min 25 - # (change requires restart) - -# - Cost-Based Vacuum Delay - - -#vacuum_cost_delay = 0 # 0-100 milliseconds -#vacuum_cost_page_hit = 1 # 0-10000 credits -#vacuum_cost_page_miss = 10 # 0-10000 credits -#vacuum_cost_page_dirty = 20 # 0-10000 credits -#vacuum_cost_limit = 200 # 1-10000 credits - -# - Background Writer - - -#bgwriter_delay = 200ms # 10-10000ms between rounds -#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables -#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round -#bgwriter_flush_after = 0 # measured in pages, 0 disables - -# - Asynchronous Behavior - - -#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching -#max_worker_processes = 8 # (change requires restart) -#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers -#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers -#parallel_leader_participation = on -#max_parallel_workers = 8 # maximum number of max_worker_processes that - # can be used in parallel operations -#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate - # (change requires restart) -#backend_flush_after = 0 # measured in pages, 0 disables - - -#------------------------------------------------------------------------------ -# WRITE-AHEAD LOG -#------------------------------------------------------------------------------ - -# - Settings - - -#wal_level = replica # minimal, replica, or logical - # (change requires restart) -#fsync = on # flush data to disk for crash safety - # (turning this off can cause - # unrecoverable data corruption) -#synchronous_commit = on # synchronization level; - # off, local, remote_write, remote_apply, or on -#wal_sync_method = fsync # the default is the first option - # supported by the operating system: - # open_datasync - # fdatasync (default on Linux and FreeBSD) - # fsync - # fsync_writethrough - # open_sync -#full_page_writes = on # recover from partial page writes -#wal_compression = off # enable compression of full-page writes -#wal_log_hints = off # also do full page writes of non-critical updates - # (change requires restart) -#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers - # (change requires restart) -#wal_writer_delay = 200ms # 1-10000 milliseconds -#wal_writer_flush_after = 1MB # measured in pages, 0 disables - -#commit_delay = 0 # range 0-100000, in microseconds -#commit_siblings = 5 # range 1-1000 - -# - Checkpoints - - -#checkpoint_timeout = 5min # range 30s-1d -#max_wal_size = 1GB -#min_wal_size = 80MB -#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 -#checkpoint_flush_after = 0 # measured in pages, 0 disables -#checkpoint_warning = 30s # 0 disables - -# - Archiving - - -#archive_mode = off # enables archiving; off, on, or always - # (change requires restart) -#archive_command = '' # command to use to archive a logfile segment - # placeholders: %p = path of file to archive - # %f = file name only - # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' -#archive_timeout = 0 # force a logfile segment switch after this - # number of seconds; 0 disables - - -#------------------------------------------------------------------------------ -# REPLICATION -#------------------------------------------------------------------------------ - -# - Sending Servers - - -# Set these on the master and on any standby that will send replication data. - -#max_wal_senders = 10 # max number of walsender processes - # (change requires restart) -#wal_keep_segments = 0 # in logfile segments; 0 disables -#wal_sender_timeout = 60s # in milliseconds; 0 disables - -#max_replication_slots = 10 # max number of replication slots - # (change requires restart) -#track_commit_timestamp = off # collect timestamp of transaction commit - # (change requires restart) - -# - Master Server - - -# These settings are ignored on a standby server. - -#synchronous_standby_names = '' # standby servers that provide sync rep - # method to choose sync standbys, number of sync standbys, - # and comma-separated list of application_name - # from standby(s); '*' = all -#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed - -# - Standby Servers - - -# These settings are ignored on a master server. - -#hot_standby = on # "off" disallows queries during recovery - # (change requires restart) -#max_standby_archive_delay = 30s # max delay before canceling queries - # when reading WAL from archive; - # -1 allows indefinite delay -#max_standby_streaming_delay = 30s # max delay before canceling queries - # when reading streaming WAL; - # -1 allows indefinite delay -#wal_receiver_status_interval = 10s # send replies at least this often - # 0 disables -#hot_standby_feedback = off # send info from standby to prevent - # query conflicts -#wal_receiver_timeout = 60s # time that receiver waits for - # communication from master - # in milliseconds; 0 disables -#wal_retrieve_retry_interval = 5s # time to wait before retrying to - # retrieve WAL after a failed attempt - -# - Subscribers - - -# These settings are ignored on a publisher. - -#max_logical_replication_workers = 4 # taken from max_worker_processes - # (change requires restart) -#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers - - -#------------------------------------------------------------------------------ -# QUERY TUNING -#------------------------------------------------------------------------------ - -# - Planner Method Configuration - - -#enable_bitmapscan = on -#enable_hashagg = on -#enable_hashjoin = on -#enable_indexscan = on -#enable_indexonlyscan = on -#enable_material = on -#enable_mergejoin = on -#enable_nestloop = on -#enable_parallel_append = on -#enable_seqscan = on -#enable_sort = on -#enable_tidscan = on -#enable_partitionwise_join = off -#enable_partitionwise_aggregate = off -#enable_parallel_hash = on -#enable_partition_pruning = on - -# - Planner Cost Constants - - -#seq_page_cost = 1.0 # measured on an arbitrary scale -#random_page_cost = 4.0 # same scale as above -#cpu_tuple_cost = 0.01 # same scale as above -#cpu_index_tuple_cost = 0.005 # same scale as above -#cpu_operator_cost = 0.0025 # same scale as above -#parallel_tuple_cost = 0.1 # same scale as above -#parallel_setup_cost = 1000.0 # same scale as above - -#jit_above_cost = 100000 # perform JIT compilation if available - # and query more expensive than this; - # -1 disables -#jit_inline_above_cost = 500000 # inline small functions if query is - # more expensive than this; -1 disables -#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if - # query is more expensive than this; - # -1 disables - -#min_parallel_table_scan_size = 8MB -#min_parallel_index_scan_size = 512kB -#effective_cache_size = 4GB - -# - Genetic Query Optimizer - - -#geqo = on -#geqo_threshold = 12 -#geqo_effort = 5 # range 1-10 -#geqo_pool_size = 0 # selects default based on effort -#geqo_generations = 0 # selects default based on effort -#geqo_selection_bias = 2.0 # range 1.5-2.0 -#geqo_seed = 0.0 # range 0.0-1.0 - -# - Other Planner Options - - -#default_statistics_target = 100 # range 1-10000 -#constraint_exclusion = partition # on, off, or partition -#cursor_tuple_fraction = 0.1 # range 0.0-1.0 -#from_collapse_limit = 8 -#join_collapse_limit = 8 # 1 disables collapsing of explicit - # JOIN clauses -#force_parallel_mode = off -#jit = off # allow JIT compilation - - -#------------------------------------------------------------------------------ -# REPORTING AND LOGGING -#------------------------------------------------------------------------------ - -# - Where to Log - - -#log_destination = 'stderr' # Valid values are combinations of - # stderr, csvlog, syslog, and eventlog, - # depending on platform. csvlog - # requires logging_collector to be on. - -# This is used when logging to stderr: -#logging_collector = off # Enable capturing of stderr and csvlog - # into log files. Required to be on for - # csvlogs. - # (change requires restart) - -# These are only used if logging_collector is on: -#log_directory = 'log' # directory where log files are written, - # can be absolute or relative to PGDATA -#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, - # can include strftime() escapes -#log_file_mode = 0600 # creation mode for log files, - # begin with 0 to use octal notation -#log_truncate_on_rotation = off # If on, an existing log file with the - # same name as the new log file will be - # truncated rather than appended to. - # But such truncation only occurs on - # time-driven rotation, not on restarts - # or size-driven rotation. Default is - # off, meaning append to existing files - # in all cases. -#log_rotation_age = 1d # Automatic rotation of logfiles will - # happen after that time. 0 disables. -#log_rotation_size = 10MB # Automatic rotation of logfiles will - # happen after that much log output. - # 0 disables. - -# These are relevant when logging to syslog: -#syslog_facility = 'LOCAL0' -#syslog_ident = 'postgres' -#syslog_sequence_numbers = on -#syslog_split_messages = on - -# This is only relevant when logging to eventlog (win32): -# (change requires restart) -#event_source = 'PostgreSQL' - -# - When to Log - - -#log_min_messages = warning # values in order of decreasing detail: - # debug5 - # debug4 - # debug3 - # debug2 - # debug1 - # info - # notice - # warning - # error - # log - # fatal - # panic - -#log_min_error_statement = error # values in order of decreasing detail: - # debug5 - # debug4 - # debug3 - # debug2 - # debug1 - # info - # notice - # warning - # error - # log - # fatal - # panic (effectively off) - -#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements - # and their durations, > 0 logs only - # statements running at least this number - # of milliseconds - - -# - What to Log - - -#debug_print_parse = off -#debug_print_rewritten = off -#debug_print_plan = off -#debug_pretty_print = on -#log_checkpoints = off -#log_connections = off -#log_disconnections = off -#log_duration = off -#log_error_verbosity = default # terse, default, or verbose messages -#log_hostname = off -#log_line_prefix = '%m [%p] ' # special values: - # %a = application name - # %u = user name - # %d = database name - # %r = remote host and port - # %h = remote host - # %p = process ID - # %t = timestamp without milliseconds - # %m = timestamp with milliseconds - # %n = timestamp with milliseconds (as a Unix epoch) - # %i = command tag - # %e = SQL state - # %c = session ID - # %l = session line number - # %s = session start timestamp - # %v = virtual transaction ID - # %x = transaction ID (0 if none) - # %q = stop here in non-session - # processes - # %% = '%' - # e.g. '<%u%%%d> ' -#log_lock_waits = off # log lock waits >= deadlock_timeout -#log_statement = 'none' # none, ddl, mod, all -#log_replication_commands = off -#log_temp_files = -1 # log temporary files equal or larger - # than the specified size in kilobytes; - # -1 disables, 0 logs all temp files -#log_timezone = 'GMT' - -#------------------------------------------------------------------------------ -# PROCESS TITLE -#------------------------------------------------------------------------------ - -#cluster_name = '' # added to process titles if nonempty - # (change requires restart) -#update_process_title = on - - -#------------------------------------------------------------------------------ -# STATISTICS -#------------------------------------------------------------------------------ - -# - Query and Index Statistics Collector - - -#track_activities = on -#track_counts = on -#track_io_timing = off -#track_functions = none # none, pl, all -#track_activity_query_size = 1024 # (change requires restart) -#stats_temp_directory = 'pg_stat_tmp' - - -# - Monitoring - - -#log_parser_stats = off -#log_planner_stats = off -#log_executor_stats = off -#log_statement_stats = off - - -#------------------------------------------------------------------------------ -# AUTOVACUUM -#------------------------------------------------------------------------------ - -#autovacuum = on # Enable autovacuum subprocess? 'on' - # requires track_counts to also be on. -#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and - # their durations, > 0 logs only - # actions running at least this number - # of milliseconds. -#autovacuum_max_workers = 3 # max number of autovacuum subprocesses - # (change requires restart) -#autovacuum_naptime = 1min # time between autovacuum runs -#autovacuum_vacuum_threshold = 50 # min number of row updates before - # vacuum -#autovacuum_analyze_threshold = 50 # min number of row updates before - # analyze -#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum -#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze -#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum - # (change requires restart) -#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age - # before forced vacuum - # (change requires restart) -#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for - # autovacuum, in milliseconds; - # -1 means use vacuum_cost_delay -#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for - # autovacuum, -1 means use - # vacuum_cost_limit - - -#------------------------------------------------------------------------------ -# CLIENT CONNECTION DEFAULTS -#------------------------------------------------------------------------------ - -# - Statement Behavior - - -#client_min_messages = notice # values in order of decreasing detail: - # debug5 - # debug4 - # debug3 - # debug2 - # debug1 - # log - # notice - # warning - # error -#search_path = '"$user", public' # schema names -#row_security = on -#default_tablespace = '' # a tablespace name, '' uses the default -#temp_tablespaces = '' # a list of tablespace names, '' uses - # only default tablespace -#check_function_bodies = on -#default_transaction_isolation = 'read committed' -#default_transaction_read_only = off -#default_transaction_deferrable = off -#session_replication_role = 'origin' -#statement_timeout = 0 # in milliseconds, 0 is disabled -#lock_timeout = 0 # in milliseconds, 0 is disabled -#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled -#vacuum_freeze_min_age = 50000000 -#vacuum_freeze_table_age = 150000000 -#vacuum_multixact_freeze_min_age = 5000000 -#vacuum_multixact_freeze_table_age = 150000000 -#vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples - # before index cleanup, 0 always performs - # index cleanup -#bytea_output = 'hex' # hex, escape -#xmlbinary = 'base64' -#xmloption = 'content' -#gin_fuzzy_search_limit = 0 -#gin_pending_list_limit = 4MB - -# - Locale and Formatting - - -#datestyle = 'iso, mdy' -#intervalstyle = 'postgres' -#timezone = 'GMT' -#timezone_abbreviations = 'Default' # Select the set of available time zone - # abbreviations. Currently, there are - # Default - # Australia (historical usage) - # India - # You can create your own file in - # share/timezonesets/. -#extra_float_digits = 0 # min -15, max 3 -#client_encoding = sql_ascii # actually, defaults to database - # encoding - -# These settings are initialized by initdb, but they can be changed. -#lc_messages = 'C' # locale for system error message - # strings -#lc_monetary = 'C' # locale for monetary formatting -#lc_numeric = 'C' # locale for number formatting -#lc_time = 'C' # locale for time formatting - -# default configuration for text search -#default_text_search_config = 'pg_catalog.simple' - -# - Shared Library Preloading - - -#shared_preload_libraries = '' # (change requires restart) -#local_preload_libraries = '' -#session_preload_libraries = '' -#jit_provider = 'llvmjit' # JIT library to use - -# - Other Defaults - - -#dynamic_library_path = '$libdir' - - -#------------------------------------------------------------------------------ -# LOCK MANAGEMENT -#------------------------------------------------------------------------------ - -#deadlock_timeout = 1s -#max_locks_per_transaction = 64 # min 10 - # (change requires restart) -#max_pred_locks_per_transaction = 64 # min 10 - # (change requires restart) -#max_pred_locks_per_relation = -2 # negative values mean - # (max_pred_locks_per_transaction - # / -max_pred_locks_per_relation) - 1 -#max_pred_locks_per_page = 2 # min 0 - - -#------------------------------------------------------------------------------ -# VERSION AND PLATFORM COMPATIBILITY -#------------------------------------------------------------------------------ - -# - Previous PostgreSQL Versions - - -#array_nulls = on -#backslash_quote = safe_encoding # on, off, or safe_encoding -#default_with_oids = off -#escape_string_warning = on -#lo_compat_privileges = off -#operator_precedence_warning = off -#quote_all_identifiers = off -#standard_conforming_strings = on -#synchronize_seqscans = on - -# - Other Platforms and Clients - - -#transform_null_equals = off - - -#------------------------------------------------------------------------------ -# ERROR HANDLING -#------------------------------------------------------------------------------ - -#exit_on_error = off # terminate session on any error? -#restart_after_crash = on # reinitialize after backend crash? -#data_sync_retry = off # retry or panic on failure to fsync - # data? - # (change requires restart) - - -#------------------------------------------------------------------------------ -# CONFIG FILE INCLUDES -#------------------------------------------------------------------------------ - -# These options allow settings to be loaded from files other than the -# default postgresql.conf. Note that these are directives, not variable -# assignments, so they can usefully be given more than once. - -#include_dir = '...' # include files ending in '.conf' from - # a directory, e.g., 'conf.d' -#include_if_exists = '...' # include file only if it exists -#include = '...' # include file - - -#------------------------------------------------------------------------------ -# CUSTOMIZED OPTIONS -#------------------------------------------------------------------------------ - -# Add settings for extensions here diff --git a/modules/postgres/resources/postgres-ssl.conf b/modules/postgres/testdata/postgres-ssl.conf similarity index 100% rename from modules/postgres/resources/postgres-ssl.conf rename to modules/postgres/testdata/postgres-ssl.conf From e28361abcedce8d3671da99f60e3bbc44132673f Mon Sep 17 00:00:00 2001 From: bstrausser Date: Fri, 26 Apr 2024 11:31:37 -0400 Subject: [PATCH 27/42] Update doc for required conf --- docs/modules/postgres.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index dd89d10d06..b8d8353ec4 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -77,9 +77,9 @@ This function can be used `WithSSLSettings` but requires your configuration corr If you would like to use SSL with the container you can use the `WithSSLSettings`. This function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/data/ca_cert.pem`, `tmp/data/server.cert` and `/tmp/data/server.key` -This function will inject a custom postgres configuration file that enables SSL and correctly sets the paths on the key material. +This function requires a custom postgres configuration file that enables SSL and correctly sets the paths on the key material. -If you use this function in conjuction with `WithConfigFile` your custom conf must set the require ssl fields. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following: +If you use this function by itself or in conjuction with `WithConfigFile` your custom conf must set the require ssl fields. The configuration must correctly align the key material provided via `SSLSettings` with the server configuration, namely the paths. Your configuration will need to contain the following: ``` ssl = on From 5d8598fca57ea488bbd52cf54190f1cb685cc3e5 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Fri, 26 Apr 2024 11:52:03 -0400 Subject: [PATCH 28/42] Error checking in the customizer --- modules/postgres/postgres.go | 12 +++++++++++- modules/postgres/postgres_test.go | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 3096c7e5f5..f5a5d68a0e 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net" + "os" "path/filepath" "strings" @@ -200,7 +201,7 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt const defaultPermission = 0o600 - return func(req *testcontainers.GenericContainerRequest) { + return func(req *testcontainers.GenericContainerRequest) error { req.Files = append(req.Files, testcontainers.ContainerFile{ HostFilePath: sslSettings.CACertFile, ContainerFilePath: postgresCaCertPath, @@ -217,9 +218,18 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt FileMode: defaultPermission, }) + expectedFiles := []string{sslSettings.CACertFile, sslSettings.CertFile, sslSettings.KeyFile} + for _, expectedFile := range expectedFiles { + _, err := os.Stat(expectedFile) + if err != nil { + return err + } + } + req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) internalEntrypoint(req) + return nil } } diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index de8cbd6999..2c70dde678 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -277,6 +277,26 @@ func TestWithSSL(t *testing.T) { assert.NotNil(t, result) } +func TestSSLValidatesKeyMaterialPath(t *testing.T) { + + ctx := context.Background() + + sslSettings := postgres.SSLSettings{} + + _, err := postgres.RunContainer(ctx, + postgres.WithConfigFile(filepath.Join("testdata", "postgres-ssl.conf")), + postgres.WithInitScripts(filepath.Join("testdata", "init-user-db.sh")), + postgres.WithDatabase(dbname), + postgres.WithUsername(user), + postgres.WithPassword(password), + testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), + postgres.WithSSLSettings(sslSettings), + ) + if err == nil { + t.Fatal(err) + } + +} func TestWithInitScript(t *testing.T) { ctx := context.Background() From dffe996fd1ce66b96d16cd300d0163c5c9736429 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Fri, 26 Apr 2024 11:58:29 -0400 Subject: [PATCH 29/42] Few formatting fix --- modules/postgres/postgres.go | 6 ++---- modules/postgres/postgres_test.go | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index f5a5d68a0e..01bd14e0e2 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -22,10 +22,8 @@ const ( defaultSnapshotName = "migrated_template" ) -var ( - //go:embed resources/customEntrypoint.sh - embeddedCustomEntrypoint string -) +//go:embed resources/customEntrypoint.sh +var embeddedCustomEntrypoint string // PostgresContainer represents the postgres container type used in the module type PostgresContainer struct { diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 2c70dde678..29e123b5f6 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -33,7 +33,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e tmpDir := t.TempDir() certsDir := tmpDir + "/certs" - if err := os.MkdirAll(certsDir, 0755); err != nil { + if err := os.MkdirAll(certsDir, 0o755); err != nil { t.Fatal(err) } @@ -48,7 +48,6 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e }) if caCert == nil { - return caCert, nil, errors.New("Unable to create CA Authority") } @@ -278,7 +277,6 @@ func TestWithSSL(t *testing.T) { } func TestSSLValidatesKeyMaterialPath(t *testing.T) { - ctx := context.Background() sslSettings := postgres.SSLSettings{} @@ -295,8 +293,8 @@ func TestSSLValidatesKeyMaterialPath(t *testing.T) { if err == nil { t.Fatal(err) } - } + func TestWithInitScript(t *testing.T) { ctx := context.Background() From 53d9a1ac70e954800b79f24feb2ee407570f5db5 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Fri, 26 Apr 2024 12:04:17 -0400 Subject: [PATCH 30/42] Use non-nil error when err is nil --- modules/postgres/postgres_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 29e123b5f6..02a9c59da6 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -291,7 +291,7 @@ func TestSSLValidatesKeyMaterialPath(t *testing.T) { postgres.WithSSLSettings(sslSettings), ) if err == nil { - t.Fatal(err) + t.Fatal("Error should not have been nil. Container creation should have failed due to empty key material") } } From 146f47d89ec093effdbb75857825588c8e8d313f Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Mon, 23 Dec 2024 16:27:04 -0500 Subject: [PATCH 31/42] Update modules/postgres/postgres_test.go Co-authored-by: Steven Hartland --- modules/postgres/postgres_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 02a9c59da6..9e9a34b356 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -38,7 +38,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e } t.Cleanup(func() { - os.RemoveAll(tmpDir) + require.NoError(t, os.RemoveAll(tmpDir)) }) caCert := tlscert.SelfSignedFromRequest(tlscert.Request{ From 94bd7f46191f558b0841a79ee0e1ac2ccdd958a1 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Mon, 23 Dec 2024 16:27:50 -0500 Subject: [PATCH 32/42] Update modules/postgres/postgres_test.go Co-authored-by: Steven Hartland --- modules/postgres/postgres_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 9e9a34b356..ee3ef83321 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -66,9 +66,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e func createSSLSettings(t *testing.T) postgres.SSLSettings { caCert, serverCerts, err := createSSLCerts(t) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) return postgres.SSLSettings{ CACertFile: caCert.CertPath, From dfa322e9b161c8ce624887a610d4820b37ceec5d Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Mon, 23 Dec 2024 16:28:13 -0500 Subject: [PATCH 33/42] Update modules/postgres/postgres.go Co-authored-by: Steven Hartland --- modules/postgres/postgres.go | 67 ++++++++++++++---------------------- 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 01bd14e0e2..6f97f4785c 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -200,51 +200,36 @@ func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOpt const defaultPermission = 0o600 return func(req *testcontainers.GenericContainerRequest) error { - req.Files = append(req.Files, testcontainers.ContainerFile{ - HostFilePath: sslSettings.CACertFile, - ContainerFilePath: postgresCaCertPath, - FileMode: defaultPermission, - }) - req.Files = append(req.Files, testcontainers.ContainerFile{ - HostFilePath: sslSettings.CertFile, - ContainerFilePath: postgresCertPath, - FileMode: defaultPermission, - }) - req.Files = append(req.Files, testcontainers.ContainerFile{ - HostFilePath: sslSettings.KeyFile, - ContainerFilePath: postgresKeyPath, - FileMode: defaultPermission, - }) - - expectedFiles := []string{sslSettings.CACertFile, sslSettings.CertFile, sslSettings.KeyFile} - for _, expectedFile := range expectedFiles { - _, err := os.Stat(expectedFile) - if err != nil { - return err - } - } - - req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("database system is ready to accept connections")) - - internalEntrypoint(req) + const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" + + req.Files = append(req.Files, + testcontainers.ContainerFile{ + HostFilePath: caCertFile, + ContainerFilePath: "/tmp/certs/ca_cert.pem", + FileMode: defaultPermission, + }, + testcontainers.ContainerFile{ + HostFilePath: certFile, + ContainerFilePath: "/tmp/certs/server.cert", + FileMode: defaultPermission, + }, + testcontainers.ContainerFile{ + HostFilePath: keyFile, + ContainerFilePath: "/tmp/data/server.key", + FileMode: defaultPermission, + }, + testcontainers.ContainerFile{ + Reader: strings.NewReader(embeddedCustomEntrypoint), + ContainerFilePath: entrypointPath , + FileMode: defaultPermission, + }, + ) + req.Entrypoint = []string{"sh", entrypointPath} + return nil } } -func internalEntrypoint(req *testcontainers.GenericContainerRequest) { - const entrypointPath = "/usr/local/bin/docker-entrypoint-ssl.bash" - - reader := strings.NewReader(embeddedCustomEntrypoint) - - req.Files = append(req.Files, testcontainers.ContainerFile{ - Reader: reader, - ContainerFilePath: entrypointPath, - FileMode: 0o666, - }) - - req.Entrypoint = []string{"sh", entrypointPath} -} - // Snapshot takes a snapshot of the current state of the database as a template, which can then be restored using // the Restore method. By default, the snapshot will be created under a database called migrated_template, you can // customize the snapshot name with the options. From 18ab8b7c2c09cb4cbac27489b642d772a34d5071 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Mon, 23 Dec 2024 16:28:23 -0500 Subject: [PATCH 34/42] Update modules/postgres/postgres.go Co-authored-by: Steven Hartland --- modules/postgres/postgres.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 6f97f4785c..8a40e66b5f 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -192,7 +192,7 @@ func WithSnapshotName(name string) SnapshotOption { // WithSSLSettings configures the Postgres server to run with the provided CA Chain // This will not function if the corresponding postgres conf is not correctly configured. // Namely the paths below must match what is set in the conf file -func WithSSLSettings(sslSettings SSLSettings) testcontainers.CustomizeRequestOption { +func WithSSLCert(caCertFile, certFile, keyFile) testcontainers.CustomizeRequestOption { const postgresCaCertPath = "/tmp/data/ca_cert.pem" const postgresCertPath = "/tmp/data/server.cert" const postgresKeyPath = "/tmp/data/server.key" From ab0e3ffc38825b717cd15963c24ce129b1af12a7 Mon Sep 17 00:00:00 2001 From: Barrett Strausser Date: Mon, 23 Dec 2024 16:28:52 -0500 Subject: [PATCH 35/42] Update modules/postgres/postgres_test.go Co-authored-by: Steven Hartland --- modules/postgres/postgres_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index ee3ef83321..8d418f7897 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -33,9 +33,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e tmpDir := t.TempDir() certsDir := tmpDir + "/certs" - if err := os.MkdirAll(certsDir, 0o755); err != nil { - t.Fatal(err) - } + require.NoError(t, os.MkdirAll(certsDir, 0o755)) t.Cleanup(func() { require.NoError(t, os.RemoveAll(tmpDir)) From 9e15ac52ee3d7907ee63afae018aa6e0831b5e61 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 23 Dec 2024 19:48:20 -0500 Subject: [PATCH 36/42] Addresses review modulo cleanup --- docs/modules/postgres.md | 8 ++++---- modules/postgres/postgres.go | 18 ++++++------------ modules/postgres/postgres_test.go | 14 +++++++------- modules/postgres/resources/customEntrypoint.sh | 10 ++++++---- modules/postgres/testdata/postgres-ssl.conf | 6 +++--- 5 files changed, 26 insertions(+), 30 deletions(-) diff --git a/docs/modules/postgres.md b/docs/modules/postgres.md index 33cdf826e2..ef4d6c52c3 100644 --- a/docs/modules/postgres.md +++ b/docs/modules/postgres.md @@ -75,7 +75,7 @@ This function can be used `WithSSLSettings` but requires your configuration corr - Not available until the next release of testcontainers-go :material-tag: main -If you would like to use SSL with the container you can use the `WithSSLSettings`. This function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/data/ca_cert.pem`, `tmp/data/server.cert` and `/tmp/data/server.key` +If you would like to use SSL with the container you can use the `WithSSLSettings`. This function accepts a `SSLSettings` which has the required secret material, namely the ca-certificate, server certificate and key. The container will copy this material to `/tmp/testcontainers-go/postgres/ca_cert.pem`, `/tmp/testcontainers-go/postgres/server.cert` and `/tmp/testcontainers-go/postgres/server.key` This function requires a custom postgres configuration file that enables SSL and correctly sets the paths on the key material. @@ -83,9 +83,9 @@ If you use this function by itself or in conjuction with `WithConfigFile` your c ``` ssl = on -ssl_ca_file = '/tmp/data/ca_cert.pem' -ssl_cert_file = '/tmp/data/server.cert' -ssl_key_file = '/tmp/data/server.key' +ssl_ca_file = '/tmp/testcontainers-go/postgres/ca_cert.pem' +ssl_cert_file = '/tmp/testcontainers-go/postgres/server.cert' +ssl_key_file = '/tmp/testcontainers-go/postgres/server.key' ``` This function assumes the postgres user in the container is `postgres` diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index 8a40e66b5f..c236c946d7 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -5,14 +5,12 @@ import ( "fmt" "io" "net" - "os" "path/filepath" "strings" _ "embed" "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" ) const ( @@ -192,11 +190,7 @@ func WithSnapshotName(name string) SnapshotOption { // WithSSLSettings configures the Postgres server to run with the provided CA Chain // This will not function if the corresponding postgres conf is not correctly configured. // Namely the paths below must match what is set in the conf file -func WithSSLCert(caCertFile, certFile, keyFile) testcontainers.CustomizeRequestOption { - const postgresCaCertPath = "/tmp/data/ca_cert.pem" - const postgresCertPath = "/tmp/data/server.cert" - const postgresKeyPath = "/tmp/data/server.key" - +func WithSSLCert(caCertFile string, certFile string, keyFile string) testcontainers.CustomizeRequestOption { const defaultPermission = 0o600 return func(req *testcontainers.GenericContainerRequest) error { @@ -205,27 +199,27 @@ func WithSSLCert(caCertFile, certFile, keyFile) testcontainers.CustomizeRequestO req.Files = append(req.Files, testcontainers.ContainerFile{ HostFilePath: caCertFile, - ContainerFilePath: "/tmp/certs/ca_cert.pem", + ContainerFilePath: "/tmp/testcontainers-go/postgres/ca_cert.pem", FileMode: defaultPermission, }, testcontainers.ContainerFile{ HostFilePath: certFile, - ContainerFilePath: "/tmp/certs/server.cert", + ContainerFilePath: "/tmp/testcontainers-go/postgres/server.cert", FileMode: defaultPermission, }, testcontainers.ContainerFile{ HostFilePath: keyFile, - ContainerFilePath: "/tmp/data/server.key", + ContainerFilePath: "/tmp/testcontainers-go/postgres/server.key", FileMode: defaultPermission, }, testcontainers.ContainerFile{ Reader: strings.NewReader(embeddedCustomEntrypoint), - ContainerFilePath: entrypointPath , + ContainerFilePath: entrypointPath, FileMode: defaultPermission, }, ) req.Entrypoint = []string{"sh", entrypointPath} - + return nil } } diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 8d418f7897..f0c0b41783 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -30,6 +30,7 @@ const ( ) func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, error) { + t.Helper() tmpDir := t.TempDir() certsDir := tmpDir + "/certs" @@ -46,7 +47,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e }) if caCert == nil { - return caCert, nil, errors.New("Unable to create CA Authority") + return caCert, nil, errors.New("unable to create CA Authority") } cert := tlscert.SelfSignedFromRequest(tlscert.Request{ @@ -56,7 +57,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e ParentDir: certsDir, }) if cert == nil { - return caCert, cert, errors.New("Unable to create Server Certificates") + return caCert, cert, errors.New("unable to create Server Certificates") } return caCert, cert, nil @@ -247,7 +248,7 @@ func TestWithSSL(t *testing.T) { postgres.WithUsername(user), postgres.WithPassword(password), testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), - postgres.WithSSLSettings(sslSettings), + postgres.WithSSLCert(sslSettings.CACertFile, sslSettings.CertFile, sslSettings.KeyFile), ) if err != nil { t.Fatal(err) @@ -284,11 +285,10 @@ func TestSSLValidatesKeyMaterialPath(t *testing.T) { postgres.WithUsername(user), postgres.WithPassword(password), testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), - postgres.WithSSLSettings(sslSettings), + postgres.WithSSLCert(sslSettings.CACertFile, sslSettings.CertFile, sslSettings.KeyFile), ) - if err == nil { - t.Fatal("Error should not have been nil. Container creation should have failed due to empty key material") - } + + require.Error(t, err, "Error should not have been nil. Container creation should have failed due to empty key material") } func TestWithInitScript(t *testing.T) { diff --git a/modules/postgres/resources/customEntrypoint.sh b/modules/postgres/resources/customEntrypoint.sh index 8b0102df6b..25d61ca507 100644 --- a/modules/postgres/resources/customEntrypoint.sh +++ b/modules/postgres/resources/customEntrypoint.sh @@ -7,16 +7,18 @@ pGID=$(id -g postgres) if [ -z "$pUID" ] then + echo "Unable to find postgres user id, required in order to chown key material" exit 1 fi if [ -z "$pGID" ] then + echo "Unable to find postgres group id, required in order to chown key material" exit 1 fi -chown "$pUID":"$pGID" /tmp/data/ca_cert.pem -chown "$pUID":"$pGID" /tmp/data/server.cert -chown "$pUID":"$pGID" /tmp/data/server.key +chown "$pUID":"$pGID" /tmp/testcontainers-go/postgres/ca_cert.pem +chown "$pUID":"$pGID" /tmp/testcontainers-go/postgres/server.cert +chown "$pUID":"$pGID" /tmp/testcontainers-go/postgres/server.key -/usr/local/bin/docker-entrypoint.sh "$@" \ No newline at end of file +/usr/local/bin/docker-entrypoint.sh "$@" diff --git a/modules/postgres/testdata/postgres-ssl.conf b/modules/postgres/testdata/postgres-ssl.conf index 51f92d5621..5e49f16a4f 100644 --- a/modules/postgres/testdata/postgres-ssl.conf +++ b/modules/postgres/testdata/postgres-ssl.conf @@ -66,10 +66,10 @@ listen_addresses = '*' # - SSL - ssl = on -ssl_ca_file = '/tmp/data/ca_cert.pem' -ssl_cert_file = '/tmp/data/server.cert' +ssl_ca_file = '/tmp/testcontainers-go/postgres/ca_cert.pem' +ssl_cert_file = '/tmp/testcontainers-go/postgres/server.cert' #ssl_crl_file = '' -ssl_key_file = '/tmp/data/server.key' +ssl_key_file = '/tmp/testcontainers-go/postgres/server.key' #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers #ssl_prefer_server_ciphers = on #ssl_ecdh_curve = 'prime256v1' From c1cda1ec4c4c3c0c7ea267a9a5e2be92c342e8c2 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 23 Dec 2024 19:55:07 -0500 Subject: [PATCH 37/42] Remove unused type --- modules/postgres/options.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/postgres/options.go b/modules/postgres/options.go index 3cedfa80d2..27532e61ff 100644 --- a/modules/postgres/options.go +++ b/modules/postgres/options.go @@ -1,7 +1,5 @@ package postgres -type SSLVerificationMode string - type SSLSettings struct { // Path to the CA certificate file CACertFile string From ff6ce3e9030a9234ed61a9351fbd2a085197adde Mon Sep 17 00:00:00 2001 From: bstrausser Date: Mon, 23 Dec 2024 23:37:54 -0500 Subject: [PATCH 38/42] Use ContainerCleanup --- modules/postgres/postgres_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 0f30a00ed0..719bc24d2c 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -235,13 +235,9 @@ func TestWithSSL(t *testing.T) { testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), postgres.WithSSLCert(sslSettings.CACertFile, sslSettings.CertFile, sslSettings.KeyFile), ) - require.NoError(t, err) - t.Cleanup(func() { - if err := container.Terminate(ctx); err != nil { - t.Fatalf("failed to terminate container: %s", err) - } - }) + testcontainers.CleanupContainer(t, container) + require.NoError(t, err) connStr, err := container.ConnectionString(ctx, "sslmode=require") require.NoError(t, err) From 6adf44c51ce3ebcd849c634a825628c608cd3b85 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Tue, 24 Dec 2024 13:18:48 -0500 Subject: [PATCH 39/42] Lint pass --- modules/postgres/postgres.go | 3 +-- modules/postgres/postgres_test.go | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/postgres/postgres.go b/modules/postgres/postgres.go index fc3e1fe5da..e25bc667a6 100644 --- a/modules/postgres/postgres.go +++ b/modules/postgres/postgres.go @@ -3,6 +3,7 @@ package postgres import ( "context" "database/sql" + _ "embed" "errors" "fmt" "io" @@ -10,8 +11,6 @@ import ( "path/filepath" "strings" - _ "embed" - "github.com/testcontainers/testcontainers-go" ) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 719bc24d2c..03aa473d63 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -15,7 +15,6 @@ import ( _ "github.com/jackc/pgx/v5/stdlib" _ "github.com/lib/pq" "github.com/mdelapenya/tlscert" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" From 6fb54553d0cf070682ab5d7f1e2a2dff95af4920 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Tue, 24 Dec 2024 14:21:22 -0500 Subject: [PATCH 40/42] Add t.Helper and Linting --- modules/postgres/postgres_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index 03aa473d63..af119eedc2 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -64,6 +64,7 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e } func createSSLSettings(t *testing.T) postgres.SSLSettings { + t.Helper() caCert, serverCerts, err := createSSLCerts(t) require.NoError(t, err) From 81a8d182bd4a554179560a94522b5eff228137e4 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Tue, 24 Dec 2024 19:32:49 -0500 Subject: [PATCH 41/42] Remove SSLSetting struct, use raw paths --- modules/postgres/options.go | 9 --------- modules/postgres/postgres_test.go | 21 ++++----------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/modules/postgres/options.go b/modules/postgres/options.go index 71e46b5f92..5779f85c04 100644 --- a/modules/postgres/options.go +++ b/modules/postgres/options.go @@ -37,12 +37,3 @@ func WithSQLDriver(driver string) Option { o.SQLDriverName = driver } } - -type SSLSettings struct { - // Path to the CA certificate file - CACertFile string - // Path to the client certificate file - CertFile string - // Path to the key file - KeyFile string -} diff --git a/modules/postgres/postgres_test.go b/modules/postgres/postgres_test.go index af119eedc2..d143539063 100644 --- a/modules/postgres/postgres_test.go +++ b/modules/postgres/postgres_test.go @@ -63,18 +63,6 @@ func createSSLCerts(t *testing.T) (*tlscert.Certificate, *tlscert.Certificate, e return caCert, cert, nil } -func createSSLSettings(t *testing.T) postgres.SSLSettings { - t.Helper() - caCert, serverCerts, err := createSSLCerts(t) - require.NoError(t, err) - - return postgres.SSLSettings{ - CACertFile: caCert.CertPath, - CertFile: serverCerts.CertPath, - KeyFile: serverCerts.KeyPath, - } -} - func TestPostgres(t *testing.T) { ctx := context.Background() @@ -224,7 +212,8 @@ func TestWithConfigFile(t *testing.T) { func TestWithSSL(t *testing.T) { ctx := context.Background() - sslSettings := createSSLSettings(t) + caCert, serverCerts, err := createSSLCerts(t) + require.NoError(t, err) container, err := postgres.RunContainer(ctx, postgres.WithConfigFile(filepath.Join("testdata", "postgres-ssl.conf")), @@ -233,7 +222,7 @@ func TestWithSSL(t *testing.T) { postgres.WithUsername(user), postgres.WithPassword(password), testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), - postgres.WithSSLCert(sslSettings.CACertFile, sslSettings.CertFile, sslSettings.KeyFile), + postgres.WithSSLCert(caCert.CertPath, serverCerts.CertPath, serverCerts.KeyPath), ) testcontainers.CleanupContainer(t, container) @@ -255,8 +244,6 @@ func TestWithSSL(t *testing.T) { func TestSSLValidatesKeyMaterialPath(t *testing.T) { ctx := context.Background() - sslSettings := postgres.SSLSettings{} - _, err := postgres.RunContainer(ctx, postgres.WithConfigFile(filepath.Join("testdata", "postgres-ssl.conf")), postgres.WithInitScripts(filepath.Join("testdata", "init-user-db.sh")), @@ -264,7 +251,7 @@ func TestSSLValidatesKeyMaterialPath(t *testing.T) { postgres.WithUsername(user), postgres.WithPassword(password), testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)), - postgres.WithSSLCert(sslSettings.CACertFile, sslSettings.CertFile, sslSettings.KeyFile), + postgres.WithSSLCert("", "", ""), ) require.Error(t, err, "Error should not have been nil. Container creation should have failed due to empty key material") From e980a90dcf6982978f3452e7b8a3a90a6d7971f0 Mon Sep 17 00:00:00 2001 From: bstrausser Date: Tue, 24 Dec 2024 19:35:04 -0500 Subject: [PATCH 42/42] Use single command for chown key material --- modules/postgres/resources/customEntrypoint.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/postgres/resources/customEntrypoint.sh b/modules/postgres/resources/customEntrypoint.sh index 25d61ca507..ff4ffa4291 100644 --- a/modules/postgres/resources/customEntrypoint.sh +++ b/modules/postgres/resources/customEntrypoint.sh @@ -17,8 +17,9 @@ then exit 1 fi -chown "$pUID":"$pGID" /tmp/testcontainers-go/postgres/ca_cert.pem -chown "$pUID":"$pGID" /tmp/testcontainers-go/postgres/server.cert -chown "$pUID":"$pGID" /tmp/testcontainers-go/postgres/server.key +chown "$pUID":"$pGID" \ + /tmp/testcontainers-go/postgres/ca_cert.pem \ + /tmp/testcontainers-go/postgres/server.cert \ + /tmp/testcontainers-go/postgres/server.key /usr/local/bin/docker-entrypoint.sh "$@"