-
Notifications
You must be signed in to change notification settings - Fork 233
/
keys.go
148 lines (127 loc) · 3.77 KB
/
keys.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
package ssh
import (
"bufio"
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/crc-org/crc/v2/pkg/crc/constants"
gossh "golang.org/x/crypto/ssh"
)
var (
ErrKeyGeneration = errors.New("Unable to generate key")
ErrPrivateKey = errors.New("Unable to marshal private key")
ErrPublicKey = errors.New("Unable to convert public key")
ErrUnableToWriteFile = errors.New("Unable to write file")
)
type KeyPair struct {
PrivateKey []byte
PublicKey []byte
}
// NewKeyPair generates a new SSH keypair
// This will return a private & public key encoded as DER.
func NewKeyPair() (keyPair *KeyPair, err error) {
priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, ErrKeyGeneration
}
privDer, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, ErrPrivateKey
}
pubSSH, err := gossh.NewPublicKey(&priv.PublicKey)
if err != nil {
return nil, ErrPublicKey
}
return &KeyPair{
PrivateKey: privDer,
PublicKey: gossh.MarshalAuthorizedKey(pubSSH),
}, nil
}
// GenerateSSHKey generates SSH keypair based on path of the private key
// The public key would be generated to the same path with ".pub" added
func GenerateSSHKey(path string) error {
if _, err := os.Stat(path); err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("Desired directory for SSH keys does not exist: %s", err)
}
kp, err := NewKeyPair()
if err != nil {
return fmt.Errorf("Error generating key pair: %s", err)
}
if err := kp.WriteToFile(path, fmt.Sprintf("%s.pub", path)); err != nil {
return fmt.Errorf("Error writing keys to file(s): %s", err)
}
}
return nil
}
func RemoveCRCHostEntriesFromKnownHosts() error {
knownHostsPath := filepath.Join(constants.GetHomeDir(), ".ssh", "known_hosts")
if _, err := os.Stat(knownHostsPath); err != nil {
return nil
}
f, err := os.Open(knownHostsPath)
if err != nil {
return fmt.Errorf("Unable to open user's 'known_hosts' file: %w", err)
}
defer f.Close()
tempHostsFile, err := os.CreateTemp(filepath.Join(constants.GetHomeDir(), ".ssh"), "crc")
if err != nil {
return fmt.Errorf("Unable to create temp file: %w", err)
}
defer func() {
tempHostsFile.Close()
os.Remove(tempHostsFile.Name())
}()
if err := tempHostsFile.Chmod(0600); err != nil {
return fmt.Errorf("Error trying to change permissions for temp file: %w", err)
}
// return each line along with the newline '\n' marker
var splitFunc = func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\n'); i >= 0 {
return i + 1, data[0 : i+1], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
var foundCRCEntries bool
scanner := bufio.NewScanner(f)
scanner.Split(splitFunc)
writer := bufio.NewWriter(tempHostsFile)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "[127.0.0.1]:2222") || strings.Contains(scanner.Text(), "192.168.130.11") {
foundCRCEntries = true
continue
}
if _, err := writer.WriteString(scanner.Text()); err != nil {
return fmt.Errorf("Error while writing hostsfile content to temp file: %w", err)
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("Error while reading content from known_hosts file: %w", err)
}
if err := writer.Flush(); err != nil {
return fmt.Errorf("Error while flushing buffered content to temp file: %w", err)
}
if foundCRCEntries {
if err := f.Close(); err != nil {
return fmt.Errorf("Error closing known_hosts file: %w", err)
}
if err := tempHostsFile.Close(); err != nil {
return fmt.Errorf("Error closing temp file: %w", err)
}
return os.Rename(tempHostsFile.Name(), knownHostsPath)
}
return nil
}