From b4dda63855d5b1fb5dd18e78dfe0ca1b59ddbb15 Mon Sep 17 00:00:00 2001 From: Collin J. Doering Date: Fri, 31 May 2024 09:08:19 -0400 Subject: balg02: Initial implementation of Guix build farm * .guix/guix-na/config/balg02.scm (%automation-user): Removed variable (only used in one place). (%cuirass-specs): New variable. (Initial) Cuirass specifications that are run by the guix-na build farm. (publish-robots.txt): New variable. Used in nginx configuration. (publish-locations): New function. Defines nginx publish locations (used for http and https servers). (balg02-locations): New function. Defines nginx publish locations used for guix-na. (%publish-url): New variable. String representing local publish URL. (%tls-settings): New variable. Captures nginx tls settings used in nginx configuration. (le): New function. Generates lets encrypt path given a host and optionally a private key. (languages-to-accept): New variable. Languages for i8ln. (accept-languages): New function. (%balg02-servers): New variable. balg02 nginx servers. (%extra-content): New variable. Extra nginx configuration content. (%nginx-configuration): New variable. Capture nginx configuration given all previous helper functions and variables. (%nginx-cache-activation): New variable. Service the ensures /var/cache/nginx exists on the first run. (%nginx-deploy-hook): New variable. Hook used upon cerbot certificate updates. (balg02 (packages)): Add btrfs-progs. (balg02): Remove root ssh key (not necessary as ssh root login is disabled). (balg02 (services)): Add cuirass, certbot, nginx, and guix-publish services. --- .guix/guix-na/config/balg02.scm | 419 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 413 insertions(+), 6 deletions(-) (limited to '.guix/guix-na/config') diff --git a/.guix/guix-na/config/balg02.scm b/.guix/guix-na/config/balg02.scm index ab17951..fb211ea 100644 --- a/.guix/guix-na/config/balg02.scm +++ b/.guix/guix-na/config/balg02.scm @@ -22,14 +22,400 @@ #:use-module (gnu system) #:use-module (gnu packages bash) #:use-module (gnu packages shells) + #:use-module (gnu packages web) #:use-module (gnu services base) #:use-module (gnu services cuirass) + #:use-module (gnu services certbot) #:use-module (gnu services networking) #:use-module (gnu services ssh) #:use-module (gnu services web) #:export (balg02 %system)) -(define %automation-user "auto") +(define %cuirass-specs + #~(list (specification + (name "guix") + (priority 0) + (build '(channels guix)) + (channels %default-channels)))) + +;; Taken from: https://git.savannah.gnu.org/cgit/guix/maintenance.git/tree/hydra/nginx/berlin.scm +(define publish-robots.txt + ;; Try to prevent good-faith crawlers from downloading substitutes. Allow + ;; indexing the root—which is expected to be static or cheap—to remain visible + ;; in search engine results for, e.g., ‘Guix CI’. + "\ +User-agent: *\r +Disallow: /\r +Allow: /$\r +\r +") + +(define (publish-locations url) + "Return the nginx location blocks for 'guix publish' running on URL." + (list (nginx-location-configuration + (uri "/nix-cache-info") + (body + (list + (string-append + "proxy_pass " url "/nix-cache-info;") + ;; Cache this file since that's always the first thing we ask + ;; for. + "proxy_cache static;" + "proxy_cache_valid 200 100d;" ; cache hits for a looong time. + "proxy_cache_valid any 5m;" ; cache misses/others for 5 min. + "proxy_ignore_client_abort on;" + + ;; We need to hide and ignore the Set-Cookie header to enable + ;; caching. + "proxy_hide_header Set-Cookie;" + "proxy_ignore_headers Set-Cookie;"))) + + (nginx-location-configuration + (uri "/nar/") + (body + (list + (string-append "proxy_pass " url ";") + "client_body_buffer_size 256k;" + + ;; Be more tolerant of delays when fetching a nar. + "proxy_read_timeout 60s;" + "proxy_send_timeout 60s;" + + ;; Enable caching for nar files, to avoid reconstructing and + ;; recompressing archives. + "proxy_cache nar;" + "proxy_cache_valid 200 30d;" ; cache hits for 1 month + "proxy_cache_valid 504 3m;" ; timeout, when hydra.gnu.org is overloaded + "proxy_cache_valid any 1h;" ; cache misses/others for 1h. + + "proxy_ignore_client_abort on;" + + ;; Nars are already compressed. + "gzip off;" + + ;; We need to hide and ignore the Set-Cookie header to enable + ;; caching. + "proxy_hide_header Set-Cookie;" + "proxy_ignore_headers Set-Cookie;" + + ;; Provide a 'content-length' header so that 'guix + ;; substitute-binary' knows upfront how much it is downloading. + ;; "add_header Content-Length $body_bytes_sent;" + ))) + + (nginx-location-configuration + (uri "~ \\.narinfo$") + (body + (list + ;; Since 'guix publish' has its own caching, and since it relies + ;; on the atime of cached narinfos to determine whether a + ;; narinfo can be removed from the cache, don't do any caching + ;; here. + (string-append "proxy_pass " url ";") + + ;; For HTTP pipelining. This has a dramatic impact on + ;; performance. + "client_body_buffer_size 128k;" + + ;; Narinfos requests are short, serve many of them on a + ;; connection. + "keepalive_requests 600;" + + ;; Do not tolerate slowness of hydra.gnu.org when fetching + ;; narinfos: better return 504 quickly than wait forever. + "proxy_connect_timeout 10s;" + "proxy_read_timeout 10s;" + "proxy_send_timeout 10s;" + + ;; 'guix publish --ttl' produces a 'Cache-Control' header for + ;; use by 'guix substitute'. Let it through rather than use + ;; nginx's "expire" directive since the expiration time defined + ;; by 'guix publish' is the right one. + "proxy_pass_header Cache-Control;" + + "proxy_ignore_client_abort on;" + + ;; We need to hide and ignore the Set-Cookie header to enable + ;; caching. + "proxy_hide_header Set-Cookie;" + "proxy_ignore_headers Set-Cookie;"))) + + ;; Content-addressed files served by 'guix publish'. + (nginx-location-configuration + (uri "/file/") + (body + (list + (string-append "proxy_pass " url ";") + + "proxy_cache cas;" + "proxy_cache_valid 200 200d;" ; cache hits + "proxy_cache_valid any 5m;" ; cache misses/others + + "proxy_ignore_client_abort on;"))) + + ;; Try to prevent good-faith crawlers from downloading substitutes. + (nginx-location-configuration + (uri "= /robots.txt") + (body + (list + #~(string-append "try_files " + #$(plain-file "robots.txt" publish-robots.txt) + " =404;") + "root /;"))))) + +;; TODO +;; (define %ci-onion +;; ;; Onion service name of ci.guix. +;; "4zwzi66wwdaalbhgnix55ea3ab4pvvw66ll2ow53kjub6se4q2bclcyd.onion") + +(define (balg02-locations publish-url) + "Return nginx location blocks with 'guix publish' reachable at +PUBLISH-URL." + (append (publish-locations publish-url) + (list + ;; Cuirass. + (nginx-location-configuration + (uri "/") + (body (list "proxy_pass http://localhost:8081;" + ;; ;; See + ;; ;; . + ;; (string-append + ;; "add_header Onion-Location http://" %ci-onion + ;; "$request_uri;") + ))) + (nginx-location-configuration + (uri "~ ^/admin") + (body + (list "if ($ssl_client_verify != SUCCESS) { return 403; } proxy_pass http://localhost:8081;"))) + + (nginx-location-configuration + (uri "/static") + (body + (list + "proxy_pass http://localhost:8081;" + ;; Cuirass adds a 'Cache-Control' header, honor it. + "proxy_cache static;" + "proxy_cache_valid 200 2d;" + "proxy_cache_valid any 10m;" + "proxy_ignore_client_abort on;"))) + + (nginx-location-configuration + (uri "/download") ;Cuirass "build products" + (body + (list + "proxy_pass http://localhost:8081;" + "expires 10d;" ;override 'Cache-Control' + "proxy_cache static;" + "proxy_cache_valid 200 30d;" + "proxy_cache_valid any 10m;" + "proxy_ignore_client_abort on;"))) + + (nginx-location-configuration ;certbot + (uri "/.well-known") + (body (list "root /var/www;")))))) + +(define %publish-url "http://localhost:3000") + +;; Taken from https://git.savannah.gnu.org/cgit/guix/maintenance.git/tree/hydra/modules/sysadmin/nginx.scm +(define %tls-settings + (list + ;; Make sure SSL is disabled. + "ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;" + ;; Disable weak cipher suites. + "ssl_ciphers HIGH:!aNULL:!MD5;" + "ssl_prefer_server_ciphers on;" + + ;; TODO: these need to be generated + ;; Use our own DH parameters created with: + ;; openssl dhparam -out dhparams.pem 2048 + ;; as suggested at . + "ssl_dhparam /etc/dhparams.pem;")) + +;; Taken from https://git.savannah.gnu.org/cgit/guix/maintenance.git/tree/hydra/modules/sysadmin/nginx.scm +(define* (le host #:optional privkey) + (string-append "/etc/letsencrypt/live/" + host "/" + (if privkey "privkey" "fullchain") + ".pem")) + +;; Taken from https://git.savannah.gnu.org/cgit/guix/maintenance.git/tree/hydra/modules/sysadmin/nginx.scm +(define languages-to-accept + ;; List of languages for redirection; see 'accept-languages' further + ;; below. + '(("en") + ("de") + ("eo") + ("es") + ("fr") + ("ja") + ("ko") + ("ru") + ("sk") + ("tr") + ("zh-CN" "zh" "zh-Hans" "zh-Hans-CN") + ("zh-TW" "zh-Hant" "zh-Hant-TW"))) + +;; Taken from https://git.savannah.gnu.org/cgit/guix/maintenance.git/tree/hydra/modules/sysadmin/nginx.scm +(define* (accept-languages + #:optional (language-lists + languages-to-accept)) + "Returns nginx configuration code to set up the $lang variable +according to the Accept-Language header in the HTTP request. The +requesting user agent will be served the files at /$lang/some/url. +Each list in LANGUAGE-LISTS starts with the $lang and is followed by +synonymous IETF language tags that should be mapped to the same $lang." + (define (language-mappings language-list) + (define (language-mapping language) + (string-join (list " " language (car language-list) ";"))) + (string-join (map language-mapping language-list) "\n")) + + (let ((directives + `(,(string-join + `("set_from_accept_language $lang_unmapped" + ,@(map string-join language-lists) + ";")) + "map $lang_unmapped $lang {" + ,@(map language-mappings language-lists) + "}"))) + (string-join directives "\n"))) + + +(define %balg02-servers + (list + ;; Redirect domains that don't explicitly support HTTP (below) to HTTPS. + (nginx-server-configuration + (listen '("80")) + (raw-content + (list "return 308 https://$host$request_uri;"))) + + ;; Domains that still explicitly support plain HTTP. + (nginx-server-configuration + (listen '("80")) + (server-name `("ci.guix.gnu.org" + ;; + "~[0-9]$" +; TODO: onion +; ,(regexp-quote %ci-onion) + )) + (locations (balg02-locations %publish-url)) + (raw-content + (list + "access_log /var/run/anonip/http.access.log;" + "proxy_set_header X-Forwarded-Host $host;" + "proxy_set_header X-Forwarded-Port $server_port;" + "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"))) + + ;; HTTPS servers + (nginx-server-configuration + (listen '("443 ssl")) + (server-name '("ci.guix.gnu.org")) + (ssl-certificate (le "ci.guix.gnu.org")) + (ssl-certificate-key (le "ci.guix.gnu.org" 'key)) + (locations (balg02-locations %publish-url)) + (raw-content + (append + %tls-settings + (list + "access_log /var/run/anonip/https.access.log;" + "proxy_set_header X-Forwarded-Host $host;" + "proxy_set_header X-Forwarded-Port $server_port;" + "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" + ;; For Cuirass admin interface authentication + "ssl_client_certificate /etc/ssl-ca/certs/ca.crt;" + "ssl_verify_client optional;")))))) + +(define %extra-content + (list + "default_type application/octet-stream;" + "sendfile on;" + + (accept-languages) + + ;; Maximum chunk size to send. Partly this is a workaround for + ;; , but also the nginx docs mention that + ;; "Without the limit, one fast connection may seize the worker + ;; process entirely." + ;; + "sendfile_max_chunk 1m;" + + "keepalive_timeout 65;" + + ;; Use HTTP 1.1 to talk to the backend so we benefit from keep-alive + ;; connections and chunked transfer encoding. The latter allows us to + ;; make sure we do not cache partial downloads. + "proxy_http_version 1.1;" + + ;; The 'inactive' parameter for caching is not very useful in our + ;; case: all that matters is that LRU sweeping happens when 'max_size' + ;; is hit. + + ;; cache for nar files + "proxy_cache_path /var/cache/nginx/nar" + " levels=2" + " inactive=8d" ; inactive keys removed after 8d + " keys_zone=nar:4m" ; nar cache meta data: ~32K keys + " max_size=10g;" ; total cache data size max + + ;; cache for content-addressed files + "proxy_cache_path /var/cache/nginx/cas" + " levels=2" + " inactive=180d" ; inactive keys removed after 180d + " keys_zone=cas:8m" ; nar cache meta data: ~64K keys + " max_size=50g;" ; total cache data size max + + ;; cache for build logs + "proxy_cache_path /var/cache/nginx/logs" + " levels=2" + " inactive=60d" ; inactive keys removed after 60d + " keys_zone=logs:8m" ; narinfo meta data: ~64K keys + " max_size=4g;" ; total cache data size max + + ;; cache for static data + "proxy_cache_path /var/cache/nginx/static" + " levels=1" + " inactive=10d" ; inactive keys removed after 10d + " keys_zone=static:1m" ; nar cache meta data: ~8K keys + " max_size=200m;" ; total cache data size max + + ;; If Hydra cannot honor these delays, then something is wrong and + ;; we'd better drop the connection and return 504. + "proxy_connect_timeout 10s;" + "proxy_read_timeout 10s;" + "proxy_send_timeout 10s;" + + ;; Cache timeouts for a little while to avoid increasing pressure. + "proxy_cache_valid 504 30s;")) + +(define %nginx-configuration + (nginx-configuration + (server-blocks %balg02-servers) + (server-names-hash-bucket-size 128) + (modules + (list + ;; Module to redirect users to the localized pages of their choice. + (file-append nginx-accept-language-module + "/etc/nginx/modules/ngx_http_accept_language_module.so"))) + (global-directives + '((worker_processes . 16) + (pcre_jit . on) + (events . ((worker_connections . 1024))))) + (extra-content + (string-join %extra-content "\n")))) + +(define %nginx-cache-activation + ;; Make sure /var/cache/nginx exists on the first run. + (simple-service 'nginx-/var/cache/nginx + activation-service-type + (with-imported-modules '((guix build utils)) + #~(begin + (use-modules (guix build utils)) + (mkdir-p "/var/cache/nginx"))))) + +(define %nginx-deploy-hook + (program-file + "nginx-deploy-hook" + #~(let ((pid (call-with-input-file "/var/run/nginx/pid" read))) + (kill pid SIGHUP)))) (define (balg02 efi-boot-uuid) (operating-system @@ -76,7 +462,7 @@ file-systems))))) (users (cons* (user-account - (name %automation-user) + (name "auto") (comment "Automation User") (group "users") (shell #~(string-append #$bash "/bin/bash")) @@ -112,7 +498,7 @@ (packages (append (map specification->package - '("nss-certs" + '("btrfs-progs" "recutils" "openssh" "tmux" @@ -137,8 +523,7 @@ ,(local-file "../../../.pubkeys/pjotr-gaeta.pub") ,(local-file "../../../.pubkeys/pjotr-napoli.pub") ,(local-file "../../../.pubkeys/pjotr-stromboli.pub") - ,(local-file "../../../.pubkeys/pjotr-tb-arm-01.pub")) - ("root" ,(local-file "../../../.pubkeys/collin.pub")))))) + ,(local-file "../../../.pubkeys/pjotr-tb-arm-01.pub")))))) (service static-networking-service-type (list (static-networking (addresses @@ -150,7 +535,29 @@ (destination "default") (gateway "216.37.76.1")))) (name-servers '("216.37.64.2" "216.37.64.3"))))) - (service ntp-service-type)) + (service ntp-service-type) + + (service certbot-service-type + (certbot-configuration + (email "collin@rekahsoft.ca") + (certificates + (list + (certificate-configuration + (domains '("cuirass.genenetwork.org")) + (deploy-hook %nginx-deploy-hook)))))) + + (service cuirass-service-type + (cuirass-configuration + (host "localhost") + (specifications %cuirass-specs))) + + %nginx-cache-activation + (service nginx-service-type %nginx-configuration) + + (service guix-publish-service-type + (guix-publish-configuration + (port 3000) + (cache "/var/cache/guix/publish")))) %base-services)))) (define %system (balg02 "3AF8-9E67")) -- cgit v1.2.3