Hosting von Gitea und FreshRSS unter OpenBSD mit httpd und relayd
Read this article in English language
Dieser Artikel versucht, die wichtigsten Schritte zu dokumentieren, um folgendes mit einem zuvor frisch installierten OpenBSD 7.4 zu erreichen:
- Statische Dateien über HTTP(S) bereitstellen
- Hosten einer Gitea-Instanz über HTTPS
- Hosten einer PHP-basierten FreshRSS-Instanz über HTTP(S)
- Let's Encrypt-Zertifikate installieren und verwalten
- Alle oben genannten Dienste über IPv4 und IPv6 bereitstellen
- SSH über sshguard schützen
- Ein vernünftiges Regelwerk zur sicheren Paketfilterung implementieren
Das Ziel ist es, eingebaute Dienste wie httpd, relayd und acme anstelle von Drittanbieterpaketen zu verwenden. Du wirst hier keine Installation/Konfiguration von Datenbanksystemen finden, da ich SQLite sowohl für Gitea als auch für FreshRSS verwende. Der Schwerpunkt liegt auf den Serverkomponenten, nicht auf den gehosteten Anwendungen, daher werde ich nicht näher darauf eingehen, wie diese zu konfigurieren sind.
Paketfilter: pf und sshguard
Installiere sshguard:
$ pkg_add sshguard
Bearbeite /etc/pf.conf, füge das Folgende ein und passe es an deine Bedürfnisse an:
table <martians> {
0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 \
127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 \
192.0.0.0/24 192.0.2.0/24 192.88.99.0/24 \
192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 \
203.0.113.0/24 224.0.0.0/3 255.255.255.255/32 \
::/128 ::/96 ::1/128 ::ffff:0:0/96 100::/64 \
2001:10::/28 2001:2::/48 2001:db8::/32 \
3ffe::/16 fec0::/10 fc00::/7 }
## Set http(80)/https (443) port here ##
webports = "{http, https}"
## enable these services ##
int_tcp_services = "{domain, ntp, smtp, www, https, ftp, ssh, 31415}"
int_udp_services = "{domain, ntp}"
set block-policy drop
set loginterface egress
## Skip loop back interface - Skip all PF processing on interface ##
set skip on lo
match in all scrub (no-df random-id max-mss 1440)
match out on egress inet from !(egress:network) to any nat-to (egress:0)
## Blocking spoofed packets
antispoof quick for egress
# Drop all Non-Routable Addresses
block in quick on egress from <martians> to any
block return out quick on egress from any to <martians>
## Set default policy ##
block all
table <sshguard> persist
block in quick proto tcp from <sshguard>
pass out quick
# Allow SSH
pass in inet proto tcp to egress port ssh
pass in inet6 proto tcp to egress port ssh
# Allow ICMP
pass in on egress inet proto icmp all icmp-type echoreq
pass in on egress inet6 proto icmp6 all icmp6-type echoreq
# Allow access to httpd and relayd
pass in inet proto { tcp udp } to egress port $webports
pass in inet6 proto { tcp udp } to egress port $webports
# Allow essential outgoing traffic
pass out quick proto tcp to any port $int_tcp_services
pass out quick proto udp to any port $int_udp_services
pass out quick inet6 proto tcp to any port $int_tcp_services
pass out quick inet6 proto udp to any port $int_udp_services
Öffne eine screen-Session und führe die folgenden Schritte aus:
$ sleep 120; pfctl -d
So können wir die Pf-Konfiguration neu laden, ohne uns dauerhaft auszusperren. Wenn wir einen Fehler machen, wird pf spätestens nach 2 Minuten deaktiviert, sodass wir uns wieder per SSH einloggen und den Fehler beheben können.
$ pfctl -n -f /etc/pf.conf
$ pfctl -f /etc/pf.conf
Versuche, dich nach dem Neuladen der Konfiguration per SSH einzuloggen, um sicherzustellen, dass zumindest dies noch funktioniert. Wenn alles in Ordnung ist, vergiss nicht, den Prozess "sleep 120; pfctl -d" zu beenden.
Webserver: httpd und PHP
In diesem Schritt werden wir httpd so konfigurieren, dass er statische HTML- und PHP-Dateien ausliefert. Außerdem wird er für den Abruf von SSL-Zertifikaten von Let's Encrypt über den ACME-Client benötigt.
Bearbeite /etc/httpd, füge das Folgende ein und passe es an deine Bedürfnisse an:
server "static.dk1mi.radio" {
listen on * port 80
root "/htdocs/static.dk1mi.radio"
log style combined
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
server "blogs.radio" {
listen on * port 80
location "/*.php*" { fastcgi socket "/run/php-fpm.sock" }
root "/freshrss/p/"
directory index index.php
log style combined
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
server "git.dk1mi.radio" {
listen on * port 80
log style combined
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
location * {
block return 301 "https://$HTTP_HOST$REQUEST_URI"
}
}
Enable und starte PHP:
$ rcctl enable php81_fpm
$ rcctl start php81_fpm
Enable und starte httpd:
$ rcctl enable httpd
$ rcctl start httpd
Jetzt ist es an der Zeit, ACME so zu konfigurieren, dass es unsere SSL-Zertifikate abruft:
Bearbeite /etc/acme-client.conf, füge das Folgende ein und passe es an deine Bedürfnisse an:
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-privkey.pem"
}
authority letsencrypt-staging {
api url "https://acme-staging.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-staging-privkey.pem"
}
domain blogs.radio {
domain key "/etc/ssl/private/blogs.radio.key"
domain full chain certificate "/etc/ssl/blogs.radio.crt"
sign with letsencrypt
}
domain git.dk1mi.radio {
domain key "/etc/ssl/private/git.dk1mi.radio.key"
domain full chain certificate "/etc/ssl/git.dk1mi.radio.crt"
sign with letsencrypt
}
domain static.dk1mi.radio {
domain key "/etc/ssl/private/static.dk1mi.radio.key"
domain full chain certificate "/etc/ssl/static.dk1mi.radio.crt"
sign with letsencrypt
}
Wir können jetzt unsere neuen Zertifikate erstellen und abrufen:
$ /usr/sbin/acme-client -v blogs.radio
$ /usr/sbin/acme-client -v git.dk1mi.radio
$ /usr/sbin/acme-client -v static.dk1mi.radio
Um sicherzustellen, dass sie regelmäßig aktualisiert werden, fügen wir die folgenden Zeilen in unsere crontab ein:
$ crontab -e
0 2 * * * /usr/sbin/acme-client -v beta.blogs.radio >> /tmp/acme.log 2>&1
5 2 * * * /usr/sbin/acme-client -v git.dk1mi.radio >> /tmp/acme.log 2>&1
10 2 * * * /usr/sbin/acme-client -v static.dk1mi.radio >> /tmp/acme.log 2>&1
Nachdem dies geschehen ist, können wir relayd konfigurieren und starten.
Relay Daemon: relayd
Wir werden relayd nutzen, um SSL-Verbindungen zu beenden und HTTP-Anfragen intern entweder an httpd oder den gitea-Daemon weiterzuleiten.
Bearbeite dazu /etc/relayd, füge das Folgende ein und passe es an deine Bedürfnisse an:
# Macros -----------------------------------
ext_ipv4="46.23.93.217"
ext_ipv6="2a03:6000:93f4:614::217"
webhost="127.0.0.1"
webhost6="::1"
table <webserver> { $webhost }
table <gitea> { $webhost }
table <webserver6> { $webhost6 }
http protocol https {
tls keypair git.dk1mi.radio
tls keypair static.dk1mi.radio
tls keypair blogs.radio
block
pass request header "Host" value "git.dk1mi.radio" \
forward to <gitea>
pass request header "Host" value "static.dk1mi.radio" \
forward to <webserver>
pass request header "Host" value "blogs.radio" \
forward to <webserver>
}
relay https {
listen on $ext_ipv4 port 443 tls
protocol https
forward to <webserver> port 80 mode roundrobin \
check http "/" code 200
forward to <gitea> port 3000
}
relay https6 {
listen on $ext_ipv6 port 443 tls
protocol https
forward to <webserver6> port 80 mode roundrobin \
check http "/" code 200
forward to <gitea> port 3000
}
Enable und starte relayd:
$ rcctl enable relayd
$ rcctl start relayd
FreshRSS
Installiere FreshRSS und einige Abhängigkeiten:
$ pkg_add freshrss php-zip php-curl
FreshRSS sollte jetzt über deinen Browser erreichbar und konfigurierbar sein.
Füge die folgende Zeile in deine crontab ein, um alle RSS-Feeds automatisch alle 5 Minuten zu aktualisieren:
*/5 * * * * doas -u www /usr/local/bin/php -f /var/www/freshrss/app/actualize_script.php > /tmp/FreshRSS.log 2>&1
Gitea
$ pkg_add gitea
Enable und starte gitea sowie gitdaemon:
$ rcctl enable gitea gitdaemon
$ rcctl enable gitea gitdaemon
$ rcctl start gitdaemon
$ rcctl start gitdaemon
Gitea sollte jetzt über deinen Browser erreichbar und konfigurierbar sein.