Ultimate Docker container with OpenVPN client and SSH daemon
GPL-3.0 License
Inspired by https://github.com/freeboson/openvpn-ssh-tunnel
Setup an OpenVPN connection to any VPN endpoint within a docker container, with an SSH daemon (OpenSSH) running. You can then create an SSH tunnel into your container that will route your traffic via the VPN, or set up your local SSH client to use container as a jump host to get to SSH services inside the VPN network (see Usage below). This is useful for having some but not all of your traffic to go through VPN.
The setup here will be like this:
(chacha20) (AES-128 CBC)
Docker
You SSH OpenVPN VPN
SSH VPN
*.ovpn
configuration fileauthorized_keys
file with your public key to authorize to SSH daemon inside the containervpn_configs
directory and put your OpenVPN client configuration file(s) there.docker-vpn
: docker build -t docker-vpn .
docker run -v $(pwd)/vpn_configs/:/vpn_configs -it --cap-add NET_ADMIN -p 22222:22 --env DVPN_CONFIG=my-vpn-config.ovpn docker-vpn
- this will grab your currentscreen(1)
or tmux(1)
. Alternatively,default
in the vpn_configs
directory and--env DVPN_CONFIG=my-vpn-config.ovpn
parameter.ssh -N -D 9000 tunnel@localhost -p 22222
(or use autossh)localhost:9000
tsocks
or similar for clients that do not support it~/.ssh/config
file:Host docker-vpn
Hostname 127.0.0.1
Port 22222
User tunnel
ServerAliveInterval 60
IdentityFile ~/.ssh/id_rsa # this should correspond to what you put in the authorized_keys file above
ForwardAgent yes # Useful if you authenticate to your SSH boxes inside VPN using SSH keys - see ssh-agent(1) for details
StrictHostKeyChecking no # It's container - key fingerprint changes after each rebuild, no point of dealing with it
UserKnownHostsFile /dev/null # Same as above option
Host *.secret-domain-behind-vpn.com
ProxyJump docker-vpn
ForwardAgent yes
Now all the ssh connections to *.secret-domain-behind-vpn.com
will be going through container.
If you have to authenticate against an SSH jumphost when connected to VPN - just
append it's name to ProxyJump
directive, like this:
ProxyJump docker-vpn,[email protected]
- SSH
will first authenticate to your container SSH, then to the jumphost SSH and then
finally - against your target SSH server you're trying to reach. This feature
might not work wit holder SSH clients, see
this link for details.
If you'd like to debug the container - supply --env DVPN_DEBUG=true
parameter
to docker run
- this will give you shell inside the container without
connecting to the VPN or starting the SSH daemon.
If you'd like to perform additional actions inside the container - create
post-run.sh
file in the vpn_configs
directory, make it executable and put
any commands you'd like to run there. The file can be a bash or Python script,
or even a compiled binary.
This bash snippet for example adds additional options to /etc/resolv.conf after the connection is made:
#!/bin/bash
DOMAIN=$(awk '/domain / {print $2}' /etc/resolv.conf)
echo "search ${DOMAIN}" >> /etc/resolv.conf
echo "options ndots:2" >> /etc/resolv.conf
Containers were designed with a concept "one process - one container" in mind;
as soon as what they call "the root process" dies - containers' life ends. In
this particular case, without shell a user would end up with "grabbed" terminal
anyway - with OpenVPN or SSH daemon running in foreground. "Detached" option
to docker run
cannot be used either - OpenVPN needs user input (to ask for
username and password). Supplying a file with credentials, or use some other
non-interactive way like env variables is not flexible, and might cause
inconveniences (especially in 2FA environments). I spawn a shell after OpenVPN
connection is made and SSH daemon started to keep the container running, and to
provide a user ability to, for example, ssh from inside container somewhere
else (though my personal preference is to use ProxyJump
ssh option, as
described above).
Having split containers (one for SSH, one for OpenVPN) makes no sense in this case - OpenVPN container will still grab the terminal due to the reasons described above.