Skip to content

Commit 3347d36

Browse files
authored
Merge pull request smallstep#103 from smallstep/ssh-validity
Add a few more validity checks to default ssh cert validator
2 parents e02dd1a + d204469 commit 3347d36

File tree

2 files changed

+197
-3
lines changed

2 files changed

+197
-3
lines changed

authority/provisioner/sign_ssh_options.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,11 @@ func (v *sshCertificateDefaultValidator) Valid(cert *ssh.Certificate) error {
264264
case len(cert.ValidPrincipals) == 0:
265265
return errors.New("ssh certificate valid principals cannot be empty")
266266
case cert.ValidAfter == 0:
267-
return errors.New("ssh certificate valid after cannot be 0")
268-
case cert.ValidBefore == 0:
269-
return errors.New("ssh certificate valid before cannot be 0")
267+
return errors.New("ssh certificate validAfter cannot be 0")
268+
case cert.ValidBefore < uint64(now().Unix()):
269+
return errors.New("ssh certificate validBefore cannot be in the past")
270+
case cert.ValidBefore < cert.ValidAfter:
271+
return errors.New("ssh certificate validBefore cannot be before validAfter")
270272
case cert.CertType == ssh.UserCert && len(cert.Extensions) == 0:
271273
return errors.New("ssh certificate extensions cannot be empty")
272274
case cert.SignatureKey == nil:
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package provisioner
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/pkg/errors"
8+
"github.com/smallstep/assert"
9+
"github.com/smallstep/cli/crypto/keys"
10+
"golang.org/x/crypto/ssh"
11+
)
12+
13+
func Test_sshCertificateDefaultValidator_Valid(t *testing.T) {
14+
pub, _, err := keys.GenerateDefaultKeyPair()
15+
assert.FatalError(t, err)
16+
sshPub, err := ssh.NewPublicKey(pub)
17+
assert.FatalError(t, err)
18+
v := sshCertificateDefaultValidator{}
19+
tests := []struct {
20+
name string
21+
cert *ssh.Certificate
22+
err error
23+
}{
24+
{
25+
"fail/zero-nonce",
26+
&ssh.Certificate{},
27+
errors.New("ssh certificate nonce cannot be empty"),
28+
},
29+
{
30+
"fail/nil-key",
31+
&ssh.Certificate{Nonce: []byte("foo")},
32+
errors.New("ssh certificate key cannot be nil"),
33+
},
34+
{
35+
"fail/zero-serial",
36+
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub},
37+
errors.New("ssh certificate serial cannot be 0"),
38+
},
39+
{
40+
"fail/unexpected-cert-type",
41+
// UserCert = 1, HostCert = 2
42+
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub, CertType: 3, Serial: 1},
43+
errors.New("ssh certificate has an unknown type: 3"),
44+
},
45+
{
46+
"fail/empty-cert-key-id",
47+
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub, Serial: 1, CertType: 1},
48+
errors.New("ssh certificate key id cannot be empty"),
49+
},
50+
{
51+
"fail/empty-valid-principals",
52+
&ssh.Certificate{Nonce: []byte("foo"), Key: sshPub, Serial: 1, CertType: 1, KeyId: "foo"},
53+
errors.New("ssh certificate valid principals cannot be empty"),
54+
},
55+
{
56+
"fail/zero-validAfter",
57+
&ssh.Certificate{
58+
Nonce: []byte("foo"),
59+
Key: sshPub,
60+
Serial: 1,
61+
CertType: 1,
62+
KeyId: "foo",
63+
ValidPrincipals: []string{"foo"},
64+
ValidAfter: 0,
65+
},
66+
errors.New("ssh certificate validAfter cannot be 0"),
67+
},
68+
{
69+
"fail/validBefore-past",
70+
&ssh.Certificate{
71+
Nonce: []byte("foo"),
72+
Key: sshPub,
73+
Serial: 1,
74+
CertType: 1,
75+
KeyId: "foo",
76+
ValidPrincipals: []string{"foo"},
77+
ValidAfter: uint64(time.Now().Add(-10 * time.Minute).Unix()),
78+
ValidBefore: uint64(time.Now().Add(-5 * time.Minute).Unix()),
79+
},
80+
errors.New("ssh certificate validBefore cannot be in the past"),
81+
},
82+
{
83+
"fail/validAfter-after-validBefore",
84+
&ssh.Certificate{
85+
Nonce: []byte("foo"),
86+
Key: sshPub,
87+
Serial: 1,
88+
CertType: 1,
89+
KeyId: "foo",
90+
ValidPrincipals: []string{"foo"},
91+
ValidAfter: uint64(time.Now().Add(15 * time.Minute).Unix()),
92+
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
93+
},
94+
errors.New("ssh certificate validBefore cannot be before validAfter"),
95+
},
96+
{
97+
"fail/empty-extensions",
98+
&ssh.Certificate{
99+
Nonce: []byte("foo"),
100+
Key: sshPub,
101+
Serial: 1,
102+
CertType: 1,
103+
KeyId: "foo",
104+
ValidPrincipals: []string{"foo"},
105+
ValidAfter: uint64(time.Now().Unix()),
106+
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
107+
},
108+
errors.New("ssh certificate extensions cannot be empty"),
109+
},
110+
{
111+
"fail/nil-signature-key",
112+
&ssh.Certificate{
113+
Nonce: []byte("foo"),
114+
Key: sshPub,
115+
Serial: 1,
116+
CertType: 1,
117+
KeyId: "foo",
118+
ValidPrincipals: []string{"foo"},
119+
ValidAfter: uint64(time.Now().Unix()),
120+
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
121+
Permissions: ssh.Permissions{
122+
Extensions: map[string]string{"foo": "bar"},
123+
},
124+
},
125+
errors.New("ssh certificate signature key cannot be nil"),
126+
},
127+
{
128+
"fail/nil-signature",
129+
&ssh.Certificate{
130+
Nonce: []byte("foo"),
131+
Key: sshPub,
132+
Serial: 1,
133+
CertType: 1,
134+
KeyId: "foo",
135+
ValidPrincipals: []string{"foo"},
136+
ValidAfter: uint64(time.Now().Unix()),
137+
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
138+
Permissions: ssh.Permissions{
139+
Extensions: map[string]string{"foo": "bar"},
140+
},
141+
SignatureKey: sshPub,
142+
},
143+
errors.New("ssh certificate signature cannot be nil"),
144+
},
145+
{
146+
"ok/userCert",
147+
&ssh.Certificate{
148+
Nonce: []byte("foo"),
149+
Key: sshPub,
150+
Serial: 1,
151+
CertType: 1,
152+
KeyId: "foo",
153+
ValidPrincipals: []string{"foo"},
154+
ValidAfter: uint64(time.Now().Unix()),
155+
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
156+
Permissions: ssh.Permissions{
157+
Extensions: map[string]string{"foo": "bar"},
158+
},
159+
SignatureKey: sshPub,
160+
Signature: &ssh.Signature{},
161+
},
162+
nil,
163+
},
164+
{
165+
"ok/hostCert",
166+
&ssh.Certificate{
167+
Nonce: []byte("foo"),
168+
Key: sshPub,
169+
Serial: 1,
170+
CertType: 2,
171+
KeyId: "foo",
172+
ValidPrincipals: []string{"foo"},
173+
ValidAfter: uint64(time.Now().Unix()),
174+
ValidBefore: uint64(time.Now().Add(10 * time.Minute).Unix()),
175+
SignatureKey: sshPub,
176+
Signature: &ssh.Signature{},
177+
},
178+
nil,
179+
},
180+
}
181+
for _, tt := range tests {
182+
t.Run(tt.name, func(t *testing.T) {
183+
if err := v.Valid(tt.cert); err != nil {
184+
if assert.NotNil(t, tt.err) {
185+
assert.HasPrefix(t, err.Error(), tt.err.Error())
186+
}
187+
} else {
188+
assert.Nil(t, tt.err)
189+
}
190+
})
191+
}
192+
}

0 commit comments

Comments
 (0)