diff options
Diffstat (limited to '')
| -rw-r--r-- | flake.nix | 2 | ||||
| -rw-r--r-- | home/profiles/eza.nix | 4 | ||||
| -rw-r--r-- | home/profiles/kitty.nix | 8 | ||||
| -rw-r--r-- | home/profiles/minimal.nix | 1 | ||||
| -rw-r--r-- | home/profiles/restic.nix | 197 | ||||
| -rw-r--r-- | home/profiles/ssh.nix | 8 | ||||
| -rw-r--r-- | justfile | 1 | ||||
| -rw-r--r-- | machines/mba-m2.nix | 1 | ||||
| -rw-r--r-- | machines/rivendell.nix | 88 | ||||
| -rw-r--r-- | profiles/defaults.nix | 5 | ||||
| -rw-r--r-- | profiles/storage-media.nix | 61 | ||||
| -rw-r--r-- | profiles/users/fcuny.nix | 2 | ||||
| -rw-r--r-- | secrets/rsync-ssh-nas.age | bin | 0 -> 721 bytes | |||
| -rw-r--r-- | secrets/secrets.nix | 6 |
14 files changed, 329 insertions, 55 deletions
@@ -155,7 +155,7 @@ }; modules = [ { - system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev; + system.configurationRevision = self.rev or self.dirtyRev or null; system.nixos.versionSuffix = nixpkgs.lib.mkForce "git.${builtins.substring 0 11 nixpkgs.rev}"; nixpkgs.pkgs = pkgs; environment.systemPackages = [ pkgs.git ]; diff --git a/home/profiles/eza.nix b/home/profiles/eza.nix index f71b9b6..0952201 100644 --- a/home/profiles/eza.nix +++ b/home/profiles/eza.nix @@ -2,15 +2,15 @@ { programs.eza = { enable = true; - icons = "never"; + icons = "auto"; colors = "always"; git = true; + # I setup my own aliases enableFishIntegration = false; extraOptions = [ "--group-directories-first" "--no-quotes" "--git-ignore" - "--icons=never" ]; }; diff --git a/home/profiles/kitty.nix b/home/profiles/kitty.nix index f5fceeb..8bfcf0c 100644 --- a/home/profiles/kitty.nix +++ b/home/profiles/kitty.nix @@ -5,9 +5,13 @@ themeFile = "Modus_Operandi_Tinted"; settings = { copy_on_select = "yes"; + bold_font = "auto"; + italic_font = "auto"; + bold_italic_font = "auto"; + window_padding_width = "3 10"; enable_audio_bell = "no"; - window_padding_width = 5; - tab_title_template = "{index}{title}"; + tab_bar_edge = "bottom"; + tab_title_template = "{title}{' :{}:'.format(num_windows) if num_windows > 1 else ''}"; tab_bar_style = "powerline"; font_family = "Source Code Pro"; font_size = "15.0"; diff --git a/home/profiles/minimal.nix b/home/profiles/minimal.nix index ac0d84f..4bccdb5 100644 --- a/home/profiles/minimal.nix +++ b/home/profiles/minimal.nix @@ -8,6 +8,7 @@ in ]; programs.bat.enable = true; + programs.tmux.enable = true; home.homeDirectory = "/home/${username}"; home.stateVersion = "25.05"; diff --git a/home/profiles/restic.nix b/home/profiles/restic.nix new file mode 100644 index 0000000..96ae8d4 --- /dev/null +++ b/home/profiles/restic.nix @@ -0,0 +1,197 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + nasHost = "nas"; + repoPath = "/backups/workstation"; + + resticRepository = "sftp:${nasHost}:${repoPath}"; + + backupPaths = [ + "${config.home.homeDirectory}/Documents" + "${config.home.homeDirectory}/Pictures" + ]; + + excludeFile = "${config.home.homeDirectory}/.config/restic/exclude"; + includeFile = "${config.home.homeDirectory}/.config/restic/includes"; +in +{ + home.packages = with pkgs; [ restic ]; + + age.secrets.restic-password = { + file = ../../secrets/restic-pw.age; + path = "${config.home.homeDirectory}/.config/restic/password"; + mode = "400"; + }; + + home.sessionVariables = { + RESTIC_REPOSITORY = resticRepository; + RESTIC_PASSWORD_FILE = config.age.secrets.restic-password.path; + }; + + home.file.".config/restic/includes" = { + text = lib.concatStringsSep "\n" backupPaths; + onChange = '' + echo "Restic backup paths updated" + ''; + }; + + home.file.".config/restic/exclude" = { + text = '' + # macOS specific + .DS_Store + .Trash + .Spotlight-V100 + .fseventsd + .TemporaryItems + .DocumentRevisions-V100 + .VolumeIcon.icns + .AppleDouble + .LSOverride + Library/Caches + Library/Logs + + # Development artifacts + **/node_modules + **/.venv + **/__pycache__ + **/*.pyc + **/venv + **/target # Rust + **/dist + **/build + **/.tox + **/.pytest_cache + **/.coverage + **/.mypy_cache + + # Large files that might not need backup + *.dmg + *.iso + *.pkg + + # Version control + **/.git/objects + **/.git/lfs + + # IDE + **/.idea + **/.vscode + *.swp + *~ + ''; + }; + + home.file.".local/bin/restic-now" = { + executable = true; + text = '' + #!/usr/bin/env bash + set -euo pipefail + + # Colors for output + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + NC='\033[0m' # No Color + + echo -e "''${GREEN}Starting restic backup...''${NC}" + + # Check if repository exists, initialize if not + echo -e "''${YELLOW}Checking repository...''${NC}" + if ! ${pkgs.restic}/bin/restic cat config > /dev/null 2>&1; then + echo -e "''${YELLOW}Repository not found. Initializing...''${NC}" + ${pkgs.restic}/bin/restic init + fi + + # Run backup + echo -e "''${GREEN}Running backup...''${NC}" + ${pkgs.restic}/bin/restic backup \ + --compression max \ + --files-from="${includeFile}" \ + --exclude-file="${excludeFile}" \ + --verbose=1 \ + --host="$(hostname -s)" + + # Unlock in case of stale locks + echo -e "''${YELLOW}Checking for stale locks...''${NC}" + ${pkgs.restic}/bin/restic unlock || true + + # Prune old snapshots + echo -e "''${GREEN}Pruning old snapshots...''${NC}" + ${pkgs.restic}/bin/restic forget \ + --prune \ + --keep-daily=7 \ + --keep-weekly=4 \ + --keep-monthly=12 \ + --compression max \ + --verbose=1 + + # Check repository integrity (optional, can be slow) + echo -e "''${GREEN}Checking repository integrity...''${NC}" + ${pkgs.restic}/bin/restic check --read-data-subset=5% + + echo -e "''${GREEN}Backup completed successfully!''${NC}" + ''; + }; + + home.file.".local/bin/restic-status" = { + executable = true; + text = '' + #!/usr/bin/env bash + set -euo pipefail + + echo "Repository: $RESTIC_REPOSITORY" + echo "" + echo "=== Latest snapshots ===" + ${pkgs.restic}/bin/restic snapshots --latest 5 --compact + echo "" + echo "=== Repository stats ===" + ${pkgs.restic}/bin/restic stats + ''; + }; + + home.file.".local/bin/restic-mount" = { + executable = true; + text = '' + #!/usr/bin/env bash + set -euo pipefail + + MOUNT_POINT="''${1:-$HOME/mnt/restic}" + + if [ ! -d "$MOUNT_POINT" ]; then + echo "Creating mount point: $MOUNT_POINT" + mkdir -p "$MOUNT_POINT" + fi + + echo "Mounting restic repository at $MOUNT_POINT" + echo "Press Ctrl+C to unmount" + ${pkgs.restic}/bin/restic mount "$MOUNT_POINT" + ''; + }; + + home.file.".local/bin/restic-restore" = { + executable = true; + text = '' + #!/usr/bin/env bash + set -euo pipefail + + if [ $# -lt 1 ]; then + echo "Usage: $0 <snapshot-id> [target-directory]" + echo "" + echo "Available snapshots:" + ${pkgs.restic}/bin/restic snapshots --compact + exit 1 + fi + + SNAPSHOT="$1" + TARGET="''${2:-$HOME/restic-restore}" + + echo "Restoring snapshot $SNAPSHOT to $TARGET" + ${pkgs.restic}/bin/restic restore "$SNAPSHOT" --target "$TARGET" + ''; + }; +} diff --git a/home/profiles/ssh.nix b/home/profiles/ssh.nix index f1ef16f..004b082 100644 --- a/home/profiles/ssh.nix +++ b/home/profiles/ssh.nix @@ -14,6 +14,14 @@ "rivendell" = { hostname = "192.168.1.114"; }; + "riv-unlock" = { + hostname = "192.168.1.114"; + user = "root"; + port = 911; + }; + "nas" = { + hostname = "192.168.1.68"; + }; "bree" = { hostname = "192.168.1.50"; }; @@ -42,6 +42,7 @@ rbuild hostname: [group('linux')] rswitch hostname: @echo "switching {{hostname}} to new config..." + nixos-rebuild switch --keep-going --flake ".#{{hostname}}" --target-host {{hostname}} --fast --use-remote-sudo --use-substitutes # sync agenix key from 1password [group('secrets')] diff --git a/machines/mba-m2.nix b/machines/mba-m2.nix index 398bf6d..e9fd500 100644 --- a/machines/mba-m2.nix +++ b/machines/mba-m2.nix @@ -43,6 +43,7 @@ ../home/profiles/mac.nix ../home/profiles/media.nix ../home/profiles/security.nix + ../home/profiles/restic.nix ]; userinfo = { email = "franck@fcuny.net"; diff --git a/machines/rivendell.nix b/machines/rivendell.nix index 4940ea3..83dcb2e 100644 --- a/machines/rivendell.nix +++ b/machines/rivendell.nix @@ -13,6 +13,7 @@ ../profiles/remote-unlock.nix ../profiles/restic-backup.nix ../profiles/server.nix + ../profiles/storage-media.nix ../profiles/users/builder.nix ../profiles/users/fcuny.nix ../profiles/wireguard.nix @@ -23,56 +24,51 @@ networking.useDHCP = lib.mkDefault true; systemd.network.wait-online.anyInterface = lib.mkDefault config.networking.useDHCP; - services.website = { - enable = true; - openFirewall = true; - }; - - services.restic.backups.local.paths = [ "/var/lib/gitolite/repositories" ]; - services.restic.backups.synology.paths = [ - "/data/archives" - "/data/media" - "/var/lib/gitolite/repositories" - ]; - - services.samba = { - enable = true; - openFirewall = true; - settings = { - global = { - security = "user"; - workgroup = "WORKGROUP"; - "server string" = config.networking.hostName; - "netbios name" = config.networking.hostName; - "hosts allow" = "192.168.1.0/24 10.100.0.0/24 localhost"; - "guest account" = "nobody"; - "map to guest" = "bad user"; - "use sendfile" = "yes"; - "load printers" = "no"; - "vfs objects" = "catia fruit streams_xattr"; - "fruit:metadata" = "stream"; - }; + services = { + website = { + enable = true; + openFirewall = true; + }; + restic.backups.local.paths = [ "/var/lib/gitolite/repositories" ]; + restic.backups.synology.paths = [ + "/data/archives" + "/data/media/music" + "/var/lib/gitolite/repositories" + ]; + samba = { + enable = true; + openFirewall = true; + settings = { + global = { + security = "user"; + workgroup = "WORKGROUP"; + "server string" = config.networking.hostName; + "netbios name" = config.networking.hostName; + "hosts allow" = "192.168.1.0/24 10.100.0.0/24 localhost"; + "guest account" = "nobody"; + "map to guest" = "bad user"; + "use sendfile" = "yes"; + "load printers" = "no"; + "vfs objects" = "catia fruit streams_xattr"; + "fruit:metadata" = "stream"; + }; - media = { - path = "/data/media"; - browseable = "yes"; - "read only" = "yes"; - "guest ok" = "yes"; + media = { + path = "/data/media"; + browseable = "yes"; + "read only" = "yes"; + "guest ok" = "yes"; + }; }; }; + avahi = { + enable = true; + nssmdns4 = true; + openFirewall = true; + }; }; - services.avahi = { - enable = true; - nssmdns4 = true; - openFirewall = true; - }; - - system.stateVersion = "23.11"; # Did you read the comment? + system.stateVersion = "23.11"; - home-manager.users.fcuny = { - imports = [ - ../home/profiles/minimal.nix - ]; - }; + home-manager.users.fcuny.imports = [ ../home/profiles/minimal.nix ]; } diff --git a/profiles/defaults.nix b/profiles/defaults.nix index d98daa0..6ada4c7 100644 --- a/profiles/defaults.nix +++ b/profiles/defaults.nix @@ -1,5 +1,4 @@ { - self, config, pkgs, lib, @@ -100,8 +99,8 @@ environment.etc.motd.text = '' Machine ${config.networking.hostName} - NixOS ${config.system.nixos.release} - @ ${self.shortRev or self.dirtyShortRev} + NixOS ${config.system.nixos.versionSuffix} + @ ${config.system.configurationRevision} ''; ## disable that slow "building man-cache" step diff --git a/profiles/storage-media.nix b/profiles/storage-media.nix new file mode 100644 index 0000000..30fb9e4 --- /dev/null +++ b/profiles/storage-media.nix @@ -0,0 +1,61 @@ +{ pkgs, config, ... }: +let + syncJobs = [ + { + name = "movies"; + source = "/data/media/movies/"; + destination = "/volume1/media/movies/"; + } + { + name = "videos"; + source = "/data/media/videos/"; + destination = "/volume1/media/videos/"; + } + ]; + remoteHost = "192.168.1.68"; + remoteUser = "nas"; +in +{ + age.secrets.rsync-ssh-key.file = ../secrets/rsync-ssh-nas.age; + + systemd.timers = pkgs.lib.listToAttrs ( + map (job: { + name = "rsync-backup-${job.name}"; + value = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "daily"; + Persistent = true; + RandomizedDelaySec = "1h"; + }; + }; + }) syncJobs + ); + + systemd.services = pkgs.lib.listToAttrs ( + map (job: { + name = "rsync-backup-${job.name}"; + value = { + description = "Rsync backup for ${job.name}"; + + serviceConfig = { + Type = "oneshot"; + DynamicUser = true; + LoadCredential = "ssh-key:${config.age.secrets.rsync-ssh-key.path}"; + PrivateTmp = true; + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + + ExecStart = pkgs.writeShellScript "rsync-backup-${job.name}" '' + ${pkgs.rsync}/bin/rsync \ + -avz \ + -e "${pkgs.openssh}/bin/ssh -i ''${CREDENTIALS_DIRECTORY}/ssh-key -o StrictHostKeyChecking=accept-new" \ + ${job.source} \ + ${remoteUser}@${remoteHost}:${job.destination} + ''; + }; + }; + }) syncJobs + ); +} diff --git a/profiles/users/fcuny.nix b/profiles/users/fcuny.nix index 1a2b490..9d4e1e2 100644 --- a/profiles/users/fcuny.nix +++ b/profiles/users/fcuny.nix @@ -5,7 +5,7 @@ uid = 1000; shell = pkgs.fish; isNormalUser = true; - hashedPassword = "$6$Llw8m62nKMLLN9mm$3.a4CKUFlqwkG8vjBryLlBNwTwgH63vpg2nhYwRoQzG76Q91vTXnlYDujS4G5yGrWoatkKZx5epCx4/NAvRh2/"; + hashedPassword = "$y$j9T$U3mXpCzXC1VUp8wV5snJz/$32vTk0KwVXvP/jLO13nMlGPHy0nCe4ZtebdvqU4hwmD"; openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINBkozy+X96u5ciX766bJ/AyQ3xm1tXZTIr5+4PVFZFi" ]; diff --git a/secrets/rsync-ssh-nas.age b/secrets/rsync-ssh-nas.age Binary files differnew file mode 100644 index 0000000..b71e4ca --- /dev/null +++ b/secrets/rsync-ssh-nas.age diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 155a88b..adb15e1 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -25,6 +25,12 @@ in hosts.rivendell ]; + # ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINE3mdcVS7+DPr7MZzIh3JsuI5t4z83j7ZAdAYxFLW4S rsync-nas + "rsync-ssh-nas.age".publicKeys = [ + users.fcuny + hosts.rivendell + ]; + # this is the SSH key we use to access the remote builder. "ssh-remote-builder.age".publicKeys = [ users.fcuny |
