feat(docker): add .docker/Dockerfile, .docker/README.md and workflow building and publishing docker images

This commit is contained in:
Amin Faez
2025-02-12 17:08:58 +01:00
parent be3c3d3d61
commit b5f6d07650
4 changed files with 415 additions and 0 deletions

75
.docker/Dockerfile Normal file
View File

@@ -0,0 +1,75 @@
# syntax=docker/dockerfile:1
ARG BASE_IMAGE=debian:bookworm-slim
# Stage 1: Base image with cargo-chef installed
FROM rust:latest AS chef
RUN cargo install cargo-chef
# install software required for liboqs-rust
RUN apt-get update && apt-get install -y clang cmake && rm -rf /var/lib/apt/lists/*
# Stage 2: Prepare the cargo-chef recipe
FROM chef AS planner
WORKDIR /app
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
# Stage 3: Cache dependencies using the recipe
FROM chef AS cacher
WORKDIR /app
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
# Stage 4: Build the application
FROM cacher AS builder
WORKDIR /app
COPY . .
RUN cargo build --release
# Stage 5: Annotate the base image with OCI Image Annotations, also install runtime-dependencies
FROM ${BASE_IMAGE} AS annotated_base_image
RUN apt-get update && apt-get install -y iproute2 && rm -rf /var/lib/apt/lists/*
ARG VERSION
ARG REF_NAME
ARG BUILD_DATE
ARG VCS_REF
ARG AUTHORS="Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>"
ARG URL="https://rosenpass.eu/"
ARG DOCUMENTATION="https://rosenpass.eu/docs/"
ARG SOURCE="https://github.com/rosenpass/rosenpass"
ARG VENDOR="Rosenpass e.V."
ARG LICENSES="MIT OR Apache-2.0"
ARG TITLE="Rosenpass"
ARG DESCRIPTION
ARG BASE_DIGEST
ARG BASE_IMAGE
LABEL org.opencontainers.image.created=${BUILD_DATE} \
org.opencontainers.image.authors=${AUTHORS} \
org.opencontainers.image.url=${URL} \
org.opencontainers.image.documentation=${DOCUMENTATION} \
org.opencontainers.image.source=${SOURCE} \
org.opencontainers.image.version=${VERSION} \
org.opencontainers.image.revision=${VCS_REF} \
org.opencontainers.image.vendor=${VENDOR} \
org.opencontainers.image.licenses=${LICENSES} \
org.opencontainers.image.ref.name=${REF_NAME} \
org.opencontainers.image.title=${TITLE} \
org.opencontainers.image.description=${DESCRIPTION} \
org.opencontainers.image.base.digest=${BASE_DIGEST} \
org.opencontainers.image.base.name=${BASE_IMAGE}
# Final Stage (rosenpass): Copy the rosenpass binary
FROM annotated_base_image AS rosenpass
COPY --from=builder /app/target/release/rosenpass /usr/local/bin/rosenpass
ENTRYPOINT [ "/usr/local/bin/rosenpass" ]
# Final Stage (rp): Copy the rp binary
FROM annotated_base_image AS rp
RUN apt-get update && apt-get install -y wireguard && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/rp /usr/local/bin/rp
ENTRYPOINT [ "/usr/local/bin/rp" ]

236
.docker/README.md Normal file
View File

@@ -0,0 +1,236 @@
# Rosenpass
Rosenpass is used to create post-quantum-secure VPNs. Rosenpass computes a shared key, [Wireguard](https://www.wireguard.com/papers/wireguard.pdf) uses the shared key to establish a secure connection. Rosenpass can also be used without WireGuard, deriving post-quantum-secure symmetric keys for another application.
The Rosenpass protocol builds on “Post-quantum WireGuard” ([PQWG](https://eprint.iacr.org/2020/379)) and improves it by using a cookie mechanism to provide security against state disruption attacks.
The rosenpass tool is written in Rust and uses liboqs. The tool establishes a symmetric key and provides it to WireGuard. Since it supplies WireGuard with key through the PSK feature using Rosenpass+WireGuard is cryptographically no less secure than using WireGuard on its own ("hybrid security"). Rosenpass refreshes the symmetric key every two minutes.
As with any application a small risk of critical security issues (such as buffer overflows, remote code execution) exists; the Rosenpass application is written in the Rust programming language which is much less prone to such issues. Rosenpass can also write keys to files instead of supplying them to WireGuard With a bit of scripting the stand alone mode of the implementation can be used to run the application in a Container, VM or on another host. This mode can also be used to integrate tools other than WireGuard with Rosenpass.
The `rp` tool written in Rust makes it easy to create a VPN using WireGuard and Rosenpass.
`rp` is easy to get started with but has a few drawbacks; it runs as root, demanding access to both WireGuard
and Rosenpass private keys, takes control of the interface and works with exactly one interface. If you do not feel confident about running Rosenpass as root, you should use the stand-alone mode to create a more secure setup using containers, jails, or virtual machines.
## Building the Docker Image
Clone the Rosenpass repository:
```
git clone https://github.com/rosenpass/rosenpass
cd rosenpass
```
Use the `docker-buildscript.sh` script to build images from the source.
```bash
bash docker-buildscript.sh
docker images
| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
|------------------------------|------------------|----------------|-----------------|--------|
| ghcr.io/rosenpass/rp | commit-aeb0671 | dc2997662d2c | 9 hours ago | 93.2MB |
| ghcr.io/rosenpass/rosenpass | commit-aeb0671 | 65ccc5e5b9fb | 9 hours ago | 93.6MB |
```
Set environment variable `TAG_AS_RELEASE=true` to tag the built images with the current versions.
Set environment variable `TAG_AS_LATEST=true` to tag the built images as latest.
```bash
export TAG_AS_RELEASE=true
export TAG_AS_LATEST=true
bash docker-buildscript.sh
docker images
| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
|-----------------------------|----------------|--------------|-------------|--------|
| ghcr.io/rosenpass/rp | 0.2.1 | 253338c948ab | 9 hours ago | 93.2MB |
| ghcr.io/rosenpass/rp | commit-05f0ac0 | 253338c948ab | 9 hours ago | 93.2MB |
| ghcr.io/rosenpass/rp | latest | 253338c948ab | 9 hours ago | 93.2MB |
| ghcr.io/rosenpass/rosenpass | 0.3.0-dev | 6958e24fd240 | 9 hours ago | 93.6MB |
| ghcr.io/rosenpass/rosenpass | commit-05f0ac0 | 6958e24fd240 | 9 hours ago | 93.6MB |
| ghcr.io/rosenpass/rosenpass | latest | 6958e24fd240 | 9 hours ago | 93.6MB |
```
## Usage - Standalone Key Exchange
The `ghcr.io/rosenpass/rosenpass` image can be used in a server-client setup to exchange quantum-secure shared keys.
This setup uses rosenpass as a standalone application, without using any other component such as wireguard.
What follows, is a simple setup for illustrative purposes.
Create a docker network that is used to connect the containers:
```bash
docker network create -d bridge rp
export NET=rp
```
Generate the server and client key pairs:
```bash
mkdir ./workdir-client ./workdir-server
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rosenpass \
gen-keys --public-key=workdir/server-public --secret-key=workdir/server-secret
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rosenpass \
gen-keys --public-key=workdir/client-public --secret-key=workdir/client-secret
# share the public keys between client and server
cp workdir-client/client-public workdir-server/client-public
cp workdir-server/server-public workdir-client/server-public
```
Start the server container:
```bash
docker run --name "rpserver" --network ${NET} \
-it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rosenpass \
exchange \
private-key workdir/server-secret \
public-key workdir/server-public \
listen 0.0.0.0:9999 \
peer public-key workdir/client-public \
outfile workdir/server-sharedkey
```
Find out the ip address of the server container:
```bash
EP="rpserver"
EP=$(docker inspect --format '{{ .NetworkSettings.Networks.rp.IPAddress }}' $EP)
```
Run the client container and perform the key exchange:
```bash
docker run --name "rpclient" --network ${NET} \
-it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rosenpass \
exchange \
private-key workdir/client-secret \
public-key workdir/client-public \
peer public-key workdir/server-public endpoint ${EP}:9999 \
outfile workdir/client-sharedkey
```
Now the containers will exchange shared keys and each put them into their respective outfile.
Comparing the outfiles shows that these shared keys equal:
```bash
cmp workdir/server-sharedkey workdir/client-sharedkey
```
It is now possible to set add these keys as pre-shared keys within a wireguard interface.
```bash
PREKEY=$(cat workdir/client-sharedkey)
wg set <interface> peer <peer-public-key> preshared-key <(echo "$PREKEY")
```
## Usage - Combined with wireguard
The `ghcr.io/rosenpass/rp` image can be used to build a VPN with WireGuard and Rosenpass.
In this example, we run two containers on the same system and connect them with a bridge network within the docker overlay network.
Create the named docker network, to be able to connect the containers.
Create a docker network that is used to connect the containers:
```bash
docker network create -d bridge rp
export NET=rp
```
Generate the server and client secret keys and extract public keys.
```bash
mkdir -p ./workdir-server ./workdir-client
# server
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rp \
genkey workdir/server.rosenpass-secret
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rp \
pubkey workdir/server.rosenpass-secret workdir/server.rosenpass-public
# client
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rp \
genkey workdir/client.rosenpass-secret
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rp \
pubkey workdir/client.rosenpass-secret workdir/client.rosenpass-public
# share the public keys between client and server
cp workdir-client/client.rosenpass-public workdir-server/client.rosenpass-public
cp workdir-server/server.rosenpass-public workdir-client/server.rosenpass-public
```
Start the server container.
Note that the `NET_ADMIN` capability is neccessary, the rp command will create and manage wireguard interfaces.
Also make sure the `wireguard` kernel module is loaded by the host. (`lsmod | grep wireguard`)
```bash
docker run --name "rpserver" --network ${NET} -it -d --rm -v ./workdir-server:/workdir \
--cap-add=NET_ADMIN \
ghcr.io/rosenpass/rp \
exchange workdir/server.rosenpass-secret dev rosenpass0 \
listen 0.0.0.0:9999 peer workdir/client.rosenpass-public allowed-ips 10.0.0.0/8
```
Now find out the ip-address of the server container and then start the client container:
```bash
EP="rpserver"
EP=$(docker inspect --format '{{ .NetworkSettings.Networks.rp.IPAddress }}' $EP)
docker run --name "rpclient" --network ${NET} -it -d --rm -v ./workdir-client:/workdir \
--cap-add=NET_ADMIN \
ghcr.io/rosenpass/rp \
exchange workdir/client.rosenpass-secret dev rosenpass1 \
peer workdir/server.rosenpass-public endpoint ${EP}:9999 allowed-ips 10.0.0.1
```
Inside the docker containers assign the IP addresses:
```bash
# server
docker exec -it rpserver ip a add 10.0.0.1/24 dev rosenpass0
# client
docker exec -it rpclient ip a add 10.0.0.2/24 dev rosenpass1
```
Done! The two containers should now be connected through a wireguard VPN (Port 1000) with pre-shared keys exchanged by rosenpass (Port 9999).
Now, test the connection by starting a shell inside the client container, and ping the server through the VPN:
```bash
# client
docker exec -it rpclient bash
apt update; apt install iputils-ping
ping 10.0.0.1
```
The ping command should continuously show ping-logs:
```
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.119 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.132 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.394 ms
...
```
While the ping is running, you may stop the server container, and verify that the ping-log halts. In another terminal do:
```
docker stop -t 1 rpserver
```
## Contributing
The rosenpass project is maintained on [Github](https://github.com/rosenpass/rosenpass).
Contributions are generally welcome. Join our [Matrix Chat](https://matrix.to/#/#rosenpass:matrix.org) if you are looking for guidance on how to contribute or for people to collaborate with.
We also have a as of now, very minimal [contributors guide](https://github.com/rosenpass/rosenpass/blob/main/CONTRIBUTING.md).
## Acknowledgements
Funded through <a href="https://nlnet.nl/">NLNet</a> with financial support for the European Commission's <a href="https://nlnet.nl/assure">NGI Assure</a> program.

1
.dockerignore Symbolic link
View File

@@ -0,0 +1 @@
.gitignore

103
.github/workflows/docker.yaml vendored Normal file
View File

@@ -0,0 +1,103 @@
name: ci
on:
push:
branches:
- 'main'
tags:
- 'v*'
pull_request:
branches:
- 'main'
jobs:
docker-image-rp:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/utilacy/rp
tags: |
type=edge,branch=main
type=semver,pattern={{version}}
labels: |
maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.title=Rosenpass
org.opencontainers.image.description=The rp command-line integrates Rosenpass and WireGuard to help you create a VPN
org.opencontainers.image.vendor=Rosenpass e.V.
org.opencontainers.image.licenses=MIT OR Apache-2.0"
org.opencontainers.image.url=https://rosenpass.eu
org.opencontainers.image.documentation=https://rosenpass.eu/docs/
org.opencontainers.image.source=https://github.com/rosenpass/rosenpass
-
name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: .docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
target: rp
platforms: linux/amd64 #,linux/arm64,linux/arm/v7
docker-image-rosenpass:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/utilacy/rosenpass
tags: |
type=edge,branch=main
type=semver,pattern={{version}}
labels: |
maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.title=Rosenpass
org.opencontainers.image.description=Reference implementation of the protocol rosenpass protocol
org.opencontainers.image.vendor=Rosenpass e.V.
org.opencontainers.image.licenses=MIT OR Apache-2.0"
org.opencontainers.image.url=https://rosenpass.eu
org.opencontainers.image.documentation=https://rosenpass.eu/docs/
org.opencontainers.image.source=https://github.com/rosenpass/rosenpass
-
name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: .docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
target: rosenpass
platforms: linux/amd64 #,linux/arm64,linux/arm/v7