Skip to content

Commit dfd3541

Browse files
authored
add blocking_command option to config (#29)
1 parent ec799ed commit dfd3541

File tree

5 files changed

+64
-4
lines changed

5 files changed

+64
-4
lines changed

cmd/sshproxy/sshproxy.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,21 @@ func mainExitCode() int {
374374
}()
375375
}
376376

377+
// launch blocking command
378+
if config.BlockingCommand != "" {
379+
args := strings.Fields(config.BlockingCommand)
380+
cmd := exec.Command(args[0], args[1:]...)
381+
cmd.Stdout = os.Stdout
382+
cmd.Stderr = os.Stderr
383+
rc, err := runCommand(cmd, false)
384+
if err != nil {
385+
log.Errorf("error running blocking command: %s", err)
386+
}
387+
if rc != 0 {
388+
return rc
389+
}
390+
}
391+
377392
// launch background command
378393
if config.BgCommand != "" {
379394
wg.Add(1)

config/sshproxy.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@
7979
# command: "sftp"
8080
# disable_dump: true
8181

82+
# A command can be launched before the bg_command and before connecting to the
83+
# destination. The standard and error outputs are displayed to the user. If the
84+
# return code of the blocking command is not 0, sshproxy will abort the
85+
# session.
86+
#blocking_command: ""
87+
8288
# A command can be launched in the background for the session duration.
8389
# The standard and error outputs are only logged in debug mode.
8490
#bg_command: ""

doc/sshproxy.yaml.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ The following keys can be defined:
4747
precisely, when all backends are either down or disabled in etcd).
4848
This message can be multiline. It is empty by default.
4949

50+
*blocking_command*::
51+
A string specifying a command which will be launched before the
52+
bg_command and before connecting to the destination. The standard and
53+
error outputs are displayed to the user. If the return code of the
54+
blocking command is not 0, sshproxy will abort the session. It is
55+
empty by default.
56+
5057
*bg_command*::
5158
a string specifying a command which will be launched in the background
5259
for the session duration. Its standard and error outputs are only

pkg/utils/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type Config struct {
5353
Etcd etcdConfig
5454
EtcdStatsInterval Duration `yaml:"etcd_stats_interval"`
5555
LogStatsInterval Duration `yaml:"log_stats_interval"`
56+
BlockingCommand string `yaml:"blocking_command"`
5657
BgCommand string `yaml:"bg_command"`
5758
SSH sshConfig
5859
TranslateCommands map[string]*TranslateCommandConfig `yaml:"translate_commands"`
@@ -110,6 +111,7 @@ type subConfig struct {
110111
Etcd interface{}
111112
EtcdStatsInterval interface{} `yaml:"etcd_stats_interval"`
112113
LogStatsInterval interface{} `yaml:"log_stats_interval"`
114+
BlockingCommand interface{} `yaml:"blocking_command"`
113115
BgCommand interface{} `yaml:"bg_command"`
114116
SSH interface{}
115117
TranslateCommands map[string]*TranslateCommandConfig `yaml:"translate_commands"`
@@ -138,6 +140,7 @@ func PrintConfig(config *Config, groups map[string]bool) []string {
138140
output = append(output, fmt.Sprintf("config.etcd = %+v", config.Etcd))
139141
output = append(output, fmt.Sprintf("config.etcd_stats_interval = %s", config.EtcdStatsInterval.Duration()))
140142
output = append(output, fmt.Sprintf("config.log_stats_interval = %s", config.LogStatsInterval.Duration()))
143+
output = append(output, fmt.Sprintf("config.blocking_command = %s", config.BlockingCommand))
141144
output = append(output, fmt.Sprintf("config.bg_command = %s", config.BgCommand))
142145
output = append(output, fmt.Sprintf("config.ssh = %+v", config.SSH))
143146
for k, v := range config.TranslateCommands {
@@ -212,6 +215,10 @@ func parseSubConfig(config *Config, subconfig *subConfig) error {
212215
}
213216
}
214217

218+
if subconfig.BlockingCommand != nil {
219+
config.BlockingCommand = subconfig.BlockingCommand.(string)
220+
}
221+
215222
if subconfig.BgCommand != nil {
216223
config.BgCommand = subconfig.BgCommand.(string)
217224
}

test/fedora-image/sshproxy_test.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func addLineSSHProxyConf(line string) {
5353

5454
func removeLineSSHProxyConf(line string) {
5555
ctx := context.Background()
56+
line = strings.ReplaceAll(line, "/", "\\/")
5657
for _, gateway := range gateways {
5758
_, _, _, err := runCommand(ctx, "ssh", []string{fmt.Sprintf("root@%s", gateway), "--", fmt.Sprintf("sed -i 's/^%s$//' %s", line, SSHPROXYCONFIG)}, nil, nil)
5859
if err != nil {
@@ -63,6 +64,7 @@ func removeLineSSHProxyConf(line string) {
6364

6465
func updateLineSSHProxyConf(key string, value string) {
6566
ctx := context.Background()
67+
value = strings.ReplaceAll(value, "/", "\\/")
6668
for _, gateway := range gateways {
6769
_, _, _, err := runCommand(ctx, "ssh", []string{fmt.Sprintf("root@%s", gateway), "--", fmt.Sprintf("sed -i '/%s:/s/: .*$/: %s/' %s", key, value, SSHPROXYCONFIG)}, nil, nil)
6870
if err != nil {
@@ -243,6 +245,29 @@ func TestSimpleConnect(t *testing.T) {
243245
}
244246
}
245247

248+
func TestBlockingCommand(t *testing.T) {
249+
addLineSSHProxyConf("blocking_command: /bin/true")
250+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
251+
defer cancel()
252+
args, cmd := prepareCommand("gateway1", 2023, "hostname")
253+
_, stdout, stderr, err := runCommand(ctx, "ssh", args, nil, nil)
254+
stdoutStr := strings.TrimSpace(string(stdout))
255+
if err != nil {
256+
t.Errorf("%s unexpected error: %v | stderr = %s", cmd, err, string(stderr))
257+
} else if stdoutStr != "server1" {
258+
t.Errorf("%s hostname = %s, want server1", cmd, stdoutStr)
259+
}
260+
updateLineSSHProxyConf("blocking_command", "/bin/false")
261+
defer removeLineSSHProxyConf("blocking_command: /bin/false")
262+
args, _ = prepareCommand("gateway1", 2023, "hostname")
263+
rc, _, _, err := runCommand(ctx, "ssh", args, nil, nil)
264+
if err == nil {
265+
t.Errorf("got no error, expected error due to blocking_command")
266+
} else if rc != 1 {
267+
t.Errorf("blocking_command rc = %d, want 1", rc)
268+
}
269+
}
270+
246271
func TestNodesets(t *testing.T) {
247272
disableHost("server[1000-1002]")
248273
checkHostState(t, "server1000:22", "disabled", true)
@@ -382,12 +407,12 @@ func TestEtcdConnections(t *testing.T) {
382407
}
383408

384409
func TestWithoutEtcd(t *testing.T) {
385-
updateLineSSHProxyConf("endpoints", "[\"https:\\/\\/void:2379\"]")
410+
updateLineSSHProxyConf("endpoints", "[\"https://void:2379\"]")
386411
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
387412
defer cancel()
388413
args, cmdStr := prepareCommand("gateway1", 2023, "hostname")
389414
_, stdout, _, err := runCommand(ctx, "ssh", args, nil, nil)
390-
updateLineSSHProxyConf("endpoints", "[\"https:\\/\\/etcd:2379\"]")
415+
updateLineSSHProxyConf("endpoints", "[\"https://etcd:2379\"]")
391416
if err != nil {
392417
log.Fatal(err)
393418
}
@@ -396,10 +421,10 @@ func TestWithoutEtcd(t *testing.T) {
396421
t.Errorf("%s got %s, expected server1", cmdStr, dest)
397422
}
398423

399-
updateLineSSHProxyConf("endpoints", "[\"https:\\/\\/void:2379\"]")
424+
updateLineSSHProxyConf("endpoints", "[\"https://void:2379\"]")
400425
updateLineSSHProxyConf("mandatory", "true")
401426
_, _, _, err = runCommand(ctx, "ssh", args, nil, nil)
402-
updateLineSSHProxyConf("endpoints", "[\"https:\\/\\/etcd:2379\"]")
427+
updateLineSSHProxyConf("endpoints", "[\"https://etcd:2379\"]")
403428
updateLineSSHProxyConf("mandatory", "false")
404429
if err == nil {
405430
t.Error("the connection should have been rejected")

0 commit comments

Comments
 (0)