From 44cbcb86602989739476acec607b5e6f7344af78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kugland?= Date: Thu, 14 Mar 2024 04:59:37 -0300 Subject: [PATCH] google-authenticator-singlesecret is now a module, not an overlay --- README.md | 60 +++++++++--- modules/default.nix | 1 + .../default.nix | 95 +++++++++++++++++++ .../singlesecret.patch | 8 +- overlays/default.nix | 4 +- .../default.nix | 5 - 6 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 modules/google-authenticator-singlesecret/default.nix rename {overlays => modules}/google-authenticator-singlesecret/singlesecret.patch (86%) delete mode 100644 overlays/google-authenticator-singlesecret/default.nix diff --git a/README.md b/README.md index f161e39..937df9b 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,6 @@ service to start the container on boot. To use this module, add the following to your `configuration.nix`: ```nix -{ config, pkgs, ... }: - { imports = [ pkgs.nur.repos.kugland.modules.qemu-user-static ]; virtualisation.qemu-user-static = { @@ -52,23 +50,55 @@ you actually have a s390x machine, means that the container is being executed wi For more information, check out `qemu-user-static`’s [GitHub repository](https://github.com/multiarch/qemu-user-static). -# Overlays - ## google-authenticator-singlesecret **I’m not a security expert, so use this at your own risk.** -[**google-authenticator-singlesecret**](./overlays/google-authenticator-singlesecret/) is a patched -version of `google-authenticator-libpam` that hardcodes the options `secret`, `user` and -`echo_verification_code` as a workaround to the Nix module `security.pam` inability to pass -arbitrary options to the Google Authenticator PAM module. With the original configuration, you -can only use TOTP secrets stored at your own home directory and readable by your own user, which -makes it pointless for use with `sudo` (the secret is readable by your user *before* you acquire -privileges). In this version, the secret is stored at `/etc/my-secrets/google-authenticator/secret`, -and is only readable by the user `totp-auth`. This setup only makes sense if you have a single user -on your system. - -I intend to make a module to configure this. +[**google-authenticator-singlesecret**](./modules/google-authenticator-singlesecret/) is a +module that overlays a patched version of `google-authenticator-libpam` that hardcodes the options +`secret`, `user` and `echo_verification_code` as a workaround to the NixOS module `security.pam` +inability to pass arbitrary options to the Google Authenticator PAM module. + +With the original configuration, you can only use secrets stored at your own home directory and +readable by your own user, making it useless for `sudo` (the secret is readable by your user +*before* you acquire privileges). In this version, the secret is readable only by a single user +(default: `totp-auth`), and resides at this user’s home directory (default: `/var/lib/totp-auth`). + +This setup only makes sense on a single-user system. + +To use this module, add the following to your `configuration.nix`: + +```nix +{ + imports = [ pkgs.nur.repos.kugland.modules.google-authenticator-singlesecret ]; + security.pam.services.google-authenticator = { + enable = true; + #user = "totp-auth"; # Change the user name if you want + #secret-dir = "/var/lib/totp-auth"; # Change the secret directory if you want + echo = true; # If you want the verification code to be echoed to the terminal, I like it. + }; + security = { + sudo = { + enable = true; + # For some reason, with this config sudo lectures us every time, let's suppress it. + extraConfig = ''Defaults lecture="never"''; + }; + pam.services = { + # Now enable Google Authenticator for su and sudo :-) + su.googleAuthenticator.enable = true; # enable 2FA for su + sudo.googleAuthenticator.enable = true; # enable 2FA for sudo + }; + }; +} +``` + +After adding this and rebuilding your system, you **MUST** run `setup-google-authenticator-singlesecret.sh` +as root to configure the secret for the user you specified. If you don’t, you will not be able +to easily `sudo` again. + +# Overlays + +*Sorry, no overlays yet.* # License diff --git a/modules/default.nix b/modules/default.nix index e26d9c1..783f6e8 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -3,4 +3,5 @@ # # my-module = ./my-module; qemu-user-static = ./qemu-user-static.nix; + google-authenticator-singlesecret = ./google-authenticator-singlesecret; } diff --git a/modules/google-authenticator-singlesecret/default.nix b/modules/google-authenticator-singlesecret/default.nix new file mode 100644 index 0000000..a0af6dd --- /dev/null +++ b/modules/google-authenticator-singlesecret/default.nix @@ -0,0 +1,95 @@ +{ + pkgs, + lib, + config, + ... +}: let + cfg = config.security.google-authenticator-singlesecret; + escapeCLang = str: lib.strings.escapeC [''"'' ''\'' "\n" "\t" "\r"] str; +in { + options = { + security.google-authenticator-singlesecret = { + enable = lib.mkEnableOption "Enable Google Authenticator (single secret)"; + user = lib.mkOption { + type = lib.types.str; + default = "totp-auth"; + description = "User to run Google Authenticator as"; + }; + secret-dir = lib.mkOption { + type = lib.types.str; + default = "/var/lib/totp-auth"; + description = "Secret to use for Google Authenticator"; + }; + echo = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Echo the code when typing it"; + }; + }; + }; + config = lib.mkIf cfg.enable { + nixpkgs.overlays = [ + (final: prev: { + google-authenticator = prev.google-authenticator.overrideAttrs (attrs: { + patches = [./singlesecret.patch]; + preBuild = let + user = escapeCLang cfg.user; + secret = escapeCLang (cfg.secret-dir + "/secret"); + echo = + if cfg.echo + then "1" + else "0"; + in '' + sed -i -e 's|@TOTP_AUTH_USER@|"${user}"|' \ + -e 's|@TOTP_AUTH_SECRET@|"${secret}"|' \ + -e 's|@TOTP_AUTH_ECHO@|${echo}|' \ + src/pam_google_authenticator.c + ''; + }); + }) + ]; + users = { + users.${cfg.user} = { + isSystemUser = true; + description = "User to run Google Authenticator as"; + home = cfg.secret-dir; + createHome = true; + homeMode = "0700"; + group = cfg.user; + shell = "${pkgs.shadow}/bin/nologin"; + hashedPassword = "!"; + }; + groups.${cfg.user} = {}; + }; + environment.systemPackages = with pkgs; [ + google-authenticator + (writeScriptBin "setup-google-authenticator-singlesecret.sh" '' + #! /usr/bin/env bash + + set -euo pipefail + + if [[ $(id -u) -ne 0 ]]; then + echo "This script must be run as root" + exit 1 + fi + + echo -e "\n\n\n\n\n" + ${pkgs.toilet}/bin/toilet --termwidth -f smblock \ + $' Now create your secret\nMake sure no one is watching!' + echo -e "\n\n\n\n\n" + echo -e "\033[31mIf you leave this root shell, you might not be able to easily get back in!" + echo -e "\033[0\n\n" + echo "Press any key to continue" + read -n 1 -s + + # Setup Google Authenticator + ${pkgs.google-authenticator}/bin/google-authenticator -s "${cfg.secret-dir}/secret" + + # Set permissions + chown -R ${cfg.user}:${cfg.user} "${cfg.secret-dir}" + chmod 0700 "${cfg.secret-dir}" + chmod 0400 "${cfg.secret-dir}/secret" + '') + ]; + }; +} diff --git a/overlays/google-authenticator-singlesecret/singlesecret.patch b/modules/google-authenticator-singlesecret/singlesecret.patch similarity index 86% rename from overlays/google-authenticator-singlesecret/singlesecret.patch rename to modules/google-authenticator-singlesecret/singlesecret.patch index f7f58fd..3f2a75c 100644 --- a/overlays/google-authenticator-singlesecret/singlesecret.patch +++ b/modules/google-authenticator-singlesecret/singlesecret.patch @@ -7,7 +7,7 @@ index 80f3641..4348e79 100644 Params *params) { params->debug = 0; - params->echocode = PAM_PROMPT_ECHO_OFF; -+ params->echocode = PAM_PROMPT_ECHO_ON; ++ params->echocode = @TOTP_AUTH_ECHO@ ? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF; for (int i = 0; i < argc; ++i) { if (!strncmp(argv[i], "secret=", 7)) { params->secret_filename_spec = argv[i] + 7; @@ -18,11 +18,11 @@ index 80f3641..4348e79 100644 - if (parse_args(pamh, argc, argv, ¶ms) < 0) { + + const char *initial[] = { -+ "user=totp-auth", -+ "secret=/etc/my-secrets/google-authenticator/secret", ++ "user=" @TOTP_AUTH_USER@, ++ "secret=" @TOTP_AUTH_SECRET@, + NULL + }; + if (parse_args(pamh, 2, initial, ¶ms) < 0 || parse_args(pamh, argc, argv, ¶ms) < 0) { return rc; } - + diff --git a/overlays/default.nix b/overlays/default.nix index 5e16724..0c2d870 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -1,3 +1,5 @@ { - google-authenticator-singlesecret = import ./google-authenticator-singlesecret; + # Add your overlays here + # + # my-overlay = import ./my-overlay; } diff --git a/overlays/google-authenticator-singlesecret/default.nix b/overlays/google-authenticator-singlesecret/default.nix deleted file mode 100644 index 78e793e..0000000 --- a/overlays/google-authenticator-singlesecret/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -final: prev: { - google-authenticator = prev.google-authenticator.overrideAttrs (attrs: { - patches = [./singlesecret.patch]; - }); -}