9
9
"strings"
10
10
11
11
"github.com/AkihiroSuda/lima/pkg/osutil"
12
+ "github.com/AkihiroSuda/lima/pkg/store"
12
13
"github.com/AkihiroSuda/lima/pkg/store/filenames"
13
14
"github.com/pkg/errors"
14
15
"github.com/sirupsen/logrus"
@@ -19,34 +20,71 @@ type PubKey struct {
19
20
Content string
20
21
}
21
22
22
- // DefaultPubKeys finds ssh public keys from ~/.ssh
23
- func DefaultPubKeys () []PubKey {
23
+ func readPublicKey (f string ) (PubKey , error ) {
24
+ entry := PubKey {
25
+ Filename : f ,
26
+ }
27
+ content , err := os .ReadFile (f )
28
+ if err == nil {
29
+ entry .Content = strings .TrimSpace (string (content ))
30
+ } else {
31
+ err = errors .Wrapf (err , "failed to read ssh public key %q" , f )
32
+ }
33
+ return entry , err
34
+ }
35
+
36
+ // DefaultPubKeys returns the public key from $LIMA_HOME/_config/user.pub.
37
+ // The key will be created if it does not yet exist. All public keys
38
+ // ~/.ssh/*.pub will be appended to make the VM accessible without specifying
39
+ // and identity explicitly.
40
+ func DefaultPubKeys () ([]PubKey , error ) {
41
+ // Read $LIMA_HOME/_config/user.pub
42
+ configDir , err := store .LimaConfigDir ()
43
+ if err != nil {
44
+ return nil , err
45
+ }
46
+ _ , err = os .Stat (filepath .Join (configDir , filenames .UserPrivateKey ))
47
+ if err != nil {
48
+ if ! errors .Is (err , os .ErrNotExist ) {
49
+ return nil , err
50
+ }
51
+ if err := os .MkdirAll (configDir , 0700 ); err != nil {
52
+ return nil , errors .Wrapf (err , "could not create %q directory" , configDir )
53
+ }
54
+ keygenCmd := exec .Command ("ssh-keygen" , "-t" , "ed25519" , "-q" , "-N" , "" , "-f" ,
55
+ filepath .Join (configDir , filenames .UserPrivateKey ))
56
+ logrus .Debugf ("executing %v" , keygenCmd .Args )
57
+ if out , err := keygenCmd .CombinedOutput (); err != nil {
58
+ return nil , errors .Wrapf (err , "failed to run %v: %q" , keygenCmd .Args , string (out ))
59
+ }
60
+ }
61
+ entry , err := readPublicKey (filepath .Join (configDir , filenames .UserPublicKey ))
62
+ if err != nil {
63
+ return nil , err
64
+ }
65
+ res := []PubKey {entry }
66
+
67
+ // Append all of ~/.ssh/*.pub
24
68
homeDir , err := os .UserHomeDir ()
25
69
if err != nil {
26
- logrus .Warn (err )
27
- return nil
70
+ return nil , err
28
71
}
29
72
files , err := filepath .Glob (filepath .Join (homeDir , ".ssh/*.pub" ))
30
73
if err != nil {
31
- logrus .Warn (err )
32
- return nil
74
+ panic (err ) // Only possible error is ErrBadPattern, so this should be unreachable.
33
75
}
34
- var res []PubKey
35
76
for _ , f := range files {
36
77
if ! strings .HasSuffix (f , ".pub" ) {
37
78
panic (errors .Errorf ("unexpected ssh public key filename %q" , f ))
38
79
}
39
- entry := PubKey {
40
- Filename : f ,
41
- }
42
- if content , err := os .ReadFile (f ); err == nil {
43
- entry .Content = strings .TrimSpace (string (content ))
44
- } else {
45
- logrus .WithError (err ).Warningf ("failed to read ssh public key %q" , f )
80
+ entry , err := readPublicKey (f )
81
+ if err == nil {
82
+ res = append (res , entry )
83
+ } else if ! errors .Is (err , os .ErrNotExist ) {
84
+ return nil , err
46
85
}
47
- res = append (res , entry )
48
86
}
49
- return res
87
+ return res , nil
50
88
}
51
89
52
90
func RemoveKnownHostEntries (sshLocalPort int ) error {
@@ -77,7 +115,40 @@ func SSHArgs(instDir string) ([]string, error) {
77
115
if len (controlSock ) >= osutil .UnixPathMax {
78
116
return nil , errors .Errorf ("socket path %q is too long: >= UNIX_PATH_MAX=%d" , controlSock , osutil .UnixPathMax )
79
117
}
80
- args := []string {
118
+ configDir , err := store .LimaConfigDir ()
119
+ if err != nil {
120
+ return nil , err
121
+ }
122
+ privateKeyPath := filepath .Join (configDir , filenames .UserPrivateKey )
123
+ _ , err = os .Stat (privateKeyPath )
124
+ if err != nil {
125
+ return nil , err
126
+ }
127
+ args := []string {"-i" , privateKeyPath }
128
+
129
+ // Append all private keys corresponding to ~/.ssh/*.pub to keep old instances workin
130
+ // that had been created before lima started using an internal identity.
131
+ homeDir , err := os .UserHomeDir ()
132
+ if err != nil {
133
+ return nil , err
134
+ }
135
+ files , err := filepath .Glob (filepath .Join (homeDir , ".ssh/*.pub" ))
136
+ if err != nil {
137
+ panic (err ) // Only possible error is ErrBadPattern, so this should be unreachable.
138
+ }
139
+ for _ , f := range files {
140
+ if ! strings .HasSuffix (f , ".pub" ) {
141
+ panic (errors .Errorf ("unexpected ssh public key filename %q" , f ))
142
+ }
143
+ privateKeyPath := strings .TrimSuffix (f , ".pub" )
144
+ _ , err = os .Stat (privateKeyPath )
145
+ if err != nil {
146
+ return nil , err
147
+ }
148
+ args = append (args , "-i" , privateKeyPath )
149
+ }
150
+
151
+ args = append (args ,
81
152
"-l" , u .Username , // guest and host have the same username, but we should specify the username explicitly (#85)
82
153
"-o" , "ControlMaster=auto" ,
83
154
"-o" , "ControlPath=" + controlSock ,
@@ -88,6 +159,6 @@ func SSHArgs(instDir string) ([]string, error) {
88
159
"-o" , "PreferredAuthentications=publickey" ,
89
160
"-o" , "Compression=no" ,
90
161
"-o" , "BatchMode=yes" ,
91
- }
162
+ )
92
163
return args , nil
93
164
}
0 commit comments