all messages for Guix-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Guix-μSD: setting up Guix on a μSD for the Librem 5
@ 2023-04-24  2:02 Zacchaeus Scheffer
  0 siblings, 0 replies; only message in thread
From: Zacchaeus Scheffer @ 2023-04-24  2:02 UTC (permalink / raw)
  To: help-guix

The following is my first draft of Guix-μSD, a literate program
detailing an optimal setup of guix home on PureOS for the Librem 5 (best
viewed in org-mode):

#! emacs --script
;; -*- org -*-
(find-file load-file-name) (org-mode) (org-babel-tangle) (kill-emacs)

Copyright (🄯) 2023  Zacchae.

Permission is granted to copy, distribute and/or modify these
documents under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts, no
Back-Cover Texts, and no History section.

Additionally, all code/functional structure contained in these
documents 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.

This 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.

* Introduction

  This guide is a literate program to help you bootstrap a working
  guix home install onto your Librem 5 running PureOS.  Guix system
  (Guix-SD) does not yet support the Librem 5, but you can still
  install the guix package manager on the Librem 5.  This process is
  supposed to get you as close as possible to a Guix-SD system,
  putting everything on a μSD card, thus I call this Guix-μSD.  I
  detail here everything I touched in PureOS, and leave the rest of
  the guix hacking to you.  The Librem 5 has only ~30GiB of internal
  memory, so this process puts guix, as well as all of your personal
  files, on a μSD, which I assume from here on that you have already
  inserted.  There is no garuntee that it will work, especially as
  systems change and get updated, and you should understand/test each
  line before running it.  In theory, you should be able to open your
  new Librem 5 and obtain this program by running (in ~):

  #+begin_src shell :tangle no
    sudo bash -c "apt update && apt upgrade -y && apt install git emacs -y"
    git clone https://zacchae.us/zmacs
    cd zmacs
    ./guid-usd.org
    ./guix-usd.bootstrap
    reboot
  #+end_src

  And follow the prompts.  However, https://zacchae.us is currently
  down (check later for an updated version), and I have made some
  changes to code here which haven't been tested, so doing so is
  dangerous.  After rebooting, you would finish setup with.

  #+begin_src shell :tangle no
    cd zmacs
    ./guix-usd.configure
    # and optionally:
    #./guix-usd.candy
  #+end_src

  Leaving you with a functional zmacs/home.scm which you can start
  from.  However, you may end up with a bricked device that must be
  re-flashed, which is [[Re-flahsing from Guix][difficult to do from Guix]].  There will be no
  warnings printed from the code, and I unashamedly pass "force" flags
  to make dangerous operations go through.  I do try to highlight
  dangers in the text here, so it is "slightly" safer if you read
  along and do things manually.  Additionally, some operations are
  performed (like appending to /etc/crypttab) that may break your
  system if run twice without reverting files.  If by some miracle,
  you follow this guide and end up with a working device, you will
  have a setup like the following:

  - At boot, when you decrypt your Librem 5's eMMC memory (i.e. /), it
    will attempt to decrypt your μSD using a key file located in /etc.
  - If the μSD decrypts successfully, then it will mount btrfs
    subvolumes from the decrypted μSD at /home, /gnu, and /var/guix.
  - This means your home, the guix store, and current state of guix
    are all stored on your μSD.
  - If the μSD fails to decrypt, then:
    - /gnu and /var/guix will fail to mount.
      - guix will be unusable
    - /home will be mounted from a file

** Final Warning

   This guide has you modify the following files:
   /etc/fstab
   /etc/crypttab

   Optionally modify the following:
   /lib/systemd/system/guix-daemon.service
   /etc/defaults/keyboard
   /etc/passwd

   Create the following files:
   /opt/usd/*
   /etc/luks-keys/*
   /gnu
   /var/guix

   And completely destroy any data on your μSD.

   This is your final warning to do a back up and pray.

* Initial setup

  We will be setting up the entire μSD as a singular encrypted device
  with a btrfs filesystem and subvolumes to hold guix and your files

  #+begin_src shell :tangle guix-usd.bootstrap :shebang #!/bin/sh
    sudo bash -c "apt update && apt upgrade -y && apt install btrfs-progs guix"
  #+end_src

  The μSD encryption keys will be in /etc/luks-keys, the special files
  for mounting /home will be in /opt/usd, and we will need mount
  points for guix folders /gnu and /var/guix

  #+begin_src shell :tangle guix-usd.bootstrap
    sudo mkdir /etc/luks-keys /opt/usd /gnu /var/guix
  #+end_src
  
* Encrypting your μSD

  To my knowledge, there is no way to prompt the user for a second
  encryption passphrase for the μSD at boot, hence the only way to
  decrypt the μSD at boot is to have a key file stored in your file
  tree.  This means that even if you remove your μSD, if you unlock
  your phone, then the keys to your μSD could be extracted from your
  phone.  Additionally, the following will erase any data which was
  previously on the μSD.

  #+begin_src shell :tangle guix-usd.bootstrap
    sudo dd if=/dev/urandom of=/etc/luks-keys/disk_secret_key bs=512 count=8
    echo Encrypting device.  Enter passphrase below (a few times).
    echo This passphrase will be usable in addition to the key file at:
    echo /etc/luks-key/disk_secret_key
    sudo cryptsetup luksFormat /dev/sda --batch-mode
    sudo cryptsetup luksAddKey /dev/sda /etc/luks-keys/disk_secret_key
  #+end_src
  
* Setting up your btr file system

  Open (map) the encrypted μSD, reformat the contents as btrfs (erases
  all data), mount the mapped device, and create subvolumes for /home,
  /gnu, and /var/guix.  Additionally, copy over all files from your
  home directory as a starting point for your new home directory.

  #+begin_src shell :tangle guix-usd.bootstrap
    sudo cryptsetup open /dev/sda crypt_sd --key-file=/etc/luks-keys/disk_secret_key
    sudo mkfs.btrfs --force /dev/mapper/crypt_sd -L btros
    sudo mount LABEL=btros /mnt
    sudo btrfs subvolume create /mnt/home
    sudo btrfs subvolume create /mnt/gnu
    sudo btrfs subvolume create /mnt/varguix
    sudo cp -a /home/purism /mnt/home/
    sudo umount /mnt
  #+end_src

* Setting up your backup /home

  As mentioned previously, /home must be mounted.  If the μSD does not
  exist, then mount it from elsewhere.  In this case, a file.  I
  allocate just 1GiB for the backup /home because I do not intend on
  using it often, but need a useable space to recover from if
  something (the μSD) fails.  Copy the contents of the current /home
  as well to be safe.

  #+begin_src shell :tangle guix-usd.bootstrap
    sudo dd if=/dev/zero of=/opt/usd/backup-home.btrfs bs=1MiB count=1024
    sudo mkfs.btrfs /opt/usd/backup-home.btrfs
    sudo mount /opt/usd/backup-home.btrfs /mnt
    sudo btrfs subvolume create /mnt/home
    cp -a /home/purism /mnt/home
  #+end_src

* Decrypting/Mounting at boot

  The following describes the steps to decrypt and mount your
  now-set-up μSD at boot.

** Decrypting the μSD

   To decrypt at boot, append the necessary entry to /etc/crypttab

   #+begin_src shell :tangle guix-usd.bootstrap
     echo crypt_sd UUID=$(sudo cryptsetup luksUUID /dev/sda) \
          /etc/luks-keys/disk_secret_key nofail,luks \
         | sudo tee -a /etc/crypttab
   #+end_src

   This will use the luks UUID to identify the μSD, and the 'nofail'
   flag ensures the system will still boot if μSD is broken or
   missing.

** Mounting /var/guix and /gnu

   To mount your now-decrypted μSD, append the necessary lines to
   /etc/fstab.  /home will be treated in the [[Mounting /home][next section]].  /var/guix
   and /gnu do not need to be accessable (guix will break) if the μSD
   is not present, so use nofail, and wait no more than 20s for the
   device to become available.

   #+begin_src shell :tangle guix-usd.bootstrap
     echo LABEL=btros /gnu btrfs \
          nofail,x-systemd.device-timeout=15,subvol=gnu 0 2 \
         | sudo tee -a /etc/fstab
     echo LABEL=btros /var/guix btrfs \
          nofail,x-systemd.device-timeout=15,subvol=varguix 0 2 \
         | sudo tee -a /etc/fstab
   #+end_src
   
** Mounting /home

   This step was very tricky.  I found a thread on the purism forum of
   people trying to accomplish having a /home mounted from μSD, with a
   fallback in case the μSD is broken or missing.  That thread died
   for a few years before I revived it with this hacky solution.
   Basically, as far as I can tell, neither /etc/fstab or .mount
   service units allow for any logic to determine if mount should
   occur.  If you specify a /home mount, and that mount fails, even if
   nofail is specified, then the system will NOT let you try and
   access files under /home.  This means that even if you have a
   perfectly fine home directory under the mount point, that you
   cannot access those files.  This means, among other things, that
   Phosh will fail to start.  However, though I couldn't figure out
   how to change IF a mount point is used, I could change WHAT device
   is used to mount.  Specifically, .mount systemd units allow for
   variable expansion, and variables can be read from a file.

   Hence, my solution is to have a systemd service which is a
   dependency of home.mount which generates an environment file which
   is subsequently read by home.mount.  The service checks if the μSD
   was successfully decrypted, and if so, directs home.mount to mount
   from the μSD.  Otherwise, home is mounted from /opt/usd/home.btrfs.

   Put the following in /etc/systemd/system/find-home.service

   #+begin_src text :tangle /sudo::/etc/systemd/system/find-home.service
     [Unit]
     Description=Check if sd card was found.  If so, use that.

     [Service]
     ExecStart=/opt/usd/find-home.sh
     Type=oneshot
     RemainAfterExit=yes

     [Install]
     RequiredBy=home.mount
   #+end_src

   Additionally, create the find-home.sh script which is to be run at
   /opt/usd/find-home.sh

   #+begin_src shell :shebang #!/bin/sh :tangle /sudo::/opt/usd/find-home.sh
     echo HOME_DEVICE=$(if [ -e /dev/mapper/crypt_sd ];
                        then echo /dev/mapper/crypt_sd;
                        else echo /opt/usd/backup-home.btrfs;
                        fi) > /opt/usd/mount_env
   #+end_src

   Finally, create the home.mount which will actually mount your home
   at /etc/systemd/system/home.mount

   #+begin_src text :tangle /sudo::/etc/systemd/system/home.mount
     [Unit]
     Description=Mount Var Test
     Requires=find-home.service
     After=find-home.service

     [Install]
     RequiredBy=phosh.service

     [Mount]
     EnvironmentFile=/opt/usd/mount_env
     What=${HOME_DEVICE}
     Where=/home
     Type=btrfs
     Options=subvol=home
   #+end_src

   Finally, enable the home.mount service

   #+begin_src shell :tangle guix-usd.bootstrap
     systemctl enable home.mount
   #+end_src

   After doing the above, it +should+ might be safe to reboot

   #+begin_src shell :tangle no
     reboot
   #+end_src

* Setting up guix home

  This section assumes you have successfully done all the setps
  described above and rebooted.  First, you should do a guix pull as
  your user, and additionally as root.

  #+begin_src shell :tangle guix-usd.configure :shebang #!/bin/sh
    guix pull
    sudo -i ~/.config/guix/current/bin/guix pull
  #+end_src

  Now that root's guix has been setup, you should modify the systemd
  unit file to use root's guix-daemon.

  #+begin_src shell :tangle guix-usd.configure
    sudo sed -i 's/\/usr\/bin\/guix-daemon/\/var\/guix\/profiles\/per-user\/root\/current-guix\/bin\/guix-daemon/' /lib/systemd/system/guix-daemon.service
    systemctl daemon-reload
    systemctl restart guix-daemon
  #+end_src

  This isn't strictly necessary, but I like that I can update
  guix-daemon with sudo -i guix pull.  This is also helpful because
  the current (as of this writing) version of guix installed by apt
  does not check bordeaux.guix.gnu.org for substitutes, something
  extremely necessary for tiny arm devices that have little RAM.  You
  can, however, enable bordeaux.guix.gnu.org by adding
  "--substitute-urls='https://bordeaux.guix.gnu.org
  https://ci.guix.gnu.org'" to the "ExecStart" feild of
  /lib/systemd/system/guix-daemon.service instead.

  One other problem I came across is that guix home environment
  variable initialization fails to properly deal with empty variables.
  There are ways to fix this by modifying guix-home, but looking at
  https://issues.guix.gnu.org/61982, it seems that they have decided
  to make changes to how guix is installed on foreign distros.  This
  means even if you guix pull after this is patched, guix home will
  probably not work properly until the next major release is adopted
  in your apt repo.

  Specifically, ~/.guix-home/setup-environment will assign to the
  empty XDG_CONFIG_DIRS variable without adding the default "/etc/xdg"
  value, so programs like phosh will not find /etc/xdg and fail to
  start.  This means if you naively install guix home on your new
  Librem 5, you will get a black screen on boot...

  To address this, I add the following line to my shell environment to
  set XDG_CONFIG_DIRS to the default value in the case that it is
  empty.

  #+begin_src scheme :tangle no
    ("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)")
  #+end_src

  From here you should be able to use the following starter home.scm
  at ./home.scm (not tested with bash).

  #+begin_src scheme :tangle home.scm
    (home-environment
      (packages (map specification->package (list "emacs")))
      (services
        (list
          (service
            home-bash-service-type
            (home-bash-configuration
              (environment-variables
               '(("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)")))))
          (service
            home-zsh-service-type
            (home-zsh-configuration
              (environment-variables
               '(("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)"))))))))
  #+end_src

  And initialize your first home environment!

  #+begin_src shell :tangle guix-usd.configure
    guix home reconfigure home.scm
  #+end_src

** Another note on substitutes

   If trying to run guix home still results in many packages trying to
   build, it could be that there was a recent push to the guix repo
   which caused a lot of rebuilds that the build farm can't keep up
   with.  The only solution I know of is to manually check
   https://ci.guix.gnu.org/jobset/master.  Go there and find a commit
   COMMIT right before a commit which resulted in a large number of
   builds.  Then run

   #+begin_src shell :tangle no
     guix pull --commit=COMMIT --allow-downgrades
   #+end_src

   This should revert your guix version to one with more substitutes
   available.

* Finishing Touches

** Changing UI scaling

   I do a lot of things at a terminal or emacs.  I want my phone
   screen to be like a real screen.  For this, I change the UI scaling
   of the builtin screen from 2 to 1 like so:

   #+begin_src shell :tangle guix-usd.candy
     echo '[output:DSI-1]
     scale = 1' >> /usr/share/phosh/phoc.ini
   #+end_src

** Changing console keyboard layout

   I like to use the dvorak layout with caps-lock as another control
   key.  For the gui, you need to change your layout in settings,
   download gnome-tweaks:

   #+begin_src shell :tangle no
     apt install gnome-tweaks
   #+end_src

   And enable "Caps Lock is also Ctrl" in gnome-tweaks>Keyboard &
   Mouse>additional Layout Options>Caps Lock behavior.  To make the
   same layout available from a tty, I ran:

   #+begin_src shell :tangle guix-usd.candy
     sudo apt install console-data
     echo '
     XKBMODEL="pc105"
     XKBLAYOUT="us"
     XKBVARIANT="dvorak"
     XKBOPTIONS="ctrl:nocaps"

     BACKSPACE="guess"
     ' > /etc/default/keyboard
   #+end_src

** Using zsh

   I personally like zsh over bash for interactive prompts.  To change
   to zsh, run:

   #+begin_src shell :tangle guix-usd.candy
     sudo apt install zsh
     echo Need user passwd to change shell to zsh
     chsh -s /bin/zsh
   #+end_src

* Re-flashing from Guix

  If you messed something up, and know what you did, you may want to try fixing your problem using Jumpdrive instead:
  https://github.com/dreemurrs-embedded/Jumpdrive
  This will allow you to mount your powered off Librem like a flash drive and make changes to fix your device.

  I don't have any automated solution for flashing a librem from guix, but I have figured out how to do it.  To do so, I mostly followed the instructions at https://developer.puri.sm/Librem5/Development_Environment/Phone/Troubleshooting/Reflashing_the_Phone.html.  First, I cloned the librem 5 flash image repository

  #+begin_src shell :tangle no
    git clone https://source.puri.sm/Librem5/librem5-flash-image
  #+end_src

  The main hiccup here is that the jenkins package required by the
  flashing script has not been packaged for guix.  To make it work, I
  manually made changes to scripts/librem5-flash-image: comment lines
  23-27, 461-484, and add `uboot_board = 'librem5'` to line 485 (line
  numbers may have changed since).

  Then, I manually tried to traverse https://arm01.puri.sm/ to find
  some images, and downloaded the necessary images

  #+begin_src shell :tangle no
    mkdir imdir
    wget https://arm01.puri.sm/job/Images/job/Image%20Build/14006/artifact/librem5r4.img.xz
    xz -d librem5r4.img.xz
    mv librem5r4.img imdir
    wget https://arm01.puri.sm/job/u-boot_builds/job/uboot_librem5_build/lastSuccessfulBuild/artifact/output/uboot-librem5/u-boot-librem5.imx
    mv u-boot-librem5.imx imdir/
  #+end_src

  Then, from a *ROOT SHELL*, I activated a guix shell, and downloaded
  necessary packages from pip:

  #+begin_src shell
    cd librem5-flash-image
    guix shel python gcc musl python-requests binutils usbutils uuu --pure
    python3 -m venv venv --upgrade-deps --system-site-packages
    source ./venv/bin/activate
    pip3 install tqdm pyyaml
    ./scripts/librem5-flash-image --skip-download --dir ./imdir
  #+end_src


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-04-24  5:39 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-24  2:02 Guix-μSD: setting up Guix on a μSD for the Librem 5 Zacchaeus Scheffer

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/guix.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.