(use-modules (gnu) (gnu packages version-control) (gnu services admin) (gnu services certbot) (gnu services cgit) (gnu services ci) (gnu services networking) (gnu services ssh) (gnu services version-control) (gnu services web)) (define letsencrypt-cert "/etc/letsencrypt/live/dthompson.us/fullchain.pem") (define letsencrypt-cert-key "/etc/letsencrypt/live/dthompson.us/privkey.pem") (define dave-pub-key (local-file "keys/dave.pub")) (define git-root "/var/lib/gitolite") (define fcgiwrap-socket "127.0.0.1:9000") (define nginx-accounts (list (user-group (name "nginx") (system? #t)) (user-account (name "nginx") (group "nginx") (supplementary-groups '("git")) (system? #t) (comment "nginx server user") (home-directory "/var/empty") (shell (file-append (specification->package "shadow") "/sbin/nologin"))))) (define (nginx-signal name signal) (program-file name #~(let ((pid (call-with-input-file "/var/run/nginx/pid" read))) (kill pid #$signal)))) ;; Need to override the default nginx service account configuration so ;; that the nginx user is a member of the git group. (define nginx-service-type* (service-type (inherit nginx-service-type) (extensions (map (lambda (extension) (if (eq? (service-extension-target extension) account-service-type) (service-extension account-service-type (const nginx-accounts)) extension)) (service-type-extensions nginx-service-type))))) (define takemi-os (operating-system (locale "en_US.utf8") (timezone "America/New_York") (keyboard-layout (keyboard-layout "us")) (host-name "takemi") (users (cons* (user-account (name "dave") (comment "David Thompson") (group "users") (home-directory "/home/dave") (supplementary-groups '("wheel" "netdev"))) (user-account (name "publish") (comment "Web file publisher") (group "publish") (home-directory "/var/www") (system? #t) (create-home-directory? #f)) %base-user-accounts)) (groups (cons* (user-group (name "publish") (system? #t)) %base-groups)) (sudoers-file (plain-file "sudoers" (string-append (plain-file-content %sudoers-specification) ;; 'guix deploy' requires no password ;; sudo capability. "dave ALL = NOPASSWD: ALL\n"))) (packages (append (map specification->package '("emacs" "nss-certs" "rsync" "git" "laminar")) %base-packages)) (services (cons* (service dhcp-client-service-type) (service openssh-service-type (openssh-configuration (password-authentication? #f) ;; So I can forward ports from my local host to ;; the server and have the ports accessible from ;; the internet. (gateway-ports? #t) (authorized-keys `(("dave" ,dave-pub-key) ("publish" ,dave-pub-key))))) ;; Git repository hosting. (service gitolite-service-type (gitolite-configuration (admin-pubkey dave-pub-key) (rc-file (gitolite-rc-file ;; Grant read access to git group so ;; cgit will work. (umask #o0027) (git-config-keys "gitweb\\..*"))))) ;; Continuous integration. (service laminar-service-type (laminar-configuration (supplementary-groups '("git")))) ;; TLS certificates. Certbot extends nginx with configuration to ;; redirect all HTTP requests to HTTPS. (service (service-type (inherit certbot-service-type) (extensions ;; Replace original nginx-service-type with ;; our modified one. (map (lambda (extension) (if (eq? (service-extension-target extension) nginx-service-type) (service-extension nginx-service-type* (@@ (gnu services certbot) certbot-nginx-server-configurations)) extension)) (service-type-extensions certbot-service-type)))) (certbot-configuration (email "dthompson2@worcester.edu") (certificates (list (certificate-configuration (domains '("dthompson.us" "www.dthompson.us" "git.dthompson.us" "ci.dthompson.us" "files.dthompson.us" "haunt.dthompson.us")) ;; Send SIGHUP signal to nginx to trigger a ;; configuration reload, thus loading the ;; updated certificates. (deploy-hook (nginx-signal "nginx-deploy-hook" SIGHUP))))) (webroot "/var/www/certbot"))) (service nginx-service-type* (nginx-configuration (server-blocks (list (nginx-server-configuration (listen '("443 ssl")) (server-name '("www.dthompson.us")) (root "/var/www/blog") (ssl-certificate letsencrypt-cert) (ssl-certificate-key letsencrypt-cert-key)) (nginx-server-configuration (listen '("443 ssl")) (server-name '("files.dthompson.us")) (root "/var/www/files") (raw-content '("autoindex on;")) (ssl-certificate letsencrypt-cert) (ssl-certificate-key letsencrypt-cert-key)) (nginx-server-configuration ;; Laminar recommends using HTTP2 here. (listen '("443 ssl http2")) (server-name '("ci.dthompson.us")) (locations (list ;; Reverse proxy to Laminar daemon. (nginx-location-configuration (uri "/") (body '("proxy_pass http://127.0.0.1:8080;" "proxy_http_version 1.1;" "proxy_set_header Connection \"\";"))) ;; Serve static files directly. (nginx-location-configuration (uri "/archive") (body '("alias /var/lib/laminar/archive/;"))))) (ssl-certificate letsencrypt-cert) (ssl-certificate-key letsencrypt-cert-key) ;; Modern TLS only. (raw-content '("ssl_protocols TLSv1.3;" "ssl_ciphers EECDH+AESGCM:EDH+AESGCM;"))) ;; I used to have the Haunt website under ;; its own subdomain, and some sites still ;; point to it. (nginx-server-configuration (listen '("443 ssl")) (server-name '("haunt.dthompson.us")) (root "/var/www/haunt") (locations (list (nginx-location-configuration (uri "/") (body '("rewrite .* https://dthompson.us/projects/haunt.html permanent;"))))) (ssl-certificate letsencrypt-cert) (ssl-certificate-key letsencrypt-cert-key)))))) ;; Git repository viewer. (let ((cgit (specification->package "cgit"))) (service (service-type (inherit cgit-service-type) (extensions ;; Replace original nginx-service-type with ;; our modified one. (map (lambda (extension) (if (eq? (service-extension-target extension) nginx-service-type) (service-extension nginx-service-type* cgit-configuration-nginx-config) extension)) (service-type-extensions cgit-service-type)))) (cgit-configuration (project-list (string-append git-root "/projects.list")) (repository-directory (string-append git-root "/repositories")) (root-desc "all i wanted was a pepsi") ; just one pepsi (enable-git-config? #t) ;; Cgit only supports the old HTTP "dumb" ;; protocol, which notably libgit2 won't even ;; entertain supporting. So, we'll disable ;; that and use Git itself to provide the HTTP ;; "smart" protocol instead. (enable-http-clone? #f) (enable-index-links? #t) (enable-index-owner? #f) (enable-commit-graph? #t) (enable-log-filecount? #t) (enable-log-linecount? #t) (remove-suffix? #t) (clone-url '("https://git.dthompson.us/$CGIT_REPO_URL.git")) ;; Is there a way to avoid this wrapper script? (source-filter (program-file "cgit-syntax-highlight" #~(apply execl (string-append #$cgit "/lib/cgit/filters/syntax-highlighting.py") (command-line)))) (nginx (list (nginx-server-configuration (listen '("443 ssl")) (server-name '("git.dthompson.us")) (root cgit) (locations (list ;; URI paths with .git are handled by ;; Git's "smart" HTTP protocol. (nginx-location-configuration (uri "~ (/.*\\.git/.*)") (body `(("fastcgi_pass " ,fcgiwrap-socket ";") ("fastcgi_param SCRIPT_FILENAME " ,git "/libexec/git-core/git-http-backend;") "fastcgi_param QUERY_STRING $query_string;" "fastcgi_param REQUEST_METHOD $request_method;" "fastcgi_param CONTENT_TYPE $content_type;" "fastcgi_param CONTENT_LENGTH $content_length;" ("fastcgi_param GIT_PROJECT_ROOT " ,git-root "/repositories;") "fastcgi_param PATH_INFO $1;"))) ;; Redirect old URLs to .git pages to ;; the new .git-less URL. This ;; doesn't handle deeper links but ;; that's okay. (nginx-location-configuration (uri "~ (/.*)\\.git") (body `("return 301 $1;"))) ;; Serve a static file if one exists, ;; otherwise send the request to ;; cgit. (nginx-location-configuration (uri "/") (body '("try_files $uri @cgit;"))) (nginx-location-configuration (uri "@cgit") (body `("fastcgi_param SCRIPT_FILENAME $document_root/lib/cgit/cgit.cgi;" "fastcgi_param PATH_INFO $uri;" "fastcgi_param QUERY_STRING $args;" "fastcgi_param HTTP_HOST $server_name;" ("fastcgi_pass " ,fcgiwrap-socket ";")))))) (ssl-certificate letsencrypt-cert) (ssl-certificate-key letsencrypt-cert-key))))))) ;; fcgriwrap wraps cgit. (service fcgiwrap-service-type (fcgiwrap-configuration ;; Use git group for read-only access to gitolite ;; repos. (group "git") (socket (string-append "tcp:" fcgiwrap-socket)))) ;; Log rotation. (simple-service 'rotate-logs rottlog-service-type (list (log-rotation (frequency 'daily) (files '("/var/log/nginx/access.log" "/var/log/nginx/error.log")) (options `("storedir /var/log/nginx" ;; Keep a week of logs. "rotate 6" ;; Run post-rotate once per ;; rotation rather than once ;; for each file. "sharedscripts" ,@%default-log-rotation-options)) ;; Tell nginx to reopen its log files after rotation. (post-rotate (nginx-signal "nginx-post-rotate-hook" SIGUSR1))) (log-rotation (frequency 'daily) (files '("/var/log/fcgiwrap.log")) (options `("rotate 6" ,@%default-log-rotation-options))))) (modify-services %base-services (guix-service-type config => (guix-configuration (inherit config) (authorized-keys (cons (local-file "keys/signing-key.pub") %default-authorized-guix-keys))))))) (bootloader (bootloader-configuration (bootloader grub-bootloader) (targets '("/dev/vda")) (keyboard-layout keyboard-layout))) (initrd-modules (append '("virtio_scsi") %base-initrd-modules)) (swap-devices (list (swap-space (target "/dev/vda2")))) (file-systems (cons* (file-system (mount-point "/") (device (uuid "f99d3ff5-57ea-4b20-bca7-bc2d58b4c364" 'ext4)) (type "ext4")) %base-file-systems)))) (define takemi-host-key "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOrptBAMgs8dGDerBkcmZQ2W/0nEXtOBCl8nLlEwjKdI") (list (machine (operating-system takemi-os) (environment managed-host-environment-type) (configuration (machine-ssh-configuration (host-name "dthompson.us") (system "x86_64-linux") (user "dave") (host-key takemi-host-key)))))