Skip to content

Commit

Permalink
enabling cert, ident and peer authentication peer
Browse files Browse the repository at this point in the history
  • Loading branch information
sebasmannem committed May 15, 2022
1 parent 8d655b1 commit c3967ed
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 64 deletions.
Binary file added .DS_Store
Binary file not shown.
187 changes: 123 additions & 64 deletions cmd/keeper/cmd/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)")
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -1837,19 +1869,19 @@ 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 {
case cluster.SUReplAccessAll:
// 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
Expand All @@ -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),
)
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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")
}
}
Expand Down
1 change: 1 addition & 0 deletions internal/timer/timer_fallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions internal/timer/timer_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c3967ed

Please sign in to comment.