forked from TrueCloudLab/certificates
Merge pull request #103 from smallstep/ssh-validity
Add a few more validity checks to default ssh cert validator
This commit is contained in:
commit
3347d36413
2 changed files with 197 additions and 3 deletions
|
@ -264,9 +264,11 @@ func (v *sshCertificateDefaultValidator) Valid(cert *ssh.Certificate) error {
|
||||||
case len(cert.ValidPrincipals) == 0:
|
case len(cert.ValidPrincipals) == 0:
|
||||||
return errors.New("ssh certificate valid principals cannot be empty")
|
return errors.New("ssh certificate valid principals cannot be empty")
|
||||||
case cert.ValidAfter == 0:
|
case cert.ValidAfter == 0:
|
||||||
return errors.New("ssh certificate valid after cannot be 0")
|
return errors.New("ssh certificate validAfter cannot be 0")
|
||||||
case cert.ValidBefore == 0:
|
case cert.ValidBefore < uint64(now().Unix()):
|
||||||
return errors.New("ssh certificate valid before cannot be 0")
|
return errors.New("ssh certificate validBefore cannot be in the past")
|
||||||
|
case cert.ValidBefore < cert.ValidAfter:
|
||||||
|
return errors.New("ssh certificate validBefore cannot be before validAfter")
|
||||||
case cert.CertType == ssh.UserCert && len(cert.Extensions) == 0:
|
case cert.CertType == ssh.UserCert && len(cert.Extensions) == 0:
|
||||||
return errors.New("ssh certificate extensions cannot be empty")
|
return errors.New("ssh certificate extensions cannot be empty")
|
||||||
case cert.SignatureKey == nil:
|
case cert.SignatureKey == nil:
|
||||||
|
|
192
authority/provisioner/sign_ssh_options_test.go
Normal file
192
authority/provisioner/sign_ssh_options_test.go
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/assert"
|
||||||
|
"github.com/smallstep/cli/crypto/keys"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_sshCertificateDefaultValidator_Valid(t *testing.T) {
|
||||||
|
pub, _, err := keys.GenerateDefaultKeyPair()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
sshPub, err := ssh.NewPublicKey(pub)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
v := sshCertificateDefaultValidator{}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cert *ssh.Certificate
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"fail/zero-nonce",
|
||||||
|
&ssh.Certificate{},
|
||||||
|
errors.New("ssh certificate nonce cannot be empty"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/nil-key",
|
||||||
|
&ssh.Certificate{Nonce: []byte("foo")},
|
||||||
|
errors.New("ssh certificate key cannot be nil"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/zero-serial",
|
||||||
|
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub},
|
||||||
|
errors.New("ssh certificate serial cannot be 0"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/unexpected-cert-type",
|
||||||
|
// UserCert = 1, HostCert = 2
|
||||||
|
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub, CertType: 3, Serial: 1},
|
||||||
|
errors.New("ssh certificate has an unknown type: 3"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/empty-cert-key-id",
|
||||||
|
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub, Serial: 1, CertType: 1},
|
||||||
|
errors.New("ssh certificate key id cannot be empty"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/empty-valid-principals",
|
||||||
|
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub, Serial: 1, CertType: 1, KeyId: "foo"},
|
||||||
|
errors.New("ssh certificate valid principals cannot be empty"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/zero-validAfter",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 1,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: 0,
|
||||||
|
},
|
||||||
|
errors.New("ssh certificate validAfter cannot be 0"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/validBefore-past",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 1,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: uint64(time.Now().Add(-10 * time.Minute).Unix()),
|
||||||
|
ValidBefore: uint64(time.Now().Add(-5 * time.Minute).Unix()),
|
||||||
|
},
|
||||||
|
errors.New("ssh certificate validBefore cannot be in the past"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/validAfter-after-validBefore",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 1,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: uint64(time.Now().Add(15 * time.Minute).Unix()),
|
||||||
|
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
|
||||||
|
},
|
||||||
|
errors.New("ssh certificate validBefore cannot be before validAfter"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/empty-extensions",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 1,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: uint64(time.Now().Unix()),
|
||||||
|
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
|
||||||
|
},
|
||||||
|
errors.New("ssh certificate extensions cannot be empty"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/nil-signature-key",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 1,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: uint64(time.Now().Unix()),
|
||||||
|
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
|
||||||
|
Permissions: ssh.Permissions{
|
||||||
|
Extensions: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors.New("ssh certificate signature key cannot be nil"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fail/nil-signature",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 1,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: uint64(time.Now().Unix()),
|
||||||
|
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
|
||||||
|
Permissions: ssh.Permissions{
|
||||||
|
Extensions: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
SignatureKey: sshPub,
|
||||||
|
},
|
||||||
|
errors.New("ssh certificate signature cannot be nil"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ok/userCert",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 1,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: uint64(time.Now().Unix()),
|
||||||
|
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
|
||||||
|
Permissions: ssh.Permissions{
|
||||||
|
Extensions: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
SignatureKey: sshPub,
|
||||||
|
Signature: &ssh.Signature{},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ok/hostCert",
|
||||||
|
&ssh.Certificate{
|
||||||
|
Nonce: []byte("foo"),
|
||||||
|
Key: sshPub,
|
||||||
|
Serial: 1,
|
||||||
|
CertType: 2,
|
||||||
|
KeyId: "foo",
|
||||||
|
ValidPrincipals: []string{"foo"},
|
||||||
|
ValidAfter: uint64(time.Now().Unix()),
|
||||||
|
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
|
||||||
|
SignatureKey: sshPub,
|
||||||
|
Signature: &ssh.Signature{},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := v.Valid(tt.cert); err != nil {
|
||||||
|
if assert.NotNil(t, tt.err) {
|
||||||
|
assert.HasPrefix(t, err.Error(), tt.err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert.Nil(t, tt.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue