diff --git a/modules/sops/default.nix b/modules/sops/default.nix index 96968bb..5a308dc 100644 --- a/modules/sops/default.nix +++ b/modules/sops/default.nix @@ -126,6 +126,7 @@ let sshKeyPaths = cfg.gnupg.sshKeyPaths; ageKeyFile = cfg.age.keyFile; ageSshKeyPaths = cfg.age.sshKeyPaths; + useTmpfs = cfg.useTmpfs; userMode = false; logging = { keyImport = builtins.elem "keyImport" cfg.log; @@ -242,6 +243,26 @@ in { ''; }; + useTmpfs = mkOption { + type = types.bool; + default = false; + description = lib.mkDoc '' + Use tmpfs in place of ramfs for secrets storage. + + *WARNING* + Enabling this option has the potential to write secrets to disk unencrypted if the tmpfs volume is written to swap. Do not use unless absolutely necessary. + + When using a swap file or device, consider enabling swap encryption by setting the `randomEncryption.enable` option + + ``` + swapDevices = [{ + device = "/dev/sdXY"; + randomEncryption.enable = true; + }]; + ``` + ''; + }; + age = { keyFile = mkOption { type = types.nullOr types.path; diff --git a/pkgs/sops-install-secrets/linux.go b/pkgs/sops-install-secrets/linux.go index 35c760a..634eac8 100644 --- a/pkgs/sops-install-secrets/linux.go +++ b/pkgs/sops-install-secrets/linux.go @@ -41,7 +41,7 @@ func SecureSymlinkChown(symlinkToCheck, expectedTarget string, owner, group int) return nil } -func MountSecretFs(mountpoint string, keysGid int, userMode bool) error { +func MountSecretFs(mountpoint string, keysGid int, useTmpfs bool, userMode bool) error { if err := os.MkdirAll(mountpoint, 0751); err != nil { return fmt.Errorf("Cannot create directory '%s': %w", mountpoint, err) } @@ -51,12 +51,19 @@ func MountSecretFs(mountpoint string, keysGid int, userMode bool) error { return nil } + var fstype string = "ramfs" + var fsmagic int32 = RAMFS_MAGIC + if useTmpfs { + fstype = "tmpfs" + fsmagic = TMPFS_MAGIC + } + buf := unix.Statfs_t{} if err := unix.Statfs(mountpoint, &buf); err != nil { return fmt.Errorf("Cannot get statfs for directory '%s': %w", mountpoint, err) } - if int32(buf.Type) != RAMFS_MAGIC { - if err := unix.Mount("none", mountpoint, "ramfs", unix.MS_NODEV|unix.MS_NOSUID, "mode=0751"); err != nil { + if int32(buf.Type) != fsmagic { + if err := unix.Mount("none", mountpoint, fstype, unix.MS_NODEV|unix.MS_NOSUID, "mode=0751"); err != nil { return fmt.Errorf("Cannot mount: %s", err) } } diff --git a/pkgs/sops-install-secrets/main.go b/pkgs/sops-install-secrets/main.go index fc32f69..d02c32c 100644 --- a/pkgs/sops-install-secrets/main.go +++ b/pkgs/sops-install-secrets/main.go @@ -54,6 +54,7 @@ type manifest struct { GnupgHome string `json:"gnupgHome"` AgeKeyFile string `json:"ageKeyFile"` AgeSshKeyPaths []string `json:"ageSshKeyPaths"` + UseTmpfs bool `json:"useTmpfs"` UserMode bool `json:"userMode"` Logging loggingConfig `json:"logging"` } @@ -304,6 +305,7 @@ func decryptSecrets(secrets []secret) error { } const RAMFS_MAGIC int32 = -2054924042 +const TMPFS_MAGIC int32 = 16914836 func prepareSecretsDir(secretMountpoint string, linkName string, keysGid int, userMode bool) (*string, error) { var generation uint64 @@ -932,7 +934,7 @@ func installSecrets(args []string) error { isDry := os.Getenv("NIXOS_ACTION") == "dry-activate" - if err := MountSecretFs(manifest.SecretsMountPoint, keysGid, manifest.UserMode); err != nil { + if err := MountSecretFs(manifest.SecretsMountPoint, keysGid, manifest.UseTmpfs, manifest.UserMode); err != nil { return fmt.Errorf("Failed to mount filesystem for secrets: %w", err) }