forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
interfaces.go
121 lines (105 loc) · 3.77 KB
/
interfaces.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
/*
Copyright 2015 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 (
"bytes"
"fmt"
"time"
"github.com/gravitational/trace"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
// Key describes a complete (signed) client key
type Key struct {
Priv []byte `json:"Priv,omitempty"`
Pub []byte `json:"Pub,omitempty"`
Cert []byte `json:"Cert,omitempty"`
// ProxyHost (optionally) contains the hostname of the proxy server
// which issued this key
ProxyHost string
}
// LocalKeyStore interface allows for different storage back-ends for TSH to load/save its keys
type LocalKeyStore interface {
// client key management
GetKeys(username string) ([]Key, error)
AddKey(host string, username string, key *Key) error
GetKey(host string, username string) (*Key, error)
DeleteKey(host string, username string) error
// interface to known_hosts file:
AddKnownHostKeys(hostname string, keys []ssh.PublicKey) error
GetKnownHostKeys(hostname string) ([]ssh.PublicKey, error)
}
// AsAgentKeys converts client.Key struct to a []*agent.AddedKey. All elements
// of the []*agent.AddedKey slice need to be loaded into the agent!
//
// This is done because OpenSSH clients older than OpenSSH 7.3/7.3p1
// (2016-08-01) have a bug in how they use certificates that have been loaded
// in an agent. Specifically when you add a certificate to an agent, you can't
// just embed the private key within the certificate, you have to add the
// certificate and private key to the agent separately. Teleport works around
// this behavior to ensure OpenSSH interoperability.
//
// For more details see the following: https://bugzilla.mindrot.org/show_bug.cgi?id=2550
func (k *Key) AsAgentKeys() ([]*agent.AddedKey, error) {
// unmarshal certificate bytes into a ssh.PublicKey
publicKey, _, _, _, err := ssh.ParseAuthorizedKey(k.Cert)
if err != nil {
return nil, trace.Wrap(err)
}
// unmarshal private key bytes into a *rsa.PrivateKey
privateKey, err := ssh.ParseRawPrivateKey(k.Priv)
if err != nil {
return nil, trace.Wrap(err)
}
// put a teleport identifier along with the teleport user into the comment field
comment := fmt.Sprintf("teleport:%v", publicKey.(*ssh.Certificate).KeyId)
// return a certificate (with embeded private key) as well as a private key
return []*agent.AddedKey{
&agent.AddedKey{
PrivateKey: privateKey,
Certificate: publicKey.(*ssh.Certificate),
Comment: comment,
LifetimeSecs: 0,
ConfirmBeforeUse: false,
},
&agent.AddedKey{
PrivateKey: privateKey,
Certificate: nil,
Comment: comment,
LifetimeSecs: 0,
ConfirmBeforeUse: false,
},
}, nil
}
// EqualsTo returns true if this key is the same as the other.
// Primarily used in tests
func (k *Key) EqualsTo(other *Key) bool {
if k == other {
return true
}
return bytes.Equal(k.Cert, other.Cert) &&
bytes.Equal(k.Priv, other.Priv) &&
bytes.Equal(k.Pub, other.Pub)
}
// CertValidBefore returns the time of the cert expiration
func (k *Key) CertValidBefore() (t time.Time, err error) {
pcert, _, _, _, err := ssh.ParseAuthorizedKey(k.Cert)
if err != nil {
return t, trace.Wrap(err)
}
cert, ok := pcert.(*ssh.Certificate)
if !ok {
return t, trace.Errorf("not supported certificate type")
}
return time.Unix(int64(cert.ValidBefore), 0), nil
}