+++ title = "Deploying Cuirass on GuixSD" date = "2024-06-11" author = "Peter Tillemans" email = "pti@snamellit.com" [taxonomies] tags = ["programming"] categories = ["guix", "linux"] +++ # Table of Contents 1. [Install Cuirass in Guix](#orge201d70) 2. [Create a postgresql server](#org0f19dc3) 3. [Enable the cuirass service](#org5f8483e) 4. [Enabling the Web Frontend](#org8d12621) 5. [Submitting jobs](#orged5a51b) 1. [Figure out a more elegant way to submit jobs](#org2ef3b08) # Install Cuirass in Guix I had a hard time installing **cuirass** on my system. I tried to collect some notes to move step by step to a working solution. GUIX is great that it is really easy to revert your step to get out or a dead end and start over from a previous point. Something I made liberally use off to get something working. However it also means my deployment process was not really linear but much more error driven. # Create a postgresql server Starting the cuirass service will pull in a postgres server, however it will use by default a version 10. Let's update that to 15 for some future proofing: (service postgresql-role-service-type (postgresql-role-configuration (roles (list (postgresql-role (name "cuirass") (create-database? #t)) (postgresql-role (name "xyz") (create-database? #t)))))) I also added a database user for me (**xyz** here stands for my user account). This will also create a database with the same name to allow the cuirass user (and me) to login to the postgres database and do database things. Run a \`guix system reconfigure\` and check if the postgres is running with \`sudo herd status postgres\`. # Enable the cuirass service With the database in place there is a fighting chance to get the service running : (service cuirass-service-type (cuirass-configuration (specifications #~(list)))) The example in the reference documentation offers something more interesting than an empty list but whatever I tried ended up with 'invalid field specifier' errors. But I get that too for nginx configuration parts so that is probably a skill issue on my part. With a bit of luck we'll see: xyz@foo ~/.config/dotfiles/guix [env]$ sudo herd status cuirass Status of cuirass: It is running since 05:19:42 PM (4 hours ago). Running value is 6513. It is enabled. Provides (cuirass). Requires (user-processes guix-daemon postgres postgres-roles networking). Will be respawned. xyz@foo ~/.config/dotfiles/guix [env]$ sudo herd status cuirass-web Status of cuirass-web: It is running since 05:22:41 PM (4 hours ago). Running value is 6679. It is enabled. Provides (cuirass-web). Requires (user-processes cuirass). Will be respawned. If not there may be some info in \`/var/log/cuirass.log\` or \`/var/log/cuirass-web.log\` Alternatively for debugging we can run the application from the git repository. First of all we have to give our user access to the database: xyz@foo ~/.config/dotfiles/guix [env]$ sudo -u cuirass psql Password: psql (15.4) Type "help" for help. cuirass=> grant all on database cuirass to xyz; GRANT if the cuirass service has initialised the database already you can add: cuirass=> grant all on all tables in schema public to xyz; GRANT cuirass=> grant all on all sequences in schema public to xyz; GRANT This allows access without jumping through the sudo hoop. The code in the **guix-cuirass** project folder will now just work. Except the postgresql socket should be exposed too in the proposed command of the reference manual: guix shell -CPNW --expose=/var/log/guix/drvs \ --expose=/var/run/dbus --expose=/run/avahi-daemon \ --expose=/etc/ssl/certs --expose=/var/run/postgresql Note that if the tables and sequences are created when running in your account it is quite possible that the **cuirass** user will not be able to access them and complain with access denied errors. In that case we have to do the grants for the **cuirass** user: xyz@foo ~/.config/dotfiles/guix [env]$ psql cuirass Password: psql (15.4) Type "help" for help. xyz=> grant all on all tables in schema public to cuirass; GRANT xyz=> grant all on all sequences in schema public to cuirass; GRANT Then restarting the service with \`sudo herd restart cuirass\` should make it start, or at least give a different error. Once **cuirass** service is running, it will be possible to run the **cuirass-web** service. It relies on the existence of the **/var/run/cuirass/bridge** file which is created by the cuirass service. Just to be sure that the web server is running : xyz@foo ~/src/guix-cuirass [env]$ wget http://localhost:8081 --2024-06-11 21:59:09-- http://localhost:8081/ Resolving localhost (localhost)... 127.0.0.1 Connecting to localhost (localhost)|127.0.0.1|:8081... connected. HTTP request sent, awaiting response... 200 OK Length: 6142 (6.0K) [text/html] Saving to: ‘index.html’ index.html 100%[================================================>] 6.00K --.-KB/s in 0s 2024-06-11 21:59:09 (378 MB/s) - ‘index.html’ saved [6142/6142] Cool, we have a 200 Ok status code and some index.html file so the web UI is running. Now exposing it to the 'net. # Enabling the Web Frontend In the reference manual there is a nice configuration to start from to configure nginx as a frontend for **cuirass-web** However you cannot start nginx https without having the certificates and you cannot get the certificates without the server running. (Well you can but that is outside the scope of this post) So we have to cut back the configuration to only start the http nginx server to do the first handschake with letsencrypt to get the initial certificates Let's start with the **certbot-service** : (service certbot-service-type (certbot-configuration (email "xyz@bar.com") (certificates (list (certificate-configuration (domains '("foo.bar.com"))) )))) Then add the minimal part for a nginx http server to do the letsencrypt dance. (service nginx-service-type (nginx-configuration (server-blocks (list ;; TLS is required for authentication; serve the site via ;; HTTPS only. (nginx-server-configuration (listen '("80")) (raw-content (list "return 308 https://$host$request_uri;"))) )))) Doing a \`guix system reconfigure\` now will start the nginx server and download fresh certificates. We can now add the https server proxy-ing the **cuirass-web** server: (service nginx-service-type (nginx-configuration (server-blocks (list ;; TLS is required for authentication; serve the site via ;; HTTPS only. (nginx-server-configuration (listen '("80")) (raw-content (list "return 308 https://$host$request_uri;"))) (nginx-server-configuration (listen '("443 ssl")) (server-name '("foo.bar.com")) (ssl-certificate "/etc/letsencrypt/live/foo.bar.com/fullchain.pem") (ssl-certificate-key "/etc/letsencrypt/live/foo.bar.com/privkey.pem") (locations (list ;; Proxy the whole Cuirass web site... (nginx-location-configuration (uri "/") (body (list "proxy_pass http://localhost:8081;"))) ;; ... but require authentication for the admin pages. (nginx-location-configuration (uri "~ ^/admin") (body (list "if ($ssl_client_verify != SUCCESS) \ { return 403; } proxy_pass http://localhost:8081;"))))) ;; (raw-content ;; ;; Register your self-generated certificate authority. ;; (list "ssl_client_certificate /etc/ssl/certs/Snamellit_CA.pem;" ;; "ssl_verify_client optional;")) ) )))) Creating and installing the root CA is a bit out of scope. For this post we'll just wave our hands and assume the certificate magically appeared in the \`/etc/ssl/certs/SnamellitCA.pem\` location. Creating a client certificate for firefox and importing it allows to access the admin section and retrigger jobs etc. Once that is setup the commented out section can be activated. # Submitting jobs For some reason I do not understand yet, the specification file must be in the loadpath of the cuirass program. For testing I add them to the **examples** folder in the **guix-cuirass** folder in the home folder of the **cuirass** user $ sudo -u cuirass -- bash # start a shell in the cuirass user $ cd guix-cuirass # enter the project folder $ cp /foo/bar/snamguix.scm examples # put spec somewhere on loadpath $ # start dev environment in container $ guix shell -CPNW --expose=/var/log/guix/drvs \ --expose=/var/run/dbus --expose=/run/avahi-daemon \ --expose=/etc/ssl/certs --expose=/var/run/postgresql guix shell: loading environment from '/home/pti/src/guix-cuirass/guix.scm'... $ ~/src/guix-cuirass [env]$ # register new recipe $ ~/src/guix-cuirass [env]$ ./pre-inst-env cuirass register -S examples/snamguix.scm 2024-06-11T20:18:08 running Fibers on 8 kernel threads 2024-06-11T20:18:08 marking stale builds as "scheduled"... 2024-06-11T20:18:08 builds will be made via the local build daemon 2024-06-11T20:18:08 will perform up to 8 evaluations concurrently 2024-06-11T20:18:08 opening bridge socket at '/tmp/cuirass-tests/var/run/cuirass/bridge' 2024-06-11T20:18:08 retrieving list of pending builds... 2024-06-11T20:18:08 unused GC roots older than 2592000s will be deleted every 86400s 2024-06-11T20:18:08 deleting old GC roots from '/var/guix/gcroots/profiles/per-user/pti/cuirass'... 2024-06-11T20:18:08 selected 0 GC roots to remove WARNING: (cuirass base): imported module (fibers) overrides core binding `sleep' 2024-06-11T20:18:08 heap: 12.18 MiB; threads: 17; file descriptors: 76 WARNING: (cuirass scripts register): imported module (fibers) overrides core binding `sleep' 2024-06-11T20:18:08 canceling 0 stale builds 2024-06-11T20:18:08 restarting 0 pending builds 2024-06-11T20:18:08 building 0 derivations in batches of 200 2024-06-11T20:18:08 done with 0 derivations 2024-06-11T20:18:08 outputs: $ This actually starts the scheduler and it just keeps running (unless we also give the –one-shot flag) but the side effect is to add the specification to the database. From now on the channel will be checked and build any updated packages. ## TODO Figure out a more elegant way to submit jobs There has to be a more elegant way