Skip to content

Commit

Permalink
Role requesting certs (#251)
Browse files Browse the repository at this point in the history
This change adds 2 endpoints to keymasterd. A role requesting ednpoint and a refresh endpoint. 

The first of this endpoints (role requesting) requires an "automationadmin" (new option in the config) to be the caller. This endpoint generates much longer lived certs (today hardcoded to 45 days) and thus we have also enhanced the verification of identities to include denylists of keys. These are currenly part of the keymasterd configuration 
Tthe second endpoint can only be called from the ipranges embedded in the certificate and provide a mechanism to upate things.
  • Loading branch information
cviecco authored Dec 9, 2024
1 parent 10b9ce0 commit d73561c
Show file tree
Hide file tree
Showing 7 changed files with 760 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ endif
BINARY=keymaster

# These are the values we want to pass for Version and BuildTime
VERSION?=1.15.5
VERSION?=1.16.0
DEFAULT_HOST?=
VERSION_FLAVOUR?=
EXTRA_LDFLAGS?=
Expand Down
66 changes: 43 additions & 23 deletions cmd/keymasterd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,15 @@ func (state *RuntimeState) getUsernameIfKeymasterSigned(VerifiedChains [][]*x509
if err != nil {
return "", time.Time{}, err
}
userPubKeyFP, err := getKeyFingerprint(chain[0].PublicKey)
if err != nil {
return "", time.Time{}, err
}
for _, revokedKeyFP := range state.Config.DenyTrustData.KeyDenyFPsshSha256 {
if userPubKeyFP == revokedKeyFP {
return "", time.Time{}, fmt.Errorf("revoked key with FP:%s", revokedKeyFP)
}
}
for _, key := range state.KeymasterPublicKeys {
fp, err := getKeyFingerprint(key)
if err != nil {
Expand Down Expand Up @@ -870,36 +879,45 @@ func (state *RuntimeState) checkAuth(w http.ResponseWriter, r *http.Request, req
state.logger.Debugf(3,
"looks like authtype tls keymaster or ip cert, r.tls=%+v", r.TLS)
if len(r.TLS.VerifiedChains) > 0 {
if (requiredAuthType & AuthTypeKeymasterX509) != 0 {
tlsAuthUser, notBefore, err :=
state.getUsernameIfKeymasterSigned(r.TLS.VerifiedChains)
if err == nil && tlsAuthUser != "" {
return &authInfo{
AuthType: AuthTypeKeymasterX509,
IssuedAt: notBefore,
Username: tlsAuthUser,
}, nil
}
var authData authInfo
tlsAuthUser, notBefore, err :=
state.getUsernameIfKeymasterSigned(r.TLS.VerifiedChains)
if err == nil && tlsAuthUser != "" {
state.logger.Debugf(4, "Auth, Is keymastercert")
authData.AuthType = authData.AuthType | AuthTypeKeymasterX509
authData.IssuedAt = notBefore
authData.Username = tlsAuthUser
}
if (requiredAuthType & AuthTypeIPCertificate) != 0 {
clientName, notBefore, userErr, err :=
state.getUsernameIfIPRestricted(r.TLS.VerifiedChains, r)
if userErr != nil {
state.writeFailureResponse(w, r, http.StatusForbidden,
fmt.Sprintf("%s", userErr))
return nil, userErr
// if not keymasterd cert AND not ipcert either then we return
// more explicit errors
if authData.Username == "" {
state.logger.Printf("after eval, but username is empty")
if userErr != nil {
state.writeFailureResponse(w, r, http.StatusForbidden,
fmt.Sprintf("%s", userErr))
return nil, userErr
}
if err != nil {
state.writeFailureResponse(w, r,
http.StatusInternalServerError, "")
return nil, err
}
}
if err != nil {
state.writeFailureResponse(w, r,
http.StatusInternalServerError, "")
return nil, err

if err == nil && userErr == nil {
authData.AuthType = authData.AuthType | AuthTypeIPCertificate
authData.IssuedAt = notBefore
authData.Username = clientName
}
return &authInfo{
AuthType: AuthTypeIPCertificate,
IssuedAt: notBefore,
Username: clientName,
}, nil
}
if authData.Username != "" {
state.logger.Debugf(4, "returning tls cert authinfo")
return &authData, nil
}
state.logger.Debugf(4, "NOT returning tls cert authinfo authData=%+v", authData)
}
}
// Next we check for cookies
Expand Down Expand Up @@ -1938,6 +1956,8 @@ func main() {
serviceMux.HandleFunc(paths.VerifyAuthToken,
runtimeState.VerifyAuthTokenHandler)
}
serviceMux.HandleFunc(getRoleRequestingPath, runtimeState.roleRequetingCertGenHandler)
serviceMux.HandleFunc(refreshRoleRequestingCertPath, runtimeState.refreshRoleRequestingCertGenHandler)
serviceMux.HandleFunc("/", runtimeState.defaultPathHandler)

cfg := &tls.Config{
Expand Down
6 changes: 6 additions & 0 deletions cmd/keymasterd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type baseConfig struct {
SecsBetweenDependencyChecks int `yaml:"secs_between_dependency_checks"`
AutomationUserGroups []string `yaml:"automation_user_groups"`
AutomationUsers []string `yaml:"automation_users"`
AutomationAdmins []string `yaml:"automation_admins"`
DisableUsernameNormalization bool `yaml:"disable_username_normalization"`
EnableLocalTOTP bool `yaml:"enable_local_totp"`
EnableBootstrapOTP bool `yaml:"enable_bootstrapotp"`
Expand Down Expand Up @@ -189,6 +190,10 @@ type SymantecVIPConfig struct {
RequireAppAproval bool `yaml:"require_app_approval"`
}

type DenyKeyConfig struct {
KeyDenyFPsshSha256 []string `yaml:"key_deny_list_ssh_sha256"`
}

type AppConfigFile struct {
Base baseConfig
AwsCerts awsCertsConfig `yaml:"aws_certs"`
Expand All @@ -202,6 +207,7 @@ type AppConfigFile struct {
OpenIDConnectIDP OpenIDConnectIDPConfig `yaml:"openid_connect_idp"`
SymantecVIP SymantecVIPConfig
ProfileStorage ProfileStorageConfig
DenyTrustData DenyKeyConfig
}

const (
Expand Down
Loading

0 comments on commit d73561c

Please sign in to comment.