Skip to content

Commit

Permalink
Merge branch 'WillFantom:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
mavi0 authored Jun 26, 2023
2 parents 70ff925 + 5c96d19 commit b6a79b5
Show file tree
Hide file tree
Showing 17 changed files with 199 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
cache-to: type=gha,mode=max
- name: Run Test Container
run: |
docker run --rm -d -e SSHD_PORT=2222 -p 2222:2222 --name jumpbox jumpbox:policy-test
docker run --rm -d -e SSHD_PORT=2222 -p 2222:2222 --name jumpbox jumpbox:policy-test && sleep 10
- name: Setup Python
uses: actions/setup-python@v4
with:
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/fail2ban.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Release Fail2Ban

on:
workflow_dispatch:
schedule:
- cron: "0 2 1 * *"
push:
tags:
- "v*"

jobs:
docker-release:
name: Fail2Ban Docker Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup QEMU
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Container Metadata
id: metadata
uses: docker/metadata-action@v4
with:
images: |
ghcr.io/${{ github.repository_owner }}/jumpbox-fail2ban
flavor: |
latest=false
prefix=
suffix=
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and Push Container
uses: docker/build-push-action@v4
with:
context: .
file: build/fail2ban.Dockerfile
push: true
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release
name: Release Jumpbox

on:
workflow_dispatch:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
example/*
!example/authorized_keys.json
!example/.env
!example/docker-compose.yml
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Whilst the objective of this project was to keep things simple (a large motivato

- **Policy**: To ensure the SSH server being deployed is hardened as desired, you can use [`ssh-audit`](https://github.com/jtesta/ssh-audit), a tool that can check if an SSH server meets a given configuration security policy. Included is a good starting policy for an SSH server using OpenSSH 9, ensuring all and only the recommended key types (Host, Kex, Macs) are supported.

- **Fail2Ban**: Writes logs to a file via a syslog server, in turn allowing [Fail2Ban](https://www.fail2ban.org/wiki/index.php/Main_Page) to block malicious connections. As Fail2Ban is expected to exist outside the jumpbox container (either on the host system or in a different container), the jumpbox itself needs no extra permissions to support this. See the example [docker-compose](./example/docker-compose.yml) for a way to set this up.

- **Actions**: The dockerized jumpbox here, along with [`watchtower`](https://containrrr.dev/watchtower/) can create an SSH server managed by GitHub Actions. Provided the SSH users and keys are [baked in](#full-usage), the Release workflow provided with this repository will:
- Validate the `authorized_keys` JSON file
- Ensure all provided users are valid usernames
Expand Down Expand Up @@ -108,7 +110,7 @@ This is perhaps the best way to use Jumpbox, especially if it is for an organiza

## Configuration

- **SSH Server**: Configure the SSH server by modifying the [`sshd_config`](sshd/sshd_config) file. Included is a sensible default for a Jumpbox host as of November 2022. To modify the internal port used for the SSH server, make sure to use the `SSHD_PORT` environment variable.
- **SSH Server**: Configure the SSH server by modifying the [`sshd_config`](sshd/sshd_config) file. Included is a sensible default for a Jumpbox host as of November 2022. To modify the internal port used for the SSH server, make sure to use the `SSHD_PORT` environment variable. Also, always ensure that the internal SSH server port is the same as the exposed port. Having a port mapping where the internal and external ports are different will break some features. See the example docker-compose file for more on how to do this easily.

- **Users & Keys**: Regardless of if you are using baked in keys or mounted, the format anc common issues are the same. See [here](./example/keys/README.md) for more.

Expand Down
20 changes: 14 additions & 6 deletions build/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
FROM alpine:3.17 as endlessh-builder
FROM alpine:3.18 as endlessh-builder

RUN apk add --no-cache build-base git
WORKDIR /src
ARG ENDLESSH_VERSION=1.1
RUN git clone -b ${ENDLESSH_VERSION} https://github.com/skeeto/endlessh .
RUN make

FROM alpine:3.17

RUN apk add --no-cache curl \
FROM alpine:3.18

RUN apk add --no-cache --progress --quiet \
bash \
curl \
gettext \
jq \
openssh-server \
shadow
rsyslog \
shadow \
tzdata

RUN sed -i '/imklog/s/^/#/' /etc/rsyslog.conf

COPY --from=endlessh-builder /src/endlessh /usr/local/bin/endlessh

Expand All @@ -27,7 +34,8 @@ COPY sshd/sshd_config /etc/ssh/sshd_config_template
COPY keys/authorized_keys.json /etc/ssh/keys.d/authorized_keys.json
RUN chown nobody:nogroup /etc/ssh/keys.d/authorized_keys.json

ENV SSHD_PORT 2222
ENV ENDLESSH_PORT 22
ENV TZ=Europe/London
ENV SSHD_PORT=2222
ENV ENDLESSH_PORT=22

ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ]
28 changes: 21 additions & 7 deletions build/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
#!/bin/ash
set -e
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi

echo "Setting timezone..."
TZ=${TZ:-UTC}
ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime
echo "${TZ}" > /etc/timezone

echo "Setting up host keys..."
hostkeys_dir=/etc/ssh/hostkeys.d
Expand All @@ -10,27 +20,31 @@ for t in ed25519 rsa-sha2-256 rsa-sha2-512; do
echo "Generating ${t} host key"
rm -f "${file_name}" "${file_name}.pub"
ssh-keygen -t $t -h -q -N "" -C "" -f "$file_name"
chmod 0600 "${file_name}"
fi
done

echo "Setting up users..."
keys_dir=/etc/ssh/keys.d
mkdir -p ${keys_dir}
echo "Creating users..."
jq -r 'keys_unsorted[]' ${keys_dir}/authorized_keys.json | while read u; do
jq -r 'keys_unsorted[]' ${keys_dir}/authorized_keys.json | while read -r u; do
echo "Creating user ${u}"
adduser -D -H -s /sbin/nologin "${u}"
sed -i s/${u}:!/"${u}:*"/g /etc/shadow
sed -i s/"${u}:!"/"${u}:*"/g /etc/shadow
done

echo "Creating sshd configuration..."
envsubst < /etc/ssh/sshd_config_template > /etc/ssh/sshd_config_envsubst
mv /etc/ssh/sshd_config_envsubst /etc/ssh/sshd_config

if [[ ${ENDLESSH_PORT} -ne "0" ]]; then
echo "Running syslog..."
rsyslogd

if [[ "${ENDLESSH_PORT}" -ne "0" ]]; then
echo "Running endlessh server..."
/usr/local/bin/endlessh -p ${ENDLESSH_PORT} -v &
/usr/local/bin/endlessh -p "${ENDLESSH_PORT}" -v &
fi

echo "Running SSH server..."
/usr/sbin/sshd -D -f /etc/ssh/sshd_config -e -p ${SSHD_PORT}
/usr/sbin/sshd -D -f /etc/ssh/sshd_config -p "${SSHD_PORT}"
24 changes: 24 additions & 0 deletions build/fail2ban.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM alpine:3.18

RUN apk add --no-cache --progress --quiet \
bash \
fail2ban \
ipset \
iptables \
ip6tables \
kmod \
nftables \
tzdata

RUN rm -r /etc/fail2ban/jail.d/* && \
rm -rf /etc/fail2ban/action.d/nftables-common.local
COPY fail2ban/jail.d/* /etc/fail2ban/jail.d/
COPY fail2ban/action.d/nftables-common.local /etc/fail2ban/action.d/nftables-common.local

COPY build/fail2ban.entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

ENV TZ=Europe/London
ENV SSHD_PORT=2222

ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ]
18 changes: 18 additions & 0 deletions build/fail2ban.entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi

echo "Setting timezone..."
TZ=${TZ:-UTC}
ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime
echo "${TZ}" > /etc/timezone

echo "Configuring fail2ban..."
sed -i "s/port = ssh/port = ${SSHD_PORT}/g" /etc/fail2ban/jail.d/sshd.conf

echo "Running fail2ban..."
fail2ban-server -x -v -f start
3 changes: 3 additions & 0 deletions example/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SSHD_PORT=2222
ENDLESSH_PORT=22
TZ=Europe/London
32 changes: 26 additions & 6 deletions example/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,46 @@ services:
jumpbox:
container_name: jumpbox
image: ghcr.io/willfantom/jumpbox:latest
restart: unless-stopped
ports:
- 22:22/tcp
- 2222:2222/tcp
- ${ENDLESSH_PORT}:${ENDLESSH_PORT}/tcp
- ${SSHD_PORT}:${SSHD_PORT}/tcp
volumes:
- ./hostkeys:/etc/ssh/hostkeys.d
- ./logs:/var/log:rw
# - ./keys:/etc/ssh/keys.d # Include to use mounted keys rather than the ones baked in
environment:
- SSHD_PORT=2222
- ENDLESSH_PORT=22
- TZ=${TZ}
- SSHD_PORT=${SSHD_PORT}
- ENDLESSH_PORT=${ENDLESSH_PORT}
labels:
- "com.centurylinklabs.watchtower.scope=sshjumpbox"
- "com.centurylinklabs.watchtower.enable=true"

# Required for auto-pulling updated images from github
watchtower:
container_name: watchtower
container_name: jumpbox_watchtower
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 30 --scope sshjumpbox
labels:
- "com.centurylinklabs.watchtower.scope=sshjumpbox"
- "com.centurylinklabs.watchtower.enable=false"

fail2ban:
container_name: jumpbox_fail2ban
image: ghcr.io/willfantom/jumpbox-fail2ban:latest
restart: unless-stopped
cap_add:
- NET_ADMIN
- NET_RAW
network_mode: host
volumes:
- ./logs:/var/log:rw
environment:
- TZ=${TZ}
- SSHD_PORT=${SSHD_PORT}
labels:
- "com.centurylinklabs.watchtower.scope=sshjumpbox"
- "com.centurylinklabs.watchtower.enable=true"
3 changes: 3 additions & 0 deletions fail2ban/action.d/nftables-common.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[Init]
table = f2b-table-docker
chain_hook = forward
2 changes: 2 additions & 0 deletions fail2ban/jail.d/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[DEFAULT]
banaction = nftables[type=multiport]
15 changes: 15 additions & 0 deletions fail2ban/jail.d/sshd.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[sshd]
enabled = true
filter = alpine-sshd
logpath = /var/log/messages
maxretry = 10
chain = DOCKER-USER
port = ssh

[sshd-ddos]
enabled = true
filter = alpine-sshd-ddos
logpath = /var/log/messages
maxretry = 10
chain = DOCKER-USER
port = ssh
10 changes: 8 additions & 2 deletions sshd/authorized_keys.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
#!/bin/ash
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi

# validate the username, sanitizing the lookup next
regex="^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$"
Expand All @@ -7,7 +13,7 @@ if [[ ! $1 =~ ${regex} ]]; then
fi

# find keys for given user
jq -r -c .${1}'[]' /etc/ssh/keys.d/authorized_keys.json | while read k; do
jq -r -c ."${1}"'[]' /etc/ssh/keys.d/authorized_keys.json | while read -r k; do
echo "$k"
exit 0
done
2 changes: 1 addition & 1 deletion sshd/banner
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ F aaa r qqq uuu aaa aaa ddd
------------------------------------


|> https://github.com/willfantom/jumpbox <|
|> https://github.com/willfantom/jumpbox <|
3 changes: 3 additions & 0 deletions sshd/sshd_config
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ ClientAliveCountMax 20
ClientAliveInterval 30
PrintMotd no
Banner /etc/ssh/banner

# Use Syslog
SyslogFacility AUTH

0 comments on commit b6a79b5

Please sign in to comment.