Skip to content

storopoli/flakes

Repository files navigation

NixOS/macOS Flake

License: MIT

"A man and his tools make a man and his trade"

-Vita Sackville-West

"We shape our tools and then the tools shape us"

-Winston Churchill

These are my NixOS/macOS Nix setup.

NixOS macOS
macOS NixOS

Common Features

NixOS

This is paranoid build with root on tmpfs. This means that everything outside of some directories of /etc and some directories of /home will be wiped out. Read more about this in the NixOs Paranoid Guide (this is also a good source NixOS tmpfs as /home).

Features

  • Hardened Kernel Boot Parameters: Based on this guide, and also on secureblue:

    • Use the memory allocator scudo, protecting against some buffer overflow exploits

    • Prevent kernel modules to be loaded after boot

    • Protect against rewriting kernel image

    • Increase containers/virtualization protection at a performance cost (L1 flush or page table isolation)

    • Apparmor is enabled by default

    • Many filesystem modules are forbidden because old/rare/not audited enough

    • Firewall: block any incoming traffic

    • clamav antivirus

    • firejail: run programs to restrict its permissions and rights. This is rather important to run web browsers with it because it will prevent them any access to the filesystem except ~/Downloads and a few required directories (local profile, /etc/resolv.conf, font cache etc...). The following packages/binaries are hardened with firejail:

      • chromium
      • tor-browser
      • signal-desktop
      • keepassxc
      • mpv
      • transmission-gtk
  • bcachefs filesystem

  • Secure Boot

  • Hyprland Wayland window manager:

  • Apps:

  • VPN support with wireguard

  • Keyboard customizations with keyd: Caps Lock as Escape (if tapped) and Control (if held).

  • Easy and automated disk partitioning with disko.

How to Install

Before starting, remember to enable a BIOS password. And disable Secure Boot.

As root:

  1. Prepare a 64-bit NixOS 23.11 minimal iso image or 64-bit NixOS unstable minimal iso image and burn it, then enter the live system. Suppose I have divided two partitions: /dev/nvme0n1p1 and /dev/nvme0n1p2

  2. Format the partitions:

    mkfs.fat -F 32 /dev/nvme0n1p1
    mkfs.ext4 /dev/nvme0n1p2 # or use LUKS with cryptsetup luksFormat /dev/nvme0n1p2 encryptedroot

    or use the disko script for bcachefs with LUKS (don't forget to clone the repo first):

    nix run github:nix-community/disko -- --mode disko linux/filesystem/<hostname>/disko.nix
    # verify the mount
    mount | grep /mnt
    # you may need to skip some commands in the next "mount" step
  3. Mount:

    mount -t tmpfs none /mnt
    mkdir -p /mnt/{boot,nix,etc/nixos}
    mount /dev/nvme0n1p2 /mnt/nix # or LUKS with mount /dev/mapper/encryptedroot /mnt/nix
    mount /dev/nvme0n1p1 /mnt/boot
    mkdir -p /mnt/nix/persist/etc/nixos
    mount -o bind /mnt/nix/persist/etc/nixos /mnt/etc/nixos
  4. Generate a basic configuration:

    nixos-generate-config --root /mnt
  5. Clone the repository locally:

    nix-shell -p git
    # recursive for git submodules
    git clone --recursive https://github.com/storopoli/flakes.git /mnt/etc/nixos/flakes
    cd /mnt/etc/nixos/flakes/
    nix develop --extra-experimental-features "nix-command flakes" --extra-experimental-features flakes
  6. If you want Secure Boot, now is the time that you should create your keys.

  7. Migrate all the custom hardware-configuration.nix from /mnt/etc/nixos into /mnt/etc/nixos/flakes/linux/system.nix and /mnt/etc/nixos/flakes/linux/filesystem.nix:

    vi /mnt/etc/nixos/flakes/linux/system.nix
    ...
    # This is just an example
    # Please refer to `https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/#step-4-1-configure-disks`
    
      fileSystems."/" =
        { device = "none";
          fsType = "tmpfs";
          options = [ "defaults" "size=12G" "mode=755"  ];
        };
    
      fileSystems."/nix" =
        { device = "/dev/disk/by-uuid/49e24551-c0e0-48ed-833d-da8289d79cdd";
          fsType = "ext4";
        };
    
      fileSystems."/boot" =
        { device = "/dev/disk/by-uuid/3C0D-7D32";
          fsType = "vfat";
        };
    
      fileSystems."/etc/nixos" =
        { device = "/nix/persist/etc/nixos";
          fsType = "none";
          options = [ "bind" ];
        };
    ...
  8. remove /mnt/etc/nixos/flakes/.git:

    rm -rf .git
  9. Username modification: edit user in /mnt/etc/nixos/flakes/flake.nix, /mnt/etc/nixos/flakes/linux/default.nix, and /mnt/etc/nixos/flakes/linux/wayland.nix; hostname modification: edit /mnt/etc/nixos/flakes/common/default.nix to modify the hostName value in the networking property group

  10. Use the hash password generated by the mkpasswd {PASSWORD} -m sha-512 command to replace the value of users.users.<name>.hashedPassword in /mnt/etc/nixos/flakes/linux/default.nix (there are two places to be edited)

  11. Perform install:

    nixos-install --no-root-passwd --flake .#laptop
  12. Reboot

    reboot
  13. If you want Secure Boot, now is the time that you should continue the setup.

  14. Enjoy it!

OPTIONAL: Secure Boot

Based on the Quickstart Guide from lanzaboote

Step 1: Create your Keys
  1. Verify if the ESP is mounted at /boot: bootctl status

  2. Create your keys with sbctl (available in the Flake shell, i.e. nix develop .)

    $ sudo sbctl create-keys
    [sudo] password for user:
    Created Owner UUID 8ec4b2c3-dc7f-4362-b9a3-0cc17e5a34cd
    Creating secure boot keys...✓
    Secure boot keys created!

    When it is done, your Secure Boot keys are located in /etc/secureboot. sbctl sets the permissions of the secret key so that only root can read it.

Step 2: Enabling Secure Boot
  1. Rebuild your system and check the sbctl verify output:

    $ sudo sbctl verify
    Verifying file database and EFI images in /boot...
    ✓ /boot/EFI/BOOT/BOOTX64.EFI is signed
    ✓ /boot/EFI/Linux/nixos-generation-355.efi is signed
    ✓ /boot/EFI/Linux/nixos-generation-356.efi is signed
    ✗ /boot/EFI/nixos/0n01vj3mq06pc31i2yhxndvhv4kwl2vp-linux-6.1.3-bzImage.efi is not signed
    ✓ /boot/EFI/systemd/systemd-bootx64.efi is signed

    It is expected that the files ending with bzImage.efi are not signed.

  2. Enable Secure Boot. On Framework Laptops:

    1. Select "Administer Secure Boot"
    2. Select "Erease all Secure Boot Settings"
    3. When you are done, press F10 to save and exit.

    On ASUS Desktop Motherboards, there is no explicit option to enter Setup Mode. Instead, choose the option to erase the existing Platform Key

Step 3: Enrolling Keys

Once you've booted your system into NixOS again, you have to enroll your keys to activate Secure Boot.

$ sudo sbctl enroll-keys --microsoft
Enrolling keys to EFI variables...
With vendor keys from microsoft...✓
Enrolled keys to the EFI variables!

Finally, reboot and check if Secure Boot is activated and in user mode:

$ bootctl status
System:
      Firmware: UEFI 2.70 (Framework 3.03)
 Firmware Arch: x64
   Secure Boot: enabled (user)
  TPM2 Support: yes
  Boot into FW: supported

How to Update

  1. First, update the input in flake:

    # update the specified input
    nix flake lock --update-input <foo> <foo>
    # or update all inputs
    nix flake update
    # also you can reclaim storage with
    nix-collect-garbage -d
  2. Then, rebuild and switch to the system after rebuild:

    doas nixos-rebuild boot --flake .#<hostname>

Wireguard VPN Configs

Sources: manpage of wg-quick, Mullvad WireGuard on Linux terminal IVPN Autostart WireGuard in systemd, and IVPN WireGuard Kill Switch

For the extra paranoid, you can use VPNs without installing their apps. You will need WireGuard.

  1. Create your configuration in /etc/wireguard/wg0.conf. You can also name wg0.conf whatever you want. Any free-form string [a-zA-Z0-9_=+.-]{1,15} will work. These configs are generally provided by your VPN provider. They generally look something like this:

    [Interface]
    PrivateKey = abcdefghijklmnopqrstuvwxyz0123456789=
    Address = x.y.z.w/32
    DNS = x.y.z.w
    [Peer]
    PublicKey = abcdefghijklmnopqrstuvwxyz0123456789=
    Endpoint = sub.wg.domain.tld:9999
    AllowedIPs = 0.0.0.0/0
  2. Add "kill switch" configs. Add the following two lines to the [Interface] section, just before the [Peer] section:

    PostUp  = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
    PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT

    You may get a problem to connect to your local network. You can modify the kill switch, so it includes an exception for your local network, for example ! -d 192.168.1.0/24:

    PostUp  = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL ! -d 192.168.1.0/24 -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
    PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL ! -d 192.168.1.0/24 -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
  3. Make sure that you have the correct permissions, so only root can read them:

    sudo chown root:root -R /etc/wireguard && sudo chmod 600 -R /etc/wireguard
  4. Start the WireGuard connection with:

    sudo wg-quick up wg0
    # to disconnect
    sudo wg-quick down wg0

Autostart WireGuard in systemd

If you are using a Linux distribution that comes with systemd, you can autostart a WireGuard connection with:

sudo systemctl enable wg-quick@wg0.service
sudo systemctl daemon-reload
sudo systemctl start wg-quick@wg0

To check status: sudo systemctl status wg-quick@wg0

To remove the service and clean up the system:

sudo systemctl stop wg-quick@wg0
sudo systemctl disable wg-quick@wg0.service
sudo rm -i /etc/systemd/system/wg-quick@wg0*
sudo systemctl daemon-reload
sudo systemctl reset-failed

Testing the Kill Switch

One way to test a down tunnel is to delete the IP address from the WireGuard network interface, like this via the Terminal:

sudo ip a del [IP address] dev [interface]

In this example, it’s possible to remove x.y.z.w from the wg0 interface:

sudo ip a del x.y.z.w/32 dev wg0

The PostUP iptables rule from above restricts all traffic to the tunnel, and all outgoing attempts to get traffic out fail. To gracefully recover from this, you will likely have to use the wg-quick command to take the connection down, then bring it back up.

macOS

The macOS configs are minimalist in approach and geared towards enhancing security and privacy. It uses the best practices described in the MacOS Hardening Guide and the MacOS Security and Privacy Guide.

Why not Homebrew?

Honestly, Homebrew is a Ruby bloatware. It is slow, non-reproducible, and a mess to maintain.

Nix is superior in every way. It is fast as fuck, and it is 100% reproducible. Migrating to new hardware or rebuilding old hardware after a wipe is a breeze.

Features

  • Tiling window manager with Rectangle.

  • Status Bar with stats

  • Apps:

  • Common developer enhancements in Finder and Search

  • MacOS privacy and security enhancements

  • Debloating of animations

Prepare your system

Before installing anything you'll need to prepare your system:

  1. Don't register an Apple ID

  2. Enable Lockdown Mode

  3. Disable all Sharing stuff: General > Sharing: Disable All

  4. Disable Notifications previews:

    • Notifications > Show Previews: Never
    • Notifications: Disable "Allow notifications when the screen is locked"
    • Lock Screen > Require password immediately
  5. Change NTP Server: General > Date & Time > Source: Change to "pool.ntp.org"

  6. Set the smart battery saver: Boost mode on AC and Low Power mode on battery

  7. Disable Siri:

    • Siri and Spotlight: Disable "Ask Siri"
    • Siri and Spotlight > Siri Suggestions > Disable all
  8. Disable Analytics:

    • Privacy and Security > Analytics > Improvements: Disable all
    • Privacy and Security > Apple Advertising > Disable personalized ads
    • Game Center: Disable all

How to Install

  1. Install Xcode Command Line Tools:

    xcode-select --install
  2. Install Nix using the official installer:

    sh <(curl -L https://nixos.org/nix/install) --daemon
  3. Enable Flake support:

    echo 'experimental-features = nix-command flakes' >> /etc/nix/nix.conf
  4. Install nix-darwin:

    # aarch64
    nix run nix-darwin -- switch --flake .#macbook
    # x86_64
    nix run nix-darwin -- switch --flake .#macbook_x86
  5. Apply changes to your system:

    darwin-rebuild switch --flake .

How to Update

  1. First, update the input in flake:

    # update the specified input
    nix flake lock --update-input <foo> <foo>
    # or update all inputs
    nix flake update
    # also you can reclaim storage with
    nix-collect-garbage -d
  2. Then, rebuild and switch to the system after rebuild:

    nix run --extra-experimental-features 'nix-command flakes' nix-darwin -- switch --flake .
    # or if nix-command and flakes are enabled:
    darwin-rebuild switch --flake .

Flakes Creed

This is my computer. There are many like it, but this one is mine. My computer is my best friend. It is my life. I must master it as I must master my life. Without me, my computer is useless. Without my computer, I am useless. I must configure my computer true. I must code more efficiently than my enemy, who is trying to outperform me. I must debug him before he debugs me. I will...

My computer and I know that what counts in war is not the lines we code, the noise of our fans, nor the smoke we make. We know that it is the runs that count. We will run...

My computer is human, even as I, because it is my life. Thus, I will learn it as a brother. I will learn its weaknesses, its strength, its parts, its accessories, its dotfiles, and its configs. I will keep my computer clean and ready, even as I am clean and ready. We will become part of each other. We will...

Before the Internet, I swear this creed. My computer and I are the defenders of my work. We are the masters of our enemy. We are the saviors of my projects.

So be it, until victory is mine and there is no enemy, but peace!