diff options
| author | Azalea Redmyer <azalea@grrlz.net> | 2026-02-05 20:33:37 -0800 |
|---|---|---|
| committer | Azalea Redmyer <azalea@grrlz.net> | 2026-02-05 20:33:37 -0800 |
| commit | 355f1bd0fabf2667fa8ce05b083af67b491f5176 (patch) | |
| tree | 50a07e58080097a742d686e86b7a414febdfd1f4 | |
| parent | ce9701aca45b19ea95176d72871fe37383edc4b5 (diff) | |
Add 'chinstrap' server configuration & supporting files
| -rw-r--r-- | bluechateau/services/version-control.scm | 615 | ||||
| -rw-r--r-- | bluechateau/systems/chinstrap.scm | 501 |
2 files changed, 1116 insertions, 0 deletions
diff --git a/bluechateau/services/version-control.scm b/bluechateau/services/version-control.scm new file mode 100644 index 0000000..01976ef --- /dev/null +++ b/bluechateau/services/version-control.scm @@ -0,0 +1,615 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2016 Nikita <nikita@n0.is> +;;; Copyright © 2016 Sou Bunnbu <iyzsong@member.fsf.org> +;;; Copyright © 2017 Oleg Pykhalov <go.wigust@gmail.com> +;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org> +;;; Copyright © 2018 Christopher Baines <mail@cbaines.net> +;;; Copyright © 2021 Julien Lepiller <julien@lepiller.eu> +;;; Copyright © 2025 Tomas Volf <~@wolfsden.cz> +;;; Copyright © 2025 Evgeny Pisemsky <mail@pisemsky.site> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +;;; This modification of the vanilla version-control service is +;;; required for cgit to properly access git repositories at the +;;; "/" URI. This is usefel when hosting via a subdomain, e.g. +;;; git.example.org. +;;; (See: https://thanosapollo.org/posts/why-guix/) + +(define-module (bluechateau services version-control) + #:use-module (gnu services) + #:use-module (gnu services base) + #:use-module (gnu services shepherd) + #:use-module (gnu services web) + #:use-module (gnu system shadow) + #:use-module (gnu packages version-control) + #:use-module (gnu packages admin) + #:use-module (guix deprecation) + #:use-module (guix records) + #:use-module (guix gexp) + #:use-module (guix store) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) + #:use-module (ice-9 format) + #:use-module (ice-9 match) + #:export (git-daemon-service + git-daemon-service-type + git-daemon-configuration + git-daemon-configuration? + + git-http-configuration + git-http-configuration? + git-http-nginx-location-configuration + + <gitolite-configuration> + gitolite-configuration + gitolite-configuration-package + gitolite-configuration-user + gitolite-configuration-rc-file + gitolite-configuration-admin-pubkey + gitolite-configuration-admin-name + + <gitolite-rc-file> + gitolite-rc-file + gitolite-rc-file-local-code + gitolite-rc-file-umask + gitolite-rc-file-unsafe-pattern + gitolite-rc-file-git-config-keys + gitolite-rc-file-log-extra + gitolite-rc-file-host-name + gitolite-rc-file-roles + gitolite-rc-file-enable + gitolite-rc-file-extra-content + gitolite-rc-file-default-enable + + <gitolite-git-configuration> + gitolite-git-configuration + gitolite-git-configuration? + gitolite-git-configuration-name + gitolite-git-configuration-email + gitolite-git-configuration-default-branch + gitolite-git-configuration-receive-fsck-objects + gitolite-git-configuration-extra-content + + gitolite-service-type + + gitile-configuration + gitile-configuration-package + gitile-configuration-host + gitile-configuration-port + gitile-configuration-database + gitile-configuration-repositories + gitile-configuration-git-owner-validation? + gitile-configuration-base-git-url + gitile-configuration-index-title + gitile-configuration-intro + gitile-configuration-footer + gitile-configuration-nginx + + gitile-service-type)) + +;;; Commentary: +;;; +;;; Version Control related services. +;;; +;;; Code: + + +;;; +;;; Git daemon. +;;; + +(define-record-type* <git-daemon-configuration> + git-daemon-configuration + make-git-daemon-configuration + git-daemon-configuration? + (package git-daemon-configuration-package ;file-like + (default git)) + (export-all? git-daemon-configuration-export-all ;boolean + (default #f)) + (base-path git-daemon-configuration-base-path ;string | #f + (default "/srv/git")) + (user-path git-daemon-configuration-user-path ;string | #f + (default #f)) + (listen git-daemon-configuration-listen ;list of string + (default '())) + (port git-daemon-configuration-port ;number | #f + (default #f)) + (whitelist git-daemon-configuration-whitelist ;list of string + (default '())) + (extra-options git-daemon-configuration-extra-options ;list of string + (default '()))) + +(define git-daemon-shepherd-service + (match-lambda + (($ <git-daemon-configuration> + package export-all? base-path user-path + listen port whitelist extra-options) + (let* ((git (file-append package "/bin/git")) + (command `(,git + "daemon" "--syslog" "--reuseaddr" + ,@(if export-all? + '("--export-all") + '()) + ,@(if base-path + `(,(string-append "--base-path=" base-path)) + '()) + ,@(if user-path + `(,(string-append "--user-path=" user-path)) + '()) + ,@(map (cut string-append "--listen=" <>) listen) + ,@(if port + `(,(string-append + "--port=" (number->string port))) + '()) + ,@extra-options + ,@whitelist))) + (list (shepherd-service + (documentation "Run the git-daemon.") + (requirement '(user-processes networking)) + (provision '(git-daemon)) + (start #~(make-forkexec-constructor '#$command + #:user "git-daemon" + #:group "git-daemon")) + (stop #~(make-kill-destructor)))))))) + +(define %git-daemon-accounts + ;; User account and group for git-daemon. + (list (user-group + (name "git-daemon") + (system? #t)) + (user-account + (name "git-daemon") + (system? #t) + (group "git-daemon") + (comment "Git daemon user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin"))))) + +(define (git-daemon-activation config) + "Return the activation gexp for git-daemon using CONFIG." + (let ((base-path (git-daemon-configuration-base-path config))) + #~(begin + (use-modules (guix build utils)) + ;; Create the 'base-path' directory when it's not '#f'. + (and=> #$base-path mkdir-p)))) + +(define git-daemon-service-type + (service-type + (name 'git-daemon) + (extensions + (list (service-extension shepherd-root-service-type + git-daemon-shepherd-service) + (service-extension account-service-type + (const %git-daemon-accounts)) + (service-extension activation-service-type + git-daemon-activation))) + (description + "Expose Git repositories over the insecure @code{git://} TCP-based +protocol.") + (default-value (git-daemon-configuration)))) + +(define-deprecated (git-daemon-service #:key (config (git-daemon-configuration))) + git-daemon-service-type + "Return a service that runs @command{git daemon}, a simple TCP server to +expose repositories over the Git protocol for anonymous access. + +The optional @var{config} argument should be a +@code{<git-daemon-configuration>} object, by default it allows read-only +access to exported repositories under @file{/srv/git}." + (service git-daemon-service-type config)) + + +;;; +;;; HTTP access. Add the result of calling +;;; git-http-nginx-location-configuration to an nginx-server-configuration's +;;; "locations" field. +;;; + +(define-record-type* <git-http-configuration> + git-http-configuration + make-git-http-configuration + git-http-configuration? + (package git-http-configuration-package ;file-like + (default git)) + (git-root git-http-configuration-git-root ;string + (default "/srv/git")) + (export-all? git-http-configuration-export-all? ;boolean + (default #f)) + (uri-path git-http-configuration-uri-path ;string + (default "/git/")) + (fcgiwrap-socket git-http-configuration-fcgiwrap-socket ;string + (default "127.0.0.1:9000"))) + +(define* (git-http-nginx-location-configuration #:optional + (config + (git-http-configuration))) + (match config + (($ <git-http-configuration> package git-root export-all? + uri-path fcgiwrap-socket) + (nginx-location-configuration + (uri (string-append "~ " + (if (string=? uri-path "/") + "" + (string-append "/" (string-trim-both uri-path #\/))) + "/(info/refs|git-upload-pack|git-receive-pack)")) + (body + (list + (list "fastcgi_pass " fcgiwrap-socket ";") + (list "fastcgi_param SCRIPT_FILENAME " + package "/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;" + (if export-all? + "fastcgi_param GIT_HTTP_EXPORT_ALL \"\";" + "") + (list "fastcgi_param GIT_PROJECT_ROOT " git-root ";") + (list "fastcgi_param GIT_CONFIG_GLOBAL " + (plain-file "gitconfig" + (string-append "[safe]\n\tdirectory = " git-root "/*\n")) + ";") + "fastcgi_param PATH_INFO $uri;")))))) + + +;;; +;;; Gitolite +;;; + +(define gitolite-rc-file-default-enable + '("help" + "desc" + "info" + "perms" + "writable" + "ssh-authkeys" + "git-config" + "daemon" + "gitweb")) + +(define-record-type* <gitolite-rc-file> + gitolite-rc-file make-gitolite-rc-file + gitolite-rc-file? + (umask gitolite-rc-file-umask + (default #o0077)) + (local-code gitolite-rc-file-local-code + (default "$rc{GL_ADMIN_BASE}/local")) + (unsafe-pattern gitolite-rc-file-unsafe-pattern + (default #f)) + (git-config-keys gitolite-rc-file-git-config-keys + (default "")) + (log-extra gitolite-rc-file-log-extra + (default #f)) + (host-name gitolite-rc-file-host-name + (default #f)) + (roles gitolite-rc-file-roles + (default '(("READERS" . 1) + ("WRITERS" . 1)))) + (enable gitolite-rc-file-enable + (default gitolite-rc-file-default-enable)) + (extra-content gitolite-rc-extra-content + (default ""))) + +(define-gexp-compiler (gitolite-rc-file-compiler + (file <gitolite-rc-file>) system target) + (match-record file <gitolite-rc-file> + ( umask local-code unsafe-pattern git-config-keys log-extra + host-name roles enable extra-content) + (apply text-file* "gitolite.rc" + `("%RC = (\n" + " UMASK => " ,(format #f "~4,'0o" umask) ",\n" + " GIT_CONFIG_KEYS => '" ,git-config-keys "',\n" + ,(if local-code + (simple-format #f " LOCAL_CODE => \"~A\",\n" local-code) + "") + ,(if log-extra + " LOG_EXTRA => 1,\n" + "") + ,(if host-name + (simple-format #f " HOSTNAME => \"~A\",\n" host-name) + "") + " ROLES => {\n" + ,@(map (match-lambda + ((role . value) + (simple-format #f " ~A => ~A,\n" role value))) + roles) + " },\n" + "\n" + " ENABLE => [\n" + ,@(map (lambda (value) + (simple-format #f " '~A',\n" value)) + enable) + " ],\n" + ,extra-content "\n" + ");\n" + "\n" + ,(if unsafe-pattern + (string-append "$UNSAFE_PATT = qr(" unsafe-pattern ");") + "") + "1;\n" + "# Local variables:\n" + "# mode: perl\n" + "# End:\n" + "# vim: set syn=perl:\n")))) + +(define-record-type* <gitolite-git-configuration> + gitolite-git-configuration make-gitolite-git-configuration + gitolite-git-configuration? + (name gitolite-git-configuration-name + (default "GNU Guix")) + (email gitolite-git-configuration-email + (default "guix@localhost")) + (default-branch gitolite-git-configuration-default-branch + (default #f)) + (receive-fsck-objects gitolite-git-configuration-receive-fsck-objects + (default #f)) + (extra-content gitolite-git-configuration-extra-content + (default ""))) + +(define-gexp-compiler (gitolite-git-configuration-compiler + (config <gitolite-git-configuration>) system target) + (match-record config <gitolite-git-configuration> + (name email default-branch receive-fsck-objects extra-content) + (apply text-file* "gitconfig" + `("[user]\n" + "name = " ,name "\n" + "email = " ,email "\n" + ,@(if default-branch + `("[init]\n" + "defaultBranch = " ,default-branch "\n") + '()) + ,@(if receive-fsck-objects + `("[receive]\n" + "fsckObjects = true\n") + '()) + ,extra-content "\n")))) + +(define-record-type* <gitolite-configuration> + gitolite-configuration make-gitolite-configuration + gitolite-configuration? + (package gitolite-configuration-package + (default gitolite)) + (user gitolite-configuration-user + (default "git")) + (group gitolite-configuration-group + (default "git")) + (home-directory gitolite-configuration-home-directory + (default "/var/lib/gitolite")) + (rc-file gitolite-configuration-rc-file + (default (gitolite-rc-file))) + (git-config gitolite-configuration-git-config + (default (gitolite-git-configuration))) + (admin-pubkey gitolite-configuration-admin-pubkey) + (admin-name gitolite-configuration-admin-name + (default #f))) + +(define (gitolite-accounts config) + (match-record config <gitolite-configuration> + (user group home-directory) + ;; User group and account to run Gitolite. + (list (user-group + (name group) + (system? #t)) + (user-account + (name user) + (group group) + (system? #t) + (comment "Gitolite user") + (home-directory home-directory))))) + +(define (gitolite-activation config) + (match-record config <gitolite-configuration> + ( package user group home-directory rc-file admin-pubkey + admin-name git-config) + #~(begin + (use-modules (ice-9 match) + (guix build utils)) + + (let* ((user-info (getpwnam #$user)) + (admin-pubkey #$admin-pubkey) + (pubkey-file (if #$admin-name + (string-append #$admin-name ".pub") + (string-append + #$home-directory "/" + (basename + (strip-store-file-name admin-pubkey))))) + (rc-file #$(string-append home-directory "/.gitolite.rc"))) + + ;; activate-users+groups in (gnu build activation) sets the + ;; permission flags of home directories to #o700 and mentions that + ;; services needing looser permissions should chmod it during + ;; service activation. We also want the git group to be able to + ;; read from the gitolite home directory, so a chmod'ing we will + ;; go! + (chmod #$home-directory #o750) + + (simple-format #t "guix: gitolite: installing ~A\n" #$rc-file) + (copy-file #$rc-file rc-file) + ;; ensure gitolite's user can read the configuration + (chown rc-file + (passwd:uid user-info) + (passwd:gid user-info)) + + ;; The key must be writable, so copy it from the store + (copy-file admin-pubkey pubkey-file) + + (chmod pubkey-file #o500) + (chown pubkey-file + (passwd:uid user-info) + (passwd:gid user-info)) + + ;; Set the git configuration, to avoid gitolite trying to use + ;; the hostname command, as the network might not be up yet + (copy-file #$git-config + #$(string-append home-directory "/.gitconfig")) + + ;; Run Gitolite setup, as this updates the hooks and include the + ;; admin pubkey if specified. The admin pubkey is required for + ;; initial setup, and will replace the previous key if run after + ;; initial setup + (match (primitive-fork) + (0 + ;; Exit with a non-zero status code if an exception is thrown. + (dynamic-wind + (const #t) + (lambda () + (setenv "HOME" (passwd:dir user-info)) + (setenv "USER" #$user) + (setgid (passwd:gid user-info)) + (setuid (passwd:uid user-info)) + (primitive-exit + (system* #$(file-append package "/bin/gitolite") + "setup" + "-m" "gitolite setup by GNU Guix" + "-pk" pubkey-file))) + (lambda () + (primitive-exit 1)))) + (pid (waitpid pid))) + + (when (file-exists? pubkey-file) + (delete-file pubkey-file)))))) + +(define gitolite-service-type + (service-type + (name 'gitolite) + (extensions + (list (service-extension activation-service-type + gitolite-activation) + (service-extension account-service-type + gitolite-accounts))) + (description + "Set up @command{gitolite}, a Git hosting tool providing access over SSH. +By default, the @code{git} user is used, but this is configurable. +Additionally, Gitolite can integrate with with tools like gitweb or cgit to +provide a web interface to view selected repositories."))) + +;;; +;;; Gitile +;;; + +(define-record-type* <gitile-configuration> + gitile-configuration make-gitile-configuration gitile-configuration? + (package gitile-configuration-package + (default gitile)) + (host gitile-configuration-host + (default "127.0.0.1")) + (port gitile-configuration-port + (default 8080)) + (database gitile-configuration-database + (default "/var/lib/gitile/gitile-db.sql")) + (repositories gitile-configuration-repositories + (default "/var/lib/gitolite/repositories")) + (git-owner-validation? gitile-configuration-git-owner-validation? + (default #t)) + (base-git-url gitile-configuration-base-git-url) + (index-title gitile-configuration-index-title + (default "Index")) + (intro gitile-configuration-intro + (default '())) + (footer gitile-configuration-footer + (default '())) + (nginx gitile-configuration-nginx)) + +(define (gitile-config-file host port database repositories + git-owner-validation? base-git-url + index-title intro footer) + (define build + #~(write `(config + (port #$port) + (host #$host) + (database #$database) + (repositories #$repositories) + (git-owner-validation? #$git-owner-validation?) + (base-git-url #$base-git-url) + (index-title #$index-title) + (intro #$intro) + (footer #$footer)) + (open-output-file #$output))) + + (computed-file "gitile.conf" build)) + +(define gitile-nginx-server-block + (match-lambda + (($ <gitile-configuration> package host port database repositories + git-owner-validation? base-git-url index-title intro footer nginx) + (list (nginx-server-configuration + (inherit nginx) + (locations + (append + (list + (nginx-location-configuration + (uri "/") + (body + (list + #~(string-append "proxy_pass http://" #$host + ":" (number->string #$port) + "/;"))))) + (map + (lambda (loc) + (nginx-location-configuration + (uri loc) + (body + (list + #~(string-append "root " #$package "/share/gitile/assets;"))))) + '("/css" "/js" "/images")) + (nginx-server-configuration-locations nginx)))))))) + +(define gitile-shepherd-service + (match-lambda + (($ <gitile-configuration> package host port database repositories + git-owner-validation? base-git-url index-title intro footer nginx) + (list (shepherd-service + (provision '(gitile)) + (requirement '(loopback)) + (documentation "gitile") + (start (let ((gitile (file-append package "/bin/gitile"))) + #~(make-forkexec-constructor + `(,#$gitile "-c" #$(gitile-config-file + host port database + repositories + git-owner-validation? + base-git-url index-title + intro footer)) + #:user "gitile" + #:group "git"))) + (stop #~(make-kill-destructor))))))) + +(define %gitile-accounts + (list (user-group + (name "git") + (system? #t)) + (user-account + (name "gitile") + (group "git") + (system? #t) + (comment "Gitile user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin"))))) + +(define gitile-service-type + (service-type + (name 'gitile) + (description "Run Gitile, a small Git forge. Expose public repositories +on the web.") + (extensions + (list (service-extension account-service-type + (const %gitile-accounts)) + (service-extension shepherd-root-service-type + gitile-shepherd-service) + (service-extension nginx-service-type + gitile-nginx-server-block))))) diff --git a/bluechateau/systems/chinstrap.scm b/bluechateau/systems/chinstrap.scm new file mode 100644 index 0000000..bc6ba82 --- /dev/null +++ b/bluechateau/systems/chinstrap.scm @@ -0,0 +1,501 @@ +;; ======================================================================= +;; Guix System configuration for my local server-in-a-closet, `chinstrap`. +;; ======================================================================= +;; +;; TODO: I'd like to split this into separate files. I would also like to +;; rewrite this as a 'literate' configuration at some point in the future. + +(define-module (bluechateau systems chinstrap) + #:use-module (gnu) + #:use-module (gnu packages databases) + #:use-module (gnu packages emacs) + #:use-module (gnu packages guile-xyz) + #:use-module (gnu packages ssh) + #:use-module (gnu packages version-control) + #:use-module (gnu packages web) + #:use-module (gnu services admin) + #:use-module (gnu services certbot) + #:use-module (gnu services cgit) + #:use-module (gnu services containers) + #:use-module (gnu services dbus) + #:use-module (gnu services databases) + #:use-module (gnu services desktop) + #:use-module (gnu services docker) + #:use-module (gnu services networking) + #:use-module (gnu services ssh) + ;;#:use-module (gnu services version-control) + #:use-module (gnu services virtualization) + #:use-module (gnu services web) + #:use-module (gnu system) + #:use-module (guix gexp) + #:use-module (nongnu packages linux) + #:use-module (nongnu system linux-initrd) + #:use-module (bluechateau services version-control)) + +(define ssl-domains '("bluechateau.org" + "www.bluechateau.org" + "git.bluechateau.org" + "search.bluechateau.org" + "fwiki.bluechateau.org" + "vtt.bluechateau.org" + "docs.bluechateau.org" + "sbdocs.bluechateau.org")) + +(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"))))) + +;; Override the default nginx service account configuration so +;; the nginx user is a member of the 'git' group. (required for +;; cgit) +(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 blog-nginx-server-configuration + (nginx-server-configuration + (listen '("443 ssl")) + (server-name '("bluechateau.org")) + (root "/srv/blog/site/") + (ssl-certificate + "/etc/certs/bluechateau/fullchain.pem") + (ssl-certificate-key + "/etc/certs/bluechateau/privkey.pem"))) + +(define git-fcgiwrap-service + (service fcgiwrap-service-type + (fcgiwrap-configuration + ;; Use 'git' user to read from the Git repositories. + (user "git") + (socket "tcp:127.0.0.1:9000")))) + +;; Gitolite (git repository hosting) +(define gitolite-service + (service gitolite-service-type + (gitolite-configuration + (admin-pubkey (local-file "../../keys/azalea.pub")) + (home-directory "/srv/git") + (rc-file (gitolite-rc-file + ;; Grant read access to 'git' group so + ;; cgit will work. + (umask #o0027) + ;; In order to hide repositories in cgit, + ;; this value must be set. + (git-config-keys ".*") + (extra-content "WRITER_CAN_UPDATE_DESC => 1")))))) + +;; CGit (git repository browser) +(define cgit-service + (let ((cgit (specification->package "cgit"))) + (service (service-type + (inherit cgit-service-type) + (extensions + ;; Replace original nginx-service with + ;; the 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 + (repository-directory "/srv/git/repositories") + (root-desc "Move slow and build things.") + (enable-git-config? #t) + (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.bluechateau.org/$CGIT_REPO_URL.git")) + ;; Source code syntax highlighting 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.bluechateau.org")) + (root cgit) + (ssl-certificate + "/etc/certs/bluechateau/fullchain.pem") + (ssl-certificate-key + "/etc/certs/bluechateau/privkey.pem") + (locations + (list + (nginx-location-configuration + (uri "~ (/.*\\.git/.*)") + (body + `("fastcgi_pass 127.0.0.1:9000;" + ("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 /srv/git/repositories;" + "fastcgi_param PATH_INFO $1;"))) + (git-http-nginx-location-configuration + (git-http-configuration + (uri-path "/") + (git-root "/srv/git/repositories") + (export-all? #t))) + (nginx-location-configuration + (uri "~ (/.*)\\.git") + (body + '("return 301 $1;"))) + (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 127.0.0.1:9000;")))))))))))) + +(define searxng-oci-container-configuration + (oci-container-configuration + (image "docker.io/searxng/searxng") + (ports + '(("8888" . "8080"))) + (volumes + '("searx-config:/etc/searxng/" + "searx-data:/var/cache/searxng/")))) + +(define searxng-nginx-server-configuration + (nginx-server-configuration + (listen '("443 ssl")) + (server-name '("search.bluechateau.org")) + (ssl-certificate + "/etc/certs/bluechateau/fullchain.pem") + (ssl-certificate-key + "/etc/certs/bluechateau/privkey.pem") + (locations + (list + (nginx-location-configuration + (uri "/") + (body + '("proxy_pass http://localhost:8888;" + "proxy_set_header X-Real-IP $remote_addr;" + "proxy_set_header Host $host;" + "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"))))))) + +(define cryptpad-config + (plain-file "config.js" + "module.exports = { + httpUnsafeOrigin: 'https://docs.bluechateau.org', + httpSafeOrigin: 'https://sbdocs.bluechateau.org', + httpAddress: '0.0.0.0', + httpPort: 3000, + websocketPort: 3003, + adminKeys: [], + filePath: './datastore/', + archivePath: './data/archive', + pinPath: './data/pins', + taskPath: './data/tasks', + blockPath: './block', + blobPath: './blob', + blobStagingPath: './data/blobstage', + decreePath: './data/decrees', + logPath: './data/logs', + logToStdout: false, + logLevel: 'info', + logFeedback: false, + verbose: false, + installMethod: 'docker', +};")) + +(define cryptpad-oci-container-configuration + (oci-container-configuration + (image "cryptpad/cryptpad") + (ports + '(("3000" . "3000") + ("3003" . "3003"))) + (volumes + `("cryptpad-blob:/cryptpad/blob" + "cryptpad-block:/cryptpad/block" + "cryptpad-customize:/cryptpad/customize" + "cryptpad-data:/cryptpad/data" + "cryptpad-datastore:/cryptpad/datastore" + "cryptpad-onlyoffice-dist:/cryptpad/www/common/onlyoffice/dist" + "cryptpad-onlyoffice-conf:/cryptpad/onlyoffice-conf" + (,cryptpad-config . "/cryptpad/config/config.js"))) + (environment + '("CPAD_MAIN_DOMAIN=https://docs.bluechateau.org" + "CPAD_SANDBOX_DOMAIN=https://sbdocs.bluechateau.org" + "CPAD_CONF=/cryptpad/config/config.js")) + (extra-arguments + '("-h cryptpad")))) + +(define cryptpad-nginx-server-configuration + (nginx-server-configuration + (listen '("443 ssl")) + (server-name '("docs.bluechateau.org" + "sbdocs.bluechateau.org")) + (ssl-certificate + "/etc/certs/bluechateau/fullchain.pem") + (ssl-certificate-key + "/etc/certs/bluechateau/privkey.pem") + (raw-content + '("ssl_dhparam /srv/cryptpad/dhparam.pem;" + "ssl_session_timeout 1d;" + "ssl_session_cache shared:MozSSL:10m;" + "ssl_session_tickets off;" + "ssl_protocols TLSv1.2 TLSv1.3;" + "ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;" + "ssl_prefer_server_ciphers off;" + "add_header Strict-Transport-Security \"max-age=63072000; includeSubDomains\" always;")) + (locations + (list + (nginx-location-configuration + (uri "/") + (body + '("proxy_pass http://localhost:3000;" + "proxy_set_header X-Real-IP $remote_addr;" + "proxy_set_header Host $host;" + "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" + "client_max_body_size 150m;" + "proxy_http_version 1.1;" + "proxy_set_header Upgrade $http_upgrade;" + "proxy_set_header Connection upgrade;"))) + (nginx-location-configuration + (uri "^~ /cryptpad_websocket") + (body + '("proxy_pass http://localhost:3003;" + "proxy_set_header X-Real-IP $remote_addr;" + "proxy_set_header Host $host;" + "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" + "proxy_http_version 1.1;" + "proxy_set_header Upgrade $http_upgrade;" + "proxy_set_header Connection upgrade;"))))))) + +(define planarally-config + (plain-file "config.toml" + "[general] +save_file = \"data/planar.sqlite\" +admin_user = \"azalea\" +allow_signups = true +enable_export = true +max_log_size_in_bytes = 200000 +max_log_backups = 5 + +[assets] +max_single_asset_size_in_bytes = 0 +max_total_asset_size_in_bytes = 0 + +[webserver] +max_upload_size_in_bytes = 10485760 + +[webserver.connection] +type = \"hostport\" +host = \"0.0.0.0\" +port = 8000")) + +(define planarally-oci-container-configuration + (oci-container-configuration + (image "kruptein/planarally") + (ports + '(("8000" . "8000"))) + (volumes + `("pa-data:/planarally/data" + "pa-assets:/planarally/static/assets" + "pa-mods:/planarally/static/mods" + (,planarally-config . "/planarally/data/config.toml"))))) + +(define planarally-nginx-server-configuration + (nginx-server-configuration + (listen '("443 ssl")) + (server-name '("vtt.bluechateau.org")) + (ssl-certificate + "/etc/certs/bluechateau/fullchain.pem") + (ssl-certificate-key + "/etc/certs/bluechateau/privkey.pem") + (locations + (list + (nginx-location-configuration + (uri "/") + (body + '("proxy_set_header Host $http_host;" + "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" + "proxy_redirect off;" + "proxy_buffering off;" + "proxy_set_header X-Real-IP $remote_addr;" + "proxy_pass http://localhost:8000;"))) + (nginx-location-configuration + (uri "/socket.io/") + (body + '("proxy_http_version 1.1;" + "proxy_set_header Upgrade $http_upgrade;" + "proxy_set_header Connection \"Upgrade\";" + "proxy_set_header Host $host;" + "proxy_pass http://localhost:8000/socket.io/;"))))))) + +(define funhole-nginx-server-configuration + (nginx-server-configuration + (listen '("443 ssl")) + (server-name '("fwiki.bluechateau.org")) + (root "/srv/fwiki/") + (ssl-certificate + "/etc/certs/bluechateau/fullchain.pem") + (ssl-certificate-key + "/etc/certs/bluechateau/privkey.pem") + (locations + (list + (nginx-location-configuration + (uri "/") + (body + `(("default_type text/html;") + "try_files $uri $uri/ =404;"))))))) + +(define chinstrap + (operating-system + (kernel linux) + (firmware (list linux-firmware)) + (initrd microcode-initrd) + (locale "en_US.utf8") + (timezone "America/Los_Angeles") + (keyboard-layout (keyboard-layout "us")) + (host-name "chinstrap") + + (users (cons* (user-account + (name "moshimoshi") + (comment "Captain 'Moshi-Moshi' Momo") + (group "users") + (home-directory "/home/moshimoshi") + (supplementary-groups + '("docker" "git" "netdev" "wheel"))) + %base-user-accounts)) + + (bootloader (bootloader-configuration + (bootloader grub-efi-bootloader) + (targets (list "/boot/efi")) + (keyboard-layout keyboard-layout))) + + (file-systems (cons* (file-system + (mount-point "/boot/efi") + (device (uuid "BBEC-EE14" + 'fat32)) + (type "vfat")) + (file-system + (mount-point "/") + (device (uuid + "74a221a4-2d9d-4250-b0de-cb5a6a5962ae" + 'ext4)) + (type "ext4")) %base-file-systems)) + + (swap-devices (list (swap-space + (target (uuid + "a46ba488-1ad1-4c5e-a4f6-e177d4c498aa"))))) + + (packages + (append + (map specification->package + '("emacs" "git")) + %base-packages)) + + (services + (append + (list + ;; Basic system services + (service dhcpcd-service-type) + (service dbus-root-service-type) + (service elogind-service-type) + + ;; OpenSSH + (service openssh-service-type + (openssh-configuration + (openssh openssh-sans-x) + (port-number 22))) + + ;; Docker containers & virtual machines + (service containerd-service-type) + (service docker-service-type) + (service libvirt-service-type + (libvirt-configuration + (unix-sock-group "libvirt") + (tls-port "16555"))) + (simple-service 'oci-service + oci-service-type + (oci-extension + (containers + (list + ;; SearXNG + searxng-oci-container-configuration + ;; CryptPad + cryptpad-oci-container-configuration + ;; PlanarAlly + planarally-oci-container-configuration)))) + + ;; Certbot (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 with + ;; the 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 "mononoke@anche.no") + (certificates + (list + (certificate-configuration + (name "bluechateau") + (domains ssl-domains)))))) + + ;; NGINX (web hosting) + ;; Be sure to use the custom service defined at the + ;; top of this file! + (service nginx-service-type* + (nginx-configuration + (server-blocks + (list + ;; Static content + blog-nginx-server-configuration + ;; SearXNG + searxng-nginx-server-configuration + ;; Funhole wiki mirror + funhole-nginx-server-configuration + ;; CryptPad + cryptpad-nginx-server-configuration + ;; PlanarAlly VTT + planarally-nginx-server-configuration)))) + + git-fcgiwrap-service + gitolite-service + cgit-service) + %base-services)))) + +chinstrap |
