commit 344afb8c81f9683b5de72abbaedd37234538e001 Author: Patrick Gniza Date: Sat Nov 8 16:56:11 2025 +0100 first commit diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..672bf4a --- /dev/null +++ b/.drone.yml @@ -0,0 +1,96 @@ +kind: pipeline +type: docker +name: build-sign-and-release + +steps: + # -------------------------------------------------- + # 1️⃣ Build & Push Image + # -------------------------------------------------- + - name: build-and-push + image: docker:26 + privileged: true + environment: + REGISTRY_URL: + from_secret: REGISTRY_URL + DOCKER_USER: + from_secret: DOCKER_USER + DOCKER_PASS: + from_secret: DOCKER_PASS + volumes: + - name: docker_sock + path: /var/run/docker.sock + commands: + - echo "=== 🏗️ Building and Pushing Image ===" + - docker login $REGISTRY_URL -u "$DOCKER_USER" -p "$DOCKER_PASS" + - VERSION_TAG="v$DRONE_BUILD_NUMBER" + - IMAGE_NAME="public/drone-publish-tool" + - IMAGE_FULL="$REGISTRY_URL/$IMAGE_NAME:$VERSION_TAG" + - echo "Building image $IMAGE_FULL ..." + - docker build -t $IMAGE_FULL . + - docker tag $IMAGE_FULL $REGISTRY_URL/$IMAGE_NAME:latest + - echo "Pushing images to $REGISTRY_URL ..." + - docker push $IMAGE_FULL + - docker push $REGISTRY_URL/$IMAGE_NAME:latest + - echo "VERSION_TAG=$VERSION_TAG" >> build.env + - echo "IMAGE_FULL=$IMAGE_FULL" >> build.env + - echo "✅ Build and push complete." + + # -------------------------------------------------- + # 2️⃣ Sign Image with Cosign (Secret-Key aus Variable) + # -------------------------------------------------- + - name: sign-image + image: gcr.io/projectsigstore/cosign:v2.4.0 + environment: + COSIGN_KEY: + from_secret: COSIGN_KEY + COSIGN_PASSWORD: + from_secret: COSIGN_PASSWORD + REGISTRY_URL: + from_secret: REGISTRY_URL + DOCKER_USER: + from_secret: DOCKER_USER + DOCKER_PASS: + from_secret: DOCKER_PASS + commands: + - echo "=== 🔏 Signing image with Cosign ===" + - . build.env + - echo "$DOCKER_PASS" | cosign login --username "$DOCKER_USER" --password-stdin "$REGISTRY_URL" + # 🔐 Cosign-Key aus Secret in temporäre Datei schreiben + - echo "$COSIGN_KEY" > /tmp/cosign.key + - chmod 600 /tmp/cosign.key + - cosign sign --yes --key /tmp/cosign.key "$IMAGE_FULL" + - shred -u /tmp/cosign.key || rm -f /tmp/cosign.key + - echo "✅ Image successfully signed." + + # -------------------------------------------------- + # 3️⃣ Create Gitea Release + # -------------------------------------------------- + - name: create-release + image: curlimages/curl:8.10.1 + environment: + GITEA_URL: + from_secret: GITEA_URL + GITEA_TOKEN: + from_secret: GITEA_TOKEN + commands: + - echo "=== 🏷️ Creating Gitea release ===" + - . build.env + - RELEASE_NAME="Release $VERSION_TAG" + - RELEASE_BODY="Automatisch erstellter Release für Build $DRONE_BUILD_NUMBER\n\nImage:\n\`\`\`\n$IMAGE_FULL\n\`\`\`" + - | + curl -s -X POST "$GITEA_URL/api/v1/repos/${DRONE_REPO_OWNER}/${DRONE_REPO_NAME}/releases" \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"tag_name\": \"$VERSION_TAG\", + \"name\": \"$RELEASE_NAME\", + \"body\": \"$RELEASE_BODY\", + \"draft\": false, + \"prerelease\": false + }" + - echo "✅ Release created in Gitea." + +volumes: + - name: docker_sock + host: + path: /var/run/docker.sock diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..27d4b62 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# ======================================================= +# Drone Publish Tool - Beispielkonfiguration (.env.example) +# Bitte kopiere sie zu `.env` und trage deine echten Werte ein. +# ======================================================= + +VERSION_TAG=v1.0.0 + +GITEA_REPO=username/drone-publish-tool diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1af46a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# =============================== +# Drone Publish Tool - .gitignore +# =============================== + +# 🧩 Lokale Umgebungsdateien +.env +.env.local +.env.secret + +# 🔑 Schlüsseldateien +*.key +*.pub +*.pem + +# 🧱 Build-Artefakte / temporäre Dateien +*.log +*.tmp +*.bak +*.swp +*~ + +# 🐋 Docker-Artefakte +*.tar +*.pid +docker-compose.override.yml +docker-compose.local.yml + +# 🧰 System / Editor-spezifisch +.DS_Store +Thumbs.db +.vscode/ +.idea/ +__pycache__/ +*.pyc + +# 📦 Optional: falls du lokale Testdaten oder Push-Caches hast +/build/ +/dist/ +.cache/ +output/ + +# 🧪 Drone / CI Cache +.drone-secrets/ +.drone-cache/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ce3f350 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +FROM alpine:3.20 + +# --- Metadaten / OCI Labels --- +LABEL org.opencontainers.image.title="Drone Publish Tool" \ + org.opencontainers.image.description="Automatisiertes Drone CI Tool zum Signieren (Cosign) und Erstellen von Gitea-Releases" \ + org.opencontainers.image.authors="Patrick Buchhorst " \ + org.opencontainers.image.vendor="Buchhorster IT" \ + org.opencontainers.image.source="https://gitea.buchhorster.de/patrick/drone-publish-tool" \ + org.opencontainers.image.licenses="MIT" + +# Optionaler Build-ARG für Versionstag (falls über Drone gesetzt) +ARG VERSION=1.0.0 +LABEL org.opencontainers.image.version=$VERSION + +# --- Systemabhängigkeiten --- +RUN apk add --no-cache \ + bash \ + curl \ + git \ + jq \ + tini \ + wget \ + ca-certificates \ + docker-cli && \ + update-ca-certificates + +# --- Cosign installieren --- +RUN wget -qO /usr/local/bin/cosign \ + https://github.com/sigstore/cosign/releases/download/v2.4.0/cosign-linux-amd64 && \ + chmod +x /usr/local/bin/cosign + +# --- Entrypoint-Skript --- +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + +# --- Saubere Startumgebung --- +ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2dc5f6e --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +# ===== Konfiguration ===== +include .env + +IMAGE_NAME=$(REGISTRY_URL)/public/drone-publish-tool +IMAGE_FULL=$(IMAGE_NAME):$(VERSION_TAG) + +# ===== Targets ===== + +.PHONY: all build push sign release clean + +all: build push sign release + +build: + @echo "=== Building $(IMAGE_FULL) ===" + docker build -t $(IMAGE_FULL) . + docker tag $(IMAGE_FULL) $(IMAGE_NAME):latest + +push: + @echo "=== Pushing images to $(REGISTRY_URL) ===" + @docker login $(REGISTRY_URL) -u "$(REGISTRY_USER)" -p "$(REGISTRY_PASS)" + docker push $(IMAGE_FULL) + docker push $(IMAGE_NAME):latest + +sign: + @echo "=== Signing $(IMAGE_FULL) ===" + @if [ -z "$(COSIGN_KEY)" ]; then echo "❌ Kein COSIGN_KEY definiert"; exit 1; fi + docker run --rm -v $(COSIGN_KEY):/key:ro \ + -e COSIGN_PASSWORD="$(COSIGN_PASSWORD)" \ + gcr.io/projectsigstore/cosign:v2.4.0 \ + sign --yes --key /key $(IMAGE_FULL) + +release: + @echo "=== Creating Gitea Release $(VERSION_TAG) ===" + curl -s -X POST "$(GITEA_URL)/api/v1/repos/$(GITEA_REPO)/releases" \ + -H "Authorization: token $(GITEA_TOKEN)" \ + -H "Content-Type: application/json" \ + -d "{\"tag_name\":\"$(VERSION_TAG)\",\"name\":\"Release $(VERSION_TAG)\",\"body\":\"Automatischer Release für $(VERSION_TAG)\\n\\nImage: $(IMAGE_FULL)\",\"draft\":false,\"prerelease\":false}" + +clean: + @echo "=== Cleaning up local images ===" + -docker rmi $(IMAGE_FULL) $(IMAGE_NAME):latest 2>/dev/null || true diff --git a/README.md b/README.md new file mode 100644 index 0000000..51f8dc6 --- /dev/null +++ b/README.md @@ -0,0 +1,177 @@ +# 🚀 Drone Publish Tool + +Ein leichtgewichtiges CI-Tool auf Basis von **Alpine Linux**, das in einer Drone CI-Pipeline automatisch: + +1. Docker-Images mit **[Cosign](https://github.com/sigstore/cosign)** signiert 🔏 +2. und **Releases in [Gitea](https://gitea.io/)** erstellt 🏷️ + +Das Tool vereinfacht den gesamten Veröffentlichungsprozess — von der Signierung bis zum Release — +und funktioniert sowohl **innerhalb von Drone** als auch **manuell**. + +--- + +## 🧩 Features + +- ✅ Automatisches Signieren von Container-Images via Cosign +- ✅ Automatische Release-Erstellung in Gitea +- ✅ Vollständig über Umgebungsvariablen steuerbar (ideal für Drone Secrets) +- ✅ Kompatibel mit privaten Docker-Registries (z. B. Zot, Harbor, etc.) +- ✅ Minimal, sicher, portabel – basiert auf Alpine Linux + +--- + +## ⚙️ Voraussetzungen + +1. **Drone CI** ist mit Gitea verbunden (z. B. via OAuth oder Token). +2. Dein Docker-Image ist bereits gebaut und in der Registry verfügbar. +3. Du hast die unten genannten Secrets in Drone hinterlegt. +4. Für die Signierung ist ein **Cosign-Schlüssel** vorhanden (siehe unten). + +--- + +## 🔐 Erforderliche Secrets + +| Secret Name | Beschreibung | Beispielwert | +|--------------|--------------|---------------| +| `REGISTRY_URL` | Basis-URL deiner Registry | `registry.example.com` | +| `DOCKER_USER` | Benutzername für Registry-Login | `ci-user` | +| `DOCKER_PASS` | Passwort oder Token für Registry | `changeme123` | +| `COSIGN_KEY` | Inhalt des privaten Cosign-Schlüssels (nicht der Pfad!) | `-----BEGIN ENCRYPTED PRIVATE KEY-----...` | +| `COSIGN_PASSWORD` | Passwort für den Cosign-Schlüssel (kann leer sein) | *(leer)* | +| `GITEA_URL` | Basis-URL deines Gitea-Servers | `https://gitea.example.com` | +| `GITEA_TOKEN` | Personal Access Token für Gitea API | `ghp_abc123tokenxyz` | + +--- + +## 🔑 Cosign-Schlüssel erzeugen + +Falls du noch keinen Cosign-Schlüssel hast, kannst du ihn einfach per Docker erzeugen: + +```bash +docker run --rm -v $(pwd):/keys \ + -e COSIGN_PASSWORD="meinpasswort" \ + gcr.io/projectsigstore/cosign:latest generate-key-pair +``` + +Danach findest du zwei Dateien: + +- 🗝️ **`cosign.key`** → Privater Schlüssel (als Secret `COSIGN_KEY` in Drone hinterlegen) +- 🔓 **`cosign.pub`** → Öffentlicher Schlüssel (optional in Zot für Signaturprüfung hinterlegen) + +--- + +## ⚡ Beispiel: Verwendung in Drone CI + +Nach erfolgreichem Build kann das Tool als separater Step eingebunden werden, +um das erstellte Image zu signieren und in Gitea automatisch als Release zu veröffentlichen. + +```yaml +kind: pipeline +type: docker +name: release + +steps: + - name: build + image: docker:26 + privileged: true + environment: + REGISTRY_URL: + from_secret: REGISTRY_URL + DOCKER_USER: + from_secret: DOCKER_USER + DOCKER_PASS: + from_secret: DOCKER_PASS + volumes: + - name: docker_sock + path: /var/run/docker.sock + commands: + - docker login $REGISTRY_URL -u "$DOCKER_USER" -p "$DOCKER_PASS" + - docker build -t $REGISTRY_URL/public/myapp:$DRONE_BUILD_NUMBER . + - docker push $REGISTRY_URL/public/myapp:$DRONE_BUILD_NUMBER + - echo "IMAGE_FULL=$REGISTRY_URL/public/myapp:$DRONE_BUILD_NUMBER" >> build.env + - echo "VERSION_TAG=v$DRONE_BUILD_NUMBER" >> build.env + + - name: publish + image: registry.example.com/public/drone-publish-tool:latest + environment: + COSIGN_KEY: + from_secret: COSIGN_KEY + COSIGN_PASSWORD: + from_secret: COSIGN_PASSWORD + GITEA_URL: + from_secret: GITEA_URL + GITEA_TOKEN: + from_secret: GITEA_TOKEN + REGISTRY_URL: + from_secret: REGISTRY_URL + DOCKER_USER: + from_secret: DOCKER_USER + DOCKER_PASS: + from_secret: DOCKER_PASS + commands: + - . build.env + - export GITEA_REPO="$DRONE_REPO_OWNER/$DRONE_REPO_NAME" + - /usr/local/bin/entrypoint.sh + +volumes: + - name: docker_sock + host: + path: /var/run/docker.sock +``` + +💡 **Hinweis:** +`IMAGE_FULL` und `VERSION_TAG` werden aus dem Build-Step übernommen, +damit das Tool weiß, welches Image signiert und welcher Release erstellt werden soll. + +--- + +## 🧰 Beispiel: Manuelle Nutzung + +Du kannst das Tool auch außerhalb von Drone direkt ausführen, +z. B. zum Testen oder für manuelle Releases: + +```bash +docker run --rm \ + -e IMAGE_FULL=registry.example.com/public/myapp:v42 \ + -e VERSION_TAG=v42 \ + -e COSIGN_KEY="$(cat cosign.key)" \ + -e COSIGN_PASSWORD="" \ + -e GITEA_URL=https://gitea.example.com \ + -e GITEA_TOKEN=ghp_abc123tokenxyz \ + -e GITEA_REPO=patrick/myapp \ + registry.example.com/public/drone-publish-tool:latest +``` + +Damit signierst du das Image `myapp:v42` und erzeugst gleichzeitig ein Release in Gitea. +Das funktioniert auch ohne Drone CI – ideal zum Testen oder Troubleshooting. + +--- + +## 📦 Image-Metadaten (OCI Labels) + +Das Tool bringt vollständige [OCI Labels](https://github.com/opencontainers/image-spec/blob/main/annotations.md) mit, +damit es in Registries wie Zot oder Harbor korrekt beschrieben angezeigt wird: + +| Label | Beschreibung | +|--------|---------------| +| `org.opencontainers.image.title` | Drone Publish Tool | +| `org.opencontainers.image.description` | Automatisiertes Drone CI Tool zum Signieren und Erstellen von Gitea-Releases | +| `org.opencontainers.image.version` | 1.0.0 | +| `org.opencontainers.image.vendor` | Buchhorster IT | +| `org.opencontainers.image.source` | https://gitea.buchhorster.de/patrick/drone-publish-tool | +| `org.opencontainers.image.licenses` | MIT | + +--- + +## 🧾 Lizenz + +**MIT License** +© 2025 Patrick Buchhorst / Buchhorster IT + +--- + +## 🧩 Changelog + +| Version | Änderungen | +|----------|-------------| +| `1.0.0` | Erste Version mit Image-Signing (Cosign) und Gitea-Release-Funktion | diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..49b6cca --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,57 @@ +#!/bin/sh +set -e + +echo "=== 🚀 Drone Publish Tool ===" +echo "Image: $IMAGE_FULL" +echo "Version: $VERSION_TAG" + +# --- 1️⃣ Signieren --- +if [ -n "$COSIGN_KEY" ]; then + echo "🔏 Signing image using Cosign..." + + # Temporäre Datei anlegen + COSIGN_KEY_FILE=$(mktemp /tmp/cosign-key-XXXXXX) + echo "$COSIGN_KEY" > "$COSIGN_KEY_FILE" + chmod 600 "$COSIGN_KEY_FILE" + + # Optionales Passwort weitergeben + export COSIGN_PASSWORD="${COSIGN_PASSWORD:-}" + + # Signieren (mit --yes, falls ohne Interaktion) + cosign sign --yes --key "$COSIGN_KEY_FILE" "$IMAGE_FULL" + + # Digest extrahieren (zur Info oder für Gitea-Release) + SIGN_DIGEST=$(cosign verify --key "$COSIGN_KEY_FILE" "$IMAGE_FULL" 2>/dev/null | grep docker-manifest-digest | head -n1 | awk -F'"' '{print $4}') + + # Schlüssel sicher löschen + shred -u "$COSIGN_KEY_FILE" 2>/dev/null || rm -f "$COSIGN_KEY_FILE" + + echo "✅ Image successfully signed." +else + echo "⚠️ Skipping signing step (no COSIGN_KEY provided)" +fi + +# --- 2️⃣ Gitea Release erstellen --- +if [ -n "$GITEA_TOKEN" ] && [ -n "$GITEA_REPO" ] && [ -n "$GITEA_URL" ]; then + echo "🏷️ Creating Gitea release for $VERSION_TAG..." + + RELEASE_BODY="Automatischer Release für $VERSION_TAG\n\nImage: $IMAGE_FULL" + [ -n "$SIGN_DIGEST" ] && RELEASE_BODY="$RELEASE_BODY\n\nSignatur-Digest: $SIGN_DIGEST" + + curl -s -X POST "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases" \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"tag_name\": \"$VERSION_TAG\", + \"name\": \"Release $VERSION_TAG\", + \"body\": \"$RELEASE_BODY\", + \"draft\": false, + \"prerelease\": false + }" + + echo "✅ Gitea release created." +else + echo "⚠️ Skipping Gitea release creation (missing vars)" +fi + +echo "=== ✅ Done ==="