diff --git a/.docker/Dockerfile b/.docker/Dockerfile new file mode 100644 index 0000000..b01727d --- /dev/null +++ b/.docker/Dockerfile @@ -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 , wucke13 " +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" ] diff --git a/.docker/README.md b/.docker/README.md new file mode 100644 index 0000000..7292f2c --- /dev/null +++ b/.docker/README.md @@ -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 peer 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 NLNet with financial support for the European Commission's NGI Assure program. diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 0000000..3e4e48b --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 0000000..e5cb759 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -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 , wucke13 + org.opencontainers.image.authors=Karolin Varner , wucke13 + 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 , wucke13 + org.opencontainers.image.authors=Karolin Varner , wucke13 + 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