Skip to content

Commit

Permalink
google-authenticator-singlesecret is now a module, not an overlay
Browse files Browse the repository at this point in the history
  • Loading branch information
kugland committed Mar 30, 2024
1 parent bf17527 commit 44cbcb8
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 25 deletions.
60 changes: 45 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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

Expand Down
1 change: 1 addition & 0 deletions modules/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
#
# my-module = ./my-module;
qemu-user-static = ./qemu-user-static.nix;
google-authenticator-singlesecret = ./google-authenticator-singlesecret;
}
95 changes: 95 additions & 0 deletions modules/google-authenticator-singlesecret/default.nix
Original file line number Diff line number Diff line change
@@ -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"
'')
];
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,11 +18,11 @@ index 80f3641..4348e79 100644
- if (parse_args(pamh, argc, argv, &params) < 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, &params) < 0 || parse_args(pamh, argc, argv, &params) < 0) {
return rc;
}

4 changes: 3 additions & 1 deletion overlays/default.nix
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
google-authenticator-singlesecret = import ./google-authenticator-singlesecret;
# Add your overlays here
#
# my-overlay = import ./my-overlay;
}
5 changes: 0 additions & 5 deletions overlays/google-authenticator-singlesecret/default.nix

This file was deleted.

0 comments on commit 44cbcb8

Please sign in to comment.