Merge pull request #1764 from smallstep/mariano/ra-lifetime

Let the CA determine the RA lifetime
pull/1766/head
Mariano Cano 3 months ago committed by GitHub
commit fe364d5081
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -147,10 +147,10 @@ func validateJWS(next nextHTTP) nextHTTP {
sig := jws.Signatures[0]
uh := sig.Unprotected
if len(uh.KeyID) > 0 ||
if uh.KeyID != "" ||
uh.JSONWebKey != nil ||
len(uh.Algorithm) > 0 ||
len(uh.Nonce) > 0 ||
uh.Algorithm != "" ||
uh.Nonce != "" ||
len(uh.ExtraHeaders) > 0 {
render.Error(w, acme.NewError(acme.ErrorMalformedType, "unprotected header must not be used"))
return
@ -199,7 +199,7 @@ func validateJWS(next nextHTTP) nextHTTP {
return
}
if hdr.JSONWebKey != nil && len(hdr.KeyID) > 0 {
if hdr.JSONWebKey != nil && hdr.KeyID != "" {
render.Error(w, acme.NewError(acme.ErrorMalformedType, "jwk and kid are mutually exclusive"))
return
}

@ -565,7 +565,7 @@ func LogSSHCertificate(w http.ResponseWriter, cert *ssh.Certificate) {
func ParseCursor(r *http.Request) (cursor string, limit int, err error) {
q := r.URL.Query()
cursor = q.Get("cursor")
if v := q.Get("limit"); len(v) > 0 {
if v := q.Get("limit"); v != "" {
limit, err = strconv.Atoi(v)
if err != nil {
return "", 0, errs.BadRequestErr(err, "limit '%s' is not an integer", v)

@ -78,7 +78,7 @@ func Revoke(w http.ResponseWriter, r *http.Request) {
// A token indicates that we are using the api via a provisioner token,
// otherwise it is assumed that the certificate is revoking itself over mTLS.
if len(body.OTT) > 0 {
if body.OTT != "" {
logOtt(w, body.OTT)
if _, err := a.Authorize(ctx, body.OTT); err != nil {
render.Error(w, errs.UnauthorizedErr(err))

@ -38,7 +38,7 @@ func GetProvisioner(w http.ResponseWriter, r *http.Request) {
auth := mustAuthority(ctx)
db := admin.MustFromContext(ctx)
if len(id) > 0 {
if id != "" {
if p, err = auth.LoadProvisionerByID(id); err != nil {
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", id))
return
@ -116,7 +116,7 @@ func DeleteProvisioner(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
auth := mustAuthority(r.Context())
if len(id) > 0 {
if id != "" {
if p, err = auth.LoadProvisionerByID(id); err != nil {
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", id))
return

@ -857,7 +857,7 @@ func TestDB_CreateAdmin(t *testing.T) {
var _dba = new(dbAdmin)
assert.FatalError(t, json.Unmarshal(nu, _dba))
assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key))
assert.True(t, _dba.ID != "" && _dba.ID == string(key))
assert.Equals(t, _dba.AuthorityID, adm.AuthorityId)
assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId)
assert.Equals(t, _dba.Subject, adm.Subject)
@ -890,7 +890,7 @@ func TestDB_CreateAdmin(t *testing.T) {
var _dba = new(dbAdmin)
assert.FatalError(t, json.Unmarshal(nu, _dba))
assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key))
assert.True(t, _dba.ID != "" && _dba.ID == string(key))
assert.Equals(t, _dba.AuthorityID, adm.AuthorityId)
assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId)
assert.Equals(t, _dba.Subject, adm.Subject)

@ -906,7 +906,7 @@ func TestDB_CreateProvisioner(t *testing.T) {
var _dbp = new(dbProvisioner)
assert.FatalError(t, json.Unmarshal(nu, _dbp))
assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key))
assert.True(t, _dbp.ID != "" && _dbp.ID == string(key))
assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId)
assert.Equals(t, _dbp.Type, prov.Type)
assert.Equals(t, _dbp.Name, prov.Name)
@ -944,7 +944,7 @@ func TestDB_CreateProvisioner(t *testing.T) {
var _dbp = new(dbProvisioner)
assert.FatalError(t, json.Unmarshal(nu, _dbp))
assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key))
assert.True(t, _dbp.ID != "" && _dbp.ID == string(key))
assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId)
assert.Equals(t, _dbp.Type, prov.Type)
assert.Equals(t, _dbp.Name, prov.Name)
@ -1093,7 +1093,7 @@ func TestDB_UpdateProvisioner(t *testing.T) {
var _dbp = new(dbProvisioner)
assert.FatalError(t, json.Unmarshal(nu, _dbp))
assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key))
assert.True(t, _dbp.ID != "" && _dbp.ID == string(key))
assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId)
assert.Equals(t, _dbp.Type, prov.Type)
assert.Equals(t, _dbp.Name, prov.Name)
@ -1188,7 +1188,7 @@ func TestDB_UpdateProvisioner(t *testing.T) {
var _dbp = new(dbProvisioner)
assert.FatalError(t, json.Unmarshal(nu, _dbp))
assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key))
assert.True(t, _dbp.ID != "" && _dbp.ID == string(key))
assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId)
assert.Equals(t, _dbp.Type, prov.Type)
assert.Equals(t, _dbp.Name, prov.Name)

@ -203,7 +203,7 @@ func matchURIConstraint(uri *url.URL, constraint string) (bool, error) {
// domainToReverseLabels converts a textual domain name like foo.example.com to
// the list of labels in reverse order, e.g. ["com", "example", "foo"].
func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
for len(domain) > 0 {
for domain != "" {
if i := strings.LastIndexByte(domain, '.'); i == -1 {
reverseLabels = append(reverseLabels, domain)
domain = ""
@ -316,7 +316,7 @@ func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) {
} else {
// Atom ("." Atom)*
NextChar:
for len(in) > 0 {
for in != "" {
// atext from RFC 2822, Section 3.2.4
c := in[0]

@ -125,7 +125,7 @@ func (c *Collection) LoadByToken(token *jose.JSONWebToken, claims *jose.Claims)
}
// Try with azp (OIDC)
if len(payload.AuthorizedParty) > 0 {
if payload.AuthorizedParty != "" {
if p, ok := c.LoadByTokenID(payload.AuthorizedParty); ok {
return p, ok
}

@ -87,7 +87,7 @@ func (p *JWK) GetType() Type {
// GetEncryptedKey returns the base provisioner encrypted key if it's defined.
func (p *JWK) GetEncryptedKey() (string, string, bool) {
return p.Key.KeyID, p.EncryptedKey, len(p.EncryptedKey) > 0
return p.Key.KeyID, p.EncryptedKey, p.EncryptedKey != ""
}
// Init initializes and validates the fields of a JWK type.

@ -105,7 +105,7 @@ func getKeysFromJWKsURI(uri string) (jose.JSONWebKeySet, time.Duration, error) {
func getCacheAge(cacheControl string) time.Duration {
age := defaultCacheAge
if len(cacheControl) > 0 {
if cacheControl != "" {
match := maxAgeRegex.FindAllStringSubmatch(cacheControl, -1)
if len(match) > 0 {
if len(match[0]) == 2 {

@ -304,7 +304,7 @@ func (s *SCEP) Init(config Config) (err error) {
}
}
if decryptionKeyURI := s.DecrypterKeyURI; len(decryptionKeyURI) > 0 {
if decryptionKeyURI := s.DecrypterKeyURI; decryptionKeyURI != "" {
u, err := uri.Parse(s.DecrypterKeyURI)
if err != nil {
return fmt.Errorf("failed parsing decrypter key: %w", err)

@ -813,7 +813,7 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
}
tot++
}
if len(tc.claims.Step.SSH.CertType) > 0 {
if tc.claims.Step.SSH.CertType != "" {
assert.Equals(t, tot, 12)
} else {
assert.Equals(t, tot, 10)

@ -608,19 +608,19 @@ func provisionerWebhookToLinkedca(pwh *provisioner.Webhook) *linkedca.Webhook {
}
func durationsToCertificates(d *linkedca.Durations) (min, max, def *provisioner.Duration, err error) {
if len(d.Min) > 0 {
if d.Min != "" {
min, err = provisioner.NewDuration(d.Min)
if err != nil {
return nil, nil, nil, admin.WrapErrorISE(err, "error parsing minimum duration '%s'", d.Min)
}
}
if len(d.Max) > 0 {
if d.Max != "" {
max, err = provisioner.NewDuration(d.Max)
if err != nil {
return nil, nil, nil, admin.WrapErrorISE(err, "error parsing maximum duration '%s'", d.Max)
}
}
if len(d.Default) > 0 {
if d.Default != "" {
def, err = provisioner.NewDuration(d.Default)
if err != nil {
return nil, nil, nil, admin.WrapErrorISE(err, "error parsing default duration '%s'", d.Default)

@ -45,7 +45,7 @@ func (a *Authority) GetRoots() ([]*x509.Certificate, error) {
// GetFederation returns all the root certificates in the federation.
// This method implements the Authority interface.
func (a *Authority) GetFederation() (federation []*x509.Certificate, err error) {
a.certificates.Range(func(k, v interface{}) bool {
a.certificates.Range(func(_, v interface{}) bool {
crt, ok := v.(*x509.Certificate)
if !ok {
federation = nil

@ -59,7 +59,7 @@ var (
)
func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc {
return func(crt *x509.Certificate, opts provisioner.SignOptions) error {
return func(crt *x509.Certificate, _ provisioner.SignOptions) error {
if def == nil {
return errors.New("default ASN1DN template cannot be nil")
}
@ -913,10 +913,19 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
return fatal(err)
}
// Set the cert lifetime as follows:
// i) If the CA is not a StepCAS RA use 24h, else
// ii) if the CA is a StepCAS RA, leave the lifetime empty and
// let the provisioner of the CA decide the lifetime of the RA cert.
var lifetime time.Duration
if casapi.TypeOf(a.x509CAService) != casapi.StepCAS {
lifetime = 24 * time.Hour
}
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
Template: certTpl,
CSR: cr,
Lifetime: 24 * time.Hour,
Lifetime: lifetime,
Backdate: 1 * time.Minute,
IsCAServerCert: true,
})

@ -204,7 +204,7 @@ func (o *adminOptions) apply(opts []AdminOption) (err error) {
func (o *adminOptions) rawQuery() string {
v := url.Values{}
if len(o.cursor) > 0 {
if o.cursor != "" {
v.Set("cursor", o.cursor)
}
if o.limit > 0 {

@ -678,7 +678,7 @@ func (ca *CA) shouldServeSCEPEndpoints() bool {
//nolint:unused // useful for debugging
func dumpRoutes(mux chi.Routes) {
// helpful routine for logging all routes
walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
walkFunc := func(method string, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error {
fmt.Printf("%s %s\n", method, route)
return nil
}

@ -69,7 +69,7 @@ func init() {
GetClientCertificate: id.GetClientCertificateFunc(),
},
}
return func(ctx context.Context, network, address string) (net.Conn, error) {
return func(ctx context.Context, _, _ string) (net.Conn, error) {
return d.DialContext(ctx, "tcp", net.JoinHostPort(host, port))
}
}

@ -67,6 +67,14 @@ func (t Type) String() string {
return strings.ToLower(string(t))
}
// TypeOf returns the type of the given CertificateAuthorityService.
func TypeOf(c CertificateAuthorityService) Type {
if ct, ok := c.(interface{ Type() Type }); ok {
return ct.Type()
}
return ExternalCAS
}
// NotImplementedError is the type of error returned if an operation is not implemented.
type NotImplementedError struct {
Message string

@ -4,6 +4,24 @@ import (
"testing"
)
type simpleCAS struct{}
func (*simpleCAS) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
return nil, NotImplementedError{}
}
func (*simpleCAS) RenewCertificate(req *RenewCertificateRequest) (*RenewCertificateResponse, error) {
return nil, NotImplementedError{}
}
func (*simpleCAS) RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error) {
return nil, NotImplementedError{}
}
type fakeCAS struct {
simpleCAS
}
func (*fakeCAS) Type() Type { return SoftCAS }
func TestType_String(t *testing.T) {
tests := []struct {
name string
@ -25,6 +43,27 @@ func TestType_String(t *testing.T) {
}
}
func TestTypeOf(t *testing.T) {
type args struct {
c CertificateAuthorityService
}
tests := []struct {
name string
args args
want Type
}{
{"ok", args{&simpleCAS{}}, ExternalCAS},
{"ok with type", args{&fakeCAS{}}, SoftCAS},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := TypeOf(tt.args.c); got != tt.want {
t.Errorf("TypeOf() = %v, want %v", got, tt.want)
}
})
}
}
func TestNotImplementedError_Error(t *testing.T) {
type fields struct {
Message string

@ -154,6 +154,11 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
}, nil
}
// Type returns the type of this CertificateAuthorityService.
func (c *CloudCAS) Type() apiv1.Type {
return apiv1.CloudCAS
}
// GetCertificateAuthority returns the root certificate for the given
// certificate authority. It implements apiv1.CertificateAuthorityGetter
// interface.

@ -443,6 +443,23 @@ func TestNew_real(t *testing.T) {
}
}
func TestCloudCAS_Type(t *testing.T) {
tests := []struct {
name string
want apiv1.Type
}{
{"ok", apiv1.CloudCAS},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CloudCAS{}
if got := c.Type(); got != tt.want {
t.Errorf("CloudCAS.Type() = %v, want %v", got, tt.want)
}
})
}
}
func TestCloudCAS_GetCertificateAuthority(t *testing.T) {
root := mustParseCertificate(t, testRootCertificate)
type fields struct {

@ -53,6 +53,11 @@ func New(_ context.Context, opts apiv1.Options) (*SoftCAS, error) {
}, nil
}
// Type returns the type of this CertificateAuthorityService.
func (c *SoftCAS) Type() apiv1.Type {
return apiv1.SoftCAS
}
// CreateCertificate signs a new certificate using Golang or KMS crypto.
func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
switch {

@ -252,6 +252,23 @@ func TestNew_register(t *testing.T) {
}
}
func TestSoftCAS_Type(t *testing.T) {
tests := []struct {
name string
want apiv1.Type
}{
{"ok", apiv1.SoftCAS},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &SoftCAS{}
if got := c.Type(); got != tt.want {
t.Errorf("SoftCAS.Type() = %v, want %v", got, tt.want)
}
})
}
}
func TestSoftCAS_CreateCertificate(t *testing.T) {
mockNow(t)
// Set rand.Reader to EOF

@ -65,6 +65,11 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) {
}, nil
}
// Type returns the type of this CertificateAuthorityService.
func (s *StepCAS) Type() apiv1.Type {
return apiv1.StepCAS
}
// CreateCertificate uses the step-ca sign request with the configured
// provisioner to get a new certificate from the certificate authority.
func (s *StepCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
@ -73,8 +78,8 @@ func (s *StepCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1
return nil, errors.New("createCertificateRequest `csr` cannot be nil")
case req.Template == nil:
return nil, errors.New("createCertificateRequest `template` cannot be nil")
case req.Lifetime == 0:
return nil, errors.New("createCertificateRequest `lifetime` cannot be 0")
case req.Lifetime < 0:
return nil, errors.New("createCertificateRequest `lifetime` cannot be less than 0")
}
info := &raInfo{

@ -624,6 +624,23 @@ func TestNew(t *testing.T) {
}
}
func TestStepCAS_Type(t *testing.T) {
tests := []struct {
name string
want apiv1.Type
}{
{"ok", apiv1.StepCAS},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &StepCAS{}
if got := c.Type(); got != tt.want {
t.Errorf("StepCAS.Type() = %v, want %v", got, tt.want)
}
})
}
}
func TestStepCAS_CreateCertificate(t *testing.T) {
caURL, client := testCAHelper(t)
x5c := testX5CIssuer(t, caURL, "")

@ -110,6 +110,11 @@ func New(ctx context.Context, opts apiv1.Options) (*VaultCAS, error) {
}, nil
}
// Type returns the type of this CertificateAuthorityService.
func (v *VaultCAS) Type() apiv1.Type {
return apiv1.VaultCAS
}
// CreateCertificate signs a new certificate using Hashicorp Vault.
func (v *VaultCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
switch {

@ -193,6 +193,23 @@ func TestNew_register(t *testing.T) {
}
}
func TestVaultCAS_Type(t *testing.T) {
tests := []struct {
name string
want apiv1.Type
}{
{"ok", apiv1.VaultCAS},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &VaultCAS{}
if got := c.Type(); got != tt.want {
t.Errorf("VaultCAS.Type() = %v, want %v", got, tt.want)
}
})
}
}
func TestVaultCAS_CreateCertificate(t *testing.T) {
_, client := testCAHelper(t)

@ -239,7 +239,7 @@ To get a linked authority token:
// replace resolver if requested
if resolver != "" {
net.DefaultResolver.PreferGo = true
net.DefaultResolver.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {
net.DefaultResolver.Dial = func(_ context.Context, network, _ string) (net.Conn, error) {
return net.Dial(network, resolver)
}
}

@ -116,7 +116,7 @@ func New(c *Config) (AuthDB, error) {
opts := []nosql.Option{nosql.WithDatabase(c.Database),
nosql.WithValueDir(c.ValueDir)}
if len(c.BadgerFileLoadingMode) > 0 {
if c.BadgerFileLoadingMode != "" {
opts = append(opts, nosql.WithBadgerFileLoadingMode(c.BadgerFileLoadingMode))
}

@ -80,7 +80,7 @@ func (e *Error) StatusCode() int {
// Message returns a user friendly error, if one is set.
func (e *Error) Message() string {
if len(e.Msg) > 0 {
if e.Msg != "" {
return e.Msg
}
return e.Err.Error()
@ -123,7 +123,7 @@ func Wrapf(status int, e error, format string, args ...interface{}) error {
// MarshalJSON implements json.Marshaller interface for the Error struct.
func (e *Error) MarshalJSON() ([]byte, error) {
var msg string
if len(e.Msg) > 0 {
if e.Msg != "" {
msg = e.Msg
} else {
msg = http.StatusText(e.Status)

@ -288,7 +288,7 @@ func checkNameConstraints(
// domainToReverseLabels converts a textual domain name like foo.example.com to
// the list of labels in reverse order, e.g. ["com", "example", "foo"].
func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
for len(domain) > 0 {
for domain != "" {
if i := strings.LastIndexByte(domain, '.'); i == -1 {
reverseLabels = append(reverseLabels, domain)
domain = ""
@ -401,7 +401,7 @@ func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) {
} else {
// Atom ("." Atom)*
NextChar:
for len(in) > 0 {
for in != "" {
// atext from RFC 2822, Section 3.2.4
c := in[0]

Loading…
Cancel
Save