nixos/tailscale-auth: init module

This additional module allows the tailscale auth proxy to be configured
independently of nginx. The tailscale auth proxy works with both caddy
and traefik. All prior nginx/tailscale-auth options are retained as
aliases.
This commit is contained in:
Dan Theriault 2024-03-06 23:15:44 -05:00
parent 68142254d2
commit 3cf6c4ddb3
3 changed files with 125 additions and 80 deletions

View file

@ -1164,6 +1164,7 @@
./services/networking/syncthing-relay.nix
./services/networking/syncthing.nix
./services/networking/tailscale.nix
./services/networking/tailscale-auth.nix
./services/networking/tayga.nix
./services/networking/tcpcrypt.nix
./services/networking/teamspeak3.nix

View file

@ -0,0 +1,104 @@
{ config, lib, pkgs, ... }:
let
inherit (lib)
getExe
maintainers
mkEnableOption
mkPackageOption
mkIf
mkOption
types
;
cfg = config.services.tailscaleAuth;
in
{
options.services.tailscaleAuth = {
enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate users via tailscale.";
package = mkPackageOption pkgs "tailscale-nginx-auth" {};
user = mkOption {
type = types.str;
default = "tailscale-nginx-auth";
description = "User which runs tailscale-nginx-auth";
};
group = mkOption {
type = types.str;
default = "tailscale-nginx-auth";
description = "Group which runs tailscale-nginx-auth";
};
socketPath = mkOption {
default = "/run/tailscale-nginx-auth/tailscale-nginx-auth.sock";
type = types.path;
description = ''
Path of the socket listening to authorization requests.
'';
};
};
config = mkIf cfg.enable {
services.tailscale.enable = true;
users.users.${cfg.user} = {
isSystemUser = true;
inherit (cfg) group;
};
users.groups.${cfg.group} = { };
systemd.sockets.tailscale-nginx-auth = {
description = "Tailscale NGINX Authentication socket";
partOf = [ "tailscale-nginx-auth.service" ];
wantedBy = [ "sockets.target" ];
listenStreams = [ cfg.socketPath ];
socketConfig = {
SocketMode = "0660";
SocketUser = cfg.user;
SocketGroup = cfg.group;
};
};
systemd.services.tailscale-nginx-auth = {
description = "Tailscale NGINX Authentication service";
requires = [ "tailscale-nginx-auth.socket" ];
serviceConfig = {
ExecStart = getExe cfg.package;
RuntimeDirectory = "tailscale-nginx-auth";
User = cfg.user;
Group = cfg.group;
BindPaths = [ "/run/tailscale/tailscaled.sock" ];
CapabilityBoundingSet = "";
DeviceAllow = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
RestrictNamespaces = true;
RestrictAddressFamilies = [ "AF_UNIX" ];
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = [
"@system-service"
"~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid"
];
};
};
};
meta.maintainers = with maintainers; [ dan-theriault phaer ];
}

View file

@ -1,28 +1,29 @@
{ config, lib, pkgs, ... }:
with lib;
let
inherit (lib)
genAttrs
maintainers
mkAliasOptionModule
mkEnableOption
mkIf
mkOption
types
;
cfg = config.services.nginx.tailscaleAuth;
cfgAuth = config.services.tailscaleAuth;
in
{
imports = [
(mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "package" ] [ "services" "tailscaleAuth" "package" ])
(mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "user" ] [ "services" "tailscaleAuth" "user" ])
(mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "group" ] [ "services" "tailscaleAuth" "group" ])
(mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "socketPath" ] [ "services" "tailscaleAuth" "socketPath" ])
];
options.services.nginx.tailscaleAuth = {
enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate nginx users via tailscale.";
package = lib.mkPackageOptionMD pkgs "tailscale-nginx-auth" {};
user = mkOption {
type = types.str;
default = "tailscale-nginx-auth";
description = "User which runs tailscale-nginx-auth";
};
group = mkOption {
type = types.str;
default = "tailscale-nginx-auth";
description = "Group which runs tailscale-nginx-auth";
};
expectedTailnet = mkOption {
default = "";
type = types.nullOr types.str;
@ -33,14 +34,6 @@ in
'';
};
socketPath = mkOption {
default = "/run/tailscale-nginx-auth/tailscale-nginx-auth.sock";
type = types.path;
description = ''
Path of the socket listening to nginx authorization requests.
'';
};
virtualHosts = mkOption {
type = types.listOf types.str;
default = [];
@ -51,67 +44,14 @@ in
};
config = mkIf cfg.enable {
services.tailscale.enable = true;
services.tailscaleAuth.enable = true;
services.nginx.enable = true;
users.users.${cfg.user} = {
isSystemUser = true;
inherit (cfg) group;
};
users.groups.${cfg.group} = { };
users.users.${config.services.nginx.user}.extraGroups = [ cfg.group ];
systemd.sockets.tailscale-nginx-auth = {
description = "Tailscale NGINX Authentication socket";
partOf = [ "tailscale-nginx-auth.service" ];
wantedBy = [ "sockets.target" ];
listenStreams = [ cfg.socketPath ];
socketConfig = {
SocketMode = "0660";
SocketUser = cfg.user;
SocketGroup = cfg.group;
};
};
users.users.${config.services.nginx.user}.extraGroups = [ cfgAuth.group ];
systemd.services.tailscale-nginx-auth = {
description = "Tailscale NGINX Authentication service";
after = [ "nginx.service" ];
wants = [ "nginx.service" ];
requires = [ "tailscale-nginx-auth.socket" ];
serviceConfig = {
ExecStart = "${lib.getExe cfg.package}";
RuntimeDirectory = "tailscale-nginx-auth";
User = cfg.user;
Group = cfg.group;
BindPaths = [ "/run/tailscale/tailscaled.sock" ];
CapabilityBoundingSet = "";
DeviceAllow = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
RestrictNamespaces = true;
RestrictAddressFamilies = [ "AF_UNIX" ];
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = [
"@system-service"
"~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid"
];
};
};
services.nginx.virtualHosts = genAttrs
@ -121,7 +61,7 @@ in
extraConfig = ''
internal;
proxy_pass http://unix:${cfg.socketPath};
proxy_pass http://unix:${cfgAuth.socketPath};
proxy_pass_request_body off;
# Upstream uses $http_host here, but we are using gixy to check nginx configurations