Skip to content

Commit 79349b4

Browse files
committed
Add options to use custom renewal methods.
1 parent 3898156 commit 79349b4

File tree

5 files changed

+93
-25
lines changed

5 files changed

+93
-25
lines changed

authority/authority.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ type Authority struct {
7070
startTime time.Time
7171

7272
// Custom functions
73-
sshBastionFunc func(ctx context.Context, user, hostname string) (*config.Bastion, error)
74-
sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error)
75-
sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error)
76-
getIdentityFunc provisioner.GetIdentityFunc
73+
sshBastionFunc func(ctx context.Context, user, hostname string) (*config.Bastion, error)
74+
sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error)
75+
sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error)
76+
getIdentityFunc provisioner.GetIdentityFunc
77+
authorizeRenewFunc provisioner.AuthorizeRenewFunc
78+
authorizeSSHRenewFunc provisioner.AuthorizeSSHRenewFunc
7779

7880
adminMutex sync.RWMutex
7981
}

authority/authorize_test.go

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,23 @@ func TestAuthority_authorizeSSHSign(t *testing.T) {
10111011
}
10121012

10131013
func TestAuthority_authorizeSSHRenew(t *testing.T) {
1014+
now := time.Now().UTC()
1015+
sshpop := func(a *Authority) (*ssh.Certificate, string) {
1016+
p, ok := a.provisioners.Load("sshpop/sshpop")
1017+
assert.Fatal(t, ok, "sshpop provisioner not found in test authority")
1018+
key, err := pemutil.Read("./testdata/secrets/ssh_host_ca_key")
1019+
assert.FatalError(t, err)
1020+
signer, ok := key.(crypto.Signer)
1021+
assert.Fatal(t, ok, "could not cast ssh signing key to crypto signer")
1022+
sshSigner, err := ssh.NewSignerFromSigner(signer)
1023+
assert.FatalError(t, err)
1024+
cert, jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.HostCert}, sshSigner)
1025+
assert.FatalError(t, err)
1026+
token, err := generateToken("foo", p.GetName(), testAudiences.SSHRenew[0]+"#sshpop/sshpop", []string{"foo.smallstep.com"}, now, jwk, withSSHPOPFile(cert))
1027+
assert.FatalError(t, err)
1028+
return cert, token
1029+
}
1030+
10141031
a := testAuthority(t)
10151032

10161033
jwk, err := jose.ReadKey("testdata/secrets/step_cli_key_priv.jwk", jose.WithPassword([]byte("pass")))
@@ -1020,8 +1037,6 @@ func TestAuthority_authorizeSSHRenew(t *testing.T) {
10201037
(&jose.SignerOptions{}).WithType("JWT").WithHeader("kid", jwk.KeyID))
10211038
assert.FatalError(t, err)
10221039

1023-
now := time.Now().UTC()
1024-
10251040
validIssuer := "step-cli"
10261041

10271042
type authorizeTest struct {
@@ -1058,27 +1073,34 @@ func TestAuthority_authorizeSSHRenew(t *testing.T) {
10581073
code: http.StatusUnauthorized,
10591074
}
10601075
},
1076+
"fail/WithAuthorizeSSHRenewFunc": func(t *testing.T) *authorizeTest {
1077+
aa := testAuthority(t, WithAuthorizeSSHRenewFunc(func(ctx context.Context, p *provisioner.Controller, cert *ssh.Certificate) error {
1078+
return errs.Forbidden("forbidden")
1079+
}))
1080+
_, token := sshpop(aa)
1081+
return &authorizeTest{
1082+
auth: aa,
1083+
token: token,
1084+
err: errors.New("authority.authorizeSSHRenew: forbidden"),
1085+
code: http.StatusForbidden,
1086+
}
1087+
},
10611088
"ok": func(t *testing.T) *authorizeTest {
1062-
key, err := pemutil.Read("./testdata/secrets/ssh_host_ca_key")
1063-
assert.FatalError(t, err)
1064-
signer, ok := key.(crypto.Signer)
1065-
assert.Fatal(t, ok, "could not cast ssh signing key to crypto signer")
1066-
sshSigner, err := ssh.NewSignerFromSigner(signer)
1067-
assert.FatalError(t, err)
1068-
1069-
cert, _jwk, err := createSSHCert(&ssh.Certificate{CertType: ssh.HostCert}, sshSigner)
1070-
assert.FatalError(t, err)
1071-
1072-
p, ok := a.provisioners.Load("sshpop/sshpop")
1073-
assert.Fatal(t, ok, "sshpop provisioner not found in test authority")
1074-
1075-
tok, err := generateToken("foo", p.GetName(), testAudiences.SSHRenew[0]+"#sshpop/sshpop",
1076-
[]string{"foo.smallstep.com"}, now, _jwk, withSSHPOPFile(cert))
1077-
assert.FatalError(t, err)
1078-
1089+
cert, token := sshpop(a)
10791090
return &authorizeTest{
10801091
auth: a,
1081-
token: tok,
1092+
token: token,
1093+
cert: cert,
1094+
}
1095+
},
1096+
"ok/WithAuthorizeSSHRenewFunc": func(t *testing.T) *authorizeTest {
1097+
aa := testAuthority(t, WithAuthorizeSSHRenewFunc(func(ctx context.Context, p *provisioner.Controller, cert *ssh.Certificate) error {
1098+
return nil
1099+
}))
1100+
cert, token := sshpop(aa)
1101+
return &authorizeTest{
1102+
auth: aa,
1103+
token: token,
10821104
cert: cert,
10831105
}
10841106
},

authority/options.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,24 @@ func WithGetIdentityFunc(fn func(ctx context.Context, p provisioner.Interface, e
9292
}
9393
}
9494

95+
// WithAuthorizeRenewFunc sets a custom function that authorizes the renewal of
96+
// an X.509 certificate.
97+
func WithAuthorizeRenewFunc(fn func(ctx context.Context, p *provisioner.Controller, cert *x509.Certificate) error) Option {
98+
return func(a *Authority) error {
99+
a.authorizeRenewFunc = fn
100+
return nil
101+
}
102+
}
103+
104+
// WithAuthorizeSSHRenewFunc sets a custom function that authorizes the renewal
105+
// of a SSH certificate.
106+
func WithAuthorizeSSHRenewFunc(fn func(ctx context.Context, p *provisioner.Controller, cert *ssh.Certificate) error) Option {
107+
return func(a *Authority) error {
108+
a.authorizeSSHRenewFunc = fn
109+
return nil
110+
}
111+
}
112+
95113
// WithSSHBastionFunc sets a custom function to get the bastion for a
96114
// given user-host pair.
97115
func WithSSHBastionFunc(fn func(ctx context.Context, user, host string) (*config.Bastion, error)) Option {

authority/provisioners.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ func (a *Authority) generateProvisionerConfig(ctx context.Context) (provisioner.
108108
UserKeys: sshKeys.UserKeys,
109109
HostKeys: sshKeys.HostKeys,
110110
},
111-
GetIdentityFunc: a.getIdentityFunc,
111+
GetIdentityFunc: a.getIdentityFunc,
112+
AuthorizeRenewFunc: a.authorizeRenewFunc,
113+
AuthorizeSSHRenewFunc: a.authorizeSSHRenewFunc,
112114
}, nil
113115

114116
}

authority/tls_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,19 @@ func TestAuthority_Renew(t *testing.T) {
802802
code: http.StatusUnauthorized,
803803
}, nil
804804
},
805+
"fail/WithAuthorizeRenewFunc": func() (*renewTest, error) {
806+
aa := testAuthority(t, WithAuthorizeRenewFunc(func(ctx context.Context, p *provisioner.Controller, cert *x509.Certificate) error {
807+
return errs.Unauthorized("not authorized")
808+
}))
809+
aa.x509CAService = a.x509CAService
810+
aa.config.AuthorityConfig.Template = a.config.AuthorityConfig.Template
811+
return &renewTest{
812+
auth: aa,
813+
cert: cert,
814+
err: errors.New("authority.Rekey: authority.authorizeRenew: not authorized"),
815+
code: http.StatusUnauthorized,
816+
}, nil
817+
},
805818
"ok": func() (*renewTest, error) {
806819
return &renewTest{
807820
auth: a,
@@ -820,6 +833,17 @@ func TestAuthority_Renew(t *testing.T) {
820833
cert: cert,
821834
}, nil
822835
},
836+
"ok/WithAuthorizeRenewFunc": func() (*renewTest, error) {
837+
aa := testAuthority(t, WithAuthorizeRenewFunc(func(ctx context.Context, p *provisioner.Controller, cert *x509.Certificate) error {
838+
return nil
839+
}))
840+
aa.x509CAService = a.x509CAService
841+
aa.config.AuthorityConfig.Template = a.config.AuthorityConfig.Template
842+
return &renewTest{
843+
auth: aa,
844+
cert: cert,
845+
}, nil
846+
},
823847
}
824848

825849
for name, genTestCase := range tests {

0 commit comments

Comments
 (0)