Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd0b02e5

Browse files
feat: Improve experience with local SSH keys (#3835)
* feat: Improve experience with local SSH keysThis change means that users can place SSH keys in the default locationsfor OpenSSH, like `~/.ssh/id_rsa` and it will be automatically pickedup (as per a default OpenSSH experience).Fixes#3126* fix: Ensure gitssh cleans up temporary file on interruptCo-authored-by: Dean Sheather <dean@deansheather.com>
1 parent66ad86a commitd0b02e5

File tree

2 files changed

+352
-81
lines changed

2 files changed

+352
-81
lines changed

‎cli/gitssh.go

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package cli
22

33
import (
4+
"bufio"
5+
"bytes"
6+
"context"
47
"fmt"
8+
"io"
59
"os"
610
"os/exec"
11+
"os/signal"
12+
"path/filepath"
713
"strings"
814

915
"github.com/spf13/cobra"
@@ -13,16 +19,30 @@ import (
1319
)
1420

1521
funcgitssh()*cobra.Command {
16-
return&cobra.Command{
22+
cmd:=&cobra.Command{
1723
Use:"gitssh",
1824
Hidden:true,
1925
Short:`Wraps the "ssh" command and uses the coder gitssh key for authentication`,
2026
RunE:func(cmd*cobra.Command,args []string)error {
27+
ctx:=cmd.Context()
28+
env:=os.Environ()
29+
30+
// Catch interrupt signals to ensure the temporary private
31+
// key file is cleaned up on most cases.
32+
ctx,stop:=signal.NotifyContext(ctx,interruptSignals...)
33+
deferstop()
34+
35+
// Early check so errors are reported immediately.
36+
identityFiles,err:=parseIdentityFilesForHost(ctx,args,env)
37+
iferr!=nil {
38+
returnerr
39+
}
40+
2141
client,err:=createAgentClient(cmd)
2242
iferr!=nil {
2343
returnxerrors.Errorf("create agent client: %w",err)
2444
}
25-
key,err:=client.AgentGitSSHKey(cmd.Context())
45+
key,err:=client.AgentGitSSHKey(ctx)
2646
iferr!=nil {
2747
returnxerrors.Errorf("get agent git ssh token: %w",err)
2848
}
@@ -44,8 +64,23 @@ func gitssh() *cobra.Command {
4464
returnxerrors.Errorf("close temp gitsshkey file: %w",err)
4565
}
4666

47-
args=append([]string{"-i",privateKeyFile.Name()},args...)
48-
c:=exec.CommandContext(cmd.Context(),"ssh",args...)
67+
// Append our key, giving precedence to user keys. Note that
68+
// OpenSSH server are typically configured with MaxAuthTries
69+
// set to the default value of 6. This means that only the 6
70+
// first keys can be tried. However, we will assume that if
71+
// a user has configured 6+ keys for a host, they know what
72+
// they're doing. This behavior is critical if a server has
73+
// been configured with MaxAuthTries set to 1.
74+
identityFiles=append(identityFiles,privateKeyFile.Name())
75+
76+
varidentityArgs []string
77+
for_,id:=rangeidentityFiles {
78+
identityArgs=append(identityArgs,"-i",id)
79+
}
80+
81+
args=append(identityArgs,args...)
82+
c:=exec.CommandContext(ctx,"ssh",args...)
83+
c.Env=append(c.Env,env...)
4984
c.Stderr=cmd.ErrOrStderr()
5085
c.Stdout=cmd.OutOrStdout()
5186
c.Stdin=cmd.InOrStdin()
@@ -69,4 +104,86 @@ func gitssh() *cobra.Command {
69104
returnnil
70105
},
71106
}
107+
108+
returncmd
109+
}
110+
111+
// fallbackIdentityFiles is the list of identity files SSH tries when
112+
// none have been defined for a host.
113+
varfallbackIdentityFiles=strings.Join([]string{
114+
"identityfile ~/.ssh/id_rsa",
115+
"identityfile ~/.ssh/id_dsa",
116+
"identityfile ~/.ssh/id_ecdsa",
117+
"identityfile ~/.ssh/id_ecdsa_sk",
118+
"identityfile ~/.ssh/id_ed25519",
119+
"identityfile ~/.ssh/id_ed25519_sk",
120+
"identityfile ~/.ssh/id_xmss",
121+
},"\n")
122+
123+
// parseIdentityFilesForHost uses ssh -G to discern what SSH keys have
124+
// been enabled for the host (via the users SSH config) and returns a
125+
// list of existing identity files.
126+
//
127+
// We do this because when no keys are defined for a host, SSH uses
128+
// fallback keys (see above). However, by passing `-i` to attach our
129+
// private key, we're effectively disabling the fallback keys.
130+
//
131+
// Example invocation:
132+
//
133+
//ssh -G -o SendEnv=GIT_PROTOCOL git@github.com git-upload-pack 'coder/coder'
134+
//
135+
// The extra arguments work without issue and lets us run the command
136+
// as-is without stripping out the excess (git-upload-pack 'coder/coder').
137+
funcparseIdentityFilesForHost(ctx context.Context,args,env []string) (identityFiles []string,errorerror) {
138+
home,err:=os.UserHomeDir()
139+
iferr!=nil {
140+
returnnil,xerrors.Errorf("get user home dir failed: %w",err)
141+
}
142+
143+
varoutBuf bytes.Buffer
144+
varr io.Reader=&outBuf
145+
146+
args=append([]string{"-G"},args...)
147+
cmd:=exec.CommandContext(ctx,"ssh",args...)
148+
cmd.Env=append(cmd.Env,env...)
149+
cmd.Stdout=&outBuf
150+
cmd.Stderr=io.Discard
151+
err=cmd.Run()
152+
iferr!=nil {
153+
// If ssh -G failed, the SSH version is likely too old, fallback
154+
// to using the default identity files.
155+
r=strings.NewReader(fallbackIdentityFiles)
156+
}
157+
158+
s:=bufio.NewScanner(r)
159+
fors.Scan() {
160+
line:=s.Text()
161+
ifstrings.HasPrefix(line,"identityfile ") {
162+
id:=strings.TrimPrefix(line,"identityfile ")
163+
ifstrings.HasPrefix(id,"~/") {
164+
id=home+id[1:]
165+
}
166+
// OpenSSH on Windows is weird, it supports using (and does
167+
// use) mixed \ and / in paths.
168+
//
169+
// Example: C:\Users\ZeroCool/.ssh/known_hosts
170+
//
171+
// To check the file existence in Go, though, we want to use
172+
// proper Windows paths.
173+
// OpenSSH is amazing, this will work on Windows too:
174+
// C:\Users\ZeroCool/.ssh/id_rsa
175+
id=filepath.FromSlash(id)
176+
177+
// Only include the identity file if it exists.
178+
if_,err:=os.Stat(id);err==nil {
179+
identityFiles=append(identityFiles,id)
180+
}
181+
}
182+
}
183+
iferr:=s.Err();err!=nil {
184+
// This should never happen, the check is for completeness.
185+
returnnil,xerrors.Errorf("scan ssh output: %w",err)
186+
}
187+
188+
returnidentityFiles,nil
72189
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp