294 lines
8.4 KiB
Nix
294 lines
8.4 KiB
Nix
{ config, pkgs, ... }:
|
|
let
|
|
webroot = "/var/www";
|
|
geminiRoot = "/var/gemini";
|
|
deployCommand = "${pkgs.systemd}/bin/systemctl start deploy-c3d2-web.service";
|
|
in
|
|
{
|
|
microvm.vcpu = 8;
|
|
microvm.mem = 1024;
|
|
c3d2.deployment = {
|
|
server = "server10";
|
|
mounts = [ "etc" "home" "var"];
|
|
};
|
|
boot.tmpOnTmpfs = true;
|
|
system.stateVersion = "22.05";
|
|
# Network setup
|
|
networking.hostName = "c3d2-web";
|
|
networking.firewall.allowedTCPPorts = [
|
|
# telme10
|
|
23
|
|
# http/https
|
|
80 443
|
|
# gemini
|
|
1965
|
|
];
|
|
|
|
security.acme.certs = {
|
|
# agate cannot load "ec256" keys
|
|
"www.c3d2.de".keyType = "rsa4096";
|
|
};
|
|
|
|
# Web server
|
|
services.nginx = {
|
|
enable = true;
|
|
virtualHosts = {
|
|
# c3d2
|
|
"www.c3d2.de" = {
|
|
default = true;
|
|
serverAliases = [
|
|
"c3d2.de"
|
|
"c3dd.de" "www.c3dd.de" "openpgpkey.c3d2.de"
|
|
"cccdd.de" "www.cccdd.de"
|
|
"dresden.ccc.de" "www.dresden.ccc.de"
|
|
"netzbiotop.org" "www.netzbiotop.org"
|
|
];
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
root = "${webroot}/c3d2";
|
|
extraConfig = ''
|
|
index portal.html index.html;
|
|
'';
|
|
locations = {
|
|
# Mastodon
|
|
"/.well-known/webfinger".return = "301 https://c3d2.social$request_uri";
|
|
|
|
# SpaceAPI
|
|
"/status.png".proxyPass = "http://[${config.c3d2.hosts.spaceapi.ip6}]:3000/status.png";
|
|
"/spaceapi.json".proxyPass = "http://[${config.c3d2.hosts.spaceapi.ip6}]:3000/spaceapi.json";
|
|
|
|
# WKD: Web Key Directory for PGP Keys
|
|
"/openpgp" = {
|
|
extraConfig = ''
|
|
autoindex off;
|
|
default_type "application/octet-stream";
|
|
add_header Access-Control-Allow-Origin "* always";
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
# datenspuren
|
|
"datenspuren.de" = {
|
|
serverAliases = [
|
|
"www.datenspuren.de"
|
|
"ds.c3d2.de" "datenspuren.c3d2.de"
|
|
];
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
root = "${webroot}/c3d2/datenspuren";
|
|
extraConfig = ''
|
|
index index.html;
|
|
rewrite ^/$ /2022/ redirect;
|
|
'';
|
|
};
|
|
|
|
# autotopia
|
|
"autotopia.c3d2.de" = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
root = "${webroot}/c3d2/autotopia";
|
|
extraConfig = ''
|
|
index index.html;
|
|
rewrite ^/$ /2020/ redirect;
|
|
'';
|
|
};
|
|
|
|
# hooks, logs
|
|
"c3d2-web.serv.zentralwerk.org" = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
root = webroot;
|
|
locations."/hooks/".proxyPass = "http://localhost:9000/hooks/";
|
|
};
|
|
};
|
|
};
|
|
# Gemini server
|
|
services.agate = {
|
|
enable = true;
|
|
addresses = [
|
|
# sysctl net.ipv6.bindv6only = 0
|
|
"[::]:1965"
|
|
];
|
|
certificatesDir = "/var/lib/agate/certificates";
|
|
contentDir = geminiRoot;
|
|
language = "de";
|
|
};
|
|
|
|
systemd.packages = with pkgs; [ telme10 ];
|
|
|
|
systemd.services.telme10 = {
|
|
serviceConfig = {
|
|
AmbientCapabilities="CAP_NET_BIND_SERVICE";
|
|
};
|
|
};
|
|
|
|
# let agate access the tls certs
|
|
systemd.services.agate = {
|
|
requires = [ "agate-keys.service" ];
|
|
after = [ "agate-keys.service" ];
|
|
serviceConfig = {
|
|
Group = "keys";
|
|
};
|
|
};
|
|
systemd.services.agate-keys = {
|
|
path = with pkgs; [ openssl ];
|
|
script = let
|
|
stateDir = "/var/lib/agate/certificates";
|
|
in ''
|
|
mkdir -p ${stateDir}
|
|
openssl x509 \
|
|
-in /var/lib/acme/www.c3d2.de/cert.pem \
|
|
-out ${stateDir}/cert.der \
|
|
-outform DER
|
|
openssl rsa \
|
|
-in /var/lib/acme/www.c3d2.de/key.pem \
|
|
-out ${stateDir}/key.der \
|
|
-outform DER
|
|
chown root:keys ${stateDir}/*
|
|
chmod 0640 ${stateDir}/*
|
|
'';
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
};
|
|
};
|
|
|
|
# Build user
|
|
users.groups.c3d2-web = {};
|
|
users.users.c3d2-web = {
|
|
isSystemUser = true;
|
|
group = "c3d2-web";
|
|
home = "/var/lib/c3d2-web";
|
|
};
|
|
|
|
users.groups.telme10 = {};
|
|
users.users.telme10 = {
|
|
isSystemUser = true;
|
|
group = "telme10";
|
|
};
|
|
|
|
systemd.tmpfiles.rules = with config.users.users.c3d2-web; [
|
|
"d ${webroot}/c3d2 0755 c3d2-web ${group} -"
|
|
"d ${webroot}/log 0755 c3d2-web ${group} -"
|
|
"d ${geminiRoot} 0755 c3d2-web ${group} -"
|
|
"d ${home} 0700 c3d2-web ${group} -"
|
|
];
|
|
|
|
# Build script
|
|
systemd.services.deploy-c3d2-web = {
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-online.target" ];
|
|
path = with pkgs; [
|
|
git nix curl
|
|
(libxslt.override {
|
|
cryptoSupport = true;
|
|
}) libxml2 wget rsync gnumake bash
|
|
];
|
|
script = ''
|
|
# Build at least once
|
|
touch ${config.users.users.c3d2-web.home}/deploy-pending
|
|
|
|
status() {
|
|
curl -X POST \
|
|
"https://gitea.c3d2.de/api/v1/repos/c3d2/c3d2-web/statuses/$REV?token=${pkgs.c3d2-web.giteaToken}" \
|
|
-H "Accept: application/json" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$1"
|
|
}
|
|
|
|
if [ ! -d c3d2-web ]; then
|
|
git clone --depth=1 https://gitea.c3d2.de/c3d2/c3d2-web.git
|
|
cd c3d2-web
|
|
else
|
|
cd c3d2-web
|
|
git fetch origin
|
|
git reset --hard origin/master
|
|
|
|
# `make export` may have created read-only files,
|
|
# fix that before cleaning up
|
|
chmod -R u+w .
|
|
git clean -d -f -x
|
|
fi
|
|
|
|
# Loop in case the webhook was called while we were building
|
|
while [ -e ${config.users.users.c3d2-web.home}/deploy-pending ]; do
|
|
rm ${config.users.users.c3d2-web.home}/deploy-pending
|
|
git pull
|
|
REV=$(git rev-parse HEAD)
|
|
|
|
# web
|
|
set +e
|
|
status "{ \"context\": \"c3d2-web\", \"description\": \"building...\", \"state\": \"pending\", \"target_url\": \"https://c3d2-web.serv.zentralwerk.org/log/build-$REV.txt\"}"
|
|
|
|
make -j$(nproc) export DESTDIR=${webroot}/c3d2 \
|
|
&> ${webroot}/log/build-$REV.txt
|
|
|
|
if [ $? = 0 ]; then
|
|
status "{ \"context\": \"c3d2-web\", \"description\": \"deployed\", \"state\": \"success\", \"target_url\": \"https://c3d2-web.serv.zentralwerk.org/log/build-$REV.txt\"}"
|
|
else
|
|
status "{ \"context\": \"c3d2-web\", \"description\": \"build failure\", \"state\": \"failure\", \"target_url\": \"https://c3d2-web.serv.zentralwerk.org/log/build-$REV.txt\"}"
|
|
fi
|
|
|
|
git clean -fx
|
|
# gemini
|
|
status "{ \"context\": \"c3d2-gemini\", \"description\": \"building...\", \"state\": \"pending\", \"target_url\": \"https://c3d2-web.serv.zentralwerk.org/log/build-gemini-$REV.txt\"}"
|
|
|
|
make -f Makefile.gemini -j$(nproc) export DESTDIR=${geminiRoot} \
|
|
&> ${webroot}/log/build-gemini-$REV.txt
|
|
|
|
if [ $? = 0 ]; then
|
|
status "{ \"context\": \"c3d2-gemini\", \"description\": \"deployed\", \"state\": \"success\", \"target_url\": \"https://c3d2-web.serv.zentralwerk.org/log/build-gemini-$REV.txt\"}"
|
|
else
|
|
status "{ \"context\": \"c3d2-gemini\", \"description\": \"build failure\", \"state\": \"failure\", \"target_url\": \"https://c3d2-web.serv.zentralwerk.org/log/build-gemini-$REV.txt\"}"
|
|
fi
|
|
set -e
|
|
done
|
|
'';
|
|
serviceConfig = {
|
|
User = "c3d2-web";
|
|
Group = config.users.users.c3d2-web.group;
|
|
PrivateTmp = true;
|
|
ProtectSystem = "full";
|
|
WorkingDirectory = config.users.users.c3d2-web.home;
|
|
ReadWritePaths = [ webroot config.users.users.c3d2-web.home ];
|
|
};
|
|
};
|
|
|
|
systemd.timers.deploy-c3d2-web = {
|
|
partOf = [ "deploy-c3d2-web.service" ];
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig.OnCalendar = "hourly";
|
|
};
|
|
|
|
security.sudo.extraRules = [ {
|
|
users = [ "c3d2-web" ];
|
|
commands = [ {
|
|
command = deployCommand;
|
|
options = [ "NOPASSWD" ];
|
|
} ];
|
|
} ];
|
|
|
|
systemd.services.webhook =
|
|
let
|
|
hooksJson = pkgs.writeText "hooks.json" (builtins.toJSON [ {
|
|
id = "deploy-c3d2-web";
|
|
execute-command = pkgs.writeShellScript "deploy-c3d2-web" ''
|
|
# Request (re-)deployment
|
|
touch ${config.users.users.c3d2-web.home}/deploy-pending
|
|
|
|
# Start deploy-c3d2-web.service if not already running
|
|
exec /run/wrappers/bin/sudo ${deployCommand}
|
|
'';
|
|
} ]);
|
|
in {
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
ExecStart = "${pkgs.webhook}/bin/webhook -hooks ${hooksJson} -verbose -ip 127.0.0.1";
|
|
User = "c3d2-web";
|
|
Group = config.users.users.c3d2-web.group;
|
|
PrivateTmp = true;
|
|
ProtectSystem = "full";
|
|
};
|
|
};
|
|
}
|