Skip to content

Commit 7ca0082

Browse files
committed
Add possibility to set environment variables for specific services
Add possibility of persistent connections
1 parent 28e1372 commit 7ca0082

File tree

13 files changed

+415
-41
lines changed

13 files changed

+415
-41
lines changed

.cirrus.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ task:
55
VERSION: 1.13
66
VERSION: 1.14
77
VERSION: 1.15
8+
VERSION: 1.16
89

910
container:
1011
image: golang:$VERSION

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ bashcompdir ?= /etc/bash_completion.d
1111
GO ?= go
1212

1313
ASCIIDOC_OPTS = -asshproxy_version=$(SSHPROXY_VERSION)
14-
GO_OPTS = $(GO_OPTS_EXTRA) -ldflags "-X main.SshproxyVersion=$(SSHPROXY_VERSION)"
14+
GO_OPTS = $(GO_OPTS_EXTRA) -mod=vendor -ldflags "-X main.SshproxyVersion=$(SSHPROXY_VERSION)"
1515

1616
SSHPROXY_SRC = $(wildcard cmd/sshproxy/*.go)
1717
SSHPROXY_DUMPD_SRC = $(wildcard cmd/sshproxy-dumpd/*.go)

cmd/sshproxy/sshproxy.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,23 +93,24 @@ func (c *etcdChecker) doCheck(hostport string) utils.State {
9393
// findDestination finds a reachable destination for the sshd server according
9494
// to the etcd database if available or the routes and route_select algorithm.
9595
// It returns a string with the service name, a string with host:port, a string
96-
// containing ForceCommand value and a bool containing CommandMustMatch; a
96+
// containing ForceCommand value, a bool containing CommandMustMatch, an int64
97+
// with etcd_keyttl and a map of strings with the environment variables; a
9798
// string with the service name and an empty string if no destination is found
9899
// or an error if any.
99-
func findDestination(cli *utils.Client, username string, routes map[string]*utils.RouteConfig, sshdHostport string, checkInterval utils.Duration) (string, string, string, bool, error) {
100+
func findDestination(cli *utils.Client, username string, routes map[string]*utils.RouteConfig, sshdHostport string, checkInterval utils.Duration) (string, string, string, bool, int64, map[string]string, error) {
100101
checker := &etcdChecker{
101102
checkInterval: checkInterval,
102103
cli: cli,
103104
}
104105

105106
service, err := findService(routes, sshdHostport)
106107
if err != nil {
107-
return "", "", "", false, err
108+
return "", "", "", false, 0, nil, err
108109
}
109110
key := fmt.Sprintf("%s@%s", username, service)
110111

111112
if routes[service].Mode == "sticky" && cli != nil && cli.IsAlive() {
112-
dest, err := cli.GetDestination(key)
113+
dest, err := cli.GetDestination(key, routes[service].EtcdKeyTTL)
113114
if err != nil {
114115
if err != utils.ErrKeyNotFound {
115116
log.Errorf("problem with etcd: %v", err)
@@ -118,7 +119,7 @@ func findDestination(cli *utils.Client, username string, routes map[string]*util
118119
if utils.IsDestinationInRoutes(dest, routes[service].Dest) {
119120
if checker.Check(dest) {
120121
log.Debugf("found destination in etcd: %s", dest)
121-
return service, dest, routes[service].ForceCommand, routes[service].CommandMustMatch, nil
122+
return service, dest, routes[service].ForceCommand, routes[service].CommandMustMatch, routes[service].EtcdKeyTTL, routes[service].Environment, nil
122123
}
123124
log.Infof("cannot connect %s to already existing connection(s) to %s: host %s", key, dest, checker.LastState)
124125
} else {
@@ -129,10 +130,10 @@ func findDestination(cli *utils.Client, username string, routes map[string]*util
129130

130131
if len(routes[service].Dest) > 0 {
131132
selected, err := utils.SelectRoute(routes[service].RouteSelect, routes[service].Dest, checker, cli, key)
132-
return service, selected, routes[service].ForceCommand, routes[service].CommandMustMatch, err
133+
return service, selected, routes[service].ForceCommand, routes[service].CommandMustMatch, routes[service].EtcdKeyTTL, routes[service].Environment, err
133134
}
134135

135-
return service, "", "", false, fmt.Errorf("no destination set for service %s", service)
136+
return service, "", "", false, 0, nil, fmt.Errorf("no destination set for service %s", service)
136137
}
137138

138139
// findService finds the first service containing a suitable source in the conf,
@@ -326,7 +327,7 @@ func mainExitCode() int {
326327
log.Errorf("Cannot contact etcd cluster to update state: %v", err)
327328
}
328329

329-
service, hostport, forceCommand, commandMustMatch, err := findDestination(cli, username, config.Routes, sshInfos.Dst(), config.CheckInterval)
330+
service, hostport, forceCommand, commandMustMatch, etcdKeyTTL, environment, err := findDestination(cli, username, config.Routes, sshInfos.Dst(), config.CheckInterval)
330331
switch {
331332
case err != nil:
332333
log.Fatalf("Finding destination: %s", err)
@@ -345,6 +346,8 @@ func mainExitCode() int {
345346
log.Fatalf("Invalid destination '%s': %s", hostport, err)
346347
}
347348

349+
setEnvironment(environment)
350+
348351
// waitgroup and channel to stop our background command when exiting.
349352
var wg sync.WaitGroup
350353
ctx, cancel := context.WithCancel(context.Background())
@@ -366,7 +369,7 @@ func mainExitCode() int {
366369
// Register destination in etcd and keep it alive while running.
367370
if cli != nil && cli.IsAlive() {
368371
key := fmt.Sprintf("%s@%s", username, service)
369-
keepAliveChan, eP, err := cli.SetDestination(ctx, key, sshInfos.Dst(), hostport)
372+
keepAliveChan, eP, err := cli.SetDestination(ctx, key, sshInfos.Dst(), hostport, etcdKeyTTL)
370373
etcdPath = eP
371374
if err != nil {
372375
log.Warningf("setting destination in etcd: %v", err)

config/sshproxy.yaml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,16 @@
129129
# the selection is random. For "bandwidth", it's the same as "connections", but
130130
# based on the bandwidth used, with a rollback on connections (which is
131131
# frequent for new simultaneous connections). The mode value defines the
132-
# stickiness of a connection. It can be "sticky" or "balanced". If "sticky",
133-
# then all connections of a user will be made on the same destination host. If
134-
# "balanced", the route_select algorithm will be used for every connection.
135-
# Finally, the force_command can be set to override the command asked by the
136-
# user. If command_must_match is set to true, then the connection is closed if
137-
# the original command is not the same as the force_command. command_must_match
138-
# defaults to false.
132+
# stickiness of a connection. It can be "sticky" or "balanced" (defaults to
133+
# sticky). If "sticky", then all connections of a user will be made on the same
134+
# destination host. If "balanced", the route_select algorithm will be used for
135+
# every connection. Finally, the force_command can be set to override the
136+
# command asked by the user. If command_must_match is set to true, then the
137+
# connection is closed if the original command is not the same as the
138+
# force_command. command_must_match defaults to false. etcd_keyttl defauts to
139+
# 0. If a value is set (in seconds), the chosen backen will be remembered for
140+
# this amount of time. Environment variables can be set if needed. The '{user}'
141+
# pattern will be replaced with the user login.
139142
#routes:
140143
# service1:
141144
# source: ["192.168.0.1"]
@@ -147,6 +150,9 @@
147150
# mode: balanced
148151
# force_command: "internal-sftp"
149152
# command_must_match: true
153+
# etcd_keyttl: 3600
154+
# environment:
155+
# XAUTHORITY: /dev/shm/.Xauthority_{user}
150156
# default:
151157
# dest: ["host5:4222"]
152158

doc/sshproxy.yaml.txt

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,14 @@ executed by the ssh forked by sshproxy. *translate_commands* is an associative
102102
array which keys are strings containing the exact user command. The value is
103103
an associative array containing:
104104

105-
*ssh_args*::
106-
an optional list of options that will be passed to ssh.
105+
An associative array *ssh* specifies the SSH options:
106+
107+
*exe*::
108+
path or command to use for the SSH client ('ssh' by default).
107109

108-
*command*::
109-
a mandatory string, the actual executed command.
110+
*args*::
111+
a list of arguments for the SSH client. Its default value is: '["-q",
112+
"-Y"]'.
110113

111114
*disable_dump*::
112115
false by default. If true, no dumps will be done for this command.
@@ -178,6 +181,9 @@ listening IP address of the SSH daemon:
178181
mode: balanced
179182
force_command: "internal-sftp"
180183
command_must_match: true
184+
etcd_keyttl: 3600
185+
environment:
186+
XAUTHORITY: /dev/shm/.Xauthority_{user}
181187
default:
182188
dest: ["host5:4222"]
183189

@@ -195,28 +201,23 @@ priority, then the hosts with less global connections, and in case of a draw,
195201
the selection is random. For 'bandwidth', it's the same as 'connections', but
196202
based on the bandwidth used, with a rollback on connections (which is
197203
frequent for new simultaneous connections). The mode value defines the
198-
stickiness of a connection. It can be 'sticky' or 'balanced'. If 'sticky',
199-
then all connections of a user will be made on the same destination host. If
200-
'balanced', the route_select algorithm will be used for every connections.
201-
Finally, the force_command can be set to override the command asked by the
202-
user. If command_must_match is set to true, then the connection is closed if
203-
the original command is not the same as the force_command. command_must_match
204-
defaults to false.
204+
stickiness of a connection. It can be 'sticky' or 'balanced' (defaults to
205+
'sticky'). If 'sticky', then all connections of a user will be made on the
206+
same destination host. If 'balanced', the route_select algorithm will be used
207+
for every connections. Finally, the force_command can be set to override the
208+
command asked by the user. If command_must_match is set to true, then the
209+
connection is closed if the original command is not the same as the
210+
force_command. command_must_match defaults to false. etcd_keyttl defauts to 0.
211+
If a value is set (in seconds), the chosen backen will be remembered for this
212+
amount of time. An associative array *environment* can be used to set
213+
environment variables. The pattern '\{user}' will be replaced with the user
214+
login.
205215

206216
In the previous example, a client connected to '192.168.0.1' will be proxied
207217
to 'host1' and, if the host is not reachable, to 'host2'. If a client does not
208218
connect to '192.168.0.1' or '192.168.0.2' it will be proxied to the sshd
209219
daemon listening on port 4222 on 'host5'.
210220

211-
An associative array *ssh* specifies the SSH options:
212-
213-
*exe*::
214-
path or command to use for the SSH client ('ssh' by default).
215-
216-
*args*::
217-
a list of arguments for the SSH client. Its default value is: '["-q",
218-
"-Y"]'.
219-
220221
Each of the previous parameters can be overridden for a group thanks to the
221222
*groups* associative array.
222223

go.mod

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module github.com/cea-hpc/sshproxy
2+
3+
go 1.14
4+
5+
require (
6+
github.com/BurntSushi/toml v1.0.0 // indirect
7+
github.com/coreos/etcd v2.3.8+incompatible // indirect
8+
github.com/docker/docker v20.10.13+incompatible
9+
github.com/gogo/protobuf v1.3.2 // indirect
10+
github.com/golang/protobuf v1.5.2 // indirect
11+
github.com/kr/pty v1.1.8
12+
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
13+
github.com/olekukonko/tablewriter v0.0.5
14+
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
15+
go.etcd.io/etcd v2.3.8+incompatible
16+
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
17+
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
18+
golang.org/x/tools v0.1.10 // indirect
19+
gopkg.in/yaml.v2 v2.4.0
20+
honnef.co/go/tools v0.2.2 // indirect
21+
)

0 commit comments

Comments
 (0)