11 Basic NixOS setup
Izorkin edited this page 10 months ago

Basic NixOS setup

Любая ОС linux после установки требует базовой настройки - установки необходимых приложений, настройку базовых служб и безопасности. Обычно для таких целей я создаю текстовый файл и в него копирую базовые команды по настройке ОС и конфигурационные файлы. У каждой версии дистрибутива Linux базовая настройка системы может отличаться. Несколько лет назад наткнулся на Linux дистрибутив NixOS с декларативным описанием конфигурации системы и функциональным менеджером пакетов nix. Вся конфигурация NixOS описывается в конфигурационном файле /etc/nixos/configuration.nix, в котором указывается настройки системы, устанавливаемые пакеты и сервисы. На основе этого файла производится создание конфигурации системы командой nixos-rebuild.

Возможные практические применения дистрибутива NixOS:

  • Централизованное хранение конфигурации для различных серверов, например в git репозитории.
  • Централизованное управление, конфигурирование и обновление групп серверов с помощью утилиты nixops.
  • Возможность полного воспроизведения состояния системы или определённого приложения на другом сервере для отладки, так как поддерживается механизм повторяющихся сборок.
  • Возможность использования полностью идентичного набора приложений на различных ОС Linux c помошью пакетного менеджера nix.

К примеру, в производстве ОС NixOS используют такие компании:

На практике покажу как установить NixOS и сделать первоначальную настройку системы. Конфигурацию сделал заранее, осталось только скопировать её в нашу систему и активировать. Ниже приведу объяснения за что отвечает каждый файл конфигурации. Будем ставить NixOS на виртуальную машину. Т.к. я использую нестабильную ветку, то будем ставить её. Скачиваем загрузочный ISO орбраз nixos-minimal с этой страницы https://nixos.org/channels/nixos-unstable и грузимся с него. Дополнительную информацию по установке можно взять отсюда https://nixos.org/nixos/manual/index.html#sec-installation.

Для удобства работы запустим openssh сервис:

sudo systemctl start sshd

И зададим пароль для пользователя nixos:

passwd nixos

Смотрим текущий IP адрес виртуальной машины и подключаемся по этому адресу по ssh под пользователем nixos.

ip addr show

После подключения к системе проверяем какие диски у нас подключены:

lsblk
NAME  MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
loop0   7:0    0 505.7M  1 loop /nix/.ro-store
sr0    11:0    1   552M  0 rom  /iso
vda   253:0    0    20G  0 disk

Создаём разделы на диске vda (в зависимости от типа виртуальной машины диски могут отличаться) и монтируем их:

sudo parted /dev/vda -- mklabel msdos
sudo parted /dev/vda -- mkpart primary linux-swap 1MiB 4GiB
sudo parted /dev/vda -- mkpart primary 4GiB 100%

sudo mkswap -L swap /dev/vda1
sudo mkfs.ext3 -L NixOS /dev/vda2

sudo swapon /dev/vda1
sudo mount /dev/vda2 /mnt

Генерируем первоначальную конфигурацию системы:

sudo nixos-generate-config --root /mnt

После выполнения команды создаются 2 файла:

/mnt/etc/nixos/configuration.nix
/mnt/etc/nixos/hardware-configuration.nix

Приведём файл конфигурации /mnt/etc/nixos/configuration.nix к такому виду:

sudo nano /mnt/etc/nixos/configuration.nix
{ config, pkgs, ... }:
{
  imports = [
    ./hardware-configuration.nix
  ];

  boot.loader.grub.enable = true;
  boot.loader.grub.version = 2;
  boot.loader.grub.device = "/dev/vda";

  networking.hostName = "NixOS-example";

  environment.systemPackages = with pkgs; [
    wget vim mkpasswd
  ];

  services.openssh.enable = true;

  users.users.rebrain = {
    isNormalUser = true;
    uid = 51011;
    group = "rebrain";
    extraGroups = [ "wheel" "users" ];
  };

  users.groups.rebrain = {
    gid = 51011;
  };

  system.stateVersion = "20.09";
}

И запустим установку системы:

sudo nixos-install --no-root-passwd --root /mnt

Внимание: указывая параметр --no-root-passwd мы запрещаем вход в систему root пользователю. После установки системы установим пароль для нашего пользователя rebrain, чтобы после перезагрузки системы мы смогли подключиться удалённо по ssh:

sudo nixos-enter --root /mnt -c 'passwd rebrain'

Размонтируем разделы и выключаем виртуальную машину:

sudo umount /mnt
sudo swapoff /dev/vda1
sudo shutdown -h now

Первоначальная установка завершена, приходим к базовой настройке. В настройках виртуальной машины отключаем ISO образ и загружаемся в установленную систему. Заходим по SSH с аутентификацией по паролю, скачиваем и распаковываем архив с конфигурацией в директорию /etc/nixos

cd /tmp
wget https://git.elven.pw/Rebrain/basic-nixos/archive/master.tar.gz -O basic-nixos-config.tar.gz
tar xzf basic-nixos-config.tar.gz
sudo cp -r /tmp/basic-nixos/nix-config /etc/nixos

У нас получается такая структура конфигурации:

  • основные настройки системы хранятся в директории /etc/nixos/nix-config/generic.
  • индивидуальные настройки текущего сервера в директории /etc/nixos/nix-config/servers/example.

Приводим конфигурацию /etc/nixos/configuration.nix к такому виду:

sudo nano /etc/nixos/configuration.nix
{ config, pkgs, ... }:
{
  imports = [
    ./hardware-configuration.nix
    ./nix-config/servers/example/configuration.nix
  ];

  system.stateVersion = "20.09";
}

Генерируем хеш пароля для пользователя rebrain командой:

mkpasswd -m sha-512

И вставляем результат в /etc/nixos/nix-config/servers/example/users.nix в hashedPassword - _generated_hash_pass_ заменяем на хеш. В итоге конфигурация будет выглядеть так:

sudo nano /etc/nixos/nix-config/servers/example/users.nix
{ config, ... }:
let
  ssh-keys = import ./../../generic/security/ssh-keys.nix;

in {
  users = with ssh-keys; {
    users.root = {
      hashedPassword = null;
    };
    users.rebrain = {
      isNormalUser = true;
      uid = config.uid-gid.rebrain;
      group = "rebrain";
      extraGroups = [ "wheel" "users" ];
      openssh.authorizedKeys.keys = [
        work.rebrain_example
      ];
      hashedPassword = "_generated_hash_pass_";
    };

    groups.rebrain = {
      gid = config.uid-gid.rebrain;
    };

    groups.ssh-users = {
      members = [ "rebrain" ];
    };
  };
}

Чтобы авторизоваться на сервере по ключу прописываем в файл generic/security/ssh-keys.nix публичный ключ:

sudo nano /etc/nixos/nix-config/generic/security/ssh-keys.nix
{
  work = {
    rebrain_example = "_ssh_public_key_example";
  };
}

Ссылка на ключ work.rebrain_example уже прописана в файле servers/example/users.nix:

...
    users.rebrain = {
...
      openssh.authorizedKeys.keys = [
        work.rebrain_example
      ];
...

Внимание, параметр users.root = { hashedPassword = null; }; запрещает вход в систему root пользователю. Если необходимо управлять текущей системой c помощью утилиты nixops, то добавляем ssh ключ root пользователю:

sudo nano /etc/nixos/nix-config/generic/security/ssh-keys.nix
{
  work = {
...
    root_example = "_ssh_public_key_example";
  };
}
sudo nano /etc/nixos/nix-config/servers/example/users.nix
...
    users.root = {
      hashedPassword = null;
      openssh.authorizedKeys.keys = [
        work.root_example
      ];
    };
...

Если в системе используется вместо vda диска используется sda, то в файле /etc/nixos/nix-config/servers/example/configuration.nixменяем/dev/vdaна/dev/sda`:

sudo nano /etc/nixos/nix-config/servers/example/configuration.nix
...
  boot = {
    loader.grub.device = "/dev/sda";
...

Обновляем конфигурацию системы и перезагружаемся:

sudo nixos-rebuild boot
sudo shutdown -r now

При первом входе zsh предлагает сконфигурировать файл ~/.zshrc, т.к. у нас уже готова для него конфигурация (в файле generic/shell.nix), то пропускаем конфигурация и создаём пустой файл - вводим 0.

Для нашей конфигурации создадим git репозиторий, который, в будущем, позволит отслеживать историю обновления системы:

sudo git config --global user.email "rebrain@NixOS-example"
sudo git config --global user.name "rebrain"

cd /etc/nixos
sudo git init && sudo git add -A && sudo git commit -m "first commit"

Все команды с git выполняем от имени root, т.к. права папки /etc/nixos root пользователю. Теперь мы можем отслеживать свою конфигурацию. Первоначальная настрока системы завершена. Пройдемся по основным настройкам:

  • generic/nix-config/core.nix

    • imports = [ <nixpkgs/nixos/modules/profiles/hardened.nix> - используем защищенный профиль системы. В некоторых случаях может привести к снижению производительности.
    • mkOverride 700 - задаёт приоритет настройкам, если указан. Например, если в конфигурации servers/example/configuration.nix указать boot.kernelPackages = pkgs.linuxPackages_5_4; то применится последний вариант.
    • boot.kernelPackages = pkgs.linuxPackages_latest_hardened; - указывает применять hardened ядро с патчами https://github.com/anthraxx/linux-hardened
    • hardware.ksm - активируем дедупликацию памяти, в некоторых случаях позволяет сэкономить ОЗУ.
    • environment.memoryAllocator.provider = mkOverride 700 "jemalloc"; - использовать менеджер памяти Jemalloc.
    • networking.enableIPv6.false; - отключаем ipv6 протокол,т.к. я его не использую.
    • networking.dhcpcd.extraConfig = "\nnoipv6rs \nnoipv6"; - отключаем ipv6 в службе DHCPD.
    • networking.firewall.enable = false; - отключаем файерволл iptables, вместо него используем nftables.
    • networking.useHostResolvConf = false; - используется в контейнерах - использовать resolv.conf от хостовой машины. Отключаем.
    • networking.usePredictableInterfaceNames = false; - возвращаем именование сетевых интерфейсов в ethX, т.к. у меня в виртуальных машинах используется идентичные интерфейсы и их количество не превышает двух.
    • security.sudo.wheelNeedsPassword = false; - не запрашивать каждый раз пароль перед запуском приложения от имени root, т.к. мы используем аутентификацию по ключам, то не вижу смысла каждый раз требовать пароль.
    • services.journald - ограничиваем размер журналов до 256 MiB.
    • services.qemuGuest - для виртуальных машин активируем QEMU Guest агент. В противном случаем в servers/custom_server/configuration.nix прописываем - services.qemuGuest.enable = false;.
    • users.mutableUsers = false; - запретить смену пароля. Пароль указываем в users.users.my-username.hashedPassword.
  • generic/nix-config/generic/overlays/default.nix

    • nur = import ... - Здесь добавляем пользовательский репозиторий с программами (полный код в примере).
  • generic/programs/ssh.nix

    • startAgent - активируем ssh agent.
    • pubkeyAcceptedKeyTypes, hostKeyAlgorithms - используем только ssh-ed25519 и rsa-sha2-512 ключи.
    • kexAlgorithms - используем KEX алгоритм curve25519-sha256@libssh.org.
    • ciphers - используем шифр chacha20-poly1305@openssh.com.
    • macs - используем hmac-sha2-512-etm@openssh.com алгоритм аутентификации кода сообщения.
    • extraConfig - небольшие твики ssh клиента.
  • generic/security/ssh-hosts.nix

    • programs.ssh.knownHosts - указываем принудительно публичные ключи известных хостов. По IP можно так же добавить другие виртуальные машины, куда требуется доступ по ssh. Если удалённый сервер каким либо образом подменили, то ssh клиент выдаст ошибку.
  • generic/security/ssh-keys.nix - записываем публичные ключи для ssh аутентификации.

  • generic/services/fail2ban.nix - настраиваем работу утилиты fail2ban c файерволлом nftables, добавляем какие IP адреса игнорировать. Для сервиса sshd задаём агрессивный метод сканирования.

  • generic/services/syslog-ng.nix - сохраняем логи ssh сервера в отдельный файл.

  • generic/services/logrotate.nix - настраиваем ротацию ssh логов.

  • generic/services/sshd.nix

    • allowSFTP = true; - включаем поддержку SFTP.
    • forwardX11 = mkDefault false; - запрещаем использовать проброс X11. Если требуется проброс X11 приложений, добавляем в файл servers/example/configuration.nix строку services.openssh.forwardX11 = true;.
    • startWhenNeeded = mkDefault true; - запускать SSH сервер только при активации запроса на порт сервиса (активация по systemd socket).
    • permitRootLogin = mkDefault "yes"; - разрешить пользователю root заходить по SSH. Требуется для работы утилиты nixops. Можно отключить, если не используете nixops - "no";.
    • passwordAuthentication = false; challengeResponseAuthentication = false; - запрещаем вход по паролю.
    • useDns = false; - не использовать DNS серверы, при аутентификации.
    • macs - используем hmac-sha2-512-etm@openssh.com алгоритм аутентификации кода сообщения.
    • ciphers - используем шифр chacha20-poly1305@openssh.com.
    • kexAlgorithms - используем KEX алгоритм curve25519-sha256@libssh.org. Большинство ssh сканеров будут отваливаться, т.к. не могут подключиться по указанным алгоритмам.
    • authorizedKeysFiles - использовать открытые ключи авторизации из файлов, удовлетворяющих шаблону /etc/ssh/authorized_keys.d/%u, где %u - имя пользователя.
    • hostKeys - указываем использовать только rsa и ed25519 ключи.
    • extraConfig - небольшие твики sshd сервера.
  • generic/services/unbound.nix - ставим локальный ДНС сервер для кэширования запросов.

  • generic/ids.nix - задаём пользователям фиксированный UID.

  • generic/locale.nix - настраиваем шрифт, кодировку, локализацию, синхронизацию с сервером времени и часовой пояс.

  • generic/pkgs.nix - устанавливаем часто используемые программы.

  • generic/shell.nix - настраиваем shell, алисы, программу nano и ZSH.

  • generic/tweaks.nix - небольшие твики системы.

  • servers/example/configuration.nix - здесь сохраняем индивидуальные настройки для текущей виртуальной машины example.

    • boot.loader.grub.device = "/dev/vda"; - загружаемся с диска /dev/vda.
    • boot.kernelParams = [ "lockdown=confidentiality" ]; - блокируются возможности, позволяющие вносить изменения в работающее ядро из пространства пользователя и отключается функциональность, которую можно использовать для извлечения конфиденциальной информации из ядра.
  • servers/example/users.nix - настраиваем пользователя rebrain.

    • hashedPassword - указываем фиксированный хеш пароля. Опция users.mutableUsers = false; позволяет запретить смену пароля из консоли.
    • openssh.authorizedKeys.keys - прописываем открытые публичные ssh ключи указанные в generic/security/ssh-keys.nix по которым пользователь может авторизоваться на сервере.
    • groups.ssh-users.members - прописываем, каким пользователям разрешён вход по ssh.
  • servers/example/services/fail2ban.nix

    • services.fail2ban.enable - активируем сервис fail2ban. По умолчанию сервис отключен в generic/services/fail2ban.nix.
    • services.fail2ban.jails.sshd - активируем jail sshd.
  • servers/example/services/firefall-nft.nix - активируем файерволл nftables, открываем 22 порт.

В итоге мы закрыли некоторые ветки атаки и подготовили шаблон, который можно использовать на других системах на базе Linux NixOS.

Теперь подробнее о Linux NixOS. NixOS сохраняет все приложения и различные версии конфигурации системы в собственных подкаталогах директории /nix/store. Например приложение nano находится в директории /nix/store/17xx786q2gdvjz01hx0j8rihmm5h2m88-nano-4.9.3 и на него создаётся символьная ссылка:

ls -lah `which nano`
lrwxrwxrwx 1 root root 63 янв  1  1970 /run/current-system/sw/bin/nano -> /nix/store/17xx786q2gdvjz01hx0j8rihmm5h2m88-nano-4.9.3/bin/nano

Текущая конфигурация системы с установленными приложениями расположена в /run/current-system, которая ссылается на подкаталог в /nix/store

ls -lah /run/current-system
lrwxrwxrwx 1 root root 97 июл 13 12:51 /run/current-system -> /nix/store/r0ly0d2k314wfkmqkbci27hldpy5bplh-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2

При обновлении системы создаётся подкаталог с новой конфигурацией системы в директории /nix/store и символьная ссылка /run/current-system переключается на неё. В загрузчик grub добавляется новая запись с конфигурацией системы. Добавим программу Node.js версии 14 - nodejs-14_x:

sudo nano /etc/nixos/nix-config/generic/pkgs.nix
cd /etc/nixos
git diff
diff --git a/nix-config/generic/pkgs.nix b/nix-config/generic/pkgs.nix
index c1ff0d3..41e026c 100644
--- a/nix-config/generic/pkgs.nix
+++ b/nix-config/generic/pkgs.nix
@@ -27,5 +27,6 @@
     ccze lnav multitail
     fio inxi spectre-meltdown-checker
     lego mariadb.client
+    nodejs-14_x
   ];
 }

Сделаем коммит в нашем репозитории:

sudo git add nix-config/generic/pkgs.nix
sudo git commit -m "config: add nodejs v14 package"

Теперь запускаем обновление системы:

sudo nixos-rebuild switch

У нас установилось новое приложение и символьная ссылка /run/current-system переключилась на новую конфигурацию:

ls -lah /run/current-system
lrwxrwxrwx 1 root root 97 июл 13 13:05 /run/current-system -> /nix/store/6vn0pwczz9blrm7759iij3k7fmjq0xb2-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2

Обновим переменную $PATH и проверим версию Node.js:

rehash
node --version
v14.18.0

Примерно так выглядят несколько конфигураций системы в /boot/grub/grub.cfg:

...
menuentry "NixOS - Default" --unrestricted {
search --set=drive1 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
search --set=drive2 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
  linux ($drive2)/nix/store/8rzh2pkxb4cxh68cmfk9d0kd4lylx5cd-linux-5.7.7/bzImage systemConfig=/nix/store/6vn0pwczz9blrm7759iij3k7fmjq0xb2-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2 init=/nix/store/6vn0pwczz9blrm7759iij3k7fmjq0xb2-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2/init slub_debug=FZP page_poison=1 page_alloc.shuffle=1 lockdown=confidentiality loglevel=4 net.ifnames=0 nohibernate nosmt pti=on kvm-intel.vmentry_l1d_flush=always apparmor=1 security=apparmor
  initrd ($drive2)/nix/store/1aj37hakkbww966zk8n8x12xnlqbw839-initrd-linux-5.7.7/initrd
}


submenu "NixOS - All configurations" {
menuentry "NixOS - Configuration 4 (2020-07-13 - 20.09pre233323.dc80d7bc4a2)"  {
search --set=drive1 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
search --set=drive2 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
  linux ($drive2)/nix/store/8rzh2pkxb4cxh68cmfk9d0kd4lylx5cd-linux-5.7.7/bzImage systemConfig=/nix/store/6vn0pwczz9blrm7759iij3k7fmjq0xb2-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2 init=/nix/store/6vn0pwczz9blrm7759iij3k7fmjq0xb2-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2/init slub_debug=FZP page_poison=1 page_alloc.shuffle=1 lockdown=confidentiality loglevel=4 net.ifnames=0 nohibernate nosmt pti=on kvm-intel.vmentry_l1d_flush=always apparmor=1 security=apparmor
  initrd ($drive2)/nix/store/1aj37hakkbww966zk8n8x12xnlqbw839-initrd-linux-5.7.7/initrd
}

menuentry "NixOS - Configuration 3 (2020-07-13 - 20.09pre233323.dc80d7bc4a2)"  {
search --set=drive1 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
search --set=drive2 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
  linux ($drive2)/nix/store/8rzh2pkxb4cxh68cmfk9d0kd4lylx5cd-linux-5.7.7/bzImage systemConfig=/nix/store/r0ly0d2k314wfkmqkbci27hldpy5bplh-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2 init=/nix/store/r0ly0d2k314wfkmqkbci27hldpy5bplh-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2/init slub_debug=FZP page_poison=1 page_alloc.shuffle=1 lockdown=confidentiality loglevel=4 net.ifnames=0 nohibernate nosmt pti=on kvm-intel.vmentry_l1d_flush=always apparmor=1 security=apparmor
  initrd ($drive2)/nix/store/1aj37hakkbww966zk8n8x12xnlqbw839-initrd-linux-5.7.7/initrd
}

menuentry "NixOS - Configuration 2 (2020-07-13 - 20.09pre233323.dc80d7bc4a2)"  {
search --set=drive1 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
search --set=drive2 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
  linux ($drive2)/nix/store/8rzh2pkxb4cxh68cmfk9d0kd4lylx5cd-linux-5.7.7/bzImage systemConfig=/nix/store/6dznb8ackalph62mw6zaan0cpq0ihpfs-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2 init=/nix/store/6dznb8ackalph62mw6zaan0cpq0ihpfs-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2/init slub_debug=FZP page_poison=1 page_alloc.shuffle=1 lockdown=confidentiality loglevel=4 net.ifnames=0 nohibernate nosmt pti=on kvm-intel.vmentry_l1d_flush=always apparmor=1 security=apparmor
  initrd ($drive2)/nix/store/1aj37hakkbww966zk8n8x12xnlqbw839-initrd-linux-5.7.7/initrd
}

menuentry "NixOS - Configuration 1 (2020-07-13 - 20.09pre233323.dc80d7bc4a2)"  {
search --set=drive1 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
search --set=drive2 --fs-uuid 1ea2e5e9-04be-4ae1-9ac4-b7f72602ddfc
  linux ($drive2)/nix/store/lhc845bkk8ilfk9xsicqhwcq3ngzay8y-linux-5.4.50/bzImage systemConfig=/nix/store/x9mf5rqwnvbkizhlvd1dcin9wgfb49x3-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2 init=/nix/store/x9mf5rqwnvbkizhlvd1dcin9wgfb49x3-nixos-system-NixOS-example-20.09pre233323.dc80d7bc4a2/init loglevel=4
  initrd ($drive2)/nix/store/8f5zvilxc6y0cy05wrc6b5wqabfhd0aq-initrd-linux-5.4.50/initrd
}

...

Параметр systemConfig указывает какую версию конфигурации использовать для загрузки системы.

Просмотреть текущие конфигурации системы можно командой:

sudo nix-env --list-generations --profile /nix/var/nix/profiles/system

Команда выдаст примерно такой результат:

   1   2020-07-13 12:08:28
   2   2020-07-13 12:27:27
   3   2020-07-13 12:51:09
   4   2020-07-13 13:05:37   (current)

Откатимся на предыдущую конфигурацию

sudo nixos-rebuild switch --rollback
switching from generation 4 to 3
updating GRUB 2 menu...
activating the configuration...
setting up /etc...
reloading user units for rebrain...
...
setting up tmpfiles
reloading the following units: dbus.service
the following new units were started: logrotate.service
Time: 0h:00m:04s

Попробуем запустить Node.js:

node --version
node: command not found

Конфигурация системы откатилась и у нас программа Node.js не установлена.

Вернёмся к последней конфигурации:

sudo nixos-rebuild switch

Также любую версию конфигурации системы можно выбрать при запуске системы, в меню grub. Очистить старые конфигурации и освободить место на диске можно командой:

sudo nix-collect-garbage --delete-old

Давайте добавим ещё один сервис для мониторинга нашей виртуальной машины. Создаём файл nix-config/servers/example/services/netdata.nix

sudo nano /etc/nixos/nix-config/servers/example/services/netdata.nix
{
  services.netdata = {
    enable = true;
  };
}

Добавляем его в nix-config/servers/example/configuration.nix

sudo nano /etc/nixos/nix-config/servers/example/configuration.nix
...
  imports =[
...
    ./services/netdata.nix
    ./users.nix
  ];
...

И открываем 19999 порт в файерволле nftables для нашего сервиса:

sudo nano /etc/nixos/nix-config/servers/example/services/firefall-nft.nix
...
        chain input {
          type filter hook input priority 0; policy drop;
          jump checks-base
          iif "lo" notrack accept
          ip protocol icmp icmp type { echo-request} ct state new accept
          tcp dport { 22 } ct state new accept
          tcp dport { 19999 } ct state new accept
        }
...

Обновляем систему:

sudo nixos-rebuild switch

Сервис мониторинга активирован:

systemctl status netdata
● netdata.service - Real time performance monitoring
     Loaded: loaded (/nix/store/14jbfi7018j5m75ad4j23sjg811yq26l-unit-netdata.service/netdata.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2020-07-13 13:33:31 MSK; 6min ago
   Main PID: 8484
         IP: 124.9K in, 775.6K out
      Tasks: 34 (limit: 2373)
     Memory: 43.6M
        CPU: 13.842s
     CGroup: /system.slice/netdata.service
             ├─8484 /nix/store/0z9dcqk244jb0b95flpljcbs2arg5y52-netdata-1.23.0/bin/netdata -P /run/netdata/netdata.pid -D -c /nix/store/f68ak9vl3p2a8b57ypv2cq7r80a8wigc-netdata.conf
             ├─8491 /nix/store/0z9dcqk244jb0b95flpljcbs2arg5y52-netdata-1.23.0/bin/netdata --special-spawn-server
             ├─8692 /nix/store/0z9dcqk244jb0b95flpljcbs2arg5y52-netdata-1.23.0/libexec/netdata/plugins.d/go.d.plugin 1
             ├─8694 /nix/store/wm8d1lz8rr3i1r9vxgx123mx7rs1r5xh-wrapped-plugins/libexec/netdata/plugins.d/apps.plugin 1
             └─8697 /nix/store/0z9dcqk244jb0b95flpljcbs2arg5y52-netdata-1.23.0/libexec/netdata/plugins.d/nfacct.plugin 1
...

Открываем в браузере по 19999 порту и проверяем работу.

Делаем коммит в нашем репозитории:

sudo git add -A
sudo git commit -m "config: add netdata service"

Проверяем изменения в коммите:

git diff HEAD^1
diff --git a/nix-config/servers/example/configuration.nix b/nix-config/servers/example/configuration.nix
index da117ed..d9bf023 100644
--- a/nix-config/servers/example/configuration.nix
+++ b/nix-config/servers/example/configuration.nix
@@ -3,6 +3,7 @@
     ./../../core.nix
     ./services/fail2ban.nix
     ./services/firefall-nft.nix
+    ./services/netdata.nix
     ./users.nix
   ];

diff --git a/nix-config/servers/example/services/firefall-nft.nix b/nix-config/servers/example/services/firefall-nft.nix
index afbab23..f6c2648 100644
--- a/nix-config/servers/example/services/firefall-nft.nix
+++ b/nix-config/servers/example/services/firefall-nft.nix
@@ -16,6 +16,7 @@
           iif "lo" notrack accept
           ip protocol icmp icmp type { echo-request} ct state new accept
           tcp dport { 22 } ct state new accept
+          tcp dport { 19999 } ct state new accept
         }
         chain output {
           type filter hook output priority 0; policy accept;
diff --git a/nix-config/servers/example/services/netdata.nix b/nix-config/servers/example/services/netdata.nix
new file mode 100644
index 0000000..e8b5066
--- /dev/null
+++ b/nix-config/servers/example/services/netdata.nix
@@ -0,0 +1,5 @@
+{
+  services.netdata = {
+    enable = true;
+  };
+}

В итоге мы немного ознакомились с возможностями Linux NixOS, подготовили шаблон для развёртывания ОС, закрыли некоторые ветки атаки и добавили историю изменения конфигурации с помощью git репозитория.

Примечание: Если при обновлении системы возникает ошибка:

updating GRUB 2 menu...
failed to create initrd secrets: No such file or directory
warning: error(s) occurred while switching to the new configuration

То перед обновлением выполняем команду:

sudo rm /etc/ld-nix.so.preload

В последних изменения была ошибка, которая приводила к некорректной сборке системы из-за использования провайдера распределения памяти - environment.memoryAllocator.provider.