# Rust Package Builder Minimalny automat Docker, który cyklicznie sprawdza branch Git, buduje projekt Rust i publikuje podpisane repozytoria APT oraz RPM/DNF. Projekt zawiera również mały, utwardzony serwer HTTP udostępniający gotowe repozytorium i stronę z instrukcją instalacji. ## Obsługiwane cele - architektura: amd64 / x86_64 - Debian 12, Debian 13 - Ubuntu 24.04, Ubuntu 26.04 - RHEL/Rocky/Alma 9 i 10 Obraz buildera bazuje na Rocky Linux 9. Dzięki temu binarka jest budowana na glibc 2.34; builder dodatkowo odrzuca wynik wymagający nowszego symbolu glibc. Ta sama binarka może być pakowana do DEB i RPM dla wymienionych, nowszych systemów x86_64. ## RustProxy Dla dostarczonego RustProxy builder automatycznie używa plików znajdujących się w repozytorium źródłowym: - `debian/rustproxy.service` - `debian/rustproxy.environment` - `debian/rustproxy.logrotate` - `debian/rustproxy.tmpfiles` - logiki `debian/rustproxy.postinst` - `grafana/rustproxy-overview.json` Nie utrzymuje drugiej kopii konfiguracji systemd RustProxy. Wersja i nazwa binarki pochodzą z metadanych Cargo, a nazwa pakietu i maintainer są przejmowane z `debian/control`, jeśli plik istnieje. ## Uruchomienie Ustawiasz ręcznie tylko dwie wartości: ```bash cp .env.example .env nano .env ``` Przykład dla serwera `192.168.1.100`: ```env SOURCE_REPO=https://git.linuxiarz.pl/gru/RustProxy.git REPOSITORY_URL=http://192.168.1.100 ``` Następnie: ```bash ./start.sh ``` Jeżeli w `REPOSITORY_URL` nie podasz portu, `start.sh` automatycznie ustawi `18765` i zapisze efektywny adres, np.: ```text http://192.168.1.100:18765 ``` Jeżeli chcesz inny port, wystarczy wpisać go bezpośrednio w adresie: ```env REPOSITORY_URL=http://192.168.1.100:23456 ``` Nie trzeba ustawiać osobnego `HTTP_PORT` ręcznie — skrypt pobierze go z adresu. `HTTP_PORT` jest zapisywany pomocniczo do `.env`, aby Compose zachował tę samą konfigurację. `start.sh` automatycznie: - sprawdza Docker i Docker Compose, - tworzy katalogi `data/` i `gpg/`, - buduje obraz buildera, - przy pierwszym uruchomieniu generuje trwały klucz GPG RSA 3072 bez hasła, - zapisuje klucz prywatny jako `gpg/private-key.asc`, publiczny jako `gpg/public-key.asc` oraz fingerprint jako `gpg/fingerprint`, - przy kolejnych uruchomieniach używa tego samego klucza, - uruchamia builder i mikroserwer HTTP, - builder natychmiast sprawdza repozytorium Git i po udanym buildzie publikuje APT oraz RPM/DNF, - strona główna HTTP automatycznie pokazuje instrukcję instalacji i linki do klucza/metadanych. Można też uruchomić bez wcześniejszego tworzenia `.env`: ```bash SOURCE_REPO=https://git.linuxiarz.pl/gru/RustProxy.git \ REPOSITORY_URL=http://192.168.1.100 \ ./start.sh ``` ## Mikro HTTP Repozytorium jest wystawiane przez osobny kontener `repository-http` oparty o `nginxinc/nginx-unprivileged:stable-alpine-slim`. Domyślnie: ```text http://ADRES_SERWERA:18765/ ``` Serwer HTTP jest celowo bardzo prosty: - działa jako użytkownik nieuprzywilejowany, - kontener ma `cap_drop: ALL`, - ma `no-new-privileges`, - root filesystem jest read-only, - repozytorium jest montowane read-only, - obsługuje wyłącznie `GET` i `HEAD`, - nie pozwala na upload, - nie pokazuje listingów katalogów, - blokuje pliki ukryte, - dodaje podstawowe nagłówki bezpieczeństwa, - posiada `/healthz`, - logi idą do `docker compose logs`. Przed pierwszym udanym buildem `/` wyświetla prostą informację, że repozytorium jest inicjalizowane. Po publikacji builder atomowo podmienia stronę na właściwą instrukcję instalacji. ### HTTPS Wbudowany mikroserwer świadomie obsługuje tylko HTTP. Jeżeli `REPOSITORY_URL` zaczyna się od `https://`, zakładane jest, że przed nim stoi zewnętrzny reverse proxy/TLS (np. RustProxy/nginx/Caddy). Sam builder nadal wystawia lokalny HTTP na porcie `18765` lub `HTTP_PORT`. Dla kompletnego uruchomienia bez reverse proxy użyj `http://IP_LUB_HOST`. ## Klucz GPG Klucz jest generowany tylko wtedy, gdy `gpg/private-key.asc` nie istnieje. Nie usuwaj katalogu `gpg/` po publikacji repozytorium: zmiana klucza spowoduje, że klienci z wcześniej zainstalowanym kluczem przestaną ufać nowym podpisom. Do backupu zachowaj co najmniej: ```text gpg/private-key.asc gpg/public-key.asc gpg/fingerprint data/ ``` ## Konfiguracja Wymagane: ```text SOURCE_REPO REPOSITORY_URL ``` Domyślne / automatyczne: ```text SOURCE_BRANCH=master POLL_INTERVAL=300 HTTP_PORT=18765 REPO_DISTRIBUTION=stable REPO_COMPONENT=main PACKAGE_RELEASE=auto MAX_GLIBC_VERSION=2.34 GPG_PRIVATE_KEY_FILE=/run/secrets/gpg/private-key.asc GPG_PASSPHRASE_FILE=/run/secrets/gpg/passphrase ``` `GPG_PASSPHRASE_FILE` nie musi istnieć dla automatycznie generowanego klucza bez hasła. `GPG_KEY_ID` może być podany przez ENV; jeśli go brak, builder wybiera pierwszy zaimportowany klucz prywatny. ## Dane trwałe ```text /var/lib/package-builder/ ├── aptly/ ├── cargo/ ├── packages/ │ ├── deb/ │ └── rpm/x86_64/ ├── repository/ │ ├── current -> releases/ │ ├── apt -> current/apt │ ├── rpm -> current/rpm │ ├── repo-key.gpg -> current/repo-key.gpg │ ├── index.html -> current/index.html │ └── releases/ ├── state/ │ ├── last_commit │ ├── last_version │ └── last_package_release └── work/ ``` Poprzednie wersje pakietów nie są usuwane. Aktualne repozytoria APT i RPM zawierają historię pakietów. Przy `PACKAGE_RELEASE=auto` nowy commit z niezmienionym `Cargo.toml version` dostaje kolejną rewizję paczki (`-2`, `-3`, ...), więc nie nadpisuje wcześniej opublikowanego artefaktu. ## Publikacja bez uszkodzenia działającego repo Nowy commit przechodzi kolejno: ```text clone -> cargo build --release -> nFPM DEB/RPM -> podpisy -> aptly -> createrepo_c -> walidacja -> atomic switch ``` Publiczny symlink `repository/current` jest podmieniany dopiero na końcu. Błąd kompilacji, pakowania, podpisu lub generowania metadata nie przełącza publicznego repozytorium. `last_commit` jest zapisywany dopiero po udanej publikacji, więc nieudany commit zostanie ponowiony w następnym cyklu. Kolejne snapshoty w `repository/releases/` używają hardlinków do niezmienionych plików poprzedniego snapshotu. ## Podpisy - DEB: podpis GPG nFPM (`_gpgorigin`) oraz podpisane `Release.gpg`/`InRelease` przez aptly. - RPM: podpis GPG pakietu nFPM. - RPM metadata: `repodata/repomd.xml.asc`; instrukcja używa `repo_gpgcheck=1` i `gpgcheck=1`. - publiczny klucz: `http(s)://.../repo-key.gpg` jako ASCII-armored OpenPGP, zgodny z `gpg --dearmor` i `rpm --import`. ## Logi i kontrola ```bash docker compose logs -f rust-package-builder docker compose logs -f repository-http curl -f http://127.0.0.1:18765/healthz ``` ## Test plików dostarczonego RustProxy ```bash ./tests/static.sh ./tests/check-rustproxy-source.sh /sciezka/do/RustProxy ``` ## Własny klucz GPG (opcjonalnie) Jeżeli zamiast automatycznie generowanego klucza chcesz użyć własnego, umieść go przed pierwszym `./start.sh` jako: ```text gpg/private-key.asc ``` Przykładowy eksport: ```bash gpg --armor --export-secret-keys YOUR_KEY_ID > gpg/private-key.asc chmod 600 gpg/private-key.asc ``` `start.sh` wykryje istniejący klucz i nie wygeneruje nowego. Klucz prywatny nie jest kopiowany do obrazu. Kontener otrzymuje katalog `gpg/` wyłącznie jako read-only mount `/run/secrets/gpg`. ## Rocky Linux 9 / pakiety bazowe Dockerfile używa `dnf --allowerasing`, ponieważ obrazy Rocky Linux 9 mogą zawierać `coreutils-single` i `curl-minimal`. Podczas budowy są one zastępowane pełnymi pakietami `coreutils` i `curl`, wymaganymi przez builder.