!XLCFfvFhUkYwOMLbVx:nixos.org

agenix

346 Members
age-encrypted secrets for NixOS https://github.com/ryantm/agenix/102 Servers

Load older messages


SenderMessageTime
30 Nov 2024
@ysomic:matrix.orgySomic *

Reading this github issue, I imagine my reasoning is flawed about how home-manager integration works?

Is there some resources I could learn from, because this does not seem to work, the file is not present on my activation script nor does agenix ask for my passphrase before switching

{
  self,
  config,
  lib,
  pkgs,
  ...
}: let
  secretsDir = "${self}/data/secrets";
  commonPub = "${secretsDir}/common/pub.gpg";

  mkGpgImportsWithFunc = keys: let
    importStatements =
      lib.concatMapStrings (key: ''
        echo "Attempting to import key: ${key}"
        import_gpg_key "${config.age.secrets.${key}.path}"
      '')
      keys;
  in ''
    import_gpg_key() {
      local key_path="$1"
      if [ -f "$key_path" ]; then
        local gpg_output
        gpg_output=$(run ${pkgs.gnupg}/bin/gpg --import "$key_path" 2>&1)
        local gpg_exit_code=$?

        if echo "$gpg_output" | grep -q "not changed"; then
          echo "Warning: Key $key_name already exists and wasn't modified"
        elif echo "$gpg_output" | grep -q "secret key already exists"; then
          echo "Warning: Secret key $key_name already exists"
        fi

        if [ $gpg_exit_code -ne 0 ]; then
          echo "Error: Failed to import GPG key: $key_path"
          echo "GPG output: $gpg_output"
          failed=1
        else
          echo "GPG imported: $key_path"
        fi

        echo "Will shred GPG Key: $key_path"
        run ${pkgs.coreutils}/bin/shred -u "$key_path"
        [ "$failed" = "1" ] && return 1
      else
        echo "Path not found: $key_path"
      fi
    }

    ${importStatements}
  '';

  gpgKeys = [
    "common/gpg"
  ];

  assertions = [
    {
      assertion = lib.all (key: lib.hasAttr key config.age.secrets) gpgKeys;
      message = let
        # Find which keys are missing
        missingKeys = lib.filter (key: !(lib.hasAttr key config.age.secrets)) gpgKeys;
      in "The following GPG keys are in gpgKeys but not in age.secrets: ${toString missingKeys}";
    }
  ];
in {
  inherit assertions;

  age.secrets = {
    "common/gpg" = {
      file = "${secretsDir}/common/gpg.age";
      path = "${config.home.homeDirectory}/secrets/gpg/common.gpg.temp";
    };
  };

  programs.gpg = {
    enable = true;
    publicKeys = [
      {
        source = commonPub;
        trust = "ultimate";
      }
    ];
  };

  services.gpg-agent = {
    enable = true;
    enableBashIntegration = true;
    pinentryPackage = pkgs.pinentry-curses;
  };

  home.activation = {
    importPrivateGpgKeys =
      lib.hm.dag.entryAfter ["writeBoundary"]
      (mkGpgImportsWithFunc gpgKeys);
  };
}

Like to me, it looks like agenix doesn't even run at all

I do have these in my output though

  /nix/store/5qpmkj6682nzs2w4176c0fsvsfbqhddg-agenix-home-manager-mount-secrets.drv
  /nix/store/kpzmmrm5jibxc0rb8f7fg1acky8bf3b2-agenix.service.drv

The service is dead because it's asking for the passphrase. I thought it would've worked like nixos rebuild where it asks you during build time.

01:48:49
@soliprem:beeper.comSoliprem joined the room.08:08:30
@ysomic:matrix.orgySomic

I've changed it to this

{
  self,
  config,
  lib,
  pkgs,
  ...
}: let
  secretsDir = "${self}/data/secrets";
  commonPub = "${secretsDir}/common/pub.gpg";

  # We create a trigger file on activation to make sure we only run once per activation
  # triggerFile = "${config.home.homeDirectory}/.local/state/gpg-import-needed";
  versionFile = "${config.home.homeDirectory}/.local/state/gpg-import-version";

  # We create a version using a manual version and a hash
  # The manual version forces to reimport on key changes with the same name
  # Note, private keys will still not be imported, that's how GPG import works.
  manualVersion = "1";
  gpgKeys = ["common/gpg"];
  gpgKeysHash = builtins.hashString "sha256" (builtins.toString gpgKeys);
  combinedVersion = "${manualVersion}-${gpgKeysHash}";

  # Cleanup script, that will be ran in a separate systemd service
  # The reason it's in a separate one is, that we guarantee the run onSuccess or OnFailure.
  # If we don't do this, we need to jump around hoops and trap exits to make sure we shred the decrypted keys
  cleanupScriptFunc = keys:
    lib.concatMapStrings (key: ''
        if [ -f "${config.age.secrets."${key}".path}" ]; then
        echo "Will shred GPG Key: ${config.age.secrets."${key}".path}"
        ${pkgs.coreutils}/bin/shred -u "${config.age.secrets."${key}".path}"
      else
        echo "Key not found: ${config.age.secrets."${key}".path}"
      fi
    '')
    keys;

  cleanupScript = pkgs.writeShellScriptBin "cleanup-gpg-keys" ''
    ${cleanupScriptFunc gpgKeys}
  '';

  # Trigger keys, a list of strings.
  # Uses systemd condition path exists with an 'or' prefix.
  # More info: https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Conditions%20and%20Asserts
  triggerPaths = keys:
    map (key: "|${config.age.secrets.${key}.path}") keys;

  # Main script + import script
  mkGpgImportsWithFunc = keys:
    lib.concatMapStrings (key: ''
      echo "Attempting to import key: ${key}"
      import_gpg_key "${config.age.secrets.${key}.path}"
    '')
    keys;

  importScript = pkgs.writeShellScriptBin "import-gpg-keys" ''
    # First check version
    if [ -f "${versionFile}" ]; then
      current_version=$(cat "${versionFile}")
      if [ "$current_version" = "${combinedVersion}" ]; then
        echo "GPG keys already imported at version ${manualVersion} with current keys"
        exit 0
      else
        echo "Version mismatch:"
        echo "Current: $current_version"
        echo "New: ${combinedVersion}"
      fi
    else
      echo "No version file found, will import keys"
    fi

    # Import function
    import_gpg_key() {
      local key_path="$1"
      if [ -f "$key_path" ]; then
        local gpg_output
        gpg_output=$(run ${pkgs.gnupg}/bin/gpg --import "$key_path" 2>&1)
        local gpg_exit_code=$?
        if echo "$gpg_output" | grep -q "not changed"; then
          echo "Warning: Key $key_name already exists and wasn't modified"
        elif echo "$gpg_output" | grep -q "secret key already exists"; then
          echo "Warning: Secret key $key_name already exists"
        fi
        if [ $gpg_exit_code -ne 0 ]; then
          echo "Error: Failed to import GPG key: $key_path"
          echo "GPG output: $gpg_output"
          return 1
        else
          echo "GPG imported: $key_path"
        fi
      else
        echo "Path not found: $key_path"
        return 1
      fi
    }

    # Import statements for each key
    ${mkGpgImportsWithFunc gpgKeys}

    # Create new version
    mkdir -p "$(dirname "${versionFile}")"
    echo "${combinedVersion}" > "${versionFile}"
    echo "Updated version to ${combinedVersion}"
  '';

  assertions = [
    {
      assertion = lib.all (key: lib.hasAttr key config.age.secrets) gpgKeys;
      message = let
        # Find which keys are missing
        missingKeys = lib.filter (key: !(lib.hasAttr key config.age.secrets)) gpgKeys;
      in "The following GPG keys are in gpgKeys but not in age.secrets: ${toString missingKeys}";
    }
  ];
in {
  inherit assertions;

  age.secrets = {
    "common/gpg" = {
      file = "${secretsDir}/common/gpg.age";
      path = "${config.home.homeDirectory}/secrets/gpg/common.gpg.temp";
    };
  };

  programs.gpg = {
    enable = true;
    publicKeys = [
      {
        source = commonPub;
        trust = "ultimate";
      }
    ];
  };

  services.gpg-agent = {
    enable = true;
    enableBashIntegration = true;
    pinentryPackage = pkgs.pinentry-curses;
  };

  # Systemd services
  systemd.user.services = {
    # Main import service
    import-gpg-keys = {
      Unit = {
        Description = "Import GPG keys (v${manualVersion})";
        After = ["agenix.service"];
        ConditionPathExists = triggerPaths gpgKeys;
      };

      Install = {
        WantedBy = ["default.target"];
      };

      Service = {
        Type = "oneshot";
        RemainAfterExit = true;
        ExecStart = "${importScript}/bin/import-gpg-keys";
        ExecStopPost = "${cleanupScript}/bin/cleanup-gpg-keys";

        # # Isolate the service's /tmp directory
        # PrivateTmp = true;
        # # Prevent privilege escalation
        # NoNewPrivileges = true;
        # # Make the root filesystem read-only except /var, /run, etc
        # ProtectSystem = "strict";
        # # Allow writing to home for the version file
        # # ProtectHome = "read-write";
        # # Restrict network access since we don't need it
        # RestrictAddressFamilies = "AF_UNIX";
        # # Prevent memory exploits
        # MemoryDenyWriteExecute = true;
      };
    };
  };
}

I'm just wondering, is there still a way to use agenix for home with an ssh key that has a passphrase?

15:05:30
1 Dec 2024
@vengmark2:matrix.org@vengmark2:matrix.org left the room.00:10:17
2 Dec 2024
@pyrox:pyrox.devPyrox [ It/She/They/Xem ] changed their profile picture.19:59:14
4 Dec 2024
@bjrnmrtns:matrix.orgbjrnmrtns

I a bit lost for a while using agenix with wg-quick (wireguard)
While it is working when I rebuilt my system, it fails on boot.
While booting the encrypted age key is not present. After booting I need to rebuild the system to make sure my key is in the /run/agenix.d/ folder. I assume I'm making some obvious mistake, but my nix skills are not there yet to spot it :-).

  age = {
    secrets = {
      wg-server-private = {
        file = secrets/wg-server-private.age;
        owner = "bjorn";
      };
    };
    identityPaths = [ "/home/bjorn/.ssh/id_25519_jennifer_agenix" ];
  };

  networking.wg-quick.interfaces = {
    wg0 = {
      address = [
        "10.0.0.2/24"
        "fdc9:281f:04d7:9ee9::2/64"
      ];
      dns = [
        "10.0.0.1"
        "fdc9:281f:04d7:9ee9::1"
      ];
      # client private key
      privateKeyFile = config.age.secrets.wg-jennifer-private.path;

The error I get on startup is the following:

Nov 12 18:58:38 jennifer systemd[1]: Starting wg-quick WireGuard Tunnel - wg0...
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: Warning: `/nix/store/l0s69wazn7c1s91pkfqmynpm9x1fvq0s-config-wg0/wg0.conf' is world accessible
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link add wg0 type wireguard
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg setconf wg0 /dev/fd/63
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 address add 10.0.0.2/24 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 address add fdc9:281f:04d7:9ee9::2/64 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link set mtu 1420 up dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162481]: [#] resolvconf -a wg0 -m 0 -x
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg set wg0 fwmark 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 route add ::/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162592]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162612]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] /nix/store/p0l2c0h0j0sd0x5kx069jq88hpwrsb98-postUp.sh/bin/postUp.sh
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162619]: cat: 'aKBA0vfKjkKIiRjhF8W0GEvM0afdYp6jZsLSI981L1Y=': No such file or directory
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162620]: Invalid length key in key file
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] resolvconf -d wg0 -f
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162685]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162704]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link delete dev wg0
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Main process exited, code=exited, status=1/FAILURE
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Failed with result 'exit-code'.
Nov 12 18:58:38 jennifer systemd[1]: Failed to start wg-quick WireGuard Tunnel - wg0.
12:22:39
@bjrnmrtns:matrix.orgbjrnmrtns *

I am bit lost for a while using agenix with wg-quick (wireguard)
While it is working when I rebuilt my system, it fails on boot.
While booting the encrypted age key is not present. After booting I need to rebuild the system to make sure my key is in the /run/agenix.d/ folder. I assume I'm making some obvious mistake, but my nix skills are not there yet to spot it :-).

  age = {
    secrets = {
      wg-server-private = {
        file = secrets/wg-server-private.age;
        owner = "bjorn";
      };
    };
    identityPaths = [ "/home/bjorn/.ssh/id_25519_jennifer_agenix" ];
  };

  networking.wg-quick.interfaces = {
    wg0 = {
      address = [
        "10.0.0.2/24"
        "fdc9:281f:04d7:9ee9::2/64"
      ];
      dns = [
        "10.0.0.1"
        "fdc9:281f:04d7:9ee9::1"
      ];
      # client private key
      privateKeyFile = config.age.secrets.wg-jennifer-private.path;

The error I get on startup is the following:

Nov 12 18:58:38 jennifer systemd[1]: Starting wg-quick WireGuard Tunnel - wg0...
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: Warning: `/nix/store/l0s69wazn7c1s91pkfqmynpm9x1fvq0s-config-wg0/wg0.conf' is world accessible
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link add wg0 type wireguard
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg setconf wg0 /dev/fd/63
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 address add 10.0.0.2/24 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 address add fdc9:281f:04d7:9ee9::2/64 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link set mtu 1420 up dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162481]: [#] resolvconf -a wg0 -m 0 -x
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg set wg0 fwmark 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 route add ::/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162592]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162612]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] /nix/store/p0l2c0h0j0sd0x5kx069jq88hpwrsb98-postUp.sh/bin/postUp.sh
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162619]: cat: 'aKBA0vfKjkKIiRjhF8W0GEvM0afdYp6jZsLSI981L1Y=': No such file or directory
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162620]: Invalid length key in key file
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] resolvconf -d wg0 -f
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162685]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162704]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link delete dev wg0
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Main process exited, code=exited, status=1/FAILURE
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Failed with result 'exit-code'.
Nov 12 18:58:38 jennifer systemd[1]: Failed to start wg-quick WireGuard Tunnel - wg0.
12:22:57
@bjrnmrtns:matrix.orgbjrnmrtns *

I am a bit lost for a while using agenix with wg-quick (wireguard)
While it is working when I rebuilt my system, it fails on boot.
While booting the encrypted age key is not present. After booting I need to rebuild the system to make sure my key is in the /run/agenix.d/ folder. I assume I'm making some obvious mistake, but my nix skills are not there yet to spot it :-).

  age = {
    secrets = {
      wg-server-private = {
        file = secrets/wg-server-private.age;
        owner = "bjorn";
      };
    };
    identityPaths = [ "/home/bjorn/.ssh/id_25519_jennifer_agenix" ];
  };

  networking.wg-quick.interfaces = {
    wg0 = {
      address = [
        "10.0.0.2/24"
        "fdc9:281f:04d7:9ee9::2/64"
      ];
      dns = [
        "10.0.0.1"
        "fdc9:281f:04d7:9ee9::1"
      ];
      # client private key
      privateKeyFile = config.age.secrets.wg-jennifer-private.path;

The error I get on startup is the following:

Nov 12 18:58:38 jennifer systemd[1]: Starting wg-quick WireGuard Tunnel - wg0...
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: Warning: `/nix/store/l0s69wazn7c1s91pkfqmynpm9x1fvq0s-config-wg0/wg0.conf' is world accessible
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link add wg0 type wireguard
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg setconf wg0 /dev/fd/63
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 address add 10.0.0.2/24 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 address add fdc9:281f:04d7:9ee9::2/64 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link set mtu 1420 up dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162481]: [#] resolvconf -a wg0 -m 0 -x
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg set wg0 fwmark 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 route add ::/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162592]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162612]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] /nix/store/p0l2c0h0j0sd0x5kx069jq88hpwrsb98-postUp.sh/bin/postUp.sh
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162619]: cat: 'aKBA0vfKjkKIiRjhF8W0GEvM0afdYp6jZsLSI981L1Y=': No such file or directory
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162620]: Invalid length key in key file
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] resolvconf -d wg0 -f
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162685]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162704]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link delete dev wg0
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Main process exited, code=exited, status=1/FAILURE
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Failed with result 'exit-code'.
Nov 12 18:58:38 jennifer systemd[1]: Failed to start wg-quick WireGuard Tunnel - wg0.
12:23:12
@bjrnmrtns:matrix.orgbjrnmrtns *

I am a bit lost for a while using agenix with wg-quick (wireguard)
While it is working when I rebuilt my system, it fails on boot.
While booting the encrypted age key is not present. After booting I need to rebuild the system to make sure my key is in the /run/agenix.d/ folder. I assume I'm making some obvious mistake, but my nix skills are not there yet to spot it :-).

  age = {
    secrets = {
      wg-server-private = {
        file = secrets/wg-server-private.age;
        owner = "bjorn";
      };
    };
    identityPaths = [ "/home/bjorn/.ssh/id_25519_jennifer_agenix" ];
  };

  networking.wg-quick.interfaces = {
    wg0 = {
      address = [
        "10.0.0.2/24"
        "fdc9:281f:04d7:9ee9::2/64"
      ];
      dns = [
        "10.0.0.1"
        "fdc9:281f:04d7:9ee9::1"
      ];
      # client private key
      privateKeyFile = config.age.secrets.wg-jennifer-private.path;

The key I'm talking about is config.age.secrets.wg-jennifer-private
The error I get on startup is the following:

Nov 12 18:58:38 jennifer systemd[1]: Starting wg-quick WireGuard Tunnel - wg0...
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: Warning: `/nix/store/l0s69wazn7c1s91pkfqmynpm9x1fvq0s-config-wg0/wg0.conf' is world accessible
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link add wg0 type wireguard
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg setconf wg0 /dev/fd/63
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 address add 10.0.0.2/24 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 address add fdc9:281f:04d7:9ee9::2/64 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link set mtu 1420 up dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162481]: [#] resolvconf -a wg0 -m 0 -x
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg set wg0 fwmark 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 route add ::/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162592]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162612]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] /nix/store/p0l2c0h0j0sd0x5kx069jq88hpwrsb98-postUp.sh/bin/postUp.sh
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162619]: cat: 'aKBA0vfKjkKIiRjhF8W0GEvM0afdYp6jZsLSI981L1Y=': No such file or directory
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162620]: Invalid length key in key file
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] resolvconf -d wg0 -f
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162685]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162704]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link delete dev wg0
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Main process exited, code=exited, status=1/FAILURE
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Failed with result 'exit-code'.
Nov 12 18:58:38 jennifer systemd[1]: Failed to start wg-quick WireGuard Tunnel - wg0.
12:24:23
@bjrnmrtns:matrix.orgbjrnmrtns *

I am a bit lost for a while using agenix with wg-quick (wireguard)
While it is working when I rebuilt my system, it fails on boot.
While booting the encrypted age key is not present. After booting I need to rebuild the system to make sure my key is in the /run/agenix.d/ folder. I assume I'm making some obvious mistake, but my nix skills are not there yet to spot it :-).

  age = {
    secrets = {
      wg-server-private = {
        file = secrets/wg-server-private.age;
        owner = "bjorn";
      };
    };
    identityPaths = [ "/home/bjorn/.ssh/id_25519_jennifer_agenix" ];
  };

  networking.wg-quick.interfaces = {
    wg0 = {
      address = [
        "10.0.0.2/24"
        "fdc9:281f:04d7:9ee9::2/64"
      ];
      dns = [
        "10.0.0.1"
        "fdc9:281f:04d7:9ee9::1"
      ];
      # client private key
      privateKeyFile = config.age.secrets.wg-jennifer-private.path;

The key I'm talking about is config.age.secrets.wg-jennifer-private
The error I get on startup is the following:

Nov 12 18:58:38 jennifer systemd[1]: Starting wg-quick WireGuard Tunnel - wg0...
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: Warning: `/nix/store/l0s69wazn7c1s91pkfqmynpm9x1fvq0s-config-wg0/wg0.conf' is world accessible
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link add wg0 type wireguard
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg setconf wg0 /dev/fd/63
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 address add 10.0.0.2/24 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 address add fdc9:281f:04d7:9ee9::2/64 dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link set mtu 1420 up dev wg0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162481]: [#] resolvconf -a wg0 -m 0 -x
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] wg set wg0 fwmark 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 route add ::/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162592]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add not fwmark 51820 table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule add table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162612]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] /nix/store/p0l2c0h0j0sd0x5kx069jq88hpwrsb98-postUp.sh/bin/postUp.sh
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162619]: cat: 'aKBA0vfKjkKIiRjhF8W0GEvM0afdYp6jZsLSI981L1Y=': No such file or directory
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162620]: Invalid length key in key file
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] resolvconf -d wg0 -f
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162685]: [#] iptables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162704]: [#] ip6tables-restore -n
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -4 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table 51820
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip -6 rule delete table main suppress_prefixlength 0
Nov 12 18:58:38 jennifer wg-quick-wg0-start[162446]: [#] ip link delete dev wg0
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Main process exited, code=exited, status=1/FAILURE
Nov 12 18:58:38 jennifer systemd[1]: wg-quick-wg0.service: Failed with result 'exit-code'.
Nov 12 18:58:38 jennifer systemd[1]: Failed to start wg-quick WireGuard Tunnel - wg0.

Is somebody able to help me? Somewhere I read an issue where somebody did not have the dependency to the file being created. I thought my configuration makes sure this dependency exists.

12:25:56
@lordkekz:matrix.orgLordKekzAre you maybe using a master identity that isn't yet available (e.g. mounted or linked) in the boot stage when agenix first runs? I also had decryption problems when I tried using a SSH private key from my home folder which only gets mounted after boot by an impermanence systemd unit. I was able to resolve my issue by pointing agenix to the persistent directory which was already available in early boot.12:30:34
@bjrnmrtns:matrix.orgbjrnmrtns
In reply to @lordkekz:matrix.org
Are you maybe using a master identity that isn't yet available (e.g. mounted or linked) in the boot stage when agenix first runs?

I also had decryption problems when I tried using a SSH private key from my home folder which only gets mounted after boot by an impermanence systemd unit.
I was able to resolve my issue by pointing agenix to the persistent directory which was already available in early boot.
You might be correct my ssh private key is in ~/.ssh, so that might not be available yet.
12:32:33
@lordkekz:matrix.orgLordKekzAlso you can do `journalctl -b | grep agenix` to see the agenix logs from the initial decryption attempt at boot. (Or instead of `| grep` you can use `| less` and search for agenix, to see the surrounding lines)12:35:04
@lordkekz:matrix.orgLordKekz
In reply to @bjrnmrtns:matrix.org
You might be correct my ssh private key is in ~/.ssh, so that might not be available yet.
Ob what filesystem is your ssh key?
12:35:56
@lordkekz:matrix.orgLordKekz* In reply to @bjrnmrtns:matrix.org You might be correct my ssh private key is in ~/.ssh, so that might not be available yet. On what filesystem is your ssh key?12:36:03
@bjrnmrtns:matrix.orgbjrnmrtnsit is all in one partition in the root filesystem12:37:22
@bjrnmrtns:matrix.orgbjrnmrtnsso it should be accessible 12:37:43
@lordkekz:matrix.orgLordKekzWeird. Can you show me your `mount` output? Maybe there's some shenanigans going on anyway.12:38:38
@bjrnmrtns:matrix.orgbjrnmrtns
In reply to @lordkekz:matrix.org
Weird. Can you show me your `mount` output? Maybe there's some shenanigans going on anyway.
devtmpfs on /dev type devtmpfs (rw,nosuid,size=1576200k,nr_inodes=3938055,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=3,mode=620,ptmxmode=666)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /run type tmpfs (rw,nosuid,nodev,size=7880984k,mode=755)
ramfs on /run/keys type ramfs (rw,nosuid,nodev,relatime,mode=750)
tmpfs on /run/wrappers type tmpfs (rw,nodev,relatime,mode=755)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
/dev/nvme0n1p2 on / type btrfs (rw,relatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=259,subvol=/rootfs)
/dev/nvme0n1p2 on /nix type btrfs (rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=258,subvol=/nix)
/dev/nvme0n1p2 on /nix/store type btrfs (ro,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=258,subvol=/nix)
none on /run/agenix.d type ramfs (rw,nosuid,nodev,relatime,mode=751)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime)
efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime)
bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=65,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=2765)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,nosuid,nodev,relatime,pagesize=2M)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)
fusectl on /sys/fs/fuse/connections type fusectl (rw,nosuid,nodev,noexec,relatime)
configfs on /sys/kernel/config type configfs (rw,nosuid,nodev,noexec,relatime)
/dev/nvme0n1p2 on /disk-vdb-root type btrfs (rw,relatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/)
/dev/nvme0n1p2 on /home type btrfs (rw,relatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/home)
/dev/nvme0n1p1 on /boot type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=3152392k,nr_inodes=788098,mode=700,uid=1000,gid=100)
portal on /run/user/1000/doc type fuse.portal (rw,nosuid,nodev,relatime,user_id=1000,group_id=100)
12:39:12
@bjrnmrtns:matrix.orgbjrnmrtnsoh so there is a separate boot partition12:39:52
@lordkekz:matrix.orgLordKekzYeah that's always there.12:40:09
@bjrnmrtns:matrix.orgbjrnmrtns
In reply to @lordkekz:matrix.org

In reply to @bjrnmrtns:matrix.org
You might be correct my ssh private key is in ~/.ssh, so that might not be available yet.

On what filesystem is your ssh key?

So this might give a better clue:

[bjorn@jennifer:~/projects/config]$ journalctl -b | grep agenix
Dec 04 13:27:17 jennifer stage-2-init: [agenix] creating new generation in /run/agenix.d/1
Dec 04 13:27:17 jennifer stage-2-init: [agenix] decrypting secrets...
Dec 04 13:27:17 jennifer stage-2-init: [agenix] WARNING: config.age.identityPaths entry /home/bjorn/.ssh/id_25519_jennifer_agenix not present!
Dec 04 13:27:17 jennifer stage-2-init: decrypting '/nix/store/n5f84dj2ywrb4rnflyj5q3mpdm4scksl-bjorn-password.age' to '/run/agenix.d/1/bjorn-password'...
Dec 04 13:27:17 jennifer stage-2-init: [agenix] WARNING: no readable identities found!
Dec 04 13:27:17 jennifer stage-2-init: chmod: cannot access '/run/agenix.d/1/bjorn-password.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: mv: cannot stat '/run/agenix.d/1/bjorn-password.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: decrypting '/nix/store/bbmvbg3cvw1bfkhfn2a97636bkyf4f88-wg-iphone-private.age' to '/run/agenix.d/1/wg-iphone-private'...
Dec 04 13:27:17 jennifer stage-2-init: [agenix] WARNING: no readable identities found!
Dec 04 13:27:17 jennifer stage-2-init: chmod: cannot access '/run/agenix.d/1/wg-iphone-private.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: mv: cannot stat '/run/agenix.d/1/wg-iphone-private.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: decrypting '/nix/store/qfirm84mal87ib77pw7v1a1kg436x0qj-wg-jennifer-private.age' to '/run/agenix.d/1/wg-jennifer-private'...
Dec 04 13:27:17 jennifer stage-2-init: [agenix] WARNING: no readable identities found!
Dec 04 13:27:17 jennifer stage-2-init: chmod: cannot access '/run/agenix.d/1/wg-jennifer-private.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: mv: cannot stat '/run/agenix.d/1/wg-jennifer-private.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: decrypting '/nix/store/y1ybkk5w0ys4zslr1z7k9ql3366qjrfq-wg-server-private.age' to '/run/agenix.d/1/wg-server-private'...
Dec 04 13:27:17 jennifer stage-2-init: [agenix] WARNING: no readable identities found!
Dec 04 13:27:17 jennifer stage-2-init: chmod: cannot access '/run/agenix.d/1/wg-server-private.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: mv: cannot stat '/run/agenix.d/1/wg-server-private.tmp': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: [agenix] symlinking new secrets to /run/agenix (generation 1)...
Dec 04 13:27:17 jennifer stage-2-init: Activation script snippet 'agenixInstall' failed (1)
Dec 04 13:27:17 jennifer stage-2-init: warning: password file ‘/run/agenix/bjorn-password’ does not exist
Dec 04 13:27:17 jennifer stage-2-init: [agenix] chowning...
Dec 04 13:27:17 jennifer stage-2-init: chown: cannot access '/run/agenix.d/1/bjorn-password': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: chown: cannot access '/run/agenix.d/1/wg-iphone-private': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: chown: cannot access '/run/agenix.d/1/wg-jennifer-private': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: chown: cannot access '/run/agenix.d/1/wg-server-private': No such file or directory
Dec 04 13:27:17 jennifer stage-2-init: Activation script snippet 'agenixChown' failed (1)
Dec 04 13:27:27 jennifer wg-quick-wg0-start[2188]: cat: /run/agenix/wg-jennifer-private: No such file or directory
12:41:48
@bjrnmrtns:matrix.orgbjrnmrtnsso it is because of the private key12:42:05
@bjrnmrtns:matrix.orgbjrnmrtns
In reply to @bjrnmrtns:matrix.org
so it is because of the private key
Line 4 ;-)
12:42:54
@lordkekz:matrix.orgLordKekzYour /home is on a separate btrfs subvolume. I think it should be fine but maybe it gets mounted too late. Make sure to set `filesystems."/home".neededForBoot = true;` in your config, maybe that can help ensure it gets mounted early.12:44:24
@lordkekz:matrix.orgLordKekz
In reply to @bjrnmrtns:matrix.org
Line 4 ;-)
Just as suspected!
12:45:29
@lordkekz:matrix.orgLordKekz
In reply to @lordkekz:matrix.org
Your /home is on a separate btrfs subvolume. I think it should be fine but maybe it gets mounted too late. Make sure to set filesystems."/home".neededForBoot = true; in your config, maybe that can help ensure it gets mounted early.
Beware typos tho
12:46:32
@lordkekz:matrix.orgLordKekz* In reply to @lordkekz:matrix.org Your /home is on a separate btrfs subvolume. I think it should be fine but maybe it gets mounted too late. Make sure to set filesystems."/home".neededForBoot = true; in your config, maybe that can help ensure it gets mounted early. Beware typos tho, since I'm on my phone and just doing it from memory12:47:02
@bjrnmrtns:matrix.orgbjrnmrtns
In reply to @lordkekz:matrix.org

In reply to @lordkekz:matrix.org
Your /home is on a separate btrfs subvolume. I think it should be fine but maybe it gets mounted too late. Make sure to set filesystems."/home".neededForBoot = true; in your config, maybe that can help ensure it gets mounted early.

Beware typos tho, since I'm on my phone and just doing it from memory

That makes a lot of sense. I'm going to try it and report back. Thanks a lot so far!
12:48:34
@bjrnmrtns:matrix.orgbjrnmrtns
In reply to @lordkekz:matrix.org

In reply to @lordkekz:matrix.org
Your /home is on a separate btrfs subvolume. I think it should be fine but maybe it gets mounted too late. Make sure to set filesystems."/home".neededForBoot = true; in your config, maybe that can help ensure it gets mounted early.

Beware typos tho, since I'm on my phone and just doing it from memory

fileSystems."/home".neededForBoot = true; did the trick.
Thanks a lot for the help with debugging. I was fighting this problem already a few times, but couldn't find the issue.
12:57:05

Show newer messages


Back to Room ListRoom Version: 6