]> Dogcows Code - chaz/docker-connect/blob - docker-connect
93c5bac3ada0f9f5925a4993d785abeb53e75363
[chaz/docker-connect] / docker-connect
1 #!/bin/sh
2
3 : <<'=cut'
4 =pod
5
6 =head1 NAME
7
8 docker-connect - Easily connect to Docker sockets over SSH
9
10 =head1 VERSION
11
12 Version 0.80
13
14 =head1 SYNOPSIS
15
16 docker-connect HOSTNAME [SHELL_ARGS]...
17
18 # launch a new shell wherein docker commands go to staging-01.acme.tld
19 docker-connect staging-01.acme.tld
20
21 # list the docker processes running on staging-01.acme.tld
22 docker-connect staging-01.acme.tld -c 'docker ps'
23
24 =head1 DESCRIPTION
25
26 This script provides an alternative to Docker Machine for connecting your Docker client to a remote
27 Docker daemon. Instead of connecting directly to a Docker daemon listening on an external TCP port,
28 this script sets up a connection to the UNIX socket via SSH.
29
30 Why?
31
32 The main use case for this is when dealing with "permanent" app servers in an environment where you
33 have a team of individuals who all need access.
34
35 Machine doesn't have a great way to support multiple concurrent users. You can add an existing
36 machine to which you have SSH access using the generic driver on your computer, but if your
37 colleague does the same then Machine will regenerate the Docker daemon TLS certificates, replacing
38 the ones Machine set up for you.
39
40 Furthermore, the Docker daemon relies on TLS certificates for client authorization, which is all
41 fine and good, but organizations are typically not as prepared to deal with the management of client
42 TLS certificates as they are with the management of SSH keys. Worse, the Docker daemon doesn't
43 support certificate revocation lists! So if a colleague leaves, you must replace the certificate
44 authority and recreate and distribute certificates for each remaining member of the team. Ugh!
45
46 Much easier to just use SSH for authorization.
47
48 To be clear, this script isn't a full replacement for Docker Machine. For one thing, Machine has
49 a lot more features and can actually create machines. This script just assists with a particular
50 workflow that is currently underserved by Machine.
51
52 =head1 REQUIREMENTS
53
54 =over
55
56 =item * a Bourne-compatible, POSIX-compatible shell
57
58 This program is written in shell script.
59
60 =item * L<OpenSSH|https://www.openssh.com> 6.7+
61
62 Needed to make the socket connection.
63
64 =item * L<Docker|https://www.docker.com> client
65
66 Not technically required, but this program isn't useful without it.
67
68 =back
69
70 =head1 INSTALL
71
72 =for markdown [![Build Status](https://travis-ci.org/chazmcgarvey/docker-connect.svg?branch=master)](https://travis-ci.org/chazmcgarvey/docker-connect)
73
74 To install, just copy F<docker-connect> into your C<PATH> and make sure it is executable.
75
76 # Assuming you have "$HOME/bin" in your $PATH:
77 cp docker-connect ~/bin/
78 chmod +x ~/bin/docker-connect
79
80 =head1 ENVIRONMENT
81
82 The following environment variables may affect or will be set by this program:
83
84 =over
85
86 =item * C<DOCKER_CONNECT_SOCKET>
87
88 The absolute path to the local socket.
89
90 =item * C<DOCKER_CONNECT_HOSTNAME>
91
92 The hostname of the remote peer.
93
94 =item * C<DOCKER_CONNECT_PID>
95
96 The PID of the SSH process maintaining the connection.
97
98 =item * C<DOCKER_HOST>
99
100 The URI of the local socket.
101
102 =back
103
104 =head1 TIPS
105
106 If you run many shells and connections, having the hostname of the host that the Docker client is
107 connected to in your prompt may be handy. Try something like this in your local shell config file:
108
109 if [ -n "$DOCKER_CONNECT_HOSTNAME" ]
110 then
111 PS1="[docker:$DOCKER_CONNECT_HOSTNAME] $PS1"
112 fi
113
114 =head1 AUTHOR
115
116 Charles McGarvey <chazmcgarvey@brokenzipper.com>
117
118 =head1 LICENSE
119
120 This software is copyright (c) 2017 by Charles McGarvey.
121
122 This is free software, licensed under:
123
124 The MIT (X11) License
125
126 =cut
127
128 set -e
129
130 prog=$(basename "$0")
131 version="0.80"
132 quiet=0
133 socket="$DOCKER_CONNECT_SOCKET"
134 remote_socket=${REMOTE_SOCKET:-/run/docker.sock}
135 timeout=${TIMEOUT:-15}
136
137 usage() {
138 cat <<END
139 $prog [OPTIONS]... HOSTNAME [SHELL_ARGS]...
140 Easily connect to Docker sockets over SSH.
141
142 OPTIONS:
143 -h Show this help info and exit.
144 -q Be less verbose; can be repeated to enhance effect.
145 -r STR Specify the absolute path of the remote socket.
146 -s STR Specify the absolute path of the local socket.
147 -v Show the program version.
148 END
149 }
150
151 log() {
152 _l=$1
153 shift
154 if [ "$_l" -ge "$quiet" ]
155 then
156 echo >&2 "$prog: $@"
157 fi
158 }
159
160 while getopts "hqr:s:v" opt
161 do
162 case "$opt" in
163 q)
164 quiet=$(expr $quiet + 1)
165 ;;
166 s)
167 socket="$OPTARG"
168 ;;
169 r)
170 remote_socket="$OPTARG"
171 ;;
172 h)
173 usage
174 exit 0
175 ;;
176 v)
177 echo "docker-connect $version"
178 exit 0
179 ;;
180 *)
181 usage
182 exit 1
183 ;;
184 esac
185 done
186 shift $(expr $OPTIND - 1)
187
188 connect=$1
189 if [ -z "$connect" ]
190 then
191 echo >&2 "Missing HOSTNAME."
192 usage
193 exit 1
194 fi
195 shift
196
197 if [ -z "$socket" ]
198 then
199 socket_dir="${TMPDIR:-/tmp}/docker-connect-$(id -u)"
200 mkdir -p "$socket_dir"
201 chmod 0700 "$socket_dir"
202 socket="$socket_dir/docker-$$.sock"
203 fi
204
205 if [ -S "$socket" ]
206 then
207 if [ -n "$DOCKER_CONNECT_HOSTNAME" ]
208 then
209 log 2 "Docker is already connected to $DOCKER_CONNECT_HOSTNAME in this shell."
210 exit 2
211 else
212 log 2 "Docker socket already exists."
213 log 1 "To force a new connection, first remove the file: $socket"
214 exit 3
215 fi
216 elif [ -e "$socket" ]
217 then
218 log 2 "Cannot create socket because another file is in the way."
219 log 1 "To create a new connection, you may first remove the file: $socket"
220 exit 4
221 fi
222
223 hostname=
224 port=
225 user=
226
227 if echo "$connect" |grep -q ':'
228 then
229 hostname=$(echo "$connect" |cut -d: -f1)
230 port=$(echo "$connect" |cut -d: -f2)
231 else
232 hostname="$connect"
233 fi
234
235 if echo "$hostname" |grep -q '@'
236 then
237 user=$(echo "$hostname" |cut -d@ -f1)
238 hostname=$(echo "$hostname" |cut -d@ -f2)
239 fi
240
241 ssh_connect="$hostname"
242
243 if [ "$user" != "" ]
244 then
245 ssh_connect="$user@$ssh_connect"
246 fi
247
248 if [ "$port" != "" ]
249 then
250 ssh_connect="$ssh_connect -p$port"
251 fi
252
253 ${SSH:-ssh} $ssh_connect -L"$socket:$remote_socket" \
254 -oControlPath=none -oConnectTimeout="$timeout" -nNT &
255 ssh_pid=$!
256 ssh_connected=
257
258 handle_noconnect() {
259 log 2 "The connection could not be established."
260 log 1 "Please ensure that you can execute this command successfully:"
261 log 1 " ${SSH:-ssh} $ssh_connect -oControlPath=none echo OK"
262 exit 5
263 }
264
265 handle_disconnect() {
266 kill $ssh_pid 2>/dev/null || true
267 rm -f "$socket"
268 log 0 "Disconnected docker from $hostname."
269 }
270
271 # Wait for the socket connection to be made.
272 for i in $(seq 1 "${timeout}0")
273 do
274 if [ -S "$socket" ]
275 then
276 ssh_connected=1
277 break
278 fi
279 if ! kill -s 0 $ssh_pid 2>/dev/null
280 then
281 handle_noconnect
282 fi
283 sleep 0.1
284 done
285
286 if [ -z "$ssh_connected" ]
287 then
288 handle_noconnect
289 fi
290
291 trap handle_disconnect EXIT
292
293 export DOCKER_CONNECT_HOSTNAME="$hostname"
294 export DOCKER_CONNECT_PID="$ssh_pid"
295 export DOCKER_CONNECT_SOCKET="$socket"
296 export DOCKER_HOST="unix://$socket"
297
298 # Remove incompatible variables set by Docker Machine.
299 unset DOCKER_MACHINE_NAME
300 unset DOCKER_CERT_PATH
301 unset DOCKER_TLS_VERIFY
302
303 log 1 "Executing new shell with docker connected to $hostname."
304 log 0 "This connection will be terminated when the shell exits."
305 ${SHELL:-/bin/sh} "$@"
306
This page took 0.049335 seconds and 4 git commands to generate.