forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
keystore_test.go
277 lines (238 loc) · 9.01 KB
/
keystore_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
Copyright 2016 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"crypto/rsa"
"crypto/x509/pkix"
"fmt"
"os"
"time"
"github.com/gravitational/teleport/lib/auth/testauthority"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"golang.org/x/crypto/ssh"
"gopkg.in/check.v1"
)
type KeyStoreTestSuite struct {
storeDir string
store *FSLocalKeyStore
keygen *testauthority.Keygen
tlsca *tlsca.CertAuthority
}
var _ = fmt.Printf
var _ = check.Suite(&KeyStoreTestSuite{})
func newSelfSignedCA(privateKey []byte) (*tlsca.CertAuthority, error) {
rsaKey, err := ssh.ParseRawPrivateKey(privateKey)
if err != nil {
return nil, trace.Wrap(err)
}
key, cert, err := tlsca.GenerateSelfSignedCAWithPrivateKey(rsaKey.(*rsa.PrivateKey), pkix.Name{
CommonName: "localhost",
Organization: []string{"localhost"},
}, nil, defaults.CATTL)
return tlsca.New(cert, key)
}
func (s *KeyStoreTestSuite) SetUpSuite(c *check.C) {
utils.InitLoggerForTests()
var err error
s.keygen = testauthority.New()
s.storeDir = c.MkDir()
s.store, err = NewFSLocalKeyStore(s.storeDir)
c.Assert(err, check.IsNil)
c.Assert(s.store, check.NotNil)
c.Assert(utils.IsDir(s.store.KeyDir), check.Equals, true)
s.tlsca, err = newSelfSignedCA(CAPriv)
c.Assert(err, check.IsNil)
}
func (s *KeyStoreTestSuite) TearDownSuite(c *check.C) {
os.RemoveAll(s.storeDir)
}
func (s *KeyStoreTestSuite) SetUpTest(c *check.C) {
os.RemoveAll(s.store.KeyDir)
}
func (s *KeyStoreTestSuite) TestListKeys(c *check.C) {
const keyNum = 5
// add 5 keys for "bob"
keys := make([]Key, keyNum)
for i := 0; i < keyNum; i++ {
key := s.makeSignedKey(c, false)
host := fmt.Sprintf("host-%v", i)
s.store.AddKey(host, "bob", key)
key.ProxyHost = host
keys[i] = *key
}
// add 1 key for "sam"
samKey := s.makeSignedKey(c, false)
s.store.AddKey("sam.host", "sam", samKey)
// read all bob keys:
for i := 0; i < keyNum; i++ {
host := fmt.Sprintf("host-%v", i)
keys2, err := s.store.GetKey(host, "bob")
c.Assert(err, check.IsNil)
c.Assert(*keys2, check.DeepEquals, keys[i])
}
// read sam's key and make sure it's the same:
skey, err := s.store.GetKey("sam.host", "sam")
c.Assert(err, check.IsNil)
c.Assert(samKey.Cert, check.DeepEquals, skey.Cert)
c.Assert(samKey.Pub, check.DeepEquals, skey.Pub)
}
func (s *KeyStoreTestSuite) TestKeyCRUD(c *check.C) {
key := s.makeSignedKey(c, false)
// add key:
err := s.store.AddKey("host.a", "bob", key)
c.Assert(err, check.IsNil)
// load back and compare:
keyCopy, err := s.store.GetKey("host.a", "bob")
c.Assert(err, check.IsNil)
c.Assert(key.EqualsTo(keyCopy), check.Equals, true)
// Delete & verify that it's gone
err = s.store.DeleteKey("host.a", "bob")
c.Assert(err, check.IsNil)
keyCopy, err = s.store.GetKey("host.a", "bob")
c.Assert(err, check.NotNil)
c.Assert(trace.IsNotFound(err), check.Equals, true)
// Delete non-existing
err = s.store.DeleteKey("non-existing-host", "non-existing-user")
c.Assert(err, check.NotNil)
c.Assert(trace.IsNotFound(err), check.Equals, true)
}
func (s *KeyStoreTestSuite) TestDeleteAll(c *check.C) {
key := s.makeSignedKey(c, false)
// add keys
err := s.store.AddKey("proxy.example.com", "foo", key)
c.Assert(err, check.IsNil)
err = s.store.AddKey("proxy.example.com", "bar", key)
c.Assert(err, check.IsNil)
// check keys exist
_, err = s.store.GetKey("proxy.example.com", "foo")
c.Assert(err, check.IsNil)
_, err = s.store.GetKey("proxy.example.com", "bar")
c.Assert(err, check.IsNil)
// delete all keys
err = s.store.DeleteKeys()
c.Assert(err, check.IsNil)
// verify keys gone
_, err = s.store.GetKey("proxy.example.com", "foo")
c.Assert(err, check.NotNil)
_, err = s.store.GetKey("proxy.example.com", "bar")
c.Assert(err, check.NotNil)
}
func (s *KeyStoreTestSuite) TestKnownHosts(c *check.C) {
os.MkdirAll(s.store.KeyDir, 0777)
pub, _, _, _, err := ssh.ParseAuthorizedKey(CAPub)
c.Assert(err, check.IsNil)
_, p2, _ := s.keygen.GenerateKeyPair("")
pub2, _, _, _, _ := ssh.ParseAuthorizedKey(p2)
err = s.store.AddKnownHostKeys("example.com", []ssh.PublicKey{pub})
c.Assert(err, check.IsNil)
err = s.store.AddKnownHostKeys("example.com", []ssh.PublicKey{pub2})
c.Assert(err, check.IsNil)
err = s.store.AddKnownHostKeys("example.org", []ssh.PublicKey{pub2})
c.Assert(err, check.IsNil)
keys, err := s.store.GetKnownHostKeys("")
c.Assert(err, check.IsNil)
c.Assert(keys, check.HasLen, 3)
c.Assert(keys, check.DeepEquals, []ssh.PublicKey{pub, pub2, pub2})
// check against dupes:
before, _ := s.store.GetKnownHostKeys("")
s.store.AddKnownHostKeys("example.org", []ssh.PublicKey{pub2})
s.store.AddKnownHostKeys("example.org", []ssh.PublicKey{pub2})
after, _ := s.store.GetKnownHostKeys("")
c.Assert(len(before), check.Equals, len(after))
// check by hostname:
keys, _ = s.store.GetKnownHostKeys("badhost")
c.Assert(len(keys), check.Equals, 0)
keys, _ = s.store.GetKnownHostKeys("example.org")
c.Assert(len(keys), check.Equals, 1)
c.Assert(sshutils.KeysEqual(keys[0], pub2), check.Equals, true)
}
// makeSIgnedKey helper returns all 3 components of a user key (signed by CAPriv key)
func (s *KeyStoreTestSuite) makeSignedKey(c *check.C, makeExpired bool) *Key {
var (
err error
priv, pub, cert []byte
)
priv, pub, _ = s.keygen.GenerateKeyPair("")
username := "vincento"
allowedLogins := []string{username, "root"}
ttl := time.Duration(time.Minute * 20)
if makeExpired {
ttl = -ttl
}
// reuse the same RSA keys for SSH and TLS keys
cryptoPubKey, err := sshutils.CryptoPublicKey(pub)
c.Assert(err, check.IsNil)
clock := clockwork.NewRealClock()
identity := tlsca.Identity{
Username: username,
}
tlsCert, err := s.tlsca.GenerateCertificate(tlsca.CertificateRequest{
Clock: clock,
PublicKey: cryptoPubKey,
Subject: identity.Subject(),
NotAfter: clock.Now().UTC().Add(ttl),
})
c.Assert(err, check.IsNil)
cert, err = s.keygen.GenerateUserCert(services.UserCertParams{
PrivateCASigningKey: CAPriv,
PublicUserKey: pub,
Username: username,
AllowedLogins: allowedLogins,
TTL: ttl,
PermitAgentForwarding: false,
PermitPortForwarding: true,
})
c.Assert(err, check.IsNil)
return &Key{
Priv: priv,
Pub: pub,
Cert: cert,
TLSCert: tlsCert,
}
}
var (
CAPriv = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwBgwn+vkjCcKEr2fbX1mLN555B9amVYfD/fUZBNbXKpHaqYn
lM2WlyRR+xCrU9H/X6xT+wKJs1tsxFbxdBc1RWJtaqz/VpQCjomOulBzwumzB5hT
pJfGblGjkPvpt1zwfmKdpBg0jxXUHHR4u4N6OX0dxd0ImRQ4W9QUtEqzgqToS5u4
iwpeg6i1SoAdHBaSeqYhK9+nGrrJBAl/HVSgvL9tGn/+cQqlOiQz0t61V20+oMBA
P+rOTIiwRXn98iMKFjzVW1HTL5Lwit3oJQX0Lrd/I6tN2De6TJxbbOOkF45V/P/k
nBzbxV0fpnhcvZMnQqg1qdUmNVi6VC1O5qIPiwIDAQABAoIBAEg0T4KtLnkn63dj
41tKeW+AKJ0A1BMy9fYQl7sOM5c/QhzqW5JpPKOPOWl/uIaHNtCFfAOrzoqmYNnk
PFoApztvZeVlJY0rkVJ2jjmmJ/0pzuuZ7Ea/7gxlj2/d4NnVi2hWNR8LIiZudA5G
EWOaZgTZ7KkFDkhL+2s46pdiRNtj7l5FXn2tCh7jmFgKS4m1/QqV9KdE5EjwB2mj
BoP/j4V8O0RM05QpiYX/D5/Rr06tBavwTGW3vz/7OPIbf1el1mjfbLlt3z2tH0A5
BSGB4JEwIZ3+2xlZokHy95OSDzE46TsSzgNx3SDzGRc8UnSZN9yunxnL4ej11WYt
59YmD+ECgYEA3zxrDAtscpoxJSwcSkwqcMdElMK4D/BZw/tE9HhpHx3Pdd5XtMio
CHUkkqxwGJeVIixDjwnl4VfA1s0wy3CtHq6mmwfUviYrH2eqxe5RxNyZOZguk6is
GurZzD+ZfacsEIHyz2fZdnEAIFubu/S6x4TQPGg23oxnQpXXq1vzZFkCgYEA3Emz
W4MXvYWvRdbn+W3onHz/vty9owj/BKSP6giPGrpQFdLs8yoBUw1yTOGqAIfuWMLS
xvjULSlhei5PYD1xM2+B4luxM8K25DlqUpgRVtdmjQ/wxnzlmhDAPIMh7LUtw/6o
JJ+diAKTI86T8tokIL7WFaSvzdrz7/WrZQWkpoMCgYAPVAK1rQMhS10chE7c+yXe
4I/g9w3Ualh/kH1HnAz7yfw4x6+WBkEjc4ezWovH5ICk/A0XgUJ7mp7vIN+82FvK
w4tFEeCVveEwItojBR4wOkV7Iuvvz6EhqAaUc7mCWzw3VfTqMONJsrCjiCbFXSSG
FqSFwVIjLdjZRZitd37a4QKBgQDWfjjTIVlLY9EfWrszZu54+Ul4Sa2pAwh1N9sd
kUnuR33VUjUALGVvvgcOjyieLb1J1iGwNfc7JjDQ7CjD1+/Smn/IrWlksfKtVK6P
T5yKh2BGeEAEtPZHxom4IiM1PdEbJ2oHhxe3qHInCm2KqRdGfysrldjMw6aEfxxt
WEpTCwKBgHLZYgNf/dGgWgw7bVu/k61jxw3yZuU/0marFOPINME/AnTcSAGnkC0S
oDZhaPxjz3+2AHWAjUgW1ltTY8FsJYTOYsvzkYPfya4CgHCLg3D9ss1m4Rc7w5qo
Fa6bvW5jo543NztjlKts7XYVqroMCu0sIMS7R4JGsmw3VJcnnMP2
-----END RSA PRIVATE KEY-----`)
CAPub = []byte(`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAGDCf6+SMJwoSvZ9tfWYs3nnkH1qZVh8P99RkE1tcqkdqpieUzZaXJFH7EKtT0f9frFP7AomzW2zEVvF0FzVFYm1qrP9WlAKOiY66UHPC6bMHmFOkl8ZuUaOQ++m3XPB+Yp2kGDSPFdQcdHi7g3o5fR3F3QiZFDhb1BS0SrOCpOhLm7iLCl6DqLVKgB0cFpJ6piEr36causkECX8dVKC8v20af/5xCqU6JDPS3rVXbT6gwEA/6s5MiLBFef3yIwoWPNVbUdMvkvCK3eglBfQut38jq03YN7pMnFts46QXjlX8/+ScHNvFXR+meFy9kydCqDWp1SY1WLpULU7mog+L ekontsevoy@turing`)
)