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

Commita8cf4c3

Browse files
committed
feat: modifies config-ssh to check for Coder Connect
1 parent3b54254 commita8cf4c3

File tree

2 files changed

+167
-132
lines changed

2 files changed

+167
-132
lines changed

‎cli/configssh.go

Lines changed: 153 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,17 @@ const (
4848
typesshConfigOptionsstruct {
4949
waitEnumstring
5050
// Deprecated: moving away from prefix to hostnameSuffix
51-
userHostPrefixstring
52-
hostnameSuffixstring
53-
sshOptions []string
54-
disableAutostartbool
55-
header []string
56-
headerCommandstring
57-
removedKeysmap[string]bool
51+
userHostPrefixstring
52+
hostnameSuffixstring
53+
sshOptions []string
54+
disableAutostartbool
55+
header []string
56+
headerCommandstring
57+
removedKeysmap[string]bool
58+
globalConfigPathstring
59+
coderBinaryPathstring
60+
skipProxyCommandbool
61+
forceUnixSeparatorsbool
5862
}
5963

6064
// addOptions expects options in the form of "option=value" or "option value".
@@ -107,6 +111,78 @@ func (o sshConfigOptions) equal(other sshConfigOptions) bool {
107111
o.hostnameSuffix==other.hostnameSuffix
108112
}
109113

114+
func (osshConfigOptions)writeToBuffer(buf*bytes.Buffer)error {
115+
escapedCoderBinary,err:=sshConfigExecEscape(o.coderBinaryPath,o.forceUnixSeparators)
116+
iferr!=nil {
117+
returnxerrors.Errorf("escape coder binary for ssh failed: %w",err)
118+
}
119+
120+
escapedGlobalConfig,err:=sshConfigExecEscape(o.globalConfigPath,o.forceUnixSeparators)
121+
iferr!=nil {
122+
returnxerrors.Errorf("escape global config for ssh failed: %w",err)
123+
}
124+
125+
rootFlags:=fmt.Sprintf("--global-config %s",escapedGlobalConfig)
126+
for_,h:=rangeo.header {
127+
rootFlags+=fmt.Sprintf(" --header %q",h)
128+
}
129+
ifo.headerCommand!="" {
130+
rootFlags+=fmt.Sprintf(" --header-command %q",o.headerCommand)
131+
}
132+
133+
flags:=""
134+
ifo.waitEnum!="auto" {
135+
flags+=" --wait="+o.waitEnum
136+
}
137+
ifo.disableAutostart {
138+
flags+=" --disable-autostart=true"
139+
}
140+
141+
// Prefix block:
142+
ifo.userHostPrefix!="" {
143+
_,_=buf.WriteString("Host")
144+
145+
_,_=buf.WriteString(" ")
146+
_,_=buf.WriteString(o.userHostPrefix)
147+
_,_=buf.WriteString("*\n")
148+
149+
for_,v:=rangeo.sshOptions {
150+
_,_=buf.WriteString("\t")
151+
_,_=buf.WriteString(v)
152+
_,_=buf.WriteString("\n")
153+
}
154+
if!o.skipProxyCommand&&o.userHostPrefix!="" {
155+
_,_=buf.WriteString("\t")
156+
_,_=fmt.Fprintf(buf,
157+
"ProxyCommand %s %s ssh --stdio%s --ssh-host-prefix %s %%h",
158+
escapedCoderBinary,rootFlags,flags,o.userHostPrefix,
159+
)
160+
_,_=buf.WriteString("\n")
161+
}
162+
}
163+
164+
// Suffix block
165+
ifo.hostnameSuffix=="" {
166+
returnnil
167+
}
168+
_,_=fmt.Fprintf(buf,"\nMatch host *.%s !exec\"%s connect exists %%h\"\n",
169+
o.hostnameSuffix,escapedCoderBinary)
170+
for_,v:=rangeo.sshOptions {
171+
_,_=buf.WriteString("\t")
172+
_,_=buf.WriteString(v)
173+
_,_=buf.WriteString("\n")
174+
}
175+
if!o.skipProxyCommand {
176+
_,_=buf.WriteString("\t")
177+
_,_=fmt.Fprintf(buf,
178+
"ProxyCommand %s %s ssh --stdio%s --hostname-suffix %s %%h",
179+
escapedCoderBinary,rootFlags,flags,o.hostnameSuffix,
180+
)
181+
_,_=buf.WriteString("\n")
182+
}
183+
returnnil
184+
}
185+
110186
// slicesSortedEqual compares two slices without side-effects or regard to order.
111187
funcslicesSortedEqual[S~[]E,E constraints.Ordered](a,bS)bool {
112188
iflen(a)!=len(b) {
@@ -147,13 +223,11 @@ func (o sshConfigOptions) asList() (list []string) {
147223

148224
func (r*RootCmd)configSSH()*serpent.Command {
149225
var (
150-
sshConfigFilestring
151-
sshConfigOptssshConfigOptions
152-
usePreviousOptsbool
153-
dryRunbool
154-
skipProxyCommandbool
155-
forceUnixSeparatorsbool
156-
coderCliPathstring
226+
sshConfigFilestring
227+
sshConfigOptssshConfigOptions
228+
usePreviousOptsbool
229+
dryRunbool
230+
coderCliPathstring
157231
)
158232
client:=new(codersdk.Client)
159233
cmd:=&serpent.Command{
@@ -177,7 +251,7 @@ func (r *RootCmd) configSSH() *serpent.Command {
177251
Handler:func(inv*serpent.Invocation)error {
178252
ctx:=inv.Context()
179253

180-
ifsshConfigOpts.waitEnum!="auto"&&skipProxyCommand {
254+
ifsshConfigOpts.waitEnum!="auto"&&sshConfigOpts.skipProxyCommand {
181255
// The wait option is applied to the ProxyCommand. If the user
182256
// specifies skip-proxy-command, then wait cannot be applied.
183257
returnxerrors.Errorf("cannot specify both --skip-proxy-command and --wait")
@@ -207,18 +281,7 @@ func (r *RootCmd) configSSH() *serpent.Command {
207281
returnerr
208282
}
209283
}
210-
211-
escapedCoderBinary,err:=sshConfigExecEscape(coderBinary,forceUnixSeparators)
212-
iferr!=nil {
213-
returnxerrors.Errorf("escape coder binary for ssh failed: %w",err)
214-
}
215-
216284
root:=r.createConfig()
217-
escapedGlobalConfig,err:=sshConfigExecEscape(string(root),forceUnixSeparators)
218-
iferr!=nil {
219-
returnxerrors.Errorf("escape global config for ssh failed: %w",err)
220-
}
221-
222285
homedir,err:=os.UserHomeDir()
223286
iferr!=nil {
224287
returnxerrors.Errorf("user home dir failed: %w",err)
@@ -320,94 +383,15 @@ func (r *RootCmd) configSSH() *serpent.Command {
320383
coderdConfig.HostnamePrefix="coder."
321384
}
322385

323-
ifsshConfigOpts.userHostPrefix!="" {
324-
// Override with user flag.
325-
coderdConfig.HostnamePrefix=sshConfigOpts.userHostPrefix
326-
}
327-
ifsshConfigOpts.hostnameSuffix!="" {
328-
// Override with user flag.
329-
coderdConfig.HostnameSuffix=sshConfigOpts.hostnameSuffix
330-
}
331-
332-
// Write agent configuration.
333-
defaultOptions:= []string{
334-
"ConnectTimeout=0",
335-
"StrictHostKeyChecking=no",
336-
// Without this, the "REMOTE HOST IDENTITY CHANGED"
337-
// message will appear.
338-
"UserKnownHostsFile=/dev/null",
339-
// This disables the "Warning: Permanently added 'hostname' (RSA) to the list of known hosts."
340-
// message from appearing on every SSH. This happens because we ignore the known hosts.
341-
"LogLevel ERROR",
342-
}
343-
344-
if!skipProxyCommand {
345-
rootFlags:=fmt.Sprintf("--global-config %s",escapedGlobalConfig)
346-
for_,h:=rangesshConfigOpts.header {
347-
rootFlags+=fmt.Sprintf(" --header %q",h)
348-
}
349-
ifsshConfigOpts.headerCommand!="" {
350-
rootFlags+=fmt.Sprintf(" --header-command %q",sshConfigOpts.headerCommand)
351-
}
352-
353-
flags:=""
354-
ifsshConfigOpts.waitEnum!="auto" {
355-
flags+=" --wait="+sshConfigOpts.waitEnum
356-
}
357-
ifsshConfigOpts.disableAutostart {
358-
flags+=" --disable-autostart=true"
359-
}
360-
ifcoderdConfig.HostnamePrefix!="" {
361-
flags+=" --ssh-host-prefix "+coderdConfig.HostnamePrefix
362-
}
363-
ifcoderdConfig.HostnameSuffix!="" {
364-
flags+=" --hostname-suffix "+coderdConfig.HostnameSuffix
365-
}
366-
defaultOptions=append(defaultOptions,fmt.Sprintf(
367-
"ProxyCommand %s %s ssh --stdio%s %%h",
368-
escapedCoderBinary,rootFlags,flags,
369-
))
370-
}
371-
372-
// Create a copy of the options so we can modify them.
373-
configOptions:=sshConfigOpts
374-
configOptions.sshOptions=nil
375-
376-
// User options first (SSH only uses the first
377-
// option unless it can be given multiple times)
378-
for_,opt:=rangesshConfigOpts.sshOptions {
379-
err:=configOptions.addOptions(opt)
380-
iferr!=nil {
381-
returnxerrors.Errorf("add flag config option %q: %w",opt,err)
382-
}
383-
}
384-
385-
// Deployment options second, allow them to
386-
// override standard options.
387-
fork,v:=rangecoderdConfig.SSHConfigOptions {
388-
opt:=fmt.Sprintf("%s %s",k,v)
389-
err:=configOptions.addOptions(opt)
390-
iferr!=nil {
391-
returnxerrors.Errorf("add coderd config option %q: %w",opt,err)
392-
}
393-
}
394-
395-
// Finally, add the standard options.
396-
iferr:=configOptions.addOptions(defaultOptions...);err!=nil {
386+
configOptions,err:=mergeSSHOptions(sshConfigOpts,coderdConfig,string(root),coderBinary)
387+
iferr!=nil {
397388
returnerr
398389
}
399-
400-
hostBlock:= []string{
401-
sshConfigHostLinePatterns(coderdConfig),
402-
}
403-
// Prefix with '\t'
404-
for_,v:=rangeconfigOptions.sshOptions {
405-
hostBlock=append(hostBlock,"\t"+v)
390+
err=configOptions.writeToBuffer(buf)
391+
iferr!=nil {
392+
returnerr
406393
}
407394

408-
_,_=buf.WriteString(strings.Join(hostBlock,"\n"))
409-
_=buf.WriteByte('\n')
410-
411395
sshConfigWriteSectionEnd(buf)
412396

413397
// Write the remainder of the users config file to buf.
@@ -523,7 +507,7 @@ func (r *RootCmd) configSSH() *serpent.Command {
523507
Flag:"skip-proxy-command",
524508
Env:"CODER_SSH_SKIP_PROXY_COMMAND",
525509
Description:"Specifies whether the ProxyCommand option should be skipped. Useful for testing.",
526-
Value:serpent.BoolOf(&skipProxyCommand),
510+
Value:serpent.BoolOf(&sshConfigOpts.skipProxyCommand),
527511
Hidden:true,
528512
},
529513
{
@@ -564,7 +548,7 @@ func (r *RootCmd) configSSH() *serpent.Command {
564548
Description:"By default, 'config-ssh' uses the os path separator when writing the ssh config. "+
565549
"This might be an issue in Windows machine that use a unix-like shell. "+
566550
"This flag forces the use of unix file paths (the forward slash '/').",
567-
Value:serpent.BoolOf(&forceUnixSeparators),
551+
Value:serpent.BoolOf(&sshConfigOpts.forceUnixSeparators),
568552
// On non-windows showing this command is useless because it is a noop.
569553
// Hide vs disable it though so if a command is copied from a Windows
570554
// machine to a unix machine it will still work and not throw an
@@ -577,6 +561,63 @@ func (r *RootCmd) configSSH() *serpent.Command {
577561
returncmd
578562
}
579563

564+
funcmergeSSHOptions(
565+
usersshConfigOptions,coderd codersdk.SSHConfigResponse,globalConfigPath,coderBinaryPathstring,
566+
) (
567+
sshConfigOptions,error,
568+
) {
569+
// Write agent configuration.
570+
defaultOptions:= []string{
571+
"ConnectTimeout=0",
572+
"StrictHostKeyChecking=no",
573+
// Without this, the "REMOTE HOST IDENTITY CHANGED"
574+
// message will appear.
575+
"UserKnownHostsFile=/dev/null",
576+
// This disables the "Warning: Permanently added 'hostname' (RSA) to the list of known hosts."
577+
// message from appearing on every SSH. This happens because we ignore the known hosts.
578+
"LogLevel ERROR",
579+
}
580+
581+
// Create a copy of the options so we can modify them.
582+
configOptions:=user
583+
configOptions.sshOptions=nil
584+
585+
configOptions.globalConfigPath=globalConfigPath
586+
configOptions.coderBinaryPath=coderBinaryPath
587+
// user config takes precedence
588+
ifuser.userHostPrefix=="" {
589+
configOptions.userHostPrefix=coderd.HostnamePrefix
590+
}
591+
ifuser.hostnameSuffix=="" {
592+
configOptions.hostnameSuffix=coderd.HostnameSuffix
593+
}
594+
595+
// User options first (SSH only uses the first
596+
// option unless it can be given multiple times)
597+
for_,opt:=rangeuser.sshOptions {
598+
err:=configOptions.addOptions(opt)
599+
iferr!=nil {
600+
returnsshConfigOptions{},xerrors.Errorf("add flag config option %q: %w",opt,err)
601+
}
602+
}
603+
604+
// Deployment options second, allow them to
605+
// override standard options.
606+
fork,v:=rangecoderd.SSHConfigOptions {
607+
opt:=fmt.Sprintf("%s %s",k,v)
608+
err:=configOptions.addOptions(opt)
609+
iferr!=nil {
610+
returnsshConfigOptions{},xerrors.Errorf("add coderd config option %q: %w",opt,err)
611+
}
612+
}
613+
614+
// Finally, add the standard options.
615+
iferr:=configOptions.addOptions(defaultOptions...);err!=nil {
616+
returnsshConfigOptions{},err
617+
}
618+
returnconfigOptions,nil
619+
}
620+
580621
//nolint:revive
581622
funcsshConfigWriteSectionHeader(w io.Writer,addNewlinebool,osshConfigOptions) {
582623
nl:="\n"
@@ -844,19 +885,3 @@ func diffBytes(name string, b1, b2 []byte, color bool) ([]byte, error) {
844885
}
845886
returnb,nil
846887
}
847-
848-
funcsshConfigHostLinePatterns(config codersdk.SSHConfigResponse)string {
849-
builder:= strings.Builder{}
850-
// by inspection, WriteString always returns nil error
851-
_,_=builder.WriteString("Host")
852-
ifconfig.HostnamePrefix!="" {
853-
_,_=builder.WriteString(" ")
854-
_,_=builder.WriteString(config.HostnamePrefix)
855-
_,_=builder.WriteString("*")
856-
}
857-
ifconfig.HostnameSuffix!="" {
858-
_,_=builder.WriteString(" *.")
859-
_,_=builder.WriteString(config.HostnameSuffix)
860-
}
861-
returnbuilder.String()
862-
}

‎cli/configssh_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -612,18 +612,29 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
612612
},
613613
},
614614
{
615-
name:"Hostname Suffix",
615+
name:"Hostname Suffix ProxyCommand",
616616
args: []string{
617617
"--yes",
618618
"--hostname-suffix","testy",
619619
},
620620
wantErr:false,
621621
hasAgent:true,
622622
wantConfig:wantConfig{
623-
ssh: []string{"Host coder.* *.testy"},
624623
regexMatch:`ProxyCommand .* ssh .* --hostname-suffix testy %h`,
625624
},
626625
},
626+
{
627+
name:"Hostname Suffix Match",
628+
args: []string{
629+
"--yes",
630+
"--hostname-suffix","testy",
631+
},
632+
wantErr:false,
633+
hasAgent:true,
634+
wantConfig:wantConfig{
635+
regexMatch:`Match host \*\.testy !exec ".* connect exists %h"`,
636+
},
637+
},
627638
{
628639
name:"Hostname Prefix and Suffix",
629640
args: []string{
@@ -634,8 +645,7 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
634645
wantErr:false,
635646
hasAgent:true,
636647
wantConfig:wantConfig{
637-
ssh: []string{"Host presto.* *.testy"},
638-
regexMatch:`ProxyCommand .* ssh .* --ssh-host-prefix presto\. --hostname-suffix testy %h`,
648+
ssh: []string{"Host presto.*","Match host *.testy !exec"},
639649
},
640650
},
641651
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp