cmd, server: base64 encode multiple secrets

Two things here:

    * key secrets are now base64 encoded strings, so we get the full key
      space

    * we can pass >1 of them in so we can rotate them
This commit is contained in:
Bobby Rullo 2015-08-25 16:44:38 -07:00
parent 93f4ae2ba6
commit d0c199b62c
3 changed files with 34 additions and 11 deletions

View file

@ -28,7 +28,10 @@ func init() {
func main() { func main() {
fs := flag.NewFlagSet("dex-overlord", flag.ExitOnError) fs := flag.NewFlagSet("dex-overlord", flag.ExitOnError)
secret := fs.String("key-secret", "", "symmetric key used to encrypt/decrypt signing key data in DB")
keySecrets := pflag.NewBase64List(32)
fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.")
dbURL := fs.String("db-url", "", "DSN-formatted database connection string") dbURL := fs.String("db-url", "", "DSN-formatted database connection string")
dbMigrate := fs.Bool("db-migrate", true, "perform database migrations when starting up overlord. This includes the initial DB objects creation.") dbMigrate := fs.Bool("db-migrate", true, "perform database migrations when starting up overlord. This includes the initial DB objects creation.")
@ -59,10 +62,6 @@ func main() {
log.EnableTimestamps() log.EnableTimestamps()
} }
if len(*secret) == 0 {
log.Fatalf("--key-secret unset")
}
adminURL, err := url.Parse(*adminListen) adminURL, err := url.Parse(*adminListen)
if err != nil { if err != nil {
log.Fatalf("Unable to use --admin-listen flag: %v", err) log.Fatalf("Unable to use --admin-listen flag: %v", err)
@ -96,11 +95,32 @@ func main() {
userManager := user.NewManager(userRepo, userManager := user.NewManager(userRepo,
pwiRepo, db.TransactionFactory(dbc), user.ManagerOptions{}) pwiRepo, db.TransactionFactory(dbc), user.ManagerOptions{})
adminAPI := admin.NewAdminAPI(userManager, userRepo, pwiRepo, *localConnectorID) adminAPI := admin.NewAdminAPI(userManager, userRepo, pwiRepo, *localConnectorID)
kRepo, err := db.NewPrivateKeySetRepo(dbc, *secret) kRepo, err := db.NewPrivateKeySetRepo(dbc, keySecrets.BytesSlice()...)
if err != nil { if err != nil {
log.Fatalf(err.Error()) log.Fatalf(err.Error())
} }
var sleep time.Duration
for {
var done bool
_, err := kRepo.Get()
switch err {
case nil:
done = true
case key.ErrorNoKeys:
done = true
case db.ErrorCannotDecryptKeys:
log.Fatalf("Cannot decrypt keys using any of the given key secrets. The key secrets must be changed to include one that can decrypt the existing keys, or the existing keys must be deleted.")
}
if done {
break
}
sleep = ptime.ExpBackoff(sleep, time.Minute)
log.Errorf("Unable to get keys from repository, retrying in %v: %v", sleep, err)
time.Sleep(sleep)
}
krot := key.NewPrivateKeyRotator(kRepo, *keyPeriod) krot := key.NewPrivateKeyRotator(kRepo, *keyPeriod)
s := server.NewAdminServer(adminAPI, krot) s := server.NewAdminServer(adminAPI, krot)
h := s.HTTPHandler() h := s.HTTPHandler()

View file

@ -41,7 +41,10 @@ func main() {
// ignored if --no-db is set // ignored if --no-db is set
dbURL := fs.String("db-url", "", "DSN-formatted database connection string") dbURL := fs.String("db-url", "", "DSN-formatted database connection string")
keySecret := fs.String("key-secret", "", "symmetric key used to encrypt/decrypt signing key data in DB")
keySecrets := pflag.NewBase64List(32)
fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.")
dbMaxIdleConns := fs.Int("db-max-idle-conns", 0, "maximum number of connections in the idle connection pool") dbMaxIdleConns := fs.Int("db-max-idle-conns", 0, "maximum number of connections in the idle connection pool")
dbMaxOpenConns := fs.Int("db-max-open-conns", 0, "maximum number of open connections to the database") dbMaxOpenConns := fs.Int("db-max-open-conns", 0, "maximum number of open connections to the database")
@ -109,7 +112,7 @@ func main() {
MaxOpenConnections: *dbMaxOpenConns, MaxOpenConnections: *dbMaxOpenConns,
} }
scfg.StateConfig = &server.MultiServerConfig{ scfg.StateConfig = &server.MultiServerConfig{
KeySecret: *keySecret, KeySecrets: keySecrets.BytesSlice(),
DatabaseConfig: dbCfg, DatabaseConfig: dbCfg,
} }
} }

View file

@ -44,7 +44,7 @@ type SingleServerConfig struct {
} }
type MultiServerConfig struct { type MultiServerConfig struct {
KeySecret string KeySecrets [][]byte
DatabaseConfig db.Config DatabaseConfig db.Config
} }
@ -141,7 +141,7 @@ func (cfg *SingleServerConfig) Configure(srv *Server) error {
} }
func (cfg *MultiServerConfig) Configure(srv *Server) error { func (cfg *MultiServerConfig) Configure(srv *Server) error {
if cfg.KeySecret == "" { if len(cfg.KeySecrets) == 0 {
return errors.New("missing key secret") return errors.New("missing key secret")
} }
@ -154,7 +154,7 @@ func (cfg *MultiServerConfig) Configure(srv *Server) error {
return fmt.Errorf("unable to initialize database connection: %v", err) return fmt.Errorf("unable to initialize database connection: %v", err)
} }
kRepo, err := db.NewPrivateKeySetRepo(dbc, cfg.KeySecret) kRepo, err := db.NewPrivateKeySetRepo(dbc, cfg.KeySecrets...)
if err != nil { if err != nil {
return fmt.Errorf("unable to create PrivateKeySetRepo: %v", err) return fmt.Errorf("unable to create PrivateKeySetRepo: %v", err)
} }