From c3967ed36fa0c6234939814bbfe80aced4aa3bc6 Mon Sep 17 00:00:00 2001 From: Sebastiaan Mannem Date: Thu, 12 May 2022 22:15:07 +0200 Subject: [PATCH] enabling cert, ident and peer authentication peer --- .DS_Store | Bin 0 -> 8196 bytes cmd/keeper/cmd/keeper.go | 187 ++++++++++++++++++++----------- internal/timer/timer_fallback.go | 1 + internal/timer/timer_linux.go | 1 + 4 files changed, 125 insertions(+), 64 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f18ba9d42cf73d9030f0bc6501d09e6220f6e55c GIT binary patch literal 8196 zcmeHMU279T6upy=WfkZJ}HBK_zChF)rQilHGig1 znG=&cV;#LS(-{iWsRwfid^nblviNHfjyC&{tS z^1u1VQJAFl`VUqpmdXnir{XL+ue`T>;-$TGoV0qQ7vk!G^TdI?;b`+G5?^>$@H^=uBz*^_9J@wIPbhs9PxOF5y8GfTlAPVX_p>Rhn~8SUsj>{*;I|(dW#KUIw4blNx%Eh`~HV;wpax&y8=b!;Ihd7I~V`{ zf7y>={j&;K1^z+-R%(0g78W@DUN+Yfxweb?8dVg*n<<>fqT~LKh WQ{zk_Mo{bz0a6CrSOxy60>1&cggjXQ literal 0 HcmV?d00001 diff --git a/cmd/keeper/cmd/keeper.go b/cmd/keeper/cmd/keeper.go index dd9b2aafa..ae002ad6b 100644 --- a/cmd/keeper/cmd/keeper.go +++ b/cmd/keeper/cmd/keeper.go @@ -96,22 +96,27 @@ func (s *DBLocalState) DeepCopy() *DBLocalState { type config struct { cmd.CommonConfig - uid string - dataDir string - debug bool - pgListenAddress string - pgAdvertiseAddress string - pgPort string - pgAdvertisePort string - pgBinPath string - pgReplAuthMethod string - pgReplUsername string - pgReplPassword string - pgReplPasswordFile string - pgSUAuthMethod string - pgSUUsername string - pgSUPassword string - pgSUPasswordFile string + uid string + dataDir string + debug bool + pgListenAddress string + pgAdvertiseAddress string + pgPort string + pgAdvertisePort string + pgBinPath string + pgReplConnType string + pgReplAuthMethod string + pgReplLocalAuthMethod string + pgReplSslMode string + pgReplUsername string + pgReplPassword string + pgReplPasswordFile string + pgSUConnType string + pgSUAuthMethod string + pgSULocalAuthMethod string + pgSUUsername string + pgSUPassword string + pgSUPasswordFile string canBeMaster bool canBeSynchronousReplica bool @@ -131,11 +136,16 @@ func init() { CmdKeeper.PersistentFlags().StringVar(&cfg.pgPort, "pg-port", "5432", "postgresql instance listening port") CmdKeeper.PersistentFlags().StringVar(&cfg.pgAdvertisePort, "pg-advertise-port", "", "postgresql instance port from outside. Use it to expose port different than local port with a PAT networking config") CmdKeeper.PersistentFlags().StringVar(&cfg.pgBinPath, "pg-bin-path", "", "absolute path to postgresql binaries. If empty they will be searched in the current PATH") + CmdKeeper.PersistentFlags().StringVar(&cfg.pgReplConnType, "pg-repl-connection-type", "host", "postgres replication user connection type. Default is host.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgReplAuthMethod, "pg-repl-auth-method", "md5", "postgres replication user auth method. Default is md5.") + CmdKeeper.PersistentFlags().StringVar(&cfg.pgReplLocalAuthMethod, "pg-repl-local-auth-method", "", "postgres replication user auth method. Default is same as pg-repl-auth-method.") + CmdKeeper.PersistentFlags().StringVar(&cfg.pgReplSslMode, "pg-repl-ssl-mode", "prefer", "postgres replication user ssl-mode. Default is prefer.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgReplUsername, "pg-repl-username", "", "postgres replication user name. Required. It'll be created on db initialization. Must be the same for all keepers.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgReplPassword, "pg-repl-password", "", "postgres replication user password. Only one of --pg-repl-password or --pg-repl-passwordfile must be provided. Must be the same for all keepers.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgReplPasswordFile, "pg-repl-passwordfile", "", "postgres replication user password file. Only one of --pg-repl-password or --pg-repl-passwordfile must be provided. Must be the same for all keepers.") + CmdKeeper.PersistentFlags().StringVar(&cfg.pgSUConnType, "pg-su-connection-type", "host", "postgres superuser connection type. Default is host.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgSUAuthMethod, "pg-su-auth-method", "md5", "postgres superuser auth method. Default is md5.") + CmdKeeper.PersistentFlags().StringVar(&cfg.pgSULocalAuthMethod, "pg-su-local-auth-method", "", "postgres superuser auth method. Default is same as pg-su-auth-method.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgSUUsername, "pg-su-username", "", "postgres superuser user name. Used for keeper managed instance access and pg_rewind based synchronization. It'll be created on db initialization. Defaults to the name of the effective user running stolon-keeper. Must be the same for all keepers.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgSUPassword, "pg-su-password", "", "postgres superuser password. Only one of --pg-su-password or --pg-su-passwordfile must be provided. Must be the same for all keepers.") CmdKeeper.PersistentFlags().StringVar(&cfg.pgSUPasswordFile, "pg-su-passwordfile", "", "postgres superuser password file. Only one of --pg-su-password or --pg-su-passwordfile must be provided. Must be the same for all keepers)") @@ -296,10 +306,12 @@ func (p *PostgresKeeper) getSUConnParams(db, followedDB *cluster.DB) pg.ConnPara "port": followedDB.Status.Port, "application_name": common.StolonName(db.UID), "dbname": "postgres", - // prefer ssl if available (already the default for postgres libpq but not for golang lib pq) - "sslmode": "prefer", + // This is currently only used for pgRewind, which requires a SU (repluser might not be enough). + // Pgrewind is the only feature using SU over remote connection and with that the only type using SU with sslmode. + // Therefore we have skipped extra config option for sslmode for SU, and reuse config for sslmode for repl user instead. + "sslmode": p.pgReplSslMode, } - if p.pgSUAuthMethod != "trust" { + if p.pgSUAuthMethod == "md5" { cp.Set("password", p.pgSUPassword) } return cp @@ -314,7 +326,7 @@ func (p *PostgresKeeper) getReplConnParams(db, followedDB *cluster.DB) pg.ConnPa // prefer ssl if available (already the default for postgres libpq but not for golang lib pq) "sslmode": "prefer", } - if p.pgReplAuthMethod != "trust" { + if p.pgReplAuthMethod == "md5" { cp.Set("password", p.pgReplPassword) } return cp @@ -328,7 +340,7 @@ func (p *PostgresKeeper) getLocalConnParams() pg.ConnParams { "dbname": "postgres", // no sslmode defined since it's not needed and supported over unix sockets } - if p.pgSUAuthMethod != "trust" { + if p.pgSUAuthMethod == "md5" { cp.Set("password", p.pgSUPassword) } return cp @@ -342,7 +354,7 @@ func (p *PostgresKeeper) getLocalReplConnParams() pg.ConnParams { "port": p.pgPort, // no sslmode defined since it's not needed and supported over unix sockets } - if p.pgReplAuthMethod != "trust" { + if p.pgReplAuthMethod == "md5" { cp.Set("password", p.pgReplPassword) } return cp @@ -470,18 +482,23 @@ type PostgresKeeper struct { bootUUID string - dataDir string - pgListenAddress string - pgAdvertiseAddress string - pgPort string - pgAdvertisePort string - pgBinPath string - pgReplAuthMethod string - pgReplUsername string - pgReplPassword string - pgSUAuthMethod string - pgSUUsername string - pgSUPassword string + dataDir string + pgListenAddress string + pgAdvertiseAddress string + pgPort string + pgAdvertisePort string + pgBinPath string + pgReplConnType string + pgReplAuthMethod string + pgReplLocalAuthMethod string + pgReplSslMode string + pgReplUsername string + pgReplPassword string + pgSUConnType string + pgSUAuthMethod string + pgSULocalAuthMethod string + pgSUUsername string + pgSUPassword string sleepInterval time.Duration requestTimeout time.Duration @@ -523,17 +540,22 @@ func NewPostgresKeeper(cfg *config, end chan error) (*PostgresKeeper, error) { dataDir: dataDir, - pgListenAddress: cfg.pgListenAddress, - pgAdvertiseAddress: cfg.pgAdvertiseAddress, - pgPort: cfg.pgPort, - pgAdvertisePort: cfg.pgAdvertisePort, - pgBinPath: cfg.pgBinPath, - pgReplAuthMethod: cfg.pgReplAuthMethod, - pgReplUsername: cfg.pgReplUsername, - pgReplPassword: cfg.pgReplPassword, - pgSUAuthMethod: cfg.pgSUAuthMethod, - pgSUUsername: cfg.pgSUUsername, - pgSUPassword: cfg.pgSUPassword, + pgListenAddress: cfg.pgListenAddress, + pgAdvertiseAddress: cfg.pgAdvertiseAddress, + pgPort: cfg.pgPort, + pgAdvertisePort: cfg.pgAdvertisePort, + pgBinPath: cfg.pgBinPath, + pgReplConnType: cfg.pgReplConnType, + pgReplAuthMethod: cfg.pgReplAuthMethod, + pgReplLocalAuthMethod: cfg.pgReplLocalAuthMethod, + pgReplSslMode: cfg.pgReplSslMode, + pgReplUsername: cfg.pgReplUsername, + pgReplPassword: cfg.pgReplPassword, + pgSUConnType: cfg.pgSUConnType, + pgSUAuthMethod: cfg.pgSUAuthMethod, + pgSULocalAuthMethod: cfg.pgSULocalAuthMethod, + pgSUUsername: cfg.pgSUUsername, + pgSUPassword: cfg.pgSUPassword, sleepInterval: cluster.DefaultSleepInterval, requestTimeout: cluster.DefaultRequestTimeout, @@ -582,7 +604,7 @@ func (p *PostgresKeeper) dbLocalStateCopy() *DBLocalState { } func (p *PostgresKeeper) usePgrewind(db *cluster.DB) bool { - return p.pgSUUsername != "" && p.pgSUPassword != "" && db.Spec.UsePgrewind + return p.pgSUUsername != "" && (p.pgSUPassword != "" || p.pgSUAuthMethod == "cert") && db.Spec.UsePgrewind } func (p *PostgresKeeper) updateKeeperInfo() error { @@ -1827,6 +1849,16 @@ func IsMaster(db *cluster.DB) bool { } } +// localAuthMethod returns the authentication method that works for local connections +// cert does not work for local connections, in which case we should fall back to peer authentication +func localAuthMethod(authMethod string) string { + if authMethod == "cert" { + log.Info("using peer instead of cert for local connection authentication method") + return "peer" + } + return authMethod +} + // generateHBA generates the instance hba entries depending on the value of // DefaultSUReplAccessMode. // When onlyInternal is true only rules needed for replication will be setup @@ -1837,8 +1869,8 @@ func (p *PostgresKeeper) generateHBA(cd *cluster.ClusterData, db *cluster.DB, on // Matched local connections are for postgres database and suUsername user with md5 auth // Matched local replication connections are for replUsername user with md5 auth computedHBA := []string{ - fmt.Sprintf("local postgres %s %s", p.pgSUUsername, p.pgSUAuthMethod), - fmt.Sprintf("local replication %s %s", p.pgReplUsername, p.pgReplAuthMethod), + fmt.Sprintf("local postgres %s %s", p.pgSUUsername, localAuthMethod(p.pgSULocalAuthMethod)), + fmt.Sprintf("local replication %s %s", p.pgReplUsername, localAuthMethod(p.pgReplLocalAuthMethod)), } switch *cd.Cluster.DefSpec().DefaultSUReplAccessMode { @@ -1846,10 +1878,10 @@ func (p *PostgresKeeper) generateHBA(cd *cluster.ClusterData, db *cluster.DB, on // all the keepers will accept connections from every host computedHBA = append( computedHBA, - fmt.Sprintf("host all %s %s %s", p.pgSUUsername, "0.0.0.0/0", p.pgSUAuthMethod), - fmt.Sprintf("host all %s %s %s", p.pgSUUsername, "::0/0", p.pgSUAuthMethod), - fmt.Sprintf("host replication %s %s %s", p.pgReplUsername, "0.0.0.0/0", p.pgReplAuthMethod), - fmt.Sprintf("host replication %s %s %s", p.pgReplUsername, "::0/0", p.pgReplAuthMethod), + fmt.Sprintf("%s all %s %s %s", p.pgSUConnType, p.pgSUUsername, "0.0.0.0/0", p.pgSUAuthMethod), + fmt.Sprintf("%s all %s %s %s", p.pgSUConnType, p.pgSUUsername, "::0/0", p.pgSUAuthMethod), + fmt.Sprintf("%s replication %s %s %s", p.pgReplConnType, p.pgReplUsername, "0.0.0.0/0", p.pgReplAuthMethod), + fmt.Sprintf("%s replication %s %s %s", p.pgReplConnType, p.pgReplUsername, "::0/0", p.pgReplAuthMethod), ) case cluster.SUReplAccessStrict: // only the master keeper (primary instance or standby of a remote primary when in standby cluster mode) will accept connections only from the other standby keepers IPs @@ -1864,8 +1896,8 @@ func (p *PostgresKeeper) generateHBA(cd *cluster.ClusterData, db *cluster.DB, on for _, address := range addresses { computedHBA = append( computedHBA, - fmt.Sprintf("host all %s %s/32 %s", p.pgSUUsername, address, p.pgReplAuthMethod), - fmt.Sprintf("host replication %s %s/32 %s", p.pgReplUsername, address, p.pgReplAuthMethod), + fmt.Sprintf("%s all %s %s/32 %s", p.pgSUConnType, p.pgSUUsername, address, p.pgReplAuthMethod), + fmt.Sprintf("%s replication %s %s/32 %s", p.pgReplConnType, p.pgReplUsername, address, p.pgReplAuthMethod), ) } } @@ -1927,6 +1959,15 @@ func keeper(c *cobra.Command, args []string) { validAuthMethods := make(map[string]struct{}) validAuthMethods["trust"] = struct{}{} validAuthMethods["md5"] = struct{}{} + validAuthMethods["cert"] = struct{}{} + validAuthMethods["ident"] = struct{}{} + validAuthMethods["peer"] = struct{}{} + validConnectionTypes := make(map[string]struct{}) + validConnectionTypes["host"] = struct{}{} + validConnectionTypes["hostssl"] = struct{}{} + validConnectionTypes["hostnossl"] = struct{}{} + validConnectionTypes["hostgssenc"] = struct{}{} + validConnectionTypes["hostnogssenc"] = struct{}{} switch cfg.LogLevel { case "error": slog.SetLevel(zap.ErrorLevel) @@ -1988,39 +2029,57 @@ func keeper(c *cobra.Command, args []string) { } } + if cfg.pgSULocalAuthMethod == "" { + cfg.pgSULocalAuthMethod = cfg.pgSUAuthMethod + } + if cfg.pgReplLocalAuthMethod == "" { + cfg.pgReplLocalAuthMethod = cfg.pgReplAuthMethod + } + if _, ok := validConnectionTypes[cfg.pgReplConnType]; !ok { + log.Fatalf("--pg-repl-connection-type must be one of: host, hostssl, hostnossl, hostgssenc, hostnogssenc") + } + if _, ok := validConnectionTypes[cfg.pgSUConnType]; !ok { + log.Fatalf("--pg-su-connection-type must be one of: host, hostssl, hostnossl, hostgssenc, hostnogssenc") + } if _, ok := validAuthMethods[cfg.pgReplAuthMethod]; !ok { - log.Fatalf("--pg-repl-auth-method must be one of: md5, trust") + log.Fatalf("--pg-repl-auth-method must be one of: ident, md5, password, trust or cert") } if cfg.pgReplUsername == "" { log.Fatalf("--pg-repl-username is required") } + if _, ok := validAuthMethods[cfg.pgReplLocalAuthMethod]; !ok { + log.Fatalf("--pg-repl-local-auth-method must be one of: ident, md5, password, trust or cert") + } if cfg.pgReplAuthMethod == "trust" { log.Warn("not utilizing a password for replication between hosts is extremely dangerous") if cfg.pgReplPassword != "" || cfg.pgReplPasswordFile != "" { log.Fatalf("can not utilize --pg-repl-auth-method trust together with --pg-repl-password or --pg-repl-passwordfile") } } - if cfg.pgSUAuthMethod == "trust" { + if cfg.pgSUAuthMethod == "trust" || cfg.pgSULocalAuthMethod == "trust" { log.Warn("not utilizing a password for superuser is extremely dangerous") - if cfg.pgSUPassword != "" || cfg.pgSUPasswordFile != "" { - log.Fatalf("can not utilize --pg-su-auth-method trust together with --pg-su-password or --pg-su-passwordfile") + if (cfg.pgSUAuthMethod == "trust" || cfg.pgSULocalAuthMethod == "trust") && (cfg.pgSUPassword != "" || cfg.pgSUPasswordFile != "") { + log.Fatalf("can not utilize --pg-su-auth-method trust and --pg-su-auth-method trust together with --pg-su-password or --pg-su-passwordfile") } } - if cfg.pgReplAuthMethod != "trust" && cfg.pgReplPassword == "" && cfg.pgReplPasswordFile == "" { + if cfg.pgReplAuthMethod == "md5" && cfg.pgReplPassword == "" && cfg.pgReplPasswordFile == "" { log.Fatalf("one of --pg-repl-password or --pg-repl-passwordfile is required") } - if cfg.pgReplAuthMethod != "trust" && cfg.pgReplPassword != "" && cfg.pgReplPasswordFile != "" { + if cfg.pgReplAuthMethod == "md5" && cfg.pgReplPassword != "" && cfg.pgReplPasswordFile != "" { log.Fatalf("only one of --pg-repl-password or --pg-repl-passwordfile must be provided") } if _, ok := validAuthMethods[cfg.pgSUAuthMethod]; !ok { - log.Fatalf("--pg-su-auth-method must be one of: md5, password, trust") + log.Fatalf("--pg-su-auth-method must be one of: ident, md5, password, trust or cert") } - if cfg.pgSUAuthMethod != "trust" && cfg.pgSUPassword == "" && cfg.pgSUPasswordFile == "" { + if cfg.pgSUAuthMethod == "md5" && cfg.pgSUPassword == "" && cfg.pgSUPasswordFile == "" { log.Fatalf("one of --pg-su-password or --pg-su-passwordfile is required") } - if cfg.pgSUAuthMethod != "trust" && cfg.pgSUPassword != "" && cfg.pgSUPasswordFile != "" { + if cfg.pgSUAuthMethod == "md5" && cfg.pgSUPassword != "" && cfg.pgSUPasswordFile != "" { log.Fatalf("only one of --pg-su-password or --pg-su-passwordfile must be provided") } + if _, ok := validAuthMethods[cfg.pgSULocalAuthMethod]; !ok { + log.Fatalf("--pg-su-local-auth-method must be one of: ident, md5, password, trust or cert") + } if cfg.pgReplPasswordFile != "" { cfg.pgReplPassword, err = readPasswordFromFile(cfg.pgReplPasswordFile) @@ -2059,7 +2118,7 @@ func keeper(c *cobra.Command, args []string) { if cfg.pgReplAuthMethod != cfg.pgSUAuthMethod { log.Fatalf("do not support different auth methods when utilizing superuser for replication.") } - if cfg.pgSUPassword != cfg.pgReplPassword && cfg.pgSUAuthMethod != "trust" && cfg.pgReplAuthMethod != "trust" { + if cfg.pgSUPassword != cfg.pgReplPassword && cfg.pgSUAuthMethod == "md5" && cfg.pgReplAuthMethod == "md5" { log.Fatalf("provided superuser name and replication user name are the same but provided passwords are different") } } diff --git a/internal/timer/timer_fallback.go b/internal/timer/timer_fallback.go index 510072618..b3d4edcc6 100644 --- a/internal/timer/timer_fallback.go +++ b/internal/timer/timer_fallback.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !linux // +build !linux package timer diff --git a/internal/timer/timer_linux.go b/internal/timer/timer_linux.go index 3f6e9c3f9..3d75d9835 100644 --- a/internal/timer/timer_linux.go +++ b/internal/timer/timer_linux.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build linux // +build linux package timer