Skip to content

Commit 30cac43

Browse files
committed
Add ssh-container example image
Signed-off-by: Blake Devcich <blake.devcich@hpe.com>
1 parent 07160b0 commit 30cac43

7 files changed

Lines changed: 464 additions & 0 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: SSH Container build and push
2+
3+
on: [push]
4+
5+
env:
6+
# TEST_TARGET: Name of the testing target in the Dockerfile
7+
TEST_TARGET: testing
8+
9+
# DO_TEST - true to build and run unit tests, false to skip the tests
10+
DO_TEST: false
11+
12+
# DO_PUSH - true to push to the HPE_DEPLOY_REPO, false to not push
13+
DO_PUSH: true
14+
15+
# Container build arguments - tie these to Blake for testing
16+
USER_UID: 1060
17+
USER_GID: 100
18+
19+
# Image name for the SSH container
20+
IMAGE_NAME: ssh-container
21+
22+
jobs:
23+
build:
24+
runs-on: ubuntu-latest
25+
26+
steps:
27+
- uses: actions/checkout@v4
28+
with:
29+
fetch-depth: 0
30+
ref: ${{ github.event.pull_request.head.sha }}
31+
32+
- name: Lowercase repository name for docker build
33+
id: lowercase-repository-name
34+
run: |
35+
echo "REPO_NAME=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
36+
37+
- name: "Set tags for main/master"
38+
id: set_tags
39+
run: |
40+
echo "VERSION_TAG=$(./git-version-gen | grep -v UNKNOWN)" >> ${GITHUB_ENV}
41+
echo "TEST_TAG=$(git rev-parse HEAD)-test" >> ${GITHUB_ENV}
42+
echo "SHA_TAG=$(git rev-parse HEAD)" >> ${GITHUB_ENV}
43+
echo "${GITHUB_ENV}:"
44+
cat ${GITHUB_ENV}
45+
shell: bash
46+
47+
- name: "Docker metadata"
48+
id: meta
49+
uses: docker/metadata-action@v5
50+
with:
51+
images: |
52+
ghcr.io/${{ env.REPO_NAME }}-ssh-container
53+
tags: |
54+
# For merge to master branch, tag example: 'master'
55+
type=ref,event=branch
56+
# For PR event, tag example: 'pr-3'
57+
type=ref,event=pr
58+
# For PR event or merge event, tag example: 1.0.1.12-5667
59+
type=raw,value=${{ env.VERSION_TAG }}
60+
# For PR event or merge, tag example: 566769e04d2436cf5f42ae4f46092c7dff6e668e
61+
type=raw,value=${{ env.SHA_TAG }}
62+
# For push to semver tag, tag example: 1.0.2
63+
# This also sets 'latest'.
64+
type=semver,pattern={{version}}
65+
# For push to semver tag, tag example: 1.0
66+
type=semver,pattern={{major}}.{{minor}}
67+
68+
- name: Docker login
69+
uses: docker/login-action@v3
70+
with:
71+
registry: ghcr.io
72+
username: ${{ github.actor }}
73+
password: ${{ secrets.GITHUB_TOKEN }}
74+
75+
- name: Generate SSH keys for container build
76+
run: |
77+
cd ssh-container
78+
make ssh-keys client-keys
79+
80+
- name: Build the test Docker image
81+
if: ${{ env.DO_TEST == 'true' }}
82+
id: docker_build_test_target
83+
uses: docker/build-push-action@v6
84+
with:
85+
context: ./ssh-container
86+
push: false
87+
target: ${{ env.TEST_TARGET }}
88+
tags: ${{ env.IMAGE_NAME }}:${{ env.TEST_TAG }}
89+
build-args: |
90+
USER_UID=${{ env.USER_UID }}
91+
USER_GID=${{ env.USER_GID }}
92+
93+
- name: Run the Docker image unit tests
94+
if: ${{ env.DO_TEST == 'true' }}
95+
run: docker run ${{ env.IMAGE_NAME }}:${{ env.TEST_TAG }}
96+
97+
- name: Build the final Docker image
98+
id: docker_build
99+
uses: docker/build-push-action@v6
100+
with:
101+
context: ./ssh-container
102+
push: ${{ env.DO_PUSH == 'true' }}
103+
tags: ${{ steps.meta.outputs.tags }}
104+
build-args: |
105+
USER_UID=${{ env.USER_UID }}
106+
USER_GID=${{ env.USER_GID }}
107+
108+
- name: Peek at the docker images
109+
run: docker images | grep ${{ env.IMAGE_NAME }}
110+
111+
create_release:
112+
needs: build
113+
if: startsWith(github.ref, 'refs/tags/v')
114+
runs-on: ubuntu-latest
115+
steps:
116+
- name: Checkout
117+
uses: actions/checkout@v4
118+
with:
119+
fetch-tags: true
120+
fetch-depth: 0
121+
- name: Repair tag
122+
run: git fetch -f origin ${{ github.ref }}:${{ github.ref }}
123+
- name: Verify that the tag is annotated
124+
run: if test x$(git for-each-ref ${{ github.ref }} | awk '{print $2}') = xtag; then /bin/true; else echo "\"${{ github.ref }}\" does not look like an annotated tag!"; /bin/false; fi
125+
- name: Release
126+
uses: softprops/action-gh-release@v1
127+
with:
128+
#prerelease: true
129+
generate_release_notes: true
130+

ssh-container/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ssh_client_keys
2+
ssh_host_keys

ssh-container/Dockerfile

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
FROM ghcr.io/nearnodeflash/nnf-dm-copy-offload:0.1.19
2+
3+
# Build arguments for user UID/GID
4+
ARG USER_UID=1234
5+
ARG USER_GID=1234
6+
7+
# Set environment variables
8+
ENV USER_UID=${USER_UID}
9+
ENV USER_GID=${USER_GID}
10+
11+
ENV SESSION_DIR="/home/mpiuser/ssh-session"
12+
ENV KEYS_DIR="/home/mpiuser/ssh-session/keys"
13+
14+
# Modify existing mpiuser to use specified UID/GID
15+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
16+
RUN TARGET_GROUP=$(getent group ${USER_GID} | cut -d: -f1) && \
17+
if [ -z "$TARGET_GROUP" ]; then \
18+
groupmod -g ${USER_GID} mpiuser; \
19+
TARGET_GROUP="mpiuser"; \
20+
fi && \
21+
usermod -u ${USER_UID} -g ${USER_GID} mpiuser
22+
SHELL ["/bin/sh", "-c"]
23+
24+
# Set up SSH directory for mpiuser
25+
RUN mkdir -p /home/mpiuser/.ssh && \
26+
mkdir -p ${SESSION_DIR} && \
27+
mkdir -p ${KEYS_DIR}
28+
29+
# Copy SSH host keys
30+
COPY ssh_host_keys/ssh_host_rsa_key ${KEYS_DIR}/ssh_host_rsa_key
31+
COPY ssh_host_keys/ssh_host_rsa_key.pub ${KEYS_DIR}/ssh_host_rsa_key.pub
32+
33+
# Copy client public key for authorized access
34+
COPY ssh_client_keys/mpiuser_key.pub ${KEYS_DIR}mpiuser_key.pub
35+
RUN cat ${KEYS_DIR}mpiuser_key.pub > ${KEYS_DIR}/authorized_keys
36+
37+
# Override any entrypoint from base image and set our own
38+
COPY entrypoint.sh /entrypoint.sh
39+
RUN chmod a+x /entrypoint.sh
40+
41+
WORKDIR /home/mpiuser
42+
43+
# Configure SSH - port is set at runtime so we don't hardcode it here
44+
RUN echo "HostKey $KEYS_DIR/ssh_host_rsa_key" > "$SESSION_DIR/sshd_config" && \
45+
echo "AuthorizedKeysFile $KEYS_DIR/authorized_keys" >> "$SESSION_DIR/sshd_config" && \
46+
echo "PermitRootLogin no" >> "$SESSION_DIR/sshd_config" && \
47+
echo "PasswordAuthentication no" >> "$SESSION_DIR/sshd_config"
48+
49+
# Set proper permissions on keys
50+
RUN chmod 600 ${KEYS_DIR}/ssh_host_rsa_key && \
51+
chmod 644 ${KEYS_DIR}/ssh_host_rsa_key.pub && \
52+
chmod 600 ${KEYS_DIR}/authorized_keys && \
53+
chmod 700 /home/mpiuser/.ssh && \
54+
chown -R ${USER_UID}:${USER_GID} /home/mpiuser
55+
56+
# Labels for identifying the UID/GID
57+
LABEL uid="${USER_UID}"
58+
LABEL gid="${USER_GID}"
59+
LABEL description="SSH container with mpiuser (UID:${USER_UID}, GID:${USER_GID})"
60+
61+
ENTRYPOINT ["/entrypoint.sh"]

ssh-container/Makefile

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# TODO: Set the UID and GID To match the user who is going to use this container. Due to security, the UID and GID
2+
# *must* be baked in to the image in order for sshd to obtain the correct permissions to enable ssh security.
3+
UID ?= 1060
4+
GID ?= 100
5+
6+
# NOTE: SSH_PORT is only for local testing purposes. In Rabbit User Containers, the port is assigned dynamically via the
7+
# NNF_CONTAINER_PORTS environment variable.
8+
SSH_PORT ?= 5123
9+
10+
# SSH key files - these are generated locally and used in the container. The mpiuser_key is used for ssh authentication
11+
# to connect to the container. Do not lose the client keys.
12+
SSH_HOST_KEY_DIR = ./ssh_host_keys
13+
SSH_HOST_RSA_KEY = $(SSH_HOST_KEY_DIR)/ssh_host_rsa_key
14+
SSH_CLIENT_KEY_DIR = ./ssh_client_keys
15+
SSH_CLIENT_KEY = $(SSH_CLIENT_KEY_DIR)/mpiuser_key
16+
17+
# Image name and tag
18+
IMAGE_NAME ?= ssh-container
19+
TAG ?= latest
20+
21+
# CONTAINER_TOOL defines the container tool to be used for building images.
22+
# Be aware that the target commands are only tested with Docker which is
23+
# scaffolded by default. However, you might want to replace it to use other
24+
# tools. (i.e. podman)
25+
CONTAINER_TOOL ?= docker
26+
27+
# Container name for easier management
28+
CONTAINER_NAME = ssh-container-$(UID)-$(GID)
29+
30+
.PHONY: all docker-build clean ssh-keys client-keys keys run stop connect status help
31+
32+
all: docker-build
33+
34+
help: ## Show this help message
35+
@echo "Available targets:"
36+
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
37+
38+
# Generate SSH host keys
39+
ssh-keys: ## Generate SSH host keys for the container
40+
@echo "Generating SSH host keys..."
41+
@mkdir -p $(SSH_HOST_KEY_DIR)
42+
@if [ ! -f $(SSH_HOST_RSA_KEY) ]; then \
43+
ssh-keygen -t rsa -b 4096 -f $(SSH_HOST_RSA_KEY) -N '' -C "SSH host RSA key"; \
44+
fi
45+
@echo "SSH host keys generated successfully"
46+
47+
# Generate SSH client keys for authentication
48+
client-keys: ## Generate SSH client keys for authentication
49+
@echo "Generating SSH client keys..."
50+
@mkdir -p $(SSH_CLIENT_KEY_DIR)
51+
@if [ ! -f $(SSH_CLIENT_KEY) ]; then \
52+
ssh-keygen -t rsa -b 4096 -f $(SSH_CLIENT_KEY) -N '' -C "mpiuser@ssh-container"; \
53+
echo ""; \
54+
echo "Client key generated: $(SSH_CLIENT_KEY)"; \
55+
echo "Public key: $(SSH_CLIENT_KEY).pub"; \
56+
echo ""; \
57+
echo "To connect without password:"; \
58+
echo " ssh -i $(SSH_CLIENT_KEY) -p $(SSH_PORT) mpiuser@localhost"; \
59+
echo ""; \
60+
fi
61+
62+
# Generate both SSH host and client keys
63+
keys: ssh-keys client-keys ## Generate both SSH host and client keys
64+
@echo "SSH keys generated successfully"
65+
@echo "Host keys in: $(SSH_HOST_KEY_DIR)"
66+
@echo "Client keys in: $(SSH_CLIENT_KEY_DIR)"
67+
68+
# Build the container image
69+
docker-build:
70+
@echo "Building $(CONTAINER_TOOL) image with UID=$(UID), GID=$(GID)..."
71+
$(CONTAINER_TOOL) build \
72+
--platform linux/amd64 \
73+
--build-arg USER_UID=$(UID) \
74+
--build-arg USER_GID=$(GID) \
75+
-t $(IMAGE_NAME):$(TAG) \
76+
-t $(IMAGE_NAME):uid$(UID)-gid$(GID) \
77+
.
78+
@echo "$(CONTAINER_TOOL) image built successfully"
79+
@echo "Image tags:"
80+
@echo " - $(IMAGE_NAME):$(TAG)"
81+
@echo " - $(IMAGE_NAME):uid$(UID)-gid$(GID)"
82+
@echo ""
83+
@echo "To inspect UID/GID/PORT labels:"
84+
@echo " $(CONTAINER_TOOL) inspect $(IMAGE_NAME):$(TAG) | grep -A 10 Labels"
85+
86+
# Run the container
87+
run: docker-build ## Run the container with SSH service (as UID 1060 and GID 100)
88+
$(CONTAINER_TOOL) run -d \
89+
--platform linux/amd64 \
90+
--name $(CONTAINER_NAME) \
91+
--user $(UID):$(GID) \
92+
-p $(SSH_PORT):$(SSH_PORT) \
93+
-e NNF_CONTAINER_PORTS=$(SSH_PORT) \
94+
$(IMAGE_NAME):$(TAG)
95+
@echo "Container started with SSH on port $(SSH_PORT) (accessible via host port $(SSH_PORT))"
96+
@echo "Connect with: make connect"
97+
98+
# Stop and remove container
99+
stop: ## Stop and remove the container
100+
-$(CONTAINER_TOOL) stop $(CONTAINER_NAME)
101+
-$(CONTAINER_TOOL) rm $(CONTAINER_NAME)
102+
103+
# Check container status
104+
status: ## Check if the container is running
105+
@if $(CONTAINER_TOOL) ps | grep -q $(CONTAINER_NAME); then \
106+
echo "Container $(CONTAINER_NAME) is running"; \
107+
$(CONTAINER_TOOL) ps | grep $(CONTAINER_NAME); \
108+
else \
109+
echo "Container $(CONTAINER_NAME) is not running"; \
110+
fi
111+
112+
# Connect to the running container using SSH keys
113+
connect: ## Connect to the container via SSH
114+
@if [ ! -f $(SSH_CLIENT_KEY) ]; then \
115+
echo "Client key not found. Run 'make client-keys' first."; \
116+
exit 1; \
117+
fi
118+
@if ! $(CONTAINER_TOOL) ps | grep -q $(CONTAINER_NAME); then \
119+
echo "Container $(CONTAINER_NAME) is not running."; \
120+
echo "Run 'make run' to start the container first."; \
121+
echo "Or run 'make status' to check container status."; \
122+
exit 1; \
123+
fi
124+
@echo "Connecting to SSH container..."
125+
ssh -i $(SSH_CLIENT_KEY) -p $(SSH_PORT) -o StrictHostKeyChecking=no mpiuser@localhost
126+
127+
# Clean up generated files and containers
128+
clean: stop ## Clean up generated files and containers
129+
rm -rf $(SSH_HOST_KEY_DIR)
130+
rm -rf $(SSH_CLIENT_KEY_DIR)
131+
-$(CONTAINER_TOOL) rmi $(IMAGE_NAME):$(TAG)
132+
-$(CONTAINER_TOOL) rmi $(IMAGE_NAME):uid$(UID)-gid$(GID)

0 commit comments

Comments
 (0)