small www
This commit is contained in:
1 parent
3d6c49df65
commit
fda15b194b
6 files changed
+306
-42
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
# Rust Package Builder
|
# Rust Package Builder
|
||||||
|
|
||||||
Minimalny kontener, który cyklicznie sprawdza branch Git, buduje projekt Rust i publikuje podpisane repozytoria APT oraz RPM/DNF.
|
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
|
## Obsługiwane cele
|
||||||
|
|
||||||
@@ -26,14 +26,40 @@ Nie utrzymuje drugiej kopii konfiguracji systemd RustProxy. Wersja i nazwa binar
|
|||||||
|
|
||||||
## Uruchomienie
|
## Uruchomienie
|
||||||
|
|
||||||
Najprostszy wariant wymaga ustawienia tylko dwóch wartości:
|
Ustawiasz ręcznie tylko dwie wartości:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# edytuj .env i ustaw SOURCE_REPO oraz REPOSITORY_URL
|
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
|
./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:
|
`start.sh` automatycznie:
|
||||||
|
|
||||||
- sprawdza Docker i Docker Compose,
|
- sprawdza Docker i Docker Compose,
|
||||||
@@ -41,35 +67,57 @@ cp .env.example .env
|
|||||||
- buduje obraz buildera,
|
- buduje obraz buildera,
|
||||||
- przy pierwszym uruchomieniu generuje trwały klucz GPG RSA 3072 bez hasła,
|
- 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`,
|
- zapisuje klucz prywatny jako `gpg/private-key.asc`, publiczny jako `gpg/public-key.asc` oraz fingerprint jako `gpg/fingerprint`,
|
||||||
- przy kolejnych uruchomieniach ponownie używa tego samego klucza,
|
- przy kolejnych uruchomieniach używa tego samego klucza,
|
||||||
- uruchamia usługę przez `docker compose up -d`,
|
- uruchamia builder i mikroserwer HTTP,
|
||||||
- builder natychmiast sprawdza repozytorium Git i po udanym buildzie publikuje APT oraz RPM/DNF.
|
- 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`:
|
Można też uruchomić bez wcześniejszego tworzenia `.env`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
SOURCE_REPO=https://git.linuxiarz.pl/gru/RustProxy.git \
|
SOURCE_REPO=https://git.linuxiarz.pl/gru/RustProxy.git \
|
||||||
REPOSITORY_URL=https://packages.linuxiarz.pl \
|
REPOSITORY_URL=http://192.168.1.100 \
|
||||||
./start.sh
|
./start.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
W takim przypadku `start.sh` zapisze te dwie wartości do `.env`.
|
## Mikro HTTP
|
||||||
|
|
||||||
Publiczny katalog repozytorium:
|
Repozytorium jest wystawiane przez osobny kontener `repository-http` oparty o `nginxinc/nginx-unprivileged:stable-alpine-slim`.
|
||||||
|
|
||||||
|
Domyślnie:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
./data/repository/
|
http://ADRES_SERWERA:18765/
|
||||||
```
|
```
|
||||||
|
|
||||||
Wystaw ten katalog przez nginx/RustProxy/Apache pod adresem z `REPOSITORY_URL`. Builder celowo nie uruchamia własnego HTTP ani TLS.
|
Serwer HTTP jest celowo bardzo prosty:
|
||||||
|
|
||||||
RustProxy 0.26.6 wymaga podczas builda Python 3.10+; obraz zawiera Python 3.11 i ustawia `RUST_PROXY_PYTHON=/usr/bin/python3.11`.
|
- 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`.
|
||||||
|
|
||||||
### Klucz GPG
|
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.
|
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 wystarczy zachować co najmniej:
|
Do backupu zachowaj co najmniej:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
gpg/private-key.asc
|
gpg/private-key.asc
|
||||||
@@ -87,11 +135,12 @@ SOURCE_REPO
|
|||||||
REPOSITORY_URL
|
REPOSITORY_URL
|
||||||
```
|
```
|
||||||
|
|
||||||
Domyślne:
|
Domyślne / automatyczne:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
SOURCE_BRANCH=master
|
SOURCE_BRANCH=master
|
||||||
POLL_INTERVAL=300
|
POLL_INTERVAL=300
|
||||||
|
HTTP_PORT=18765
|
||||||
REPO_DISTRIBUTION=stable
|
REPO_DISTRIBUTION=stable
|
||||||
REPO_COMPONENT=main
|
REPO_COMPONENT=main
|
||||||
PACKAGE_RELEASE=auto
|
PACKAGE_RELEASE=auto
|
||||||
@@ -100,17 +149,17 @@ GPG_PRIVATE_KEY_FILE=/run/secrets/gpg/private-key.asc
|
|||||||
GPG_PASSPHRASE_FILE=/run/secrets/gpg/passphrase
|
GPG_PASSPHRASE_FILE=/run/secrets/gpg/passphrase
|
||||||
```
|
```
|
||||||
|
|
||||||
`GPG_PASSPHRASE_FILE` nie musi istnieć dla klucza bez hasła. `GPG_KEY_ID` może być podany przez ENV; jeśli go brak, builder wybiera pierwszy zaimportowany klucz prywatny.
|
`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
|
## Dane trwałe
|
||||||
|
|
||||||
```text
|
```text
|
||||||
/var/lib/package-builder/
|
/var/lib/package-builder/
|
||||||
├── aptly/ # baza/pool aptly
|
├── aptly/
|
||||||
├── cargo/ # registry + target cache
|
├── cargo/
|
||||||
├── packages/
|
├── packages/
|
||||||
│ ├── deb/ # historia zbudowanych DEB
|
│ ├── deb/
|
||||||
│ └── rpm/x86_64/ # historia zbudowanych RPM
|
│ └── rpm/x86_64/
|
||||||
├── repository/
|
├── repository/
|
||||||
│ ├── current -> releases/<build>
|
│ ├── current -> releases/<build>
|
||||||
│ ├── apt -> current/apt
|
│ ├── apt -> current/apt
|
||||||
@@ -137,14 +186,22 @@ clone -> cargo build --release -> nFPM DEB/RPM -> podpisy -> aptly -> createrepo
|
|||||||
|
|
||||||
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.
|
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, więc zachowanie historii nie oznacza pełnej kopii wszystkich paczek przy każdym buildzie.
|
Kolejne snapshoty w `repository/releases/` używają hardlinków do niezmienionych plików poprzedniego snapshotu.
|
||||||
|
|
||||||
## Podpisy
|
## Podpisy
|
||||||
|
|
||||||
- DEB: podpis GPG nFPM (`_gpgorigin`) oraz podpisane `Release.gpg`/`InRelease` przez aptly.
|
- DEB: podpis GPG nFPM (`_gpgorigin`) oraz podpisane `Release.gpg`/`InRelease` przez aptly.
|
||||||
- RPM: podpis GPG pakietu nFPM.
|
- RPM: podpis GPG pakietu nFPM.
|
||||||
- RPM metadata: `repodata/repomd.xml.asc`; instrukcja używa `repo_gpgcheck=1` i `gpgcheck=1`.
|
- RPM metadata: `repodata/repomd.xml.asc`; instrukcja używa `repo_gpgcheck=1` i `gpgcheck=1`.
|
||||||
- publiczny klucz: `https://.../repo-key.gpg` jako ASCII-armored OpenPGP, zgodny z `gpg --dearmor` i `rpm --import`.
|
- 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
|
## Test plików dostarczonego RustProxy
|
||||||
|
|
||||||
@@ -153,10 +210,6 @@ Kolejne snapshoty w `repository/releases/` używają hardlinków do niezmieniony
|
|||||||
./tests/check-rustproxy-source.sh /sciezka/do/RustProxy
|
./tests/check-rustproxy-source.sh /sciezka/do/RustProxy
|
||||||
```
|
```
|
||||||
|
|
||||||
## HTTP
|
|
||||||
|
|
||||||
Builder nie uruchamia serwera HTTP i nie obsługuje TLS. Generuje katalog `repository/` oraz prosty `index.html` z instrukcją instalacji dopasowaną do `REPOSITORY_URL` i nazwy pakietu.
|
|
||||||
|
|
||||||
## Własny klucz GPG (opcjonalnie)
|
## Własny klucz GPG (opcjonalnie)
|
||||||
|
|
||||||
Jeżeli zamiast automatycznie generowanego klucza chcesz użyć własnego, umieść go przed pierwszym `./start.sh` jako:
|
Jeżeli zamiast automatycznie generowanego klucza chcesz użyć własnego, umieść go przed pierwszym `./start.sh` jako:
|
||||||
@@ -173,3 +226,7 @@ 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`.
|
`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.
|
||||||
@@ -12,3 +12,20 @@ services:
|
|||||||
- ./data:/var/lib/package-builder
|
- ./data:/var/lib/package-builder
|
||||||
- ./gpg:/run/secrets/gpg:ro
|
- ./gpg:/run/secrets/gpg:ro
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
repository-http:
|
||||||
|
image: nginxinc/nginx-unprivileged:stable-alpine-slim
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:${HTTP_PORT:-18765}:8080"
|
||||||
|
volumes:
|
||||||
|
- ./data/repository:/srv/repository:ro
|
||||||
|
- ./http/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
entrypoint: ["nginx", "-g", "daemon off;"]
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /tmp:rw,noexec,nosuid,size=16m
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
restart: unless-stopped
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
worker_processes 1;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
error_log /dev/stderr warn;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
access_log /dev/stdout;
|
||||||
|
server_tokens off;
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
keepalive_timeout 15s;
|
||||||
|
client_max_body_size 1k;
|
||||||
|
|
||||||
|
client_body_temp_path /tmp/client_temp;
|
||||||
|
proxy_temp_path /tmp/proxy_temp;
|
||||||
|
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||||
|
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||||
|
scgi_temp_path /tmp/scgi_temp;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080 default_server;
|
||||||
|
listen [::]:8080 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /srv/repository;
|
||||||
|
index index.html;
|
||||||
|
autoindex off;
|
||||||
|
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header Referrer-Policy "no-referrer" always;
|
||||||
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; img-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" always;
|
||||||
|
|
||||||
|
if ($request_method !~ ^(GET|HEAD)$) {
|
||||||
|
return 405;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /healthz {
|
||||||
|
default_type text/plain;
|
||||||
|
return 200 "ok\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
location = / {
|
||||||
|
try_files /index.html @initializing;
|
||||||
|
}
|
||||||
|
|
||||||
|
location @initializing {
|
||||||
|
default_type text/html;
|
||||||
|
return 200 '<!doctype html><html lang="pl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Rust Package Repository</title><style>body{font:16px system-ui,sans-serif;max-width:760px;margin:64px auto;padding:0 20px;color:#1f2937}code{background:#f3f4f6;padding:.15rem .35rem;border-radius:.3rem}</style></head><body><h1>Rust Package Repository</h1><p>Repozytorium HTTP działa. Pierwszy build pakietu jeszcze trwa albo nie został zakończony.</p><p>Stan buildera sprawdzisz przez <code>docker compose logs -f rust-package-builder</code>.</p></body></html>';
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ (^|/)\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,20 +17,42 @@ component=os.environ['REPO_COMPONENT']
|
|||||||
h=html.escape
|
h=html.escape
|
||||||
content=f'''<!doctype html>
|
content=f'''<!doctype html>
|
||||||
<html lang="pl">
|
<html lang="pl">
|
||||||
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>{h(package)} repository</title></head>
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<meta name="referrer" content="no-referrer">
|
||||||
|
<title>{h(package)} — repozytorium pakietów</title>
|
||||||
|
<style>
|
||||||
|
:root{{color-scheme:light dark;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}}
|
||||||
|
body{{max-width:920px;margin:0 auto;padding:42px 20px 70px;line-height:1.55}}
|
||||||
|
h1{{margin-bottom:.25rem}} .lead{{opacity:.78;margin-top:0}}
|
||||||
|
.card{{border:1px solid #8885;border-radius:12px;padding:18px;margin:18px 0}}
|
||||||
|
pre{{overflow:auto;padding:16px;border-radius:9px;background:#111;color:#eee;line-height:1.45}}
|
||||||
|
code{{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}}
|
||||||
|
a{{color:inherit}} .meta{{display:flex;gap:16px;flex-wrap:wrap;opacity:.82;font-size:.94rem}}
|
||||||
|
.ok{{font-weight:700}} ul{{padding-left:1.25rem}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>{h(package)}</h1>
|
<h1>{h(package)} — repozytorium pakietów</h1>
|
||||||
|
<p class="lead">Podpisane pakiety DEB i RPM dla x86_64/amd64.</p>
|
||||||
|
<div class="meta"><span class="ok">Repozytorium aktywne</span><span>{h(url)}</span><span>APT + RPM/DNF</span></div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
<h2>Debian / Ubuntu</h2>
|
<h2>Debian / Ubuntu</h2>
|
||||||
<pre>curl -fsSL {h(url)}/repo-key.gpg \\
|
<pre><code>curl -fsSL {h(url)}/repo-key.gpg \\
|
||||||
| sudo gpg --dearmor -o /usr/share/keyrings/{h(package)}.gpg
|
| sudo gpg --dearmor -o /usr/share/keyrings/{h(package)}.gpg
|
||||||
|
|
||||||
echo "deb [signed-by=/usr/share/keyrings/{h(package)}.gpg] {h(url)}/apt {h(dist)} {h(component)}" \\
|
echo "deb [signed-by=/usr/share/keyrings/{h(package)}.gpg] {h(url)}/apt {h(dist)} {h(component)}" \\
|
||||||
| sudo tee /etc/apt/sources.list.d/{h(package)}.list
|
| sudo tee /etc/apt/sources.list.d/{h(package)}.list
|
||||||
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install {h(package)}</pre>
|
sudo apt install {h(package)}</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
<h2>RHEL / Rocky / Alma / Fedora</h2>
|
<h2>RHEL / Rocky / Alma / Fedora</h2>
|
||||||
<pre>sudo rpm --import {h(url)}/repo-key.gpg
|
<pre><code>sudo rpm --import {h(url)}/repo-key.gpg
|
||||||
|
|
||||||
sudo tee /etc/yum.repos.d/{h(package)}.repo <<'REPO'
|
sudo tee /etc/yum.repos.d/{h(package)}.repo <<'REPO'
|
||||||
[{h(package)}]
|
[{h(package)}]
|
||||||
@@ -42,7 +64,18 @@ repo_gpgcheck=1
|
|||||||
gpgkey={h(url)}/repo-key.gpg
|
gpgkey={h(url)}/repo-key.gpg
|
||||||
REPO
|
REPO
|
||||||
|
|
||||||
sudo dnf install {h(package)}</pre>
|
sudo dnf install {h(package)}</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Pliki repozytorium</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{h(url)}/repo-key.gpg">Publiczny klucz GPG</a></li>
|
||||||
|
<li><a href="{h(url)}/apt/dists/{h(dist)}/InRelease">APT InRelease</a></li>
|
||||||
|
<li><a href="{h(url)}/rpm/x86_64/repodata/repomd.xml">RPM repomd.xml</a></li>
|
||||||
|
</ul>
|
||||||
|
<p>Obsługiwane cele: Debian 12/13, Ubuntu 24.04/26.04, RHEL 9/10; architektura amd64/x86_64.</p>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -8,11 +8,17 @@ ENV_FILE="$ROOT/.env"
|
|||||||
GPG_DIR="$ROOT/gpg"
|
GPG_DIR="$ROOT/gpg"
|
||||||
DATA_DIR="$ROOT/data"
|
DATA_DIR="$ROOT/data"
|
||||||
SERVICE="rust-package-builder"
|
SERVICE="rust-package-builder"
|
||||||
|
HTTP_SERVICE="repository-http"
|
||||||
|
DEFAULT_HTTP_PORT=18765
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
printf '[start] %s\n' "$*"
|
printf '[start] %s\n' "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
printf '[start] WARN: %s\n' "$*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
printf '[start] ERROR: %s\n' "$*" >&2
|
printf '[start] ERROR: %s\n' "$*" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -33,6 +39,21 @@ ENV
|
|||||||
chmod 0600 "$ENV_FILE" 2>/dev/null || true
|
chmod 0600 "$ENV_FILE" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_env_var() {
|
||||||
|
local key=$1
|
||||||
|
local value=$2
|
||||||
|
local tmp="$ENV_FILE.tmp.$$"
|
||||||
|
|
||||||
|
awk -v key="$key" -v value="$value" '
|
||||||
|
BEGIN { found=0 }
|
||||||
|
index($0, key "=") == 1 { print key "=" value; found=1; next }
|
||||||
|
{ print }
|
||||||
|
END { if (!found) print key "=" value }
|
||||||
|
' "$ENV_FILE" > "$tmp"
|
||||||
|
mv "$tmp" "$ENV_FILE"
|
||||||
|
chmod 0600 "$ENV_FILE" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
load_env_file() {
|
load_env_file() {
|
||||||
# .env is intentionally limited to simple KEY=value entries used by Compose.
|
# .env is intentionally limited to simple KEY=value entries used by Compose.
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
@@ -41,7 +62,55 @@ load_env_file() {
|
|||||||
set +a
|
set +a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normalize_http_settings() {
|
||||||
|
local url=${REPOSITORY_URL%/}
|
||||||
|
local rest hostpart explicit_port=""
|
||||||
|
|
||||||
|
HTTP_PORT=${HTTP_PORT:-}
|
||||||
|
|
||||||
|
case "$url" in
|
||||||
|
http://*)
|
||||||
|
rest=${url#http://}
|
||||||
|
[[ "$rest" != */* ]] || die "REPOSITORY_URL dla wbudowanego HTTP nie moze zawierac sciezki: $REPOSITORY_URL"
|
||||||
|
hostpart=$rest
|
||||||
|
|
||||||
|
if [[ "$hostpart" =~ ^\[[^]]+\]:([0-9]+)$ ]]; then
|
||||||
|
explicit_port=${BASH_REMATCH[1]}
|
||||||
|
elif [[ "$hostpart" =~ ^[^:]+:([0-9]+)$ ]]; then
|
||||||
|
explicit_port=${BASH_REMATCH[1]}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$explicit_port" ]]; then
|
||||||
|
HTTP_PORT=$explicit_port
|
||||||
|
else
|
||||||
|
HTTP_PORT=${HTTP_PORT:-$DEFAULT_HTTP_PORT}
|
||||||
|
url="${url}:${HTTP_PORT}"
|
||||||
|
REPOSITORY_URL=$url
|
||||||
|
export REPOSITORY_URL
|
||||||
|
update_env_var REPOSITORY_URL "$REPOSITORY_URL"
|
||||||
|
log "REPOSITORY_URL bez portu - ustawiono $REPOSITORY_URL"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
https://*)
|
||||||
|
# HTTPS is supported as a public URL only when an external TLS proxy points
|
||||||
|
# to the local micro HTTP service. The embedded service itself is HTTP only.
|
||||||
|
HTTP_PORT=${HTTP_PORT:-$DEFAULT_HTTP_PORT}
|
||||||
|
warn "REPOSITORY_URL uzywa HTTPS. Wbudowany serwer nasluchuje po zwyklym HTTP na porcie $HTTP_PORT; HTTPS wymaga zewnetrznego reverse proxy/TLS."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
die "REPOSITORY_URL musi zaczynac sie od http:// albo https://"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[[ "$HTTP_PORT" =~ ^[0-9]+$ ]] || die "HTTP_PORT musi byc liczba"
|
||||||
|
(( HTTP_PORT >= 1024 && HTTP_PORT <= 65535 )) || die "HTTP_PORT musi byc z zakresu 1024-65535"
|
||||||
|
|
||||||
|
export HTTP_PORT
|
||||||
|
update_env_var HTTP_PORT "$HTTP_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
require_cmd docker
|
require_cmd docker
|
||||||
|
require_cmd awk
|
||||||
if ! docker info >/dev/null 2>&1; then
|
if ! docker info >/dev/null 2>&1; then
|
||||||
die "Docker daemon nie jest dostepny dla aktualnego uzytkownika"
|
die "Docker daemon nie jest dostepny dla aktualnego uzytkownika"
|
||||||
fi
|
fi
|
||||||
@@ -66,10 +135,8 @@ case "$SOURCE_REPO" in
|
|||||||
http://*|https://*|ssh://*|git@*|file://*) ;;
|
http://*|https://*|ssh://*|git@*|file://*) ;;
|
||||||
*) die "SOURCE_REPO ma nieobslugiwany format: $SOURCE_REPO" ;;
|
*) die "SOURCE_REPO ma nieobslugiwany format: $SOURCE_REPO" ;;
|
||||||
esac
|
esac
|
||||||
case "$REPOSITORY_URL" in
|
|
||||||
http://*|https://*) ;;
|
normalize_http_settings
|
||||||
*) die "REPOSITORY_URL musi zaczynac sie od http:// albo https://" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
mkdir -p "$DATA_DIR/repository" "$DATA_DIR/state" "$DATA_DIR/cargo" "$DATA_DIR/work" "$GPG_DIR"
|
mkdir -p "$DATA_DIR/repository" "$DATA_DIR/state" "$DATA_DIR/cargo" "$DATA_DIR/work" "$GPG_DIR"
|
||||||
chmod 0700 "$GPG_DIR" 2>/dev/null || true
|
chmod 0700 "$GPG_DIR" 2>/dev/null || true
|
||||||
@@ -80,8 +147,11 @@ log "budowanie obrazu buildera"
|
|||||||
if [[ ! -s "$GPG_DIR/private-key.asc" ]]; then
|
if [[ ! -s "$GPG_DIR/private-key.asc" ]]; then
|
||||||
log "brak klucza GPG - generowanie stalego klucza podpisujacego"
|
log "brak klucza GPG - generowanie stalego klucza podpisujacego"
|
||||||
|
|
||||||
IMAGE_REF=$("${COMPOSE[@]}" --env-file "$ENV_FILE" config | awk '$1 == "image:" {print $2; exit}')
|
IMAGE_REF=$("${COMPOSE[@]}" --env-file "$ENV_FILE" config | awk '''
|
||||||
[[ -n "$IMAGE_REF" ]] || die "nie mozna ustalic nazwy zbudowanego obrazu"
|
$1 == "rust-package-builder:" { in_builder=1; next }
|
||||||
|
in_builder && $1 == "image:" { print $2; exit }
|
||||||
|
''')
|
||||||
|
[[ -n "$IMAGE_REF" ]] || die "nie mozna ustalic nazwy zbudowanego obrazu buildera"
|
||||||
docker image inspect "$IMAGE_REF" >/dev/null 2>&1 || die "zbudowany obraz nie istnieje: $IMAGE_REF"
|
docker image inspect "$IMAGE_REF" >/dev/null 2>&1 || die "zbudowany obraz nie istnieje: $IMAGE_REF"
|
||||||
|
|
||||||
uid=$(id -u)
|
uid=$(id -u)
|
||||||
@@ -127,8 +197,8 @@ else
|
|||||||
log "uzywanie istniejacego klucza GPG"
|
log "uzywanie istniejacego klucza GPG"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "uruchamianie buildera przez Docker Compose"
|
log "uruchamianie buildera i mikro HTTP przez Docker Compose"
|
||||||
"${COMPOSE[@]}" --env-file "$ENV_FILE" up -d "$SERVICE"
|
"${COMPOSE[@]}" --env-file "$ENV_FILE" up -d "$SERVICE" "$HTTP_SERVICE"
|
||||||
|
|
||||||
fingerprint=""
|
fingerprint=""
|
||||||
if [[ -r "$GPG_DIR/fingerprint" ]]; then
|
if [[ -r "$GPG_DIR/fingerprint" ]]; then
|
||||||
@@ -139,11 +209,18 @@ log "gotowe"
|
|||||||
printf '\n'
|
printf '\n'
|
||||||
printf 'SOURCE_REPO: %s\n' "$SOURCE_REPO"
|
printf 'SOURCE_REPO: %s\n' "$SOURCE_REPO"
|
||||||
printf 'REPOSITORY_URL: %s\n' "$REPOSITORY_URL"
|
printf 'REPOSITORY_URL: %s\n' "$REPOSITORY_URL"
|
||||||
|
printf 'HTTP port: %s\n' "$HTTP_PORT"
|
||||||
[[ -n "$fingerprint" ]] && printf 'GPG fingerprint: %s\n' "$fingerprint"
|
[[ -n "$fingerprint" ]] && printf 'GPG fingerprint: %s\n' "$fingerprint"
|
||||||
printf 'Publiczny katalog: %s\n' "$DATA_DIR/repository"
|
printf 'Publiczny katalog: %s\n' "$DATA_DIR/repository"
|
||||||
printf '\n'
|
printf '\n'
|
||||||
printf 'Podglad logow:\n '
|
printf 'Repo/strona: %s/\n' "${REPOSITORY_URL%/}"
|
||||||
|
printf 'Healthcheck: http://127.0.0.1:%s/healthz\n' "$HTTP_PORT"
|
||||||
|
printf '\n'
|
||||||
|
printf 'Podglad logow buildera:\n '
|
||||||
printf '%q ' "${COMPOSE[@]}"
|
printf '%q ' "${COMPOSE[@]}"
|
||||||
printf 'logs -f %q\n' "$SERVICE"
|
printf 'logs -f %q\n' "$SERVICE"
|
||||||
|
printf 'Podglad logow HTTP:\n '
|
||||||
|
printf '%q ' "${COMPOSE[@]}"
|
||||||
|
printf 'logs -f %q\n' "$HTTP_SERVICE"
|
||||||
printf '\n'
|
printf '\n'
|
||||||
printf 'Builder natychmiast sprawdzi branch master (lub SOURCE_BRANCH, jezeli ustawiono go opcjonalnie) i po udanym buildzie opublikuje repozytorium.\n'
|
printf 'Builder natychmiast sprawdzi branch master (lub SOURCE_BRANCH, jezeli ustawiono go opcjonalnie) i po udanym buildzie opublikuje repozytorium.\n'
|
||||||
+14
-1
@@ -7,6 +7,7 @@ for f in "$ROOT"/start.sh "$ROOT"/scripts/*.sh "$ROOT"/tests/*.sh; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
grep -q 'FROM rockylinux:9' "$ROOT/Dockerfile"
|
grep -q 'FROM rockylinux:9' "$ROOT/Dockerfile"
|
||||||
|
grep -q 'dnf -y install --allowerasing' "$ROOT/Dockerfile"
|
||||||
grep -q 'python3.11' "$ROOT/Dockerfile"
|
grep -q 'python3.11' "$ROOT/Dockerfile"
|
||||||
grep -q 'RUST_PROXY_PYTHON=/usr/bin/python3.11' "$ROOT/Dockerfile"
|
grep -q 'RUST_PROXY_PYTHON=/usr/bin/python3.11' "$ROOT/Dockerfile"
|
||||||
grep -q 'nfpm package' "$ROOT/scripts/build-release.sh"
|
grep -q 'nfpm package' "$ROOT/scripts/build-release.sh"
|
||||||
@@ -18,10 +19,22 @@ grep -q 'repo add -force-replace' "$ROOT/scripts/build-release.sh"
|
|||||||
grep -q '/run/package-builder/gnupg' "$ROOT/scripts/entrypoint.sh"
|
grep -q '/run/package-builder/gnupg' "$ROOT/scripts/entrypoint.sh"
|
||||||
grep -q 'rustproxy-overview.json' "$ROOT/scripts/prepare-package.sh"
|
grep -q 'rustproxy-overview.json' "$ROOT/scripts/prepare-package.sh"
|
||||||
grep -q 'debian/${PACKAGE_NAME}.service' "$ROOT/scripts/prepare-package.sh"
|
grep -q 'debian/${PACKAGE_NAME}.service' "$ROOT/scripts/prepare-package.sh"
|
||||||
grep -q 'gpg/private-key.asc' "$ROOT/start.sh"
|
grep -q 'private-key.asc' "$ROOT/start.sh"
|
||||||
grep -q 'docker compose' "$ROOT/start.sh"
|
grep -q 'docker compose' "$ROOT/start.sh"
|
||||||
grep -q 'Key-Type: RSA' "$ROOT/start.sh"
|
grep -q 'Key-Type: RSA' "$ROOT/start.sh"
|
||||||
|
grep -q 'DEFAULT_HTTP_PORT=18765' "$ROOT/start.sh"
|
||||||
|
grep -q 'repository-http' "$ROOT/start.sh"
|
||||||
grep -Fq '${SOURCE_REPO:?SOURCE_REPO is required}' "$ROOT/docker-compose.yml"
|
grep -Fq '${SOURCE_REPO:?SOURCE_REPO is required}' "$ROOT/docker-compose.yml"
|
||||||
grep -Fq '${REPOSITORY_URL:?REPOSITORY_URL is required}' "$ROOT/docker-compose.yml"
|
grep -Fq '${REPOSITORY_URL:?REPOSITORY_URL is required}' "$ROOT/docker-compose.yml"
|
||||||
|
grep -q 'nginxinc/nginx-unprivileged:stable-alpine-slim' "$ROOT/docker-compose.yml"
|
||||||
|
grep -q 'read_only: true' "$ROOT/docker-compose.yml"
|
||||||
|
grep -q 'no-new-privileges:true' "$ROOT/docker-compose.yml"
|
||||||
|
grep -q 'cap_drop:' "$ROOT/docker-compose.yml"
|
||||||
|
grep -Fq '0.0.0.0:${HTTP_PORT:-18765}:8080' "$ROOT/docker-compose.yml"
|
||||||
|
grep -q 'server_tokens off' "$ROOT/http/nginx.conf"
|
||||||
|
grep -q 'GET|HEAD' "$ROOT/http/nginx.conf"
|
||||||
|
grep -q 'autoindex off' "$ROOT/http/nginx.conf"
|
||||||
|
grep -q 'X-Content-Type-Options' "$ROOT/http/nginx.conf"
|
||||||
|
grep -q 'healthz' "$ROOT/http/nginx.conf"
|
||||||
|
|
||||||
echo 'static checks: OK'
|
echo 'static checks: OK'
|
||||||
Reference in new issue
Block a user