├── .gitignore ├── .gitlab-ci.yml ├── Dockerfile ├── LICENSE ├── README.md ├── bastion ├── docker-compose.yml └── docs └── bastion_host.png /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | !.gitlab-ci.yml 4 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: docker:latest 2 | 3 | services: 4 | - docker:dind 5 | 6 | stages: 7 | - build 8 | 9 | build-dev: 10 | stage: build 11 | script: 12 | - docker info 13 | - docker build --pull -t "$CI_REGISTRY/$CI_PROJECT_NAME:dev" . 14 | - docker push "$CI_REGISTRY/$CI_PROJECT_NAME:dev" 15 | only: 16 | - dev 17 | tags: 18 | - builder 19 | 20 | build-master: 21 | stage: build 22 | script: 23 | - docker info 24 | - docker build --pull -t "$CI_REGISTRY/$CI_PROJECT_NAME:latest" . 25 | - docker push "$CI_REGISTRY/$CI_PROJECT_NAME:latest" 26 | only: 27 | - master 28 | tags: 29 | - builder 30 | 31 | build-tag: 32 | stage: build 33 | script: 34 | - IMAGE_TAG=${CI_COMMIT_TAG#v} 35 | - docker info 36 | - docker build --pull -t "$CI_REGISTRY/$CI_PROJECT_NAME:$IMAGE_TAG" . 37 | - docker push "$CI_REGISTRY/$CI_PROJECT_NAME:$IMAGE_TAG" 38 | only: 39 | - /^v(\d+\.)?(\d+\.)?(\*|\d+)$/ 40 | tags: 41 | - builder 42 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.11.6 2 | 3 | LABEL maintainer="Mark " 4 | 5 | ARG HOME=/var/lib/bastion 6 | 7 | ARG USER=bastion 8 | ARG GROUP=bastion 9 | ARG UID=4096 10 | ARG GID=4096 11 | 12 | ENV HOST_KEYS_PATH_PREFIX="/usr" 13 | ENV HOST_KEYS_PATH="${HOST_KEYS_PATH_PREFIX}/etc/ssh" 14 | 15 | COPY bastion /usr/sbin/bastion 16 | 17 | RUN addgroup -S -g ${GID} ${GROUP} \ 18 | && adduser -D -h ${HOME} -s /bin/ash -g "${USER} service" \ 19 | -u ${UID} -G ${GROUP} ${USER} \ 20 | && sed -i "s/${USER}:!/${USER}:*/g" /etc/shadow \ 21 | && set -x \ 22 | && apk add --no-cache openssh-server \ 23 | && echo "Welcome to Bastion!" > /etc/motd \ 24 | && chmod +x /usr/sbin/bastion \ 25 | && mkdir -p ${HOST_KEYS_PATH} \ 26 | && mkdir /etc/ssh/auth_principals \ 27 | && echo "bastion" > /etc/ssh/auth_principals/bastion 28 | 29 | EXPOSE 22/tcp 30 | 31 | VOLUME ${HOST_KEYS_PATH} 32 | 33 | ENTRYPOINT ["bastion"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mark 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bastion — jump host (gate) based on OpenSSH Server (sshd) 2 | 3 | > A [bastion host](https://en.wikipedia.org/wiki/Bastion_host) is a 4 | special purpose computer on a network specifically designed and 5 | configured to withstand attacks. The computer generallyhosts a single 6 | application, for example a proxy server, and all otherservices are 7 | removed or limited to reduce the threat to the computer. It is hardened 8 | in this manner primarily due to its location and purpose,which is 9 | either on the outside of a firewall or in a demilitarized zone (`DMZ`) 10 | and usually involves access from untrusted networks orcomputers. 11 | 12 | --- 13 | 14 | ![AWS Bastion](docs/bastion_host.png) 15 | 16 | ## Useful cases 17 | 18 | [Bastion](https://hub.docker.com/r/binlab/bastion) is an isolated 19 | `Docker` image that can work as a link between `Public` and `Private` 20 | network. It can be also useful for reverse `SSH` tunneling for a host 21 | behind a `NAT`. This image based on `Alpine Linux` last version. 22 | 23 | ## Usage 24 | 25 | ### Describing ENV variables 26 | 27 | * `PUBKEY_AUTHENTICATION [true | false]` - Specifies whether public key authentication is allowed. The default is `true`. Note that this option applies to protocol version 2 only. 28 | 29 | * `AUTHORIZED_KEYS [/relative/or/not/path/to/file]` - Specifies the file that contains the public keys that can be used for user authentication. `AUTHORIZED_KEYS` may contain tokens of the form `%T` which are substituted during connection setup. The following tokens are defined: `%%` is replaced by a literal `%`, `%h` is replaced by the home directory of the user being authenticated, and `%u` is replaced by the username of that user. After expansion, `AUTHORIZED_KEYS` is taken to be an absolute path or one relative to the user's home directory. The default file is `authorized_keys` and the default home directory is `/var/lib/bastion` and should be present by Docker volume mount by `-v $PWD/authorized_keys:/var/lib/bastion/authorized_keys:ro`. 30 | 31 | * `TRUSTED_USER_CA_KEYS [/full/path/to/file]` - Specifies a file containing public keys of certificate authorities that are trusted to sign user certificates for authentication, or none to not use one. Keys are listed one per line; empty lines and comments starting with `#` are allowed. If a certificate is presented for authentication and has its signing CA key listed in this file, then it may be used for authentication for any user listed in the certificate's principals list. Note that certificates that lack a list of principals will not be permitted for authentication using `TRUSTED_USER_CA_KEYS`. Directive `AuthorizedPrincipalsFile` hardcoded to `/etc/ssh/auth_principals/%u` and in time of build and generated one principals file for presented user - `/etc/ssh/auth_principals/bastion` with the one row `bastion`, and this principal should be listed in the certificate's principals list. 32 | 33 | * `GATEWAY_PORTS [true | false]` - Specifies whether remote hosts are allowed to connect to ports forwarded for the client. By default, `sshd` binds remote port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. `GATEWAY_PORTS` can be used to specify that `sshd` should allow remote port forwardings to bind to non-loopback addresses, thus allowing other hosts to connect. The argument may be `false` to force remote port forwardings to be available to the local host only, `true` to force remote port forwardings to bind to the wildcard address. The default is `false`. 34 | 35 | * `PERMIT_TUNNEL [true | false]` - Specifies whether `tun` device forwarding is allowed. The argument must be `true` or `false`. Specifying `true` permits both `point-to-point` (layer 3) and `ethernet` (layer 2). The default is `false`. 36 | 37 | * `X11_FORWARDING [true | false]` - Specifies whether `X11` forwarding is permitted. The argument must be `true` or `false`. The default is `false`. 38 | 39 | * `TCP_FORWARDING [true | false]` - Specifies whether `TCP` forwarding is permitted. The default is `true`. Note that disabling `TCP` forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. 40 | 41 | * `AGENT_FORWARDING [true | false]` - Specifies whether `ssh-agent` forwarding is permitted. The default is `true`. Note that disabling agent forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. 42 | 43 | * `LISTEN_ADDRESS [0.0.0.0]` - Specifies the local addresses should listen on. By default it **0.0.0.0**. Useful when Docker container runs in `Host mode` 44 | 45 | * `LISTEN_PORT [22]` - Specifies the port number that listens on. The default is **22**. Useful when Docker container runs in `Host mode` 46 | 47 | ### Run Bastion and `expose` port `22222` to outside a host machine 48 | 49 | The container assumes your `authorized_keys` file with `644` permissions and mounted under `/var/lib/bastion/authorized_keys`. 50 | 51 | Docker example: 52 | 53 | ```shell 54 | $ docker volume create bastion 55 | $ docker run -d \ 56 | --name bastion \ 57 | --hostname bastion \ 58 | --restart unless-stopped \ 59 | -v $PWD/authorized_keys:/var/lib/bastion/authorized_keys:ro \ 60 | -v bastion:/usr/etc/ssh:rw \ 61 | --add-host docker-host:172.17.0.1 \ 62 | -p 22222:22/tcp \ 63 | -e "PUBKEY_AUTHENTICATION=true" \ 64 | -e "GATEWAY_PORTS=false" \ 65 | -e "PERMIT_TUNNEL=false" \ 66 | -e "X11_FORWARDING=false" \ 67 | -e "TCP_FORWARDING=true" \ 68 | -e "AGENT_FORWARDING=true" \ 69 | binlab/bastion 70 | ``` 71 | 72 | Docker-compose example: 73 | 74 | ```yaml 75 | version: "3.6" 76 | services: 77 | bastion: 78 | image: binlab/bastion 79 | container_name: bastion 80 | hostname: bastion 81 | restart: unless-stopped 82 | expose: 83 | - 22/tcp 84 | ports: 85 | - 22222:22/tcp 86 | environment: 87 | PUBKEY_AUTHENTICATION: "true" 88 | GATEWAY_PORTS: "false" 89 | PERMIT_TUNNEL: "false" 90 | X11_FORWARDING: "false" 91 | TCP_FORWARDING: "true" 92 | AGENT_FORWARDING: "true" 93 | volumes: 94 | - $PWD/authorized_keys:/var/lib/bastion/authorized_keys:ro 95 | - bastion:/usr/etc/ssh:rw 96 | extra_hosts: 97 | - docker-host:172.17.0.1 98 | networks: 99 | - bastion 100 | 101 | networks: 102 | bastion: 103 | driver: bridge 104 | 105 | volumes: 106 | bastion: 107 | ``` 108 | 109 | _* When you are run `Bastion` container first time it generates `dsa`, `ecdsa`, `ed25519` and `rsa` key pair and saves them in permanent volume `bastion`, When you need to regenerate key pair, you should remove volume `bastion`._ 110 | 111 | ### 1. Connect to `Bastion` 112 | 113 | --- 114 | 115 | * Add your user to group `docker` to have possibility run `docker-compose` and `docker` from your user without `sudo`. After you should re-login or open a new terminal window. 116 | 117 | ```shell 118 | $ sudo usermod -aG docker 119 | ``` 120 | 121 | * Create custom work dir e.g. `docker`, enter to it and clone repository 122 | 123 | ```shell 124 | $ mkdir $HOME/docker 125 | $ cd $HOME/docker 126 | $ git clone https://github.com/binlab/docker-bastion.git 127 | $ cd docker-bastion 128 | ``` 129 | 130 | * Generate `rsa` pair (if you have one, skip this) 131 | 132 | ```shell 133 | $ ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f $HOME/.ssh/id_rsa 134 | ``` 135 | 136 | * Add `rsa` public key to `.bastion_keys` file 137 | 138 | ```shell 139 | $ cat $HOME/.ssh/id_rsa.pub > $PWD/.bastion_keys 140 | ``` 141 | 142 | * Run [`docker-compose.yml`](docker-compose.yml) configuration - `bastion` & `docker-ssh` 143 | 144 | ```shell 145 | $ docker-compose up 146 | ``` 147 | 148 | * And then you are can connect to it (in another terminal window) 149 | 150 | ```shell 151 | $ ssh -i $HOME/.ssh/id_rsa -p 22222 bastion@127.0.0.1 152 | ``` 153 | 154 | * You should see like this: 155 | 156 | ```shell 157 | user@localhost:~$ ssh -p 22222 bastion@127.0.0.1 158 | The authenticity of host '[127.0.0.1]:22222 ([127.0.0.1]:22222)' can't be established. 159 | ECDSA key fingerprint is 160 | SHA256:******************************************** 161 | ECDSA key fingerprint is MD5:**:**:**:**:**:**:**:**:**:**:**:**:**:**:**:**. 162 | Are you sure you want to continue connecting (yes/no)? yes 163 | Warning: Permanently added '[127.0.0.1]:22222' (ECDSA) to the list of known hosts. 164 | Welcome to Alpine! 165 | 166 | The Alpine Wiki contains a large amount of how-to guides and general 167 | information about administrating Alpine systems. 168 | See . 169 | 170 | You can setup the system with the command: setup-alpine 171 | 172 | You may change this message by editing /etc/motd. 173 | 174 | bastion:~$ 175 | ``` 176 | 177 | ### 2. Connect to `Host` through `Bastion` 178 | 179 | --- 180 | 181 | To achieve this you should add your private key to `SSH` agent and turn on `ForwardAgent` in `~/.ssh/config` or from a command line via flag `-A` 182 | 183 | > -A option enables forwarding of the authentication agent connection. 184 | > 185 | > It means that, it forwards your SSH auth schema to the remote host. > So you can use SSH over there as if you were on your local machine. 186 | 187 | * Add private key to `SSH` agent 188 | 189 | ```shell 190 | $ ssh-add $HOME/.ssh/id_rsa 191 | ``` 192 | 193 | * Test `Bastion` bridge in action 194 | 195 | ```shell 196 | $ ssh -A -J bastion@127.0.0.1:22222 @docker-host 197 | ``` 198 | 199 | ### 3. Connect to another container with `SSH` through `Bastion` 200 | 201 | --- 202 | 203 | ```shell 204 | $ ssh -A -J bastion@127.0.0.1:22222 bastion@docker-ssh 205 | ``` 206 | -------------------------------------------------------------------------------- /bastion: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | HOST_KEYS_PATH_PREFIX="${HOST_KEYS_PATH_PREFIX:='/'}" 4 | HOST_KEYS_PATH="${HOST_KEYS_PATH:='/etc/ssh'}" 5 | 6 | if [ "$PUBKEY_AUTHENTICATION" == "false" ]; then 7 | CONFIG_PUBKEY_AUTHENTICATION="-o PubkeyAuthentication=no" 8 | else 9 | CONFIG_PUBKEY_AUTHENTICATION="-o PubkeyAuthentication=yes" 10 | fi 11 | 12 | if [ -n "$AUTHORIZED_KEYS" ]; then 13 | CONFIG_AUTHORIZED_KEYS="-o AuthorizedKeysFile=$AUTHORIZED_KEYS" 14 | else 15 | CONFIG_AUTHORIZED_KEYS="-o AuthorizedKeysFile=authorized_keys" 16 | fi 17 | 18 | if [ -n "$TRUSTED_USER_CA_KEYS" ]; then 19 | CONFIG_TRUSTED_USER_CA_KEYS="-o TrustedUserCAKeys=$TRUSTED_USER_CA_KEYS" 20 | CONFIG_AUTHORIZED_PRINCIPALS_FILE="-o AuthorizedPrincipalsFile=/etc/ssh/auth_principals/%u" 21 | fi 22 | 23 | if [ "$GATEWAY_PORTS" == "true" ]; then 24 | CONFIG_GATEWAY_PORTS="-o GatewayPorts=yes" 25 | else 26 | CONFIG_GATEWAY_PORTS="-o GatewayPorts=no" 27 | fi 28 | 29 | if [ "$PERMIT_TUNNEL" == "true" ]; then 30 | CONFIG_PERMIT_TUNNEL="-o PermitTunnel=yes" 31 | else 32 | CONFIG_PERMIT_TUNNEL="-o PermitTunnel=no" 33 | fi 34 | 35 | if [ "$X11_FORWARDING" == "true" ]; then 36 | CONFIG_X11_FORWARDING="-o X11Forwarding=yes" 37 | else 38 | CONFIG_X11_FORWARDING="-o X11Forwarding=no" 39 | fi 40 | 41 | if [ "$TCP_FORWARDING" == "false" ]; then 42 | CONFIG_TCP_FORWARDING="-o AllowTcpForwarding=no" 43 | else 44 | CONFIG_TCP_FORWARDING="-o AllowTcpForwarding=yes" 45 | fi 46 | 47 | if [ "$AGENT_FORWARDING" == "false" ]; then 48 | CONFIG_AGENT_FORWARDING="-o AllowAgentForwarding=no" 49 | else 50 | CONFIG_AGENT_FORWARDING="-o AllowAgentForwarding=yes" 51 | fi 52 | 53 | if [ ! -f "$HOST_KEYS_PATH/ssh_host_rsa_key" ]; then 54 | /usr/bin/ssh-keygen -A -f "$HOST_KEYS_PATH_PREFIX" 55 | fi 56 | 57 | if [ -n "$LISTEN_ADDRESS" ]; then 58 | CONFIG_LISTEN_ADDRESS="-o ListenAddress=$LISTEN_ADDRESS" 59 | else 60 | CONFIG_LISTEN_ADDRESS="-o ListenAddress=0.0.0.0" 61 | fi 62 | 63 | if [ -n "$LISTEN_PORT" ]; then 64 | CONFIG_LISTEN_PORT="-o Port=$LISTEN_PORT" 65 | else 66 | CONFIG_LISTEN_PORT="-o Port=22" 67 | fi 68 | 69 | /usr/sbin/sshd -D -e -4 \ 70 | -o "HostKey=$HOST_KEYS_PATH/ssh_host_rsa_key" \ 71 | -o "HostKey=$HOST_KEYS_PATH/ssh_host_dsa_key" \ 72 | -o "HostKey=$HOST_KEYS_PATH/ssh_host_ecdsa_key" \ 73 | -o "HostKey=$HOST_KEYS_PATH/ssh_host_ed25519_key" \ 74 | -o "PasswordAuthentication=no" \ 75 | -o "PermitEmptyPasswords=no" \ 76 | -o "PermitRootLogin=no" \ 77 | $CONFIG_PUBKEY_AUTHENTICATION \ 78 | $CONFIG_AUTHORIZED_KEYS \ 79 | $CONFIG_GATEWAY_PORTS \ 80 | $CONFIG_PERMIT_TUNNEL \ 81 | $CONFIG_X11_FORWARDING \ 82 | $CONFIG_AGENT_FORWARDING \ 83 | $CONFIG_TCP_FORWARDING \ 84 | $CONFIG_TRUSTED_USER_CA_KEYS \ 85 | $CONFIG_AUTHORIZED_PRINCIPALS_FILE \ 86 | $CONFIG_LISTEN_ADDRESS \ 87 | $CONFIG_LISTEN_PORT 88 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.6" 2 | services: 3 | bastion: 4 | image: binlab/bastion 5 | container_name: bastion 6 | hostname: bastion 7 | restart: unless-stopped 8 | expose: 9 | - 22/tcp 10 | ports: 11 | - 22222:22/tcp 12 | volumes: 13 | - $PWD/authorized_keys:/var/lib/bastion/authorized_keys:ro 14 | - bastion:/usr/etc/ssh:rw 15 | extra_hosts: 16 | - docker-host:172.17.0.1 17 | networks: 18 | - bastion 19 | 20 | docker-ssh: 21 | image: binlab/bastion 22 | container_name: docker-ssh 23 | hostname: docker-ssh 24 | restart: unless-stopped 25 | expose: 26 | - 22/tcp 27 | volumes: 28 | - $PWD/authorized_keys:/var/lib/bastion/authorized_keys:ro 29 | - docker-ssh:/usr/etc/ssh:rw 30 | networks: 31 | - bastion 32 | 33 | networks: 34 | bastion: 35 | driver: bridge 36 | 37 | volumes: 38 | bastion: 39 | docker-ssh: 40 | -------------------------------------------------------------------------------- /docs/bastion_host.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binlab/docker-bastion/b9855473ff083ae395a2aa2bb14bf3950c2c3799/docs/bastion_host.png --------------------------------------------------------------------------------