(define-module (px system os) #:use-module (gnu bootloader) #:use-module (gnu bootloader grub) #:use-module (gnu packages linux) #:use-module (gnu services) #:use-module (gnu services base) #:use-module (gnu services networking) #:use-module (gnu services ssh) #:use-module (gnu system) #:use-module (gnu system file-systems) #:use-module (gnu system linux-initrd) #:use-module (gnu system nss) #:use-module (nongnu packages linux) #:use-module (nongnu system linux-initrd) #:use-module (guix gexp) #:use-module (guix channels) #:use-module (guix git-download) #:use-module (guix packages) #:use-module (guix records) #:use-module (ice-9 match) #:use-module (srfi srfi-1) #:export (make-os os-template os-template-service prepare-packages prepare-services prepare-swap-devices adjust-bootloader-theme %px-substitute-server-key %nonguix-substitute-server-key %px-substitute-server-url %nonguix-substitute-server-url %pantherx-default-channels apply-px-substitute-server)) ;;; ;;; Templates ;;; (define-record-type* os-template-service make-os-template-service os-template-service? (type os-template-service-type) ;type of modification required: 'add 'edit 'delete (kind os-template-service-kind) ;service-kind that needs to be matched (action os-template-service-action ;action to be applied on service (default #f))) (define-record-type* os-template make-os-template os-template? (title os-template-title) (firmwares os-template-firmwares ;list of firmwares to be installed (default '())) (packages os-template-packages ;list of to be installed (default '())) (services os-template-services ;list of (default '()))) (define (apply-template-firmwares initial-firmwares template-firmwares) (fold (lambda (firmware result) (if (memq firmware result) result (cons firmware result))) initial-firmwares template-firmwares)) (define (apply-template-package-imports initial-packages template-packages) (fold (lambda (pkg result) (if (memq pkg result) result (cons pkg result))) initial-packages template-packages)) (define (apply-template-service-modifications initial-services template-services) (fold (lambda (svc result) (match svc (($ type kind action) (case type ((add) (cons (if action (service kind action) (service kind)) result)) ((edit) (modify-services result (kind config => (action config)))) ((delete) (remove (lambda (s) (eq? (service-kind s) kind)) result)) (else result))))) initial-services template-services)) (define (apply-templates os-configuration os-templates) (fold (lambda (template result) (let ((target-firmwares (apply-template-firmwares (operating-system-firmware os-configuration) (os-template-firmwares template))) (target-packages (apply-template-package-imports (operating-system-packages result) (os-template-packages template))) (target-services (apply-template-service-modifications (operating-system-user-services result) (os-template-services template)))) (operating-system (inherit result) (firmware target-firmwares) (packages target-packages) (services target-services)))) os-configuration os-templates)) ;; ;; Firewall customization ;; (define (make-firewall-rules open-ports) (define (make-port-rules open-ports status) "Generate list of strings each is a port/service rule for nftables" (reduce-right append '() (map (match-lambda ((protocol ports ...) (map (lambda (port) (string-append " " protocol " dport " port " " status)) ports))) open-ports))) (let ((port-rules (make-port-rules open-ports "accept"))) (plain-file "nftables" (string-append "#PantherX firewall rules\n" "table inet filter {\n" " chain input {\n" " type filter hook input priority 0; policy drop; " " # early drop of invalid connections\n" " ct state invalid drop\n" " # allow established/related connections " " ct state { established, related } accept " " # allow from loopback\n" " iifname lo accept\n" " # allow icmp\n" " ip protocol icmp accept\n" " ip6 nexthdr icmpv6 accept\n" (string-join port-rules "\n" 'suffix) " # reject everything else\n" " reject with icmpx type port-unreachable " " }\n" " chain forward {\n" " type filter hook forward priority 0; policy drop; " " }\n" " chain output {\n" " type filter hook output priority 0; policy accept; " " }\n" "}\n")))) ;; ;; OS customization ;; (define %px-artwork-repository (let ((commit "ecfd456e814a59e3b6743bcda83eab5d5c12ae99")) (origin (method git-fetch) (uri (git-reference (url "https://git.pantherx.org/development/desktop/px-artwork.git") (commit commit))) (file-name (string-append "px-artwork-" (string-take commit 7) "-checkout")) (sha256 (base32 "06i47c8qp239c9rgkcizk3jd8rld4qbx90s5gg1a1rw1x90p245z"))))) (define %px-grub-theme (grub-theme (image (file-append %px-artwork-repository "/grub/PantherX-4-3.svg")))) (define %px-substitute-server-url "https://packages.pantherx.org") (define %nonguix-substitute-server-url "https://substitutes.nonguix.org") (define %px-substitute-server-key (plain-file "packages.pantherx.org.pub" "(public-key (ecc (curve Ed25519) (q #E8322D13EA02C09F06CB70FDA2ABBFD5E463F2AA34C18C692F5E25858F4E315D#) ) ) ")) (define %nonguix-substitute-server-key (plain-file "substitutes.nonguix.org.pub" "(public-key (ecc (curve Ed25519) (q #C1FD53E5D4CE971933EC50C9F307AE2171A2D3B52C804642A7A35F84F3A4EA98#) ) ) ")) (define (adjust-bootloader-theme config) (let* ((bootloader-config (operating-system-bootloader config)) (bootloader (bootloader-configuration-bootloader bootloader-config))) (case (bootloader-name bootloader) ((grub grub-efi) (bootloader-configuration (inherit bootloader-config) (theme %px-grub-theme))) (else bootloader-config)))) (define (prepare-packages config default-packages) "Check if custom packages provided in system configuration file or not. return @code{default-packages} if there was no modifications applied." (let ((package-list (operating-system-packages config))) (if (eq? package-list %base-packages) default-packages package-list))) (define (prepare-services config default-services) "Check if custom services provided in system configuration file or not. return @code{default-services} if there is no modification applied." (let ((service-list (operating-system-user-services config))) (if (eq? service-list %base-services) default-services service-list))) (define (prepare-swap-devices config default-value) "Check if custom definition provided for swap-devices or not. return @code{defaule-value} if there is no modification applied." (let ((devices (operating-system-swap-devices config))) (if (eq? devices '()) default-value devices))) (define (prepare-kernel config kernel) (case kernel ((libre) linux-libre) ((nonlibre) linux) (else (operating-system-kernel config)))) (define (prepare-initrd config kernel) (case kernel ((libre) base-initrd) ((nonlibre) microcode-initrd) (else (operating-system-initrd config)))) (define (prepare-firmwares config kernel) (case kernel ((libre) %base-firmware) ((nonlibre) (list linux-firmware)) (else (operating-system-firmware config)))) (define %pantherx-default-channels (append (list (channel (name 'pantherx) (branch "master") (url "https://channels.pantherx.org/git/panther.git") (introduction (make-channel-introduction "54b4056ac571611892c743b65f4c47dc298c49da" (openpgp-fingerprint "A36A D41E ECC7 A871 1003 5D24 524F EB1A 9D33 C9CB"))))) %default-channels)) ;; ;; OS config generation ;; (define (apply-px-substitute-server guix-config) (let ((existing-urls (guix-configuration-substitute-urls guix-config)) (existing-keys (guix-configuration-authorized-keys guix-config))) (guix-configuration (inherit guix-config) (substitute-urls (append (list %px-substitute-server-url %nonguix-substitute-server-url) existing-urls)) (authorized-keys (append (list %px-substitute-server-key %nonguix-substitute-server-key) existing-keys)) (channels %pantherx-default-channels)))) (define* (make-os config #:key (kernel 'libre) (open-ports #f) (authorized-keys '()) (templates '()) default-packages default-services) "Create definition based on provided templates and default packages and services" (define (apply-firewall-rules config) (nftables-configuration (inherit config) (ruleset (if open-ports (make-firewall-rules open-ports) (nftables-configuration-ruleset config))))) (define (apply-authorized-keys config) (if (> (length authorized-keys) 0) (openssh-configuration (inherit config) (authorized-keys authorized-keys)) config)) (define (apply-swap-changes config) (let ((devices (operating-system-swap-devices config))) (if (eq? devices '()) (list (swap-space (target "/swapfile"))) devices))) (let ((target-kernel (prepare-kernel config kernel)) (target-initrd (prepare-initrd config kernel)) (target-firmwares (prepare-firmwares config kernel)) (target-bootloader (adjust-bootloader-theme config)) (target-packages (prepare-packages config default-packages)) (target-services (prepare-services config default-services))) (apply-templates (operating-system (inherit config) (bootloader target-bootloader) (kernel target-kernel) (initrd target-initrd) (firmware target-firmwares) (swap-devices (apply-swap-changes config)) (packages target-packages) (services (fold (lambda (svc result) (let ((type (service-kind svc)) (value (service-value svc))) (cond ((eq? type guix-service-type) (cons (service guix-service-type (apply-px-substitute-server value)) result)) ((eq? type nftables-service-type) (cons (service nftables-service-type (apply-firewall-rules value)) result)) ((eq? type openssh-service-type) (cons (service openssh-service-type (apply-authorized-keys value)) result)) (else (cons svc result))))) '() target-services)) (name-service-switch %mdns-host-lookup-nss)) templates)))