This commit is contained in:
maride 2024-08-12 14:20:28 +02:00
commit 08f693c910
10 changed files with 179 additions and 0 deletions

8
Dockerfile Normal file
View File

@ -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

28
README.md Normal file
View File

@ -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.

9
aux/Release.template Normal file
View File

@ -0,0 +1,9 @@
Origin: <<DOMAIN>>
Label: <<DOMAIN>>
Suite: stable
Codename: stable
Version: 1.0
Architectures: amd64
Components: main
Description: <<DESCRIPTION>>
Date: <<DATE>>

54
aux/build.sh Executable file
View File

@ -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

18
aux/generate-release.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash -e
# release.sh: prints out a Release file header
# Arguments: release.sh <base domain> <description>
if [ "$#" -ne 2 ]; then
echo "$0: <base domain> <description>" 1>&2
exit 1
fi
DOMAIN="$1"
DESCRIPTION="$2"
DATE="$(date --utc --rfc-email)"
cat /aux/Release.template | \
sed "s/<<DOMAIN>>/$DOMAIN/g" | \
sed "s/<<DESCRIPTION>>/$DESCRIPTION/g" | \
sed "s/<<DATE>>/$DATE/g" | \
cat

32
gengpg.sh Executable file
View File

@ -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"

10
init/10-cron.sh Executable file
View File

@ -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

4
init/10-gpg.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash -e
gpg --import /private/signing.key

4
init/99-build.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
/aux/build.sh
exit $?

12
other/nginx.conf Normal file
View File

@ -0,0 +1,12 @@
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
autoindex on;
}
}