forked from TrueCloudLab/certificates
Merge pull request #150 from smallstep/backdate
Add backdate support to the x509 and SSH certificates.
This commit is contained in:
commit
8a10c5032f
22 changed files with 535 additions and 129 deletions
|
@ -28,6 +28,7 @@ var (
|
||||||
MaxVersion: 1.2,
|
MaxVersion: 1.2,
|
||||||
Renegotiation: false,
|
Renegotiation: false,
|
||||||
}
|
}
|
||||||
|
defaultBackdate = time.Minute
|
||||||
defaultDisableRenewal = false
|
defaultDisableRenewal = false
|
||||||
defaultEnableSSHCA = false
|
defaultEnableSSHCA = false
|
||||||
globalProvisionerClaims = provisioner.Claims{
|
globalProvisionerClaims = provisioner.Claims{
|
||||||
|
@ -69,6 +70,7 @@ type AuthConfig struct {
|
||||||
Template *x509util.ASN1DN `json:"template,omitempty"`
|
Template *x509util.ASN1DN `json:"template,omitempty"`
|
||||||
Claims *provisioner.Claims `json:"claims,omitempty"`
|
Claims *provisioner.Claims `json:"claims,omitempty"`
|
||||||
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
|
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
|
||||||
|
Backdate *provisioner.Duration `json:"backdate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the authority configuration.
|
// Validate validates the authority configuration.
|
||||||
|
@ -91,6 +93,17 @@ func (c *AuthConfig) Validate(audiences provisioner.Audiences) error {
|
||||||
if c.Template == nil {
|
if c.Template == nil {
|
||||||
c.Template = &x509util.ASN1DN{}
|
c.Template = &x509util.ASN1DN{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Backdate != nil {
|
||||||
|
if c.Backdate.Duration < 0 {
|
||||||
|
return errors.New("authority.backdate cannot be less than 0")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.Backdate = &provisioner.Duration{
|
||||||
|
Duration: defaultBackdate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -469,7 +469,7 @@ func (p *AWS) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
|
||||||
// Set the default extensions.
|
// Set the default extensions.
|
||||||
&sshDefaultExtensionModifier{},
|
&sshDefaultExtensionModifier{},
|
||||||
// Set the validity bounds if not set.
|
// Set the validity bounds if not set.
|
||||||
sshDefaultValidityModifier(p.claimer),
|
&sshDefaultDuration{p.claimer},
|
||||||
// Validate public key
|
// Validate public key
|
||||||
&sshDefaultPublicKeyValidator{},
|
&sshDefaultPublicKeyValidator{},
|
||||||
// Validate the validity period.
|
// Validate the validity period.
|
||||||
|
|
|
@ -209,7 +209,7 @@ func (p *Azure) Init(config Config) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseToken returuns the claims, name, group, error.
|
// parseToken returns the claims, name, group, error.
|
||||||
func (p *Azure) parseToken(token string) (*azurePayload, string, string, error) {
|
func (p *Azure) parseToken(token string) (*azurePayload, string, string, error) {
|
||||||
jwt, err := jose.ParseSigned(token)
|
jwt, err := jose.ParseSigned(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -335,7 +335,7 @@ func (p *Azure) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOptio
|
||||||
// Set the default extensions.
|
// Set the default extensions.
|
||||||
&sshDefaultExtensionModifier{},
|
&sshDefaultExtensionModifier{},
|
||||||
// Set the validity bounds if not set.
|
// Set the validity bounds if not set.
|
||||||
sshDefaultValidityModifier(p.claimer),
|
&sshDefaultDuration{p.claimer},
|
||||||
// Validate public key
|
// Validate public key
|
||||||
&sshDefaultPublicKeyValidator{},
|
&sshDefaultPublicKeyValidator{},
|
||||||
// Validate the validity period.
|
// Validate the validity period.
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Claims so that individual provisioners can override global claims.
|
// Claims so that individual provisioners can override global claims.
|
||||||
|
@ -95,6 +96,21 @@ func (c *Claimer) IsDisableRenewal() bool {
|
||||||
return *c.claims.DisableRenewal
|
return *c.claims.DisableRenewal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultSSHCertDuration returns the default SSH certificate duration for the
|
||||||
|
// given certificate type.
|
||||||
|
func (c *Claimer) DefaultSSHCertDuration(certType uint32) (time.Duration, error) {
|
||||||
|
switch certType {
|
||||||
|
case ssh.UserCert:
|
||||||
|
return c.DefaultUserSSHCertDuration(), nil
|
||||||
|
case ssh.HostCert:
|
||||||
|
return c.DefaultHostSSHCertDuration(), nil
|
||||||
|
case 0:
|
||||||
|
return 0, errors.New("ssh certificate type has not been set")
|
||||||
|
default:
|
||||||
|
return 0, errors.Errorf("ssh certificate has an unknown type: %d", certType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultUserSSHCertDuration returns the default SSH user cert duration for the
|
// DefaultUserSSHCertDuration returns the default SSH user cert duration for the
|
||||||
// provisioner. If the default is not set within the provisioner, then the
|
// provisioner. If the default is not set within the provisioner, then the
|
||||||
// global default from the authority configuration will be used.
|
// global default from the authority configuration will be used.
|
||||||
|
|
51
authority/provisioner/claims_test.go
Normal file
51
authority/provisioner/claims_test.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClaimer_DefaultSSHCertDuration(t *testing.T) {
|
||||||
|
duration := Duration{
|
||||||
|
Duration: time.Hour,
|
||||||
|
}
|
||||||
|
type fields struct {
|
||||||
|
global Claims
|
||||||
|
claims *Claims
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
certType uint32
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want time.Duration
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"user", fields{globalProvisionerClaims, &Claims{DefaultUserSSHDur: &duration}}, args{1}, time.Hour, false},
|
||||||
|
{"user global", fields{globalProvisionerClaims, nil}, args{ssh.UserCert}, 16 * time.Hour, false},
|
||||||
|
{"host global", fields{globalProvisionerClaims, &Claims{DefaultHostSSHDur: &duration}}, args{2}, time.Hour, false},
|
||||||
|
{"host global", fields{globalProvisionerClaims, nil}, args{ssh.HostCert}, 30 * 24 * time.Hour, false},
|
||||||
|
{"invalid", fields{globalProvisionerClaims, nil}, args{0}, 0, true},
|
||||||
|
{"invalid global", fields{globalProvisionerClaims, nil}, args{3}, 0, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := &Claimer{
|
||||||
|
global: tt.fields.global,
|
||||||
|
claims: tt.fields.claims,
|
||||||
|
}
|
||||||
|
got, err := c.DefaultSSHCertDuration(tt.args.certType)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Claimer.DefaultSSHCertDuration() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("Claimer.DefaultSSHCertDuration() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -378,7 +378,7 @@ func (p *GCP) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
|
||||||
// Set the default extensions
|
// Set the default extensions
|
||||||
&sshDefaultExtensionModifier{},
|
&sshDefaultExtensionModifier{},
|
||||||
// Set the validity bounds if not set.
|
// Set the validity bounds if not set.
|
||||||
sshDefaultValidityModifier(p.claimer),
|
&sshDefaultDuration{p.claimer},
|
||||||
// Validate public key
|
// Validate public key
|
||||||
&sshDefaultPublicKeyValidator{},
|
&sshDefaultPublicKeyValidator{},
|
||||||
// Validate the validity period.
|
// Validate the validity period.
|
||||||
|
|
|
@ -188,6 +188,7 @@ func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
|
||||||
if claims.Step == nil || claims.Step.SSH == nil {
|
if claims.Step == nil || claims.Step.SSH == nil {
|
||||||
return nil, errors.New("authorization token must be an SSH provisioning token")
|
return nil, errors.New("authorization token must be an SSH provisioning token")
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := claims.Step.SSH
|
opts := claims.Step.SSH
|
||||||
signOptions := []SignOption{
|
signOptions := []SignOption{
|
||||||
// validates user's SSHOptions with the ones in the token
|
// validates user's SSHOptions with the ones in the token
|
||||||
|
@ -222,7 +223,7 @@ func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
|
||||||
// Set the default extensions.
|
// Set the default extensions.
|
||||||
&sshDefaultExtensionModifier{},
|
&sshDefaultExtensionModifier{},
|
||||||
// Set the validity bounds if not set.
|
// Set the validity bounds if not set.
|
||||||
sshDefaultValidityModifier(p.claimer),
|
&sshDefaultDuration{p.claimer},
|
||||||
// Validate that the keyID is equivalent to the token subject.
|
// Validate that the keyID is equivalent to the token subject.
|
||||||
sshCertKeyIDValidator(claims.Subject),
|
sshCertKeyIDValidator(claims.Subject),
|
||||||
// Validate public key
|
// Validate public key
|
||||||
|
|
|
@ -235,13 +235,15 @@ func (p *K8sSA) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOptio
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to a user certificate with no principals if not set
|
// Default to a user certificate with no principals if not set
|
||||||
signOptions := []SignOption{sshCertificateDefaultsModifier{CertType: SSHUserCert}}
|
signOptions := []SignOption{
|
||||||
|
sshCertificateDefaultsModifier{CertType: SSHUserCert},
|
||||||
|
}
|
||||||
|
|
||||||
return append(signOptions,
|
return append(signOptions,
|
||||||
// Set the default extensions.
|
// Set the default extensions.
|
||||||
&sshDefaultExtensionModifier{},
|
&sshDefaultExtensionModifier{},
|
||||||
// Set the validity bounds if not set.
|
// Set the validity bounds if not set.
|
||||||
sshDefaultValidityModifier(p.claimer),
|
&sshDefaultDuration{p.claimer},
|
||||||
// Validate public key
|
// Validate public key
|
||||||
&sshDefaultPublicKeyValidator{},
|
&sshDefaultPublicKeyValidator{},
|
||||||
// Validate the validity period.
|
// Validate the validity period.
|
||||||
|
|
|
@ -360,7 +360,7 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
|
||||||
// Set the default extensions
|
// Set the default extensions
|
||||||
&sshDefaultExtensionModifier{},
|
&sshDefaultExtensionModifier{},
|
||||||
// Set the validity bounds if not set.
|
// Set the validity bounds if not set.
|
||||||
sshDefaultValidityModifier(o.claimer),
|
&sshDefaultDuration{o.claimer},
|
||||||
// Validate public key
|
// Validate public key
|
||||||
&sshDefaultPublicKeyValidator{},
|
&sshDefaultPublicKeyValidator{},
|
||||||
// Validate the validity period.
|
// Validate the validity period.
|
||||||
|
|
|
@ -15,10 +15,12 @@ import (
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options contains the options that can be passed to the Sign method.
|
// Options contains the options that can be passed to the Sign method. Backdate
|
||||||
|
// is automatically filled and can only be configured in the CA.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
NotAfter TimeDuration `json:"notAfter"`
|
NotAfter TimeDuration `json:"notAfter"`
|
||||||
NotBefore TimeDuration `json:"notBefore"`
|
NotBefore TimeDuration `json:"notBefore"`
|
||||||
|
Backdate time.Duration `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignOption is the interface used to collect all extra options used in the
|
// SignOption is the interface used to collect all extra options used in the
|
||||||
|
@ -189,12 +191,22 @@ func (v emailAddressesValidator) Valid(req *x509.CertificateRequest) error {
|
||||||
type profileDefaultDuration time.Duration
|
type profileDefaultDuration time.Duration
|
||||||
|
|
||||||
func (v profileDefaultDuration) Option(so Options) x509util.WithOption {
|
func (v profileDefaultDuration) Option(so Options) x509util.WithOption {
|
||||||
|
var backdate time.Duration
|
||||||
notBefore := so.NotBefore.Time()
|
notBefore := so.NotBefore.Time()
|
||||||
if notBefore.IsZero() {
|
if notBefore.IsZero() {
|
||||||
notBefore = time.Now()
|
notBefore = now()
|
||||||
|
backdate = -1 * so.Backdate
|
||||||
}
|
}
|
||||||
notAfter := so.NotAfter.RelativeTime(notBefore)
|
notAfter := so.NotAfter.RelativeTime(notBefore)
|
||||||
return x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(v))
|
return func(p x509util.Profile) error {
|
||||||
|
fn := x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(v))
|
||||||
|
if err := fn(p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
crt := p.Subject()
|
||||||
|
crt.NotBefore = crt.NotBefore.Add(backdate)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// profileLimitDuration is an x509 profile option that modifies an x509 validity
|
// profileLimitDuration is an x509 profile option that modifies an x509 validity
|
||||||
|
@ -208,10 +220,12 @@ type profileLimitDuration struct {
|
||||||
// certificate to one that is superficially imposed.
|
// certificate to one that is superficially imposed.
|
||||||
func (v profileLimitDuration) Option(so Options) x509util.WithOption {
|
func (v profileLimitDuration) Option(so Options) x509util.WithOption {
|
||||||
return func(p x509util.Profile) error {
|
return func(p x509util.Profile) error {
|
||||||
|
var backdate time.Duration
|
||||||
n := now()
|
n := now()
|
||||||
notBefore := so.NotBefore.Time()
|
notBefore := so.NotBefore.Time()
|
||||||
if notBefore.IsZero() {
|
if notBefore.IsZero() {
|
||||||
notBefore = n
|
notBefore = n
|
||||||
|
backdate = -1 * so.Backdate
|
||||||
}
|
}
|
||||||
if notBefore.After(v.notAfter) {
|
if notBefore.After(v.notAfter) {
|
||||||
return errors.Errorf("provisioning credential expiration (%s) is before "+
|
return errors.Errorf("provisioning credential expiration (%s) is before "+
|
||||||
|
@ -232,7 +246,7 @@ func (v profileLimitDuration) Option(so Options) x509util.WithOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crt := p.Subject()
|
crt := p.Subject()
|
||||||
crt.NotBefore = notBefore
|
crt.NotBefore = notBefore.Add(backdate)
|
||||||
crt.NotAfter = notAfter
|
crt.NotAfter = notAfter
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -253,12 +267,20 @@ func newValidityValidator(min, max time.Duration) *validityValidator {
|
||||||
// and total duration.
|
// and total duration.
|
||||||
func (v *validityValidator) Valid(crt *x509.Certificate) error {
|
func (v *validityValidator) Valid(crt *x509.Certificate) error {
|
||||||
var (
|
var (
|
||||||
na = crt.NotAfter
|
na = crt.NotAfter.Truncate(time.Second)
|
||||||
nb = crt.NotBefore
|
nb = crt.NotBefore.Truncate(time.Second)
|
||||||
d = na.Sub(nb)
|
now = time.Now().Truncate(time.Second)
|
||||||
now = time.Now()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// To not take into account the backdate, time.Now() will be used to
|
||||||
|
// calculate the duration if NotBefore is in the past.
|
||||||
|
var d time.Duration
|
||||||
|
if now.After(nb) {
|
||||||
|
d = na.Sub(now)
|
||||||
|
} else {
|
||||||
|
d = na.Sub(nb)
|
||||||
|
}
|
||||||
|
|
||||||
if na.Before(now) {
|
if na.Before(now) {
|
||||||
return errors.Errorf("NotAfter: %v cannot be in the past", na)
|
return errors.Errorf("NotAfter: %v cannot be in the past", na)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -275,7 +276,9 @@ func Test_validityValidator_Valid(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_profileLimitDuration_Option(t *testing.T) {
|
func Test_profileLimitDuration_Option(t *testing.T) {
|
||||||
n := now()
|
n, fn := mockNow()
|
||||||
|
defer fn()
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
pld profileLimitDuration
|
pld profileLimitDuration
|
||||||
so Options
|
so Options
|
||||||
|
@ -309,7 +312,7 @@ func Test_profileLimitDuration_Option(t *testing.T) {
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
return test{
|
return test{
|
||||||
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
||||||
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), NotAfter: d},
|
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), NotAfter: d, Backdate: 1 * time.Minute},
|
||||||
cert: new(x509.Certificate),
|
cert: new(x509.Certificate),
|
||||||
valid: func(cert *x509.Certificate) {
|
valid: func(cert *x509.Certificate) {
|
||||||
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
||||||
|
@ -320,7 +323,7 @@ func Test_profileLimitDuration_Option(t *testing.T) {
|
||||||
"ok/valid-notAfter-nil-limit-over-default": func() test {
|
"ok/valid-notAfter-nil-limit-over-default": func() test {
|
||||||
return test{
|
return test{
|
||||||
pld: profileLimitDuration{def: 1 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
pld: profileLimitDuration{def: 1 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
||||||
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour))},
|
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), Backdate: 1 * time.Minute},
|
||||||
cert: new(x509.Certificate),
|
cert: new(x509.Certificate),
|
||||||
valid: func(cert *x509.Certificate) {
|
valid: func(cert *x509.Certificate) {
|
||||||
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
||||||
|
@ -331,7 +334,7 @@ func Test_profileLimitDuration_Option(t *testing.T) {
|
||||||
"ok/valid-notAfter-nil-limit-under-default": func() test {
|
"ok/valid-notAfter-nil-limit-under-default": func() test {
|
||||||
return test{
|
return test{
|
||||||
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
pld: profileLimitDuration{def: 4 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
||||||
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour))},
|
so: Options{NotBefore: NewTimeDuration(n.Add(3 * time.Hour)), Backdate: 1 * time.Minute},
|
||||||
cert: new(x509.Certificate),
|
cert: new(x509.Certificate),
|
||||||
valid: func(cert *x509.Certificate) {
|
valid: func(cert *x509.Certificate) {
|
||||||
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
assert.Equals(t, cert.NotBefore, n.Add(3*time.Hour))
|
||||||
|
@ -339,6 +342,28 @@ func Test_profileLimitDuration_Option(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ok/over-limit-with-backdate": func() test {
|
||||||
|
return test{
|
||||||
|
pld: profileLimitDuration{def: 24 * time.Hour, notAfter: n.Add(6 * time.Hour)},
|
||||||
|
so: Options{Backdate: 1 * time.Minute},
|
||||||
|
cert: new(x509.Certificate),
|
||||||
|
valid: func(cert *x509.Certificate) {
|
||||||
|
assert.Equals(t, cert.NotBefore, n.Add(-time.Minute))
|
||||||
|
assert.Equals(t, cert.NotAfter, n.Add(6*time.Hour))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/under-limit-with-backdate": func() test {
|
||||||
|
return test{
|
||||||
|
pld: profileLimitDuration{def: 24 * time.Hour, notAfter: n.Add(30 * time.Hour)},
|
||||||
|
so: Options{Backdate: 1 * time.Minute},
|
||||||
|
cert: new(x509.Certificate),
|
||||||
|
valid: func(cert *x509.Certificate) {
|
||||||
|
assert.Equals(t, cert.NotBefore, n.Add(-time.Minute))
|
||||||
|
assert.Equals(t, cert.NotAfter, n.Add(24*time.Hour))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, run := range tests {
|
for name, run := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
@ -357,3 +382,43 @@ func Test_profileLimitDuration_Option(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_profileDefaultDuration_Option(t *testing.T) {
|
||||||
|
tm, fn := mockNow()
|
||||||
|
defer fn()
|
||||||
|
|
||||||
|
v := profileDefaultDuration(24 * time.Hour)
|
||||||
|
type args struct {
|
||||||
|
so Options
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
v profileDefaultDuration
|
||||||
|
args args
|
||||||
|
want *x509.Certificate
|
||||||
|
}{
|
||||||
|
{"default", v, args{Options{}}, &x509.Certificate{NotBefore: tm, NotAfter: tm.Add(24 * time.Hour)}},
|
||||||
|
{"backdate", v, args{Options{Backdate: 1 * time.Minute}}, &x509.Certificate{NotBefore: tm.Add(-1 * time.Minute), NotAfter: tm.Add(24 * time.Hour)}},
|
||||||
|
{"notBefore", v, args{Options{NotBefore: NewTimeDuration(tm.Add(10 * time.Second))}}, &x509.Certificate{NotBefore: tm.Add(10 * time.Second), NotAfter: tm.Add(24*time.Hour + 10*time.Second)}},
|
||||||
|
{"notAfter", v, args{Options{NotAfter: NewTimeDuration(tm.Add(1 * time.Hour))}}, &x509.Certificate{NotBefore: tm, NotAfter: tm.Add(1 * time.Hour)}},
|
||||||
|
{"notBefore and notAfter", v, args{Options{NotBefore: NewTimeDuration(tm.Add(10 * time.Second)), NotAfter: NewTimeDuration(tm.Add(1 * time.Hour))}},
|
||||||
|
&x509.Certificate{NotBefore: tm.Add(10 * time.Second), NotAfter: tm.Add(1 * time.Hour)}},
|
||||||
|
{"notBefore and backdate", v, args{Options{Backdate: 1 * time.Minute, NotBefore: NewTimeDuration(tm.Add(10 * time.Second))}},
|
||||||
|
&x509.Certificate{NotBefore: tm.Add(10 * time.Second), NotAfter: tm.Add(24*time.Hour + 10*time.Second)}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cert := &x509.Certificate{}
|
||||||
|
profile := &x509util.Leaf{}
|
||||||
|
profile.SetSubject(cert)
|
||||||
|
|
||||||
|
fn := tt.v.Option(tt.args.so)
|
||||||
|
if err := fn(profile); err != nil {
|
||||||
|
t.Errorf("profileDefaultDuration.Option() error = %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(cert, tt.want) {
|
||||||
|
t.Errorf("profileDefaultDuration.Option() = %v, \nwant %v", cert, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,14 @@ type SSHCertificateOptionsValidator interface {
|
||||||
Valid(got SSHOptions) error
|
Valid(got SSHOptions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sshModifierFunc is an adapter to allow the use of ordinary functions as SSH
|
||||||
|
// certificate modifiers.
|
||||||
|
type sshModifierFunc func(cert *ssh.Certificate) error
|
||||||
|
|
||||||
|
func (f sshModifierFunc) Modify(cert *ssh.Certificate) error {
|
||||||
|
return f(cert)
|
||||||
|
}
|
||||||
|
|
||||||
// SSHOptions contains the options that can be passed to the SignSSH method.
|
// SSHOptions contains the options that can be passed to the SignSSH method.
|
||||||
type SSHOptions struct {
|
type SSHOptions struct {
|
||||||
CertType string `json:"certType"`
|
CertType string `json:"certType"`
|
||||||
|
@ -53,6 +61,7 @@ type SSHOptions struct {
|
||||||
Principals []string `json:"principals"`
|
Principals []string `json:"principals"`
|
||||||
ValidAfter TimeDuration `json:"validAfter,omitempty"`
|
ValidAfter TimeDuration `json:"validAfter,omitempty"`
|
||||||
ValidBefore TimeDuration `json:"validBefore,omitempty"`
|
ValidBefore TimeDuration `json:"validBefore,omitempty"`
|
||||||
|
Backdate time.Duration `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type returns the uint32 representation of the CertType.
|
// Type returns the uint32 representation of the CertType.
|
||||||
|
@ -199,67 +208,91 @@ func (m *sshDefaultExtensionModifier) Modify(cert *ssh.Certificate) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sshValidityModifier is an SSHCertificateModifier that checks the
|
// sshDefaultDuration is an SSHCertificateModifier that sets the certificate
|
||||||
// validity bounds, setting them if they are not provided. It will fail if a
|
// ValidAfter and ValidBefore if they have not been set. It will fail if a
|
||||||
// CertType has not been set or is not valid.
|
// CertType has not been set or is not valid.
|
||||||
type sshValidityModifier struct {
|
type sshDefaultDuration struct {
|
||||||
*Claimer
|
*Claimer
|
||||||
validBefore time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *sshValidityModifier) Modify(cert *ssh.Certificate) error {
|
func (m *sshDefaultDuration) Option(o SSHOptions) SSHCertificateModifier {
|
||||||
var d time.Duration
|
return sshModifierFunc(func(cert *ssh.Certificate) error {
|
||||||
|
d, err := m.DefaultSSHCertDuration(cert.CertType)
|
||||||
switch cert.CertType {
|
if err != nil {
|
||||||
case ssh.UserCert:
|
return err
|
||||||
d = m.DefaultUserSSHCertDuration()
|
|
||||||
case ssh.HostCert:
|
|
||||||
d = m.DefaultHostSSHCertDuration()
|
|
||||||
case 0:
|
|
||||||
return errors.New("ssh certificate type has not been set")
|
|
||||||
default:
|
|
||||||
return errors.Errorf("unknown ssh certificate type %d", cert.CertType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLimit := !m.validBefore.IsZero()
|
var backdate uint64
|
||||||
|
|
||||||
n := now()
|
|
||||||
if cert.ValidAfter == 0 {
|
if cert.ValidAfter == 0 {
|
||||||
cert.ValidAfter = uint64(n.Truncate(time.Second).Unix())
|
backdate = uint64(o.Backdate / time.Second)
|
||||||
|
cert.ValidAfter = uint64(now().Truncate(time.Second).Unix())
|
||||||
}
|
}
|
||||||
|
if cert.ValidBefore == 0 {
|
||||||
|
cert.ValidBefore = cert.ValidAfter + uint64(d/time.Second)
|
||||||
|
}
|
||||||
|
// Apply backdate safely
|
||||||
|
if cert.ValidAfter > backdate {
|
||||||
|
cert.ValidAfter -= backdate
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// sshLimitDuration adjusts the duration to min(default, remaining provisioning
|
||||||
|
// credential duration). E.g. if the default is 12hrs but the remaining validity
|
||||||
|
// of the provisioning credential is only 4hrs, this option will set the value
|
||||||
|
// to 4hrs (the min of the two values). It will fail if a CertType has not been
|
||||||
|
// set or is not valid.
|
||||||
|
type sshLimitDuration struct {
|
||||||
|
*Claimer
|
||||||
|
NotAfter time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *sshLimitDuration) Option(o SSHOptions) SSHCertificateModifier {
|
||||||
|
if m.NotAfter.IsZero() {
|
||||||
|
defaultDuration := &sshDefaultDuration{m.Claimer}
|
||||||
|
return defaultDuration.Option(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sshModifierFunc(func(cert *ssh.Certificate) error {
|
||||||
|
d, err := m.DefaultSSHCertDuration(cert.CertType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var backdate uint64
|
||||||
|
if cert.ValidAfter == 0 {
|
||||||
|
backdate = uint64(o.Backdate / time.Second)
|
||||||
|
cert.ValidAfter = uint64(now().Truncate(time.Second).Unix())
|
||||||
|
}
|
||||||
|
|
||||||
certValidAfter := time.Unix(int64(cert.ValidAfter), 0)
|
certValidAfter := time.Unix(int64(cert.ValidAfter), 0)
|
||||||
if hasLimit && certValidAfter.After(m.validBefore) {
|
if certValidAfter.After(m.NotAfter) {
|
||||||
return errors.Errorf("provisioning credential expiration (%s) is before "+
|
return errors.Errorf("provisioning credential expiration (%s) is before requested certificate validAfter (%s)",
|
||||||
"requested certificate validAfter (%s)", m.validBefore, certValidAfter)
|
m.NotAfter, certValidAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cert.ValidBefore == 0 {
|
if cert.ValidBefore == 0 {
|
||||||
certValidBefore := certValidAfter.Add(d)
|
certValidBefore := certValidAfter.Add(d)
|
||||||
if hasLimit && m.validBefore.Before(certValidBefore) {
|
if m.NotAfter.Before(certValidBefore) {
|
||||||
certValidBefore = m.validBefore
|
certValidBefore = m.NotAfter
|
||||||
}
|
}
|
||||||
cert.ValidBefore = uint64(certValidBefore.Unix())
|
cert.ValidBefore = uint64(certValidBefore.Unix())
|
||||||
} else if hasLimit {
|
} else {
|
||||||
certValidBefore := time.Unix(int64(cert.ValidBefore), 0)
|
certValidBefore := time.Unix(int64(cert.ValidBefore), 0)
|
||||||
if m.validBefore.Before(certValidBefore) {
|
if m.NotAfter.Before(certValidBefore) {
|
||||||
return errors.Errorf("provisioning credential expiration (%s) is before "+
|
return errors.Errorf("provisioning credential expiration (%s) is before requested certificate validBefore (%s)",
|
||||||
"requested certificate validBefore (%s)", m.validBefore, certValidBefore)
|
m.NotAfter, certValidBefore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply backdate safely
|
||||||
|
if cert.ValidAfter > backdate {
|
||||||
|
cert.ValidAfter -= backdate
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
})
|
||||||
|
|
||||||
func sshDefaultValidityModifier(c *Claimer) SSHCertificateModifier {
|
|
||||||
return &sshValidityModifier{c, time.Time{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sshLimitValidityModifier adjusts the duration to
|
|
||||||
// min(default, remaining provisioning credential duration).
|
|
||||||
// E.g. if the default is 12hrs but the remaining validity of the provisioning
|
|
||||||
// credential is only 4hrs, this option will set the value to 4hrs (the min of the two values).
|
|
||||||
func sshLimitValidityModifier(c *Claimer, validBefore time.Time) SSHCertificateModifier {
|
|
||||||
return &sshValidityModifier{c, validBefore}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sshCertificateOptionsValidator validates the user SSHOptions with the ones
|
// sshCertificateOptionsValidator validates the user SSHOptions with the ones
|
||||||
|
@ -301,8 +334,15 @@ func (v *sshCertificateValidityValidator) Valid(cert *ssh.Certificate) error {
|
||||||
return errors.Errorf("unknown ssh certificate type %d", cert.CertType)
|
return errors.Errorf("unknown ssh certificate type %d", cert.CertType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// seconds
|
// To not take into account the backdate, time.Now() will be used to
|
||||||
dur := time.Duration(cert.ValidBefore-cert.ValidAfter) * time.Second
|
// calculate the duration if ValidAfter is in the past.
|
||||||
|
var dur time.Duration
|
||||||
|
if t := now().Unix(); t > int64(cert.ValidAfter) {
|
||||||
|
dur = time.Duration(int64(cert.ValidBefore)-t) * time.Second
|
||||||
|
} else {
|
||||||
|
dur = time.Duration(cert.ValidBefore-cert.ValidAfter) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case dur < min:
|
case dur < min:
|
||||||
return errors.Errorf("requested duration of %s is less than minimum "+
|
return errors.Errorf("requested duration of %s is less than minimum "+
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package provisioner
|
package provisioner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -10,6 +12,32 @@ import (
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestSSHOptions_Type(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
CertType string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want uint32
|
||||||
|
}{
|
||||||
|
{"user", fields{"user"}, 1},
|
||||||
|
{"host", fields{"host"}, 2},
|
||||||
|
{"empty", fields{""}, 0},
|
||||||
|
{"invalid", fields{"invalid"}, 0},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
o := SSHOptions{
|
||||||
|
CertType: tt.fields.CertType,
|
||||||
|
}
|
||||||
|
if got := o.Type(); got != tt.want {
|
||||||
|
t.Errorf("SSHOptions.Type() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_sshCertificateDefaultValidator_Valid(t *testing.T) {
|
func Test_sshCertificateDefaultValidator_Valid(t *testing.T) {
|
||||||
pub, _, err := keys.GenerateDefaultKeyPair()
|
pub, _, err := keys.GenerateDefaultKeyPair()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
@ -272,11 +300,13 @@ func Test_sshCertificateValidityValidator(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_sshValidityModifier(t *testing.T) {
|
func Test_sshValidityModifier(t *testing.T) {
|
||||||
n := now()
|
n, fn := mockNow()
|
||||||
|
defer fn()
|
||||||
|
|
||||||
p, err := generateX5C(nil)
|
p, err := generateX5C(nil)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
type test struct {
|
type test struct {
|
||||||
svm *sshValidityModifier
|
svm *sshLimitDuration
|
||||||
cert *ssh.Certificate
|
cert *ssh.Certificate
|
||||||
valid func(*ssh.Certificate)
|
valid func(*ssh.Certificate)
|
||||||
err error
|
err error
|
||||||
|
@ -284,7 +314,7 @@ func Test_sshValidityModifier(t *testing.T) {
|
||||||
tests := map[string]func() test{
|
tests := map[string]func() test{
|
||||||
"fail/type-not-set": func() test {
|
"fail/type-not-set": func() test {
|
||||||
return test{
|
return test{
|
||||||
svm: &sshValidityModifier{Claimer: p.claimer, validBefore: n.Add(6 * time.Hour)},
|
svm: &sshLimitDuration{Claimer: p.claimer, NotAfter: n.Add(6 * time.Hour)},
|
||||||
cert: &ssh.Certificate{
|
cert: &ssh.Certificate{
|
||||||
ValidAfter: uint64(n.Unix()),
|
ValidAfter: uint64(n.Unix()),
|
||||||
ValidBefore: uint64(n.Add(8 * time.Hour).Unix()),
|
ValidBefore: uint64(n.Add(8 * time.Hour).Unix()),
|
||||||
|
@ -294,18 +324,18 @@ func Test_sshValidityModifier(t *testing.T) {
|
||||||
},
|
},
|
||||||
"fail/type-not-recognized": func() test {
|
"fail/type-not-recognized": func() test {
|
||||||
return test{
|
return test{
|
||||||
svm: &sshValidityModifier{Claimer: p.claimer, validBefore: n.Add(6 * time.Hour)},
|
svm: &sshLimitDuration{Claimer: p.claimer, NotAfter: n.Add(6 * time.Hour)},
|
||||||
cert: &ssh.Certificate{
|
cert: &ssh.Certificate{
|
||||||
CertType: 4,
|
CertType: 4,
|
||||||
ValidAfter: uint64(n.Unix()),
|
ValidAfter: uint64(n.Unix()),
|
||||||
ValidBefore: uint64(n.Add(8 * time.Hour).Unix()),
|
ValidBefore: uint64(n.Add(8 * time.Hour).Unix()),
|
||||||
},
|
},
|
||||||
err: errors.New("unknown ssh certificate type 4"),
|
err: errors.New("ssh certificate has an unknown type: 4"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fail/requested-validAfter-after-limit": func() test {
|
"fail/requested-validAfter-after-limit": func() test {
|
||||||
return test{
|
return test{
|
||||||
svm: &sshValidityModifier{Claimer: p.claimer, validBefore: n.Add(1 * time.Hour)},
|
svm: &sshLimitDuration{Claimer: p.claimer, NotAfter: n.Add(1 * time.Hour)},
|
||||||
cert: &ssh.Certificate{
|
cert: &ssh.Certificate{
|
||||||
CertType: 1,
|
CertType: 1,
|
||||||
ValidAfter: uint64(n.Add(2 * time.Hour).Unix()),
|
ValidAfter: uint64(n.Add(2 * time.Hour).Unix()),
|
||||||
|
@ -316,7 +346,7 @@ func Test_sshValidityModifier(t *testing.T) {
|
||||||
},
|
},
|
||||||
"fail/requested-validBefore-after-limit": func() test {
|
"fail/requested-validBefore-after-limit": func() test {
|
||||||
return test{
|
return test{
|
||||||
svm: &sshValidityModifier{Claimer: p.claimer, validBefore: n.Add(1 * time.Hour)},
|
svm: &sshLimitDuration{Claimer: p.claimer, NotAfter: n.Add(1 * time.Hour)},
|
||||||
cert: &ssh.Certificate{
|
cert: &ssh.Certificate{
|
||||||
CertType: 1,
|
CertType: 1,
|
||||||
ValidAfter: uint64(n.Unix()),
|
ValidAfter: uint64(n.Unix()),
|
||||||
|
@ -325,10 +355,36 @@ func Test_sshValidityModifier(t *testing.T) {
|
||||||
err: errors.New("provisioning credential expiration ("),
|
err: errors.New("provisioning credential expiration ("),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ok/no-limit": func() test {
|
||||||
|
va, vb := uint64(n.Unix()), uint64(n.Add(16*time.Hour).Unix())
|
||||||
|
return test{
|
||||||
|
svm: &sshLimitDuration{Claimer: p.claimer},
|
||||||
|
cert: &ssh.Certificate{
|
||||||
|
CertType: 1,
|
||||||
|
},
|
||||||
|
valid: func(cert *ssh.Certificate) {
|
||||||
|
assert.Equals(t, cert.ValidAfter, va)
|
||||||
|
assert.Equals(t, cert.ValidBefore, vb)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/defaults": func() test {
|
||||||
|
va, vb := uint64(n.Unix()), uint64(n.Add(16*time.Hour).Unix())
|
||||||
|
return test{
|
||||||
|
svm: &sshLimitDuration{Claimer: p.claimer},
|
||||||
|
cert: &ssh.Certificate{
|
||||||
|
CertType: 1,
|
||||||
|
},
|
||||||
|
valid: func(cert *ssh.Certificate) {
|
||||||
|
assert.Equals(t, cert.ValidAfter, va)
|
||||||
|
assert.Equals(t, cert.ValidBefore, vb)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
"ok/valid-requested-validBefore": func() test {
|
"ok/valid-requested-validBefore": func() test {
|
||||||
va, vb := uint64(n.Unix()), uint64(n.Add(2*time.Hour).Unix())
|
va, vb := uint64(n.Unix()), uint64(n.Add(2*time.Hour).Unix())
|
||||||
return test{
|
return test{
|
||||||
svm: &sshValidityModifier{Claimer: p.claimer, validBefore: n.Add(3 * time.Hour)},
|
svm: &sshLimitDuration{Claimer: p.claimer, NotAfter: n.Add(3 * time.Hour)},
|
||||||
cert: &ssh.Certificate{
|
cert: &ssh.Certificate{
|
||||||
CertType: 1,
|
CertType: 1,
|
||||||
ValidAfter: va,
|
ValidAfter: va,
|
||||||
|
@ -343,21 +399,21 @@ func Test_sshValidityModifier(t *testing.T) {
|
||||||
"ok/empty-requested-validBefore-limit-after-default": func() test {
|
"ok/empty-requested-validBefore-limit-after-default": func() test {
|
||||||
va := uint64(n.Unix())
|
va := uint64(n.Unix())
|
||||||
return test{
|
return test{
|
||||||
svm: &sshValidityModifier{Claimer: p.claimer, validBefore: n.Add(5 * time.Hour)},
|
svm: &sshLimitDuration{Claimer: p.claimer, NotAfter: n.Add(24 * time.Hour)},
|
||||||
cert: &ssh.Certificate{
|
cert: &ssh.Certificate{
|
||||||
CertType: 1,
|
CertType: 1,
|
||||||
ValidAfter: va,
|
ValidAfter: va,
|
||||||
},
|
},
|
||||||
valid: func(cert *ssh.Certificate) {
|
valid: func(cert *ssh.Certificate) {
|
||||||
assert.Equals(t, cert.ValidAfter, va)
|
assert.Equals(t, cert.ValidAfter, va)
|
||||||
assert.Equals(t, cert.ValidBefore, uint64(n.Add(4*time.Hour).Unix()))
|
assert.Equals(t, cert.ValidBefore, uint64(n.Add(16*time.Hour).Unix()))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok/empty-requested-validBefore-limit-before-default": func() test {
|
"ok/empty-requested-validBefore-limit-before-default": func() test {
|
||||||
va := uint64(n.Unix())
|
va := uint64(n.Unix())
|
||||||
return test{
|
return test{
|
||||||
svm: &sshValidityModifier{Claimer: p.claimer, validBefore: n.Add(3 * time.Hour)},
|
svm: &sshLimitDuration{Claimer: p.claimer, NotAfter: n.Add(3 * time.Hour)},
|
||||||
cert: &ssh.Certificate{
|
cert: &ssh.Certificate{
|
||||||
CertType: 1,
|
CertType: 1,
|
||||||
ValidAfter: va,
|
ValidAfter: va,
|
||||||
|
@ -372,7 +428,7 @@ func Test_sshValidityModifier(t *testing.T) {
|
||||||
for name, run := range tests {
|
for name, run := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tt := run()
|
tt := run()
|
||||||
if err := tt.svm.Modify(tt.cert); err != nil {
|
if err := tt.svm.Option(SSHOptions{}).Modify(tt.cert); err != nil {
|
||||||
if assert.NotNil(t, tt.err) {
|
if assert.NotNil(t, tt.err) {
|
||||||
assert.HasPrefix(t, err.Error(), tt.err.Error())
|
assert.HasPrefix(t, err.Error(), tt.err.Error())
|
||||||
}
|
}
|
||||||
|
@ -384,3 +440,120 @@ func Test_sshValidityModifier(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_sshModifierFunc_Modify(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
cert *ssh.Certificate
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
f sshModifierFunc
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", func(cert *ssh.Certificate) error { return nil }, args{&ssh.Certificate{}}, false},
|
||||||
|
{"fail", func(cert *ssh.Certificate) error { return fmt.Errorf("an error") }, args{&ssh.Certificate{}}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := tt.f.Modify(tt.args.cert); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("sshModifierFunc.Modify() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_sshDefaultDuration_Option(t *testing.T) {
|
||||||
|
tm, fn := mockNow()
|
||||||
|
defer fn()
|
||||||
|
|
||||||
|
newClaimer := func(claims *Claims) *Claimer {
|
||||||
|
c, err := NewClaimer(claims, globalProvisionerClaims)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
unix := func(d time.Duration) uint64 {
|
||||||
|
return uint64(tm.Add(d).Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
Claimer *Claimer
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
o SSHOptions
|
||||||
|
cert *ssh.Certificate
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want *ssh.Certificate
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"user", fields{newClaimer(nil)}, args{SSHOptions{}, &ssh.Certificate{CertType: ssh.UserCert}},
|
||||||
|
&ssh.Certificate{CertType: ssh.UserCert, ValidAfter: unix(0), ValidBefore: unix(16 * time.Hour)}, false},
|
||||||
|
{"host", fields{newClaimer(nil)}, args{SSHOptions{}, &ssh.Certificate{CertType: ssh.HostCert}},
|
||||||
|
&ssh.Certificate{CertType: ssh.HostCert, ValidAfter: unix(0), ValidBefore: unix(30 * 24 * time.Hour)}, false},
|
||||||
|
{"user claim", fields{newClaimer(&Claims{DefaultUserSSHDur: &Duration{1 * time.Hour}})}, args{SSHOptions{}, &ssh.Certificate{CertType: ssh.UserCert}},
|
||||||
|
&ssh.Certificate{CertType: ssh.UserCert, ValidAfter: unix(0), ValidBefore: unix(1 * time.Hour)}, false},
|
||||||
|
{"host claim", fields{newClaimer(&Claims{DefaultHostSSHDur: &Duration{1 * time.Hour}})}, args{SSHOptions{}, &ssh.Certificate{CertType: ssh.HostCert}},
|
||||||
|
&ssh.Certificate{CertType: ssh.HostCert, ValidAfter: unix(0), ValidBefore: unix(1 * time.Hour)}, false},
|
||||||
|
{"user backdate", fields{newClaimer(nil)}, args{SSHOptions{Backdate: 1 * time.Minute}, &ssh.Certificate{CertType: ssh.UserCert}},
|
||||||
|
&ssh.Certificate{CertType: ssh.UserCert, ValidAfter: unix(-1 * time.Minute), ValidBefore: unix(16 * time.Hour)}, false},
|
||||||
|
{"host backdate", fields{newClaimer(nil)}, args{SSHOptions{Backdate: 1 * time.Minute}, &ssh.Certificate{CertType: ssh.HostCert}},
|
||||||
|
&ssh.Certificate{CertType: ssh.HostCert, ValidAfter: unix(-1 * time.Minute), ValidBefore: unix(30 * 24 * time.Hour)}, false},
|
||||||
|
{"user validAfter", fields{newClaimer(nil)}, args{SSHOptions{Backdate: 1 * time.Minute}, &ssh.Certificate{CertType: ssh.UserCert, ValidAfter: unix(1 * time.Hour)}},
|
||||||
|
&ssh.Certificate{CertType: ssh.UserCert, ValidAfter: unix(time.Minute), ValidBefore: unix(17 * time.Hour)}, false},
|
||||||
|
{"user validBefore", fields{newClaimer(nil)}, args{SSHOptions{Backdate: 1 * time.Minute}, &ssh.Certificate{CertType: ssh.UserCert, ValidBefore: unix(1 * time.Hour)}},
|
||||||
|
&ssh.Certificate{CertType: ssh.UserCert, ValidAfter: unix(-1 * time.Minute), ValidBefore: unix(time.Hour)}, false},
|
||||||
|
{"host validAfter validBefore", fields{newClaimer(nil)}, args{SSHOptions{Backdate: 1 * time.Minute}, &ssh.Certificate{CertType: ssh.HostCert, ValidAfter: unix(1 * time.Minute), ValidBefore: unix(2 * time.Minute)}},
|
||||||
|
&ssh.Certificate{CertType: ssh.HostCert, ValidAfter: unix(1 * time.Minute), ValidBefore: unix(2 * time.Minute)}, false},
|
||||||
|
{"fail zero", fields{newClaimer(nil)}, args{SSHOptions{}, &ssh.Certificate{}}, &ssh.Certificate{}, true},
|
||||||
|
{"fail type", fields{newClaimer(nil)}, args{SSHOptions{}, &ssh.Certificate{CertType: 3}}, &ssh.Certificate{CertType: 3}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
m := &sshDefaultDuration{
|
||||||
|
Claimer: tt.fields.Claimer,
|
||||||
|
}
|
||||||
|
v := m.Option(tt.args.o)
|
||||||
|
if err := v.Modify(tt.args.cert); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("sshDefaultDuration.Option() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.args.cert, tt.want) {
|
||||||
|
t.Errorf("sshDefaultDuration.Option() = %v, want %v", tt.args.cert, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_sshLimitDuration_Option(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
Claimer *Claimer
|
||||||
|
NotAfter time.Time
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
o SSHOptions
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want SSHCertificateModifier
|
||||||
|
}{
|
||||||
|
// TODO: Add test cases.
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
m := &sshLimitDuration{
|
||||||
|
Claimer: tt.fields.Claimer,
|
||||||
|
NotAfter: tt.fields.NotAfter,
|
||||||
|
}
|
||||||
|
if got := m.Option(tt.args.o); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("sshLimitDuration.Option() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ var (
|
||||||
DisableRenewal: &defaultDisableRenewal,
|
DisableRenewal: &defaultDisableRenewal,
|
||||||
MinUserSSHDur: &Duration{Duration: 5 * time.Minute}, // User SSH certs
|
MinUserSSHDur: &Duration{Duration: 5 * time.Minute}, // User SSH certs
|
||||||
MaxUserSSHDur: &Duration{Duration: 24 * time.Hour},
|
MaxUserSSHDur: &Duration{Duration: 24 * time.Hour},
|
||||||
DefaultUserSSHDur: &Duration{Duration: 4 * time.Hour},
|
DefaultUserSSHDur: &Duration{Duration: 16 * time.Hour},
|
||||||
MinHostSSHDur: &Duration{Duration: 5 * time.Minute}, // Host SSH certs
|
MinHostSSHDur: &Duration{Duration: 5 * time.Minute}, // Host SSH certs
|
||||||
MaxHostSSHDur: &Duration{Duration: 30 * 24 * time.Hour},
|
MaxHostSSHDur: &Duration{Duration: 30 * 24 * time.Hour},
|
||||||
DefaultHostSSHDur: &Duration{Duration: 30 * 24 * time.Hour},
|
DefaultHostSSHDur: &Duration{Duration: 30 * 24 * time.Hour},
|
||||||
|
|
|
@ -228,6 +228,7 @@ func (p *X5C) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
|
||||||
if claims.Step == nil || claims.Step.SSH == nil {
|
if claims.Step == nil || claims.Step.SSH == nil {
|
||||||
return nil, errors.New("authorization token must be an SSH provisioning token")
|
return nil, errors.New("authorization token must be an SSH provisioning token")
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := claims.Step.SSH
|
opts := claims.Step.SSH
|
||||||
signOptions := []SignOption{
|
signOptions := []SignOption{
|
||||||
// validates user's SSHOptions with the ones in the token
|
// validates user's SSHOptions with the ones in the token
|
||||||
|
@ -261,7 +262,7 @@ func (p *X5C) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
|
||||||
// Set the default extensions.
|
// Set the default extensions.
|
||||||
&sshDefaultExtensionModifier{},
|
&sshDefaultExtensionModifier{},
|
||||||
// Checks the validity bounds, and set the validity if has not been set.
|
// Checks the validity bounds, and set the validity if has not been set.
|
||||||
sshLimitValidityModifier(p.claimer, claims.chains[0][0].NotAfter),
|
&sshLimitDuration{p.claimer, claims.chains[0][0].NotAfter},
|
||||||
// set the key id to the token subject
|
// set the key id to the token subject
|
||||||
sshCertKeyIDValidator(claims.Subject),
|
sshCertKeyIDValidator(claims.Subject),
|
||||||
// Validate public key.
|
// Validate public key.
|
||||||
|
|
|
@ -646,9 +646,9 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
||||||
assert.Equals(t, int64(v), tc.claims.Step.SSH.ValidBefore.RelativeTime(nw).Unix())
|
assert.Equals(t, int64(v), tc.claims.Step.SSH.ValidBefore.RelativeTime(nw).Unix())
|
||||||
case sshCertificateDefaultsModifier:
|
case sshCertificateDefaultsModifier:
|
||||||
assert.Equals(t, SSHOptions(v), SSHOptions{CertType: SSHUserCert})
|
assert.Equals(t, SSHOptions(v), SSHOptions{CertType: SSHUserCert})
|
||||||
case *sshValidityModifier:
|
case *sshLimitDuration:
|
||||||
assert.Equals(t, v.Claimer, tc.p.claimer)
|
assert.Equals(t, v.Claimer, tc.p.claimer)
|
||||||
assert.Equals(t, v.validBefore, tc.claims.chains[0][0].NotAfter)
|
assert.Equals(t, v.NotAfter, tc.claims.chains[0][0].NotAfter)
|
||||||
case *sshCertificateValidityValidator:
|
case *sshCertificateValidityValidator:
|
||||||
assert.Equals(t, v.Claimer, tc.p.claimer)
|
assert.Equals(t, v.Claimer, tc.p.claimer)
|
||||||
case *sshDefaultExtensionModifier, *sshDefaultPublicKeyValidator,
|
case *sshDefaultExtensionModifier, *sshDefaultPublicKeyValidator,
|
||||||
|
|
|
@ -209,6 +209,9 @@ func (a *Authority) SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, sign
|
||||||
var mods []provisioner.SSHCertificateModifier
|
var mods []provisioner.SSHCertificateModifier
|
||||||
var validators []provisioner.SSHCertificateValidator
|
var validators []provisioner.SSHCertificateValidator
|
||||||
|
|
||||||
|
// Set backdate with the configured value
|
||||||
|
opts.Backdate = a.config.AuthorityConfig.Backdate.Duration
|
||||||
|
|
||||||
for _, op := range signOpts {
|
for _, op := range signOpts {
|
||||||
switch o := op.(type) {
|
switch o := op.(type) {
|
||||||
// modify the ssh.Certificate
|
// modify the ssh.Certificate
|
||||||
|
@ -365,9 +368,12 @@ func (a *Authority) RenewSSH(oldCert *ssh.Certificate) (*ssh.Certificate, error)
|
||||||
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
||||||
return nil, errors.New("rewnewSSH: cannot renew certificate without validity period")
|
return nil, errors.New("rewnewSSH: cannot renew certificate without validity period")
|
||||||
}
|
}
|
||||||
dur := time.Duration(oldCert.ValidBefore-oldCert.ValidAfter) * time.Second
|
|
||||||
va := time.Now()
|
backdate := a.config.AuthorityConfig.Backdate.Duration
|
||||||
vb := va.Add(dur)
|
duration := time.Duration(oldCert.ValidBefore-oldCert.ValidAfter) * time.Second
|
||||||
|
now := time.Now()
|
||||||
|
va := now.Add(-1 * backdate)
|
||||||
|
vb := now.Add(duration - backdate)
|
||||||
|
|
||||||
// Build base certificate with the key and some random values
|
// Build base certificate with the key and some random values
|
||||||
cert := &ssh.Certificate{
|
cert := &ssh.Certificate{
|
||||||
|
@ -490,9 +496,12 @@ func (a *Authority) RekeySSH(oldCert *ssh.Certificate, pub ssh.PublicKey, signOp
|
||||||
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
||||||
return nil, errors.New("rekeySSH: cannot rekey certificate without validity period")
|
return nil, errors.New("rekeySSH: cannot rekey certificate without validity period")
|
||||||
}
|
}
|
||||||
dur := time.Duration(oldCert.ValidBefore-oldCert.ValidAfter) * time.Second
|
|
||||||
va := time.Now()
|
backdate := a.config.AuthorityConfig.Backdate.Duration
|
||||||
vb := va.Add(dur)
|
duration := time.Duration(oldCert.ValidBefore-oldCert.ValidAfter) * time.Second
|
||||||
|
now := time.Now()
|
||||||
|
va := now.Add(-1 * backdate)
|
||||||
|
vb := now.Add(duration - backdate)
|
||||||
|
|
||||||
// Build base certificate with the key and some random values
|
// Build base certificate with the key and some random values
|
||||||
cert := &ssh.Certificate{
|
cert := &ssh.Certificate{
|
||||||
|
|
|
@ -65,6 +65,10 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
|
||||||
certValidators = []provisioner.CertificateValidator{}
|
certValidators = []provisioner.CertificateValidator{}
|
||||||
issIdentity = a.intermediateIdentity
|
issIdentity = a.intermediateIdentity
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Set backdate with the configured value
|
||||||
|
signOpts.Backdate = a.config.AuthorityConfig.Backdate.Duration
|
||||||
|
|
||||||
for _, op := range extraOpts {
|
for _, op := range extraOpts {
|
||||||
switch k := op.(type) {
|
switch k := op.(type) {
|
||||||
case provisioner.CertificateValidator:
|
case provisioner.CertificateValidator:
|
||||||
|
@ -136,14 +140,17 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error
|
||||||
// Issuer
|
// Issuer
|
||||||
issIdentity := a.intermediateIdentity
|
issIdentity := a.intermediateIdentity
|
||||||
|
|
||||||
now := time.Now().UTC()
|
// Durations
|
||||||
|
backdate := a.config.AuthorityConfig.Backdate.Duration
|
||||||
duration := oldCert.NotAfter.Sub(oldCert.NotBefore)
|
duration := oldCert.NotAfter.Sub(oldCert.NotBefore)
|
||||||
|
now := time.Now().UTC()
|
||||||
|
|
||||||
newCert := &x509.Certificate{
|
newCert := &x509.Certificate{
|
||||||
PublicKey: oldCert.PublicKey,
|
PublicKey: oldCert.PublicKey,
|
||||||
Issuer: issIdentity.Crt.Subject,
|
Issuer: issIdentity.Crt.Subject,
|
||||||
Subject: oldCert.Subject,
|
Subject: oldCert.Subject,
|
||||||
NotBefore: now,
|
NotBefore: now.Add(-1 * backdate),
|
||||||
NotAfter: now.Add(duration),
|
NotAfter: now.Add(duration - backdate),
|
||||||
KeyUsage: oldCert.KeyUsage,
|
KeyUsage: oldCert.KeyUsage,
|
||||||
UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions,
|
UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions,
|
||||||
ExtKeyUsage: oldCert.ExtKeyUsage,
|
ExtKeyUsage: oldCert.ExtKeyUsage,
|
||||||
|
|
|
@ -16,6 +16,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/errs"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
"github.com/smallstep/certificates/authority"
|
"github.com/smallstep/certificates/authority"
|
||||||
|
@ -152,8 +154,8 @@ func equalJSON(t *testing.T, a interface{}, b interface{}) bool {
|
||||||
|
|
||||||
func TestClient_Version(t *testing.T) {
|
func TestClient_Version(t *testing.T) {
|
||||||
ok := &api.VersionResponse{Version: "test"}
|
ok := &api.VersionResponse{Version: "test"}
|
||||||
internal := api.InternalServerError(fmt.Errorf("Internal Server Error"))
|
internal := errs.InternalServerError(fmt.Errorf("Internal Server Error"))
|
||||||
notFound := api.NotFound(fmt.Errorf("Not Found"))
|
notFound := errs.NotFound(fmt.Errorf("Not Found"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -207,7 +209,7 @@ func TestClient_Version(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_Health(t *testing.T) {
|
func TestClient_Health(t *testing.T) {
|
||||||
ok := &api.HealthResponse{Status: "ok"}
|
ok := &api.HealthResponse{Status: "ok"}
|
||||||
nok := api.InternalServerError(fmt.Errorf("Internal Server Error"))
|
nok := errs.InternalServerError(fmt.Errorf("Internal Server Error"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -262,7 +264,7 @@ func TestClient_Root(t *testing.T) {
|
||||||
ok := &api.RootResponse{
|
ok := &api.RootResponse{
|
||||||
RootPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
RootPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
||||||
}
|
}
|
||||||
notFound := api.NotFound(fmt.Errorf("Not Found"))
|
notFound := errs.NotFound(fmt.Errorf("Not Found"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -332,8 +334,8 @@ func TestClient_Sign(t *testing.T) {
|
||||||
NotBefore: api.NewTimeDuration(time.Now()),
|
NotBefore: api.NewTimeDuration(time.Now()),
|
||||||
NotAfter: api.NewTimeDuration(time.Now().AddDate(0, 1, 0)),
|
NotAfter: api.NewTimeDuration(time.Now().AddDate(0, 1, 0)),
|
||||||
}
|
}
|
||||||
unauthorized := api.Unauthorized(fmt.Errorf("Unauthorized"))
|
unauthorized := errs.Unauthorized(fmt.Errorf("Unauthorized"))
|
||||||
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
badRequest := errs.BadRequest(fmt.Errorf("Bad Request"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -407,8 +409,8 @@ func TestClient_Revoke(t *testing.T) {
|
||||||
OTT: "the-ott",
|
OTT: "the-ott",
|
||||||
ReasonCode: 4,
|
ReasonCode: 4,
|
||||||
}
|
}
|
||||||
unauthorized := api.Unauthorized(fmt.Errorf("Unauthorized"))
|
unauthorized := errs.Unauthorized(fmt.Errorf("Unauthorized"))
|
||||||
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
badRequest := errs.BadRequest(fmt.Errorf("Bad Request"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -483,8 +485,8 @@ func TestClient_Renew(t *testing.T) {
|
||||||
{Certificate: parseCertificate(rootPEM)},
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
unauthorized := api.Unauthorized(fmt.Errorf("Unauthorized"))
|
unauthorized := errs.Unauthorized(fmt.Errorf("Unauthorized"))
|
||||||
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
badRequest := errs.BadRequest(fmt.Errorf("Bad Request"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -541,7 +543,7 @@ func TestClient_Provisioners(t *testing.T) {
|
||||||
ok := &api.ProvisionersResponse{
|
ok := &api.ProvisionersResponse{
|
||||||
Provisioners: provisioner.List{},
|
Provisioners: provisioner.List{},
|
||||||
}
|
}
|
||||||
internalServerError := api.InternalServerError(fmt.Errorf("Internal Server Error"))
|
internalServerError := errs.InternalServerError(fmt.Errorf("Internal Server Error"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -603,7 +605,7 @@ func TestClient_ProvisionerKey(t *testing.T) {
|
||||||
ok := &api.ProvisionerKeyResponse{
|
ok := &api.ProvisionerKeyResponse{
|
||||||
Key: "an encrypted key",
|
Key: "an encrypted key",
|
||||||
}
|
}
|
||||||
notFound := api.NotFound(fmt.Errorf("Not Found"))
|
notFound := errs.NotFound(fmt.Errorf("Not Found"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -664,8 +666,8 @@ func TestClient_Roots(t *testing.T) {
|
||||||
{Certificate: parseCertificate(rootPEM)},
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
unauthorized := api.Unauthorized(fmt.Errorf("Unauthorized"))
|
unauthorized := errs.Unauthorized(fmt.Errorf("Unauthorized"))
|
||||||
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
badRequest := errs.BadRequest(fmt.Errorf("Bad Request"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -724,8 +726,8 @@ func TestClient_Federation(t *testing.T) {
|
||||||
{Certificate: parseCertificate(rootPEM)},
|
{Certificate: parseCertificate(rootPEM)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
unauthorized := api.Unauthorized(fmt.Errorf("Unauthorized"))
|
unauthorized := errs.Unauthorized(fmt.Errorf("Unauthorized"))
|
||||||
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
badRequest := errs.BadRequest(fmt.Errorf("Bad Request"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -788,7 +790,7 @@ func TestClient_SSHRoots(t *testing.T) {
|
||||||
HostKeys: []api.SSHPublicKey{{PublicKey: key}},
|
HostKeys: []api.SSHPublicKey{{PublicKey: key}},
|
||||||
UserKeys: []api.SSHPublicKey{{PublicKey: key}},
|
UserKeys: []api.SSHPublicKey{{PublicKey: key}},
|
||||||
}
|
}
|
||||||
notFound := api.NotFound(fmt.Errorf("Not Found"))
|
notFound := errs.NotFound(fmt.Errorf("Not Found"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -879,7 +881,7 @@ func Test_parseEndpoint(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_RootFingerprint(t *testing.T) {
|
func TestClient_RootFingerprint(t *testing.T) {
|
||||||
ok := &api.HealthResponse{Status: "ok"}
|
ok := &api.HealthResponse{Status: "ok"}
|
||||||
nok := api.InternalServerError(fmt.Errorf("Internal Server Error"))
|
nok := errs.InternalServerError(fmt.Errorf("Internal Server Error"))
|
||||||
|
|
||||||
httpsServer := httptest.NewTLSServer(nil)
|
httpsServer := httptest.NewTLSServer(nil)
|
||||||
defer httpsServer.Close()
|
defer httpsServer.Close()
|
||||||
|
@ -946,7 +948,7 @@ func TestClient_SSHBastion(t *testing.T) {
|
||||||
Hostname: "bastion.local",
|
Hostname: "bastion.local",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
badRequest := api.BadRequest(fmt.Errorf("Bad Request"))
|
badRequest := errs.BadRequest(fmt.Errorf("Bad Request"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
1
ca/testdata/ca.json
vendored
1
ca/testdata/ca.json
vendored
|
@ -18,6 +18,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"authority": {
|
"authority": {
|
||||||
|
"backdate": "0s",
|
||||||
"provisioners": [
|
"provisioners": [
|
||||||
{
|
{
|
||||||
"name": "max",
|
"name": "max",
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -9,7 +9,7 @@ require (
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/rs/xid v1.2.1
|
github.com/rs/xid v1.2.1
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5
|
github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15
|
||||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191218000521-3e7348324838
|
github.com/smallstep/cli v0.14.0-rc.1.0.20191218000521-3e7348324838
|
||||||
github.com/smallstep/nosql v0.2.0
|
github.com/smallstep/nosql v0.2.0
|
||||||
github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a
|
github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -109,6 +109,8 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5 h1:lX6ybsQW9Agn3qK/W1Z39Z4a6RyEMGem/gXUYW0axYk=
|
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5 h1:lX6ybsQW9Agn3qK/W1Z39Z4a6RyEMGem/gXUYW0axYk=
|
||||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
|
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
|
||||||
|
github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15 h1:kSImCuenAkXtCaBeQ1UhmzzJGRhSm8sVH7I3sHE2Qdg=
|
||||||
|
github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191023014154-4669bef8c700/go.mod h1:/WOAB2LkcjkEbKG5rDol+A22Lp3UsttkLPLkY7tVtuk=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191023014154-4669bef8c700/go.mod h1:/WOAB2LkcjkEbKG5rDol+A22Lp3UsttkLPLkY7tVtuk=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191025192352-8ef9b020ed24/go.mod h1:043iBnsMvNhQ+QFwSh0N6JR3H2yamHPPAc78vCf+8Tc=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191025192352-8ef9b020ed24/go.mod h1:043iBnsMvNhQ+QFwSh0N6JR3H2yamHPPAc78vCf+8Tc=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191126035953-e88034bea402/go.mod h1:r2UTcAZNriKlwvNNXymNAcF3iKL6mTYOYrOCtBYYGJU=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191126035953-e88034bea402/go.mod h1:r2UTcAZNriKlwvNNXymNAcF3iKL6mTYOYrOCtBYYGJU=
|
||||||
|
@ -164,6 +166,7 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRi
|
||||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
|
Loading…
Reference in a new issue