-
Notifications
You must be signed in to change notification settings - Fork 0
/
ssh_key.go
143 lines (127 loc) · 3.19 KB
/
ssh_key.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
package gerrittest
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)
// GenerateRSAKey will generate and return an SSH key pair.
func GenerateRSAKey() (*rsa.PrivateKey, error) {
private, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
return private, nil
}
// ReadSSHKeys will read the provided private key and return the public and
// private portions.
func ReadSSHKeys(path string) (ssh.PublicKey, ssh.Signer, error) {
logger := log.WithFields(log.Fields{
"cmp": "ssh",
"phase": "read-ssh-key",
"path": path,
})
logger.WithField("action", "read-file").Debug()
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, nil, err
}
logger.WithField("action", "parse-key").Debug()
signer, err := ssh.ParsePrivateKey(data)
if err != nil {
return nil, nil, err
}
return signer.PublicKey(), signer, nil
}
// WriteRSAKey will take a private key and write out the public
// and private portions to disk.
// nolint: interfacer
func WriteRSAKey(key *rsa.PrivateKey, file *os.File) error {
logger := log.WithFields(log.Fields{
"cmp": "ssh",
"phase": "write-ssh-key",
})
logger.WithField("action", "validate").Debug()
if err := key.Validate(); err != nil {
return err
}
logger.WithField("action", "encode").Debug()
defer file.Close() // nolint: errcheck
return pem.Encode(file, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
})
}
// SSHKey contains information about an ssh key.
type SSHKey struct {
Public ssh.PublicKey `json:"-"`
Private ssh.Signer `json:"-"`
Path string `json:"path"`
Generated bool `json:"generated"`
Default bool `json:"default"`
}
func (s *SSHKey) load() error {
logger := log.WithField("phase", "ssh-key")
logger.WithField("action", "check").Debug()
logger.WithFields(log.Fields{
"path": s.Path,
"action": "read",
}).Debug()
public, signer, err := ReadSSHKeys(s.Path)
if err != nil {
return err
}
s.Public = public
s.Private = signer
return err
}
// String outputs a useful string representing the struct.
func (s *SSHKey) String() string {
return fmt.Sprintf(
"SSHKey{path: %s, generated: %t, default: %t}",
s.Path, s.Generated, s.Default)
}
// Remove will remove the key material from disk but only if the key was
// generated.
func (s *SSHKey) Remove() error {
if s.Generated && !s.Default {
return os.Remove(s.Path)
}
return nil
}
// LoadSSHKey loads an SSH key from disk and returns a *SSHKey struct.
func LoadSSHKey(path string) (*SSHKey, error) {
key := &SSHKey{
Path: path,
Generated: false,
Default: true,
}
return key, key.load()
}
// NewSSHKey will generate and return an *SSHKey.
// TODO add tests
func NewSSHKey() (*SSHKey, error) {
generated, err := GenerateRSAKey()
if err != nil {
return nil, err
}
file, err := ioutil.TempFile(
"", fmt.Sprintf("%s-id_rsa-", ProjectName))
if err != nil {
return nil, err
}
if err := WriteRSAKey(generated, file); err != nil {
return nil, err
}
key := &SSHKey{
Path: file.Name(),
Generated: true,
Default: true,
}
return key, key.load()
}