Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
name: pghba-n1-689qacsi
namespace: /patroni/
scope: pghba:n1
log:
type: json
level: INFO
static_fields:
database_id: pghba
instance_id: pghba-n1-689qacsi
node_name: n1
bootstrap:
dcs:
loop_wait: 10
ttl: 30
retry_timeout: 10
postgresql:
parameters:
max_connections: 901
max_replication_slots: 16
max_wal_senders: 16
max_worker_processes: 12
track_commit_timestamp: "on"
wal_level: logical
ignore_slots:
- plugin: spock_output
initdb:
- data-checksums
etcd3:
hosts:
- i-0123456789abcdef.ec2.internal:2379
protocol: https
username: instance.pghba-n1-689qacsi
password: password
cacert: /opt/pgedge/certificates/etcd/ca.crt
cert: /opt/pgedge/certificates/etcd/client.crt
key: /opt/pgedge/certificates/etcd/client.key
postgresql:
authentication:
superuser:
username: pgedge
sslmode: verify-full
sslkey: /opt/pgedge/certificates/postgres/superuser.key
sslcert: /opt/pgedge/certificates/postgres/superuser.crt
sslrootcert: /opt/pgedge/certificates/postgres/ca.crt
replication:
username: patroni_replicator
sslmode: verify-full
sslkey: /opt/pgedge/certificates/postgres/replication.key
sslcert: /opt/pgedge/certificates/postgres/replication.crt
sslrootcert: /opt/pgedge/certificates/postgres/ca.crt
connect_address: pghba-n1-689qacsi.pghba-database:5432
data_dir: /opt/pgedge/data/pgdata
listen: "*:5432"
parameters:
archive_command: /bin/true
archive_mode: "on"
autovacuum_max_workers: 3
autovacuum_vacuum_cost_limit: 200
autovacuum_work_mem: 262144
checkpoint_completion_target: "0.9"
checkpoint_timeout: 15min
dynamic_shared_memory_type: posix
effective_cache_size: 524288
hot_standby_feedback: "on"
log_destination: stderr
log_directory: log
log_filename: postgresql-%a.log
log_line_prefix: "%m [%p] "
log_rotation_age: 1d
log_rotation_size: "0"
log_truncate_on_rotation: "on"
logging_collector: "on"
lolor.node: 1
maintenance_work_mem: 137518
max_parallel_workers: 8
password_encryption: scram-sha-256
shared_buffers: 262144
shared_preload_libraries: pg_stat_statements,snowflake,spock
snowflake.node: 1
spock.allow_ddl_from_functions: "on"
spock.conflict_log_level: DEBUG
spock.conflict_resolution: last_update_wins
spock.enable_ddl_replication: "on"
spock.include_ddl_repset: "on"
spock.save_resolutions: "on"
ssl: "on"
ssl_ca_file: /opt/pgedge/certificates/postgres/ca.crt
ssl_cert_file: /opt/pgedge/certificates/postgres/server.crt
ssl_key_file: /opt/pgedge/certificates/postgres/server.key
track_io_timing: "on"
wal_log_hints: "on"
wal_sender_timeout: 5s
pg_hba:
- local all all trust
- host all all 127.0.0.1/32 trust
- host all all ::1/128 trust
- local replication all trust
- host replication all 127.0.0.1/32 trust
- host replication all ::1/128 trust
- hostssl all pgedge,patroni_replicator 172.17.0.1/32 cert clientcert=verify-full
- hostssl replication pgedge,patroni_replicator 172.17.0.1/32 cert clientcert=verify-full
- hostssl all pgedge,patroni_replicator 10.128.165.128/26 cert clientcert=verify-full
- hostssl replication pgedge,patroni_replicator 10.128.165.128/26 cert clientcert=verify-full
- host all pgedge,patroni_replicator 0.0.0.0/0 reject
- host all pgedge,patroni_replicator ::/0 reject
- host testdb myapp_user 10.0.0.0/8 scram-sha-256
- hostssl all myapp_user 203.0.113.0/24 scram-sha-256
- host all all 0.0.0.0/0 scram-sha-256
- host all all ::/0 scram-sha-256
pg_ident:
- ssl_users CN=alice,O=example alice
use_pg_rewind: true
remove_data_directory_on_rewind_failure: true
remove_data_directory_on_diverged_timelines: true
restapi:
connect_address: pghba-n1-689qacsi.pghba-database:8888
listen: 0.0.0.0:8888
allowlist:
- 172.17.0.1/32
- 10.128.165.128/26
- 127.0.0.1
- localhost
- ::1
watchdog:
mode: "off"
45 changes: 39 additions & 6 deletions server/internal/orchestrator/common/patroni_config_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ type PatroniConfigGenerator struct {
PatroniAllowlist []string `json:"patroni_allowlist"`
// PatroniPort is the port that Patroni will listen on.
PatroniPort int `json:"patroni_port"`
// PgHbaConf are user-supplied pg_hba.conf entries (one rule per element),
// inserted in the user zone after the CP rules and before the catch-all.
PgHbaConf []string `json:"pg_hba_conf,omitempty"`
// PgIdentConf are user-supplied pg_ident.conf entries (one mapping per
// element). The CP writes no pg_ident entries of its own.
PgIdentConf []string `json:"pg_ident_conf,omitempty"`
// PostgresCertsDir is the Postgres certificates directory.
PostgresCertsDir string `json:"postgres_certs_dir"`
// PostgresPort is the port that Postgres will listen on.
Expand Down Expand Up @@ -138,6 +144,8 @@ func NewPatroniConfigGenerator(opts PatroniConfigGeneratorOptions) *PatroniConfi
PostgresPort: opts.PostgresPort,
RestoreCommand: restoreCommand,
SpecParameters: opts.Instance.PostgreSQLConf,
PgHbaConf: opts.Instance.PgHbaConf,
PgIdentConf: opts.Instance.PgIdentConf,
TenantID: opts.Instance.TenantID,
// TODO: Add allowlist field to instance and database types
// PatroniAllowlist: opts.Instance.PatroniAllowlist,
Expand Down Expand Up @@ -306,6 +314,14 @@ func (p *PatroniConfigGenerator) postgreSQL(
}
}

// The catch-all authenticates non-system users by password, so its auth
// method follows password_encryption to match how passwords are stored. It
// defaults to md5 (our DefaultGUCs value) when unset.
passwordAuthMethod := hba.AuthMethodMD5
if pe, ok := parameters["password_encryption"].(string); ok && pe != "" {
passwordAuthMethod = hba.AuthMethod(pe)
}

return &patroni.PostgreSQL{
ConnectAddress: utils.PointerTo(net.JoinHostPort(p.FQDN, strconv.Itoa(p.PostgresPort))),
DataDir: &p.DataDir,
Expand All @@ -316,7 +332,8 @@ func (p *PatroniConfigGenerator) postgreSQL(
RemoveDataDirectoryOnRewindFailure: utils.PointerTo(true),
RemoveDataDirectoryOnDivergedTimelines: utils.PointerTo(true),
Authentication: p.authentication(),
PgHba: p.pgHba(systemAddresses, extraEntries),
PgHba: p.pgHba(systemAddresses, extraEntries, passwordAuthMethod),
PgIdent: p.pgIdent(),
}
}

Expand All @@ -339,7 +356,7 @@ func (p *PatroniConfigGenerator) authentication() *patroni.Authentication {
}
}

func (p *PatroniConfigGenerator) pgHba(systemAddresses []string, extraEntries []hba.Entry) *[]string {
func (p *PatroniConfigGenerator) pgHba(systemAddresses []string, extraEntries []hba.Entry, passwordAuthMethod hba.AuthMethod) *[]string {
entries := []string{
// Trust local connections
hba.Entry{
Expand Down Expand Up @@ -427,24 +444,40 @@ func (p *PatroniConfigGenerator) pgHba(systemAddresses []string, extraEntries []
entries = append(entries, entry.String())
}

// Use MD5 for non-system users from all other connections
// TODO: Can we safely upgrade this to scram-sha-256?
// User-supplied pg_hba entries form a zone after the CP's system-user rules
// and before the catch-all. By this point system users are already matched
// or rejected, so user rules cannot affect CP-internal connectivity.
// p.PgHbaConf already has node-level entries prepended ahead of the
// database-level entries.
entries = append(entries, p.PgHbaConf...)

// Catch-all for non-system users. The auth method follows
// password_encryption so it matches how passwords are stored.
entries = append(entries,
hba.Entry{
Type: hba.EntryTypeHost,
Database: "all",
User: "all",
Address: "0.0.0.0/0",
AuthMethod: hba.AuthMethodMD5,
AuthMethod: passwordAuthMethod,
}.String(),
hba.Entry{
Type: hba.EntryTypeHost,
Database: "all",
User: "all",
Address: "::/0",
AuthMethod: hba.AuthMethodMD5,
AuthMethod: passwordAuthMethod,
}.String(),
)

return &entries
}

// pgIdent returns the user-supplied pg_ident.conf entries, or nil when there
// are none. The CP writes no pg_ident entries of its own.
func (p *PatroniConfigGenerator) pgIdent() *[]string {
if len(p.PgIdentConf) == 0 {
return nil
}
return &p.PgIdentConf
}
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,53 @@ func TestPatroniConfigGenerator(t *testing.T) {
},
},
},
{
name: "user pg_hba pg_ident and scram",
options: common.PatroniConfigGeneratorOptions{
Instance: &database.InstanceSpec{
InstanceID: "pghba-n1-689qacsi",
DatabaseID: "pghba",
HostID: "host-1",
DatabaseName: "testdb",
NodeName: "n1",
NodeOrdinal: 1,
PgEdgeVersion: ds.MustParsePgEdgeVersion("18.4", "5.0.8"),
ClusterSize: 3,
// password_encryption drives the catch-all auth method.
PostgreSQLConf: map[string]any{
"password_encryption": "scram-sha-256",
},
// Node-level entries are already prepended ahead of
// database-level entries by NodeInstances() before reaching
// the generator.
PgHbaConf: []string{
"host testdb myapp_user 10.0.0.0/8 scram-sha-256",
"hostssl all myapp_user 203.0.113.0/24 scram-sha-256",
},
PgIdentConf: []string{"ssl_users CN=alice,O=example alice"},
},
HostCPUs: 4,
HostMemoryBytes: 1024 * 1024 * 1024 * 8,
FQDN: "pghba-n1-689qacsi.pghba-database",
LogType: patroni.LogTypeJson,
PatroniPort: 8888,
PostgresPort: 5432,
Paths: database.InstancePaths{
Instance: database.Paths{BaseDir: "/opt/pgedge"},
Host: database.Paths{BaseDir: "/data/control-plane/instances/pghba-n1-689qacsi"},
PgBackRestPath: "/usr/bin/pgbackrest",
PatroniPath: "/usr/local/bin/patroni",
},
},
etcdHosts: []string{"i-0123456789abcdef.ec2.internal:2379"},
etcdCreds: &common.EtcdCreds{
Username: "instance.pghba-n1-689qacsi",
Password: "password",
},
generateOptions: common.GenerateOptions{
SystemAddresses: []string{"172.17.0.1/32", "10.128.165.128/26"},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
gen := common.NewPatroniConfigGenerator(tc.options)
Expand Down
Loading