From 08f693c910079c8c4d9f6d92413303c4147caee3 Mon Sep 17 00:00:00 2001 From: maride Date: Mon, 12 Aug 2024 14:20:28 +0200 Subject: [PATCH] Init --- Dockerfile | 8 ++++++ README.md | 28 +++++++++++++++++++++ aux/Release.template | 9 +++++++ aux/build.sh | 54 +++++++++++++++++++++++++++++++++++++++++ aux/generate-release.sh | 18 ++++++++++++++ gengpg.sh | 32 ++++++++++++++++++++++++ init/10-cron.sh | 10 ++++++++ init/10-gpg.sh | 4 +++ init/99-build.sh | 4 +++ other/nginx.conf | 12 +++++++++ 10 files changed, 179 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 aux/Release.template create mode 100755 aux/build.sh create mode 100755 aux/generate-release.sh create mode 100755 gengpg.sh create mode 100755 init/10-cron.sh create mode 100755 init/10-gpg.sh create mode 100755 init/99-build.sh create mode 100644 other/nginx.conf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..847e1f6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM nginx:mainline-bookworm + +RUN apt update && apt install -y dpkg-dev cron + +COPY aux /aux +COPY init/* /docker-entrypoint.d/ +COPY other/nginx.conf /etc/nginx/conf.d/default.conf + diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4b5f9e --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# supercow + +The [apt repo](https://wiki.debian.org/DebianRepository) server with [super-cow powers](https://unix.stackexchange.com/questions/92185/whats-the-story-behind-super-cow-powers) + +## Features + +- Periodic Auto-Signing +- Periodic Auto-Indexing +- Hosted through [nginx](https://nginx.org/) + +## Usage + +### Example + +Run `docker run -ti --name supercow -v ./signing.key:/private/signing.key:ro -v ./pkgs:/private/pkgs:ro -e REPO_DOMAIN=apt.example.com supercow` and enjoy your apt repository. + +### Environment variables + +- `REPO_DOMAIN`: the base domain of your repository, e.g. pkg.maride.cc +- `REPO_DESCRIPTION`: repository description, e.g. "maride's finest packages" + +### Volumes & Files + +- `/private/signing.key`: supercow expects an already generated [GPG key](https://gnupg.org/); see `gengpg.sh` for a quickstart +- `/private/pkgs`: contains the .deb files to be hosted. + +No file will be modified by supercow; the readonly bind option (`:ro`) may be used. + diff --git a/aux/Release.template b/aux/Release.template new file mode 100644 index 0000000..2b93d6a --- /dev/null +++ b/aux/Release.template @@ -0,0 +1,9 @@ +Origin: <> +Label: <> +Suite: stable +Codename: stable +Version: 1.0 +Architectures: amd64 +Components: main +Description: <> +Date: <> diff --git a/aux/build.sh b/aux/build.sh new file mode 100755 index 0000000..2b626e4 --- /dev/null +++ b/aux/build.sh @@ -0,0 +1,54 @@ +#!/bin/bash -ex + +# Check if cleanup happened before +if [ -d "/staging" ]; then + echo "/staging exists! Please check the logs to see why the cleanup process didn't run" 1>&2 + # ... however, we can fix that ... + echo -n "Running cleanup now... " + rm -rf /staging && echo "OK, proceeding." || exit 1 +fi + +# Create temporary folder structure +mkdir -p /staging +cd /staging +mkdir -p pool/main dists/stable/main/binary-amd64 +cp /private/pkgs/*.deb pool/main/. + +# Create Package indexes +dpkg-scanpackages --arch amd64 pool/ > dists/stable/main/binary-amd64/Packages +cat dists/stable/main/binary-amd64/Packages | gzip -9 > dists/stable/main/binary-amd64/Packages.gz + +# Create Release file +# taken from https://earthly.dev/blog/creating-and-hosting-your-own-deb-packages-and-apt-repo/ +do_hash() { + HASH_NAME=$1 + HASH_CMD=$2 + echo "${HASH_NAME}:" + for f in $(find -type f); do + f=$(echo $f | cut -c3-) # remove ./ prefix + if [ "$f" = "Release" ]; then + continue + fi + echo " $(${HASH_CMD} ${f} | cut -d" " -f1) $(wc -c $f)" + done +} + +/aux/generate-release.sh "$REPO_DOMAIN" "$REPO_DESCRIPTION" > dists/stable/Release +do_hash "MD5Sum" "md5sum" >> dists/stable/Release +do_hash "SHA1" "sha1sum" >> dists/stable/Release +do_hash "SHA256" "sha256sum" >> dists/stable/Release + +cat dists/stable/Release + +# Sign Release file +cat dists/stable/Release | gpg --default-key $(gpg --list-keys | head -n 4 | tail -n 1) --armor --detach-sign --sign --output dists/stable/Release.gpg +cat dists/stable/Release | gpg --default-key $(gpg --list-keys | head -n 4 | tail -n 1) --armor --clearsign --sign --output dists/stable/InRelease + +# Make pubkey accessible +gpg --armor --export > signing.pub + +# Move files and clean up staging directory +rm -rf /usr/share/nginx/html/* +mv /staging/* /usr/share/nginx/html/. +rm -rf /staging + diff --git a/aux/generate-release.sh b/aux/generate-release.sh new file mode 100755 index 0000000..69f2b87 --- /dev/null +++ b/aux/generate-release.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e +# release.sh: prints out a Release file header +# Arguments: release.sh + +if [ "$#" -ne 2 ]; then + echo "$0: " 1>&2 + exit 1 +fi + +DOMAIN="$1" +DESCRIPTION="$2" +DATE="$(date --utc --rfc-email)" + +cat /aux/Release.template | \ +sed "s/<>/$DOMAIN/g" | \ +sed "s/<>/$DESCRIPTION/g" | \ +sed "s/<>/$DATE/g" | \ +cat diff --git a/gengpg.sh b/gengpg.sh new file mode 100755 index 0000000..85afc35 --- /dev/null +++ b/gengpg.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e +# Mostly inspired from https://earthly.dev/blog/creating-and-hosting-your-own-deb-packages-and-apt-repo/#pgp-gpg-and-gnupgp + +if [ -f "signing.key" ]; then + echo "signing.key exists, refuse to override" 1>&2 + exit 1 +fi + +# Create temporary directory +GNUPGHOME="$(mktemp --directory /tmp/pgpkeys-XXXXXX)" +export GNUPGHOME +echo "Creating a temporary keyring at $GNUPGHOME..." +chmod 700 "$GNUPGHOME" + +# Create the request +echo "Key-Type: RSA +Key-Length: 4096 +Name-Real: supercow signing key +Name-Email: supercow@example.com +Expire-Date: 0 +%no-ask-passphrase +%no-protection +%commit" > "$GNUPGHOME/batchrequest" + +# Execute request +gpg --no-tty --batch --gen-key "$GNUPGHOME/batchrequest" +gpg --armor --export-secret-keys > signing.key + +# Cleanup +echo "Removing temporary keyring at $GNUPGHOME..." +rm -rf "$GNUPGHOME" + diff --git a/init/10-cron.sh b/init/10-cron.sh new file mode 100755 index 0000000..c4e31ee --- /dev/null +++ b/init/10-cron.sh @@ -0,0 +1,10 @@ +#!/bin/bash -e + +# Run the pull script regularly +target="/aux/build.sh" +entry="*/10 * * * * $target" +grep "$target" /etc/cron.d/supercow || echo "$entry" >> /etc/cron.d/supercow + +# start cron daemon (goes into background) +cron + diff --git a/init/10-gpg.sh b/init/10-gpg.sh new file mode 100755 index 0000000..53a9167 --- /dev/null +++ b/init/10-gpg.sh @@ -0,0 +1,4 @@ +#!/bin/bash -e + +gpg --import /private/signing.key + diff --git a/init/99-build.sh b/init/99-build.sh new file mode 100755 index 0000000..ff85141 --- /dev/null +++ b/init/99-build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +/aux/build.sh +exit $? diff --git a/other/nginx.conf b/other/nginx.conf new file mode 100644 index 0000000..c8c97a0 --- /dev/null +++ b/other/nginx.conf @@ -0,0 +1,12 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + autoindex on; + } +} + +