-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathssh.go
91 lines (75 loc) · 2.18 KB
/
ssh.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
package main
import (
"crypto/rand"
"encoding/binary"
"fmt"
"strings"
"time"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
)
type Signer struct {
CACert ssh.PublicKey
CAKey ssh.Signer
AllowedClockDiff time.Duration
MaxTTL time.Duration
}
func (s *Signer) Init(key, passphrase, pubKey []byte, allowedClockDiff, maxTTL time.Duration) error {
var err error
if len(passphrase) != 0 {
s.CAKey, err = ssh.ParsePrivateKeyWithPassphrase(key, passphrase)
} else {
s.CAKey, err = ssh.ParsePrivateKey(key)
}
if err != nil {
return errors.Wrap(err, "error parsing CA private key")
}
s.CACert, _, _, _, err = ssh.ParseAuthorizedKey(pubKey)
if err != nil {
return errors.Wrap(err, "error parsing CA public key")
}
s.MaxTTL = maxTTL
s.AllowedClockDiff = allowedClockDiff
return nil
}
// ReadCA method read CA public cert from local file
func (s Signer) ReadCA() (string, error) {
return string(ssh.MarshalAuthorizedKey(s.CACert)), nil
}
// Sign method is used to sign passed SSH Key.
func (s Signer) Sign(key []byte, keyId string, principals, sourceAddresses []string) (*ssh.Certificate, error) {
buf := make([]byte, 8)
_, err := rand.Read(buf)
if err != nil {
return nil, errors.Wrap(err, "failed to read random bytes")
}
serial := binary.LittleEndian.Uint64(buf)
pubKey, _, _, _, err := ssh.ParseAuthorizedKey(key)
if err != nil {
return nil, fmt.Errorf("failed to parse user public key: %s", err)
}
criticalOptions := map[string]string{}
if len(sourceAddresses) != 0 {
criticalOptions["source-address"] = strings.Join(sourceAddresses, ",")
}
certificate := ssh.Certificate{
Serial: serial,
Key: pubKey,
KeyId: keyId,
ValidPrincipals: principals,
ValidAfter: uint64(time.Now().UTC().Add(-s.AllowedClockDiff).Unix()),
ValidBefore: uint64(time.Now().UTC().Add(s.MaxTTL).Unix()),
CertType: ssh.UserCert,
Permissions: ssh.Permissions{
CriticalOptions: criticalOptions,
Extensions: map[string]string{
"permit-pty": "",
},
},
}
err = certificate.SignCert(rand.Reader, s.CAKey)
if err != nil {
return nil, fmt.Errorf("failed to sign user public key: %s", err)
}
return &certificate, nil
}