nixos/middleman: Add nginx-sso
This commit is contained in:
parent
7e42647288
commit
0b8b6fccc8
@ -45,12 +45,60 @@
|
|||||||
owner = "acme";
|
owner = "acme";
|
||||||
group = "acme";
|
group = "acme";
|
||||||
};
|
};
|
||||||
|
"nginx-sso.yaml" = {
|
||||||
|
owner = "nginx-sso";
|
||||||
|
group = "nginx-sso";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
firewall = {
|
firewall = {
|
||||||
tcp.allowed = [ "http" "https" 8448 ];
|
tcp.allowed = [ "http" "https" 8448 ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nginx-sso = {
|
||||||
|
enable = true;
|
||||||
|
extraConfigFile = config.age.secrets."nginx-sso.yaml".path;
|
||||||
|
configuration = {
|
||||||
|
listen = {
|
||||||
|
addr = "[::]";
|
||||||
|
port = 8082;
|
||||||
|
};
|
||||||
|
login = {
|
||||||
|
title = "${lib.my.pubDomain} login";
|
||||||
|
default_redirect = "https://${lib.my.pubDomain}";
|
||||||
|
default_method = "google_oauth";
|
||||||
|
names = {
|
||||||
|
google_oauth = "Google account";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
cookie = {
|
||||||
|
domain = ".${lib.my.pubDomain}";
|
||||||
|
secure = true;
|
||||||
|
};
|
||||||
|
audit_log = {
|
||||||
|
targets = [ "fd://stdout" ];
|
||||||
|
events = [
|
||||||
|
"access_denied"
|
||||||
|
"login_success"
|
||||||
|
"login_failure"
|
||||||
|
"logout"
|
||||||
|
#"validate"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
providers = {
|
||||||
|
google_oauth = {
|
||||||
|
client_id = "545475967061-cag4g1qf0pk33g3pdbom4v69562vboc8.apps.googleusercontent.com";
|
||||||
|
redirect_url = "https://sso.${lib.my.pubDomain}/login";
|
||||||
|
user_id_method = "user-id";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
includes = {
|
||||||
|
endpoint = "http://localhost:8082";
|
||||||
|
baseURL = "https://sso.${lib.my.pubDomain}";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
@ -167,7 +215,9 @@
|
|||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
# proxy headers
|
# proxy headers
|
||||||
|
proxy_set_header X-Origin-URI $request_uri;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Host $http_host;
|
||||||
proxy_set_header X-Forwarded-Host $http_host;
|
proxy_set_header X-Forwarded-Host $http_host;
|
||||||
proxy_set_header X-Forwarded-Server $host;
|
proxy_set_header X-Forwarded-Server $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
@ -6,6 +6,17 @@ let
|
|||||||
dualStackListen' = l: map (addr: l // { inherit addr; }) [ "0.0.0.0" "[::]" ];
|
dualStackListen' = l: map (addr: l // { inherit addr; }) [ "0.0.0.0" "[::]" ];
|
||||||
dualStackListen = ll: flatten (map dualStackListen' ll);
|
dualStackListen = ll: flatten (map dualStackListen' ll);
|
||||||
|
|
||||||
|
ssoServer = i: {
|
||||||
|
extraConfig = ''
|
||||||
|
include /etc/nginx/includes/sso/server-${i}.conf;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
ssoLoc = i: {
|
||||||
|
extraConfig = ''
|
||||||
|
include /etc/nginx/includes/sso/location-${i}.conf;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
mkWellKnown = type: content: pkgs.writeTextFile {
|
mkWellKnown = type: content: pkgs.writeTextFile {
|
||||||
name = "well-known-${type}";
|
name = "well-known-${type}";
|
||||||
destination = "/${type}";
|
destination = "/${type}";
|
||||||
@ -34,6 +45,12 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
my = {
|
||||||
|
nginx-sso.includes.instances = {
|
||||||
|
generic = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts =
|
services.nginx.virtualHosts =
|
||||||
let
|
let
|
||||||
hosts = {
|
hosts = {
|
||||||
@ -47,7 +64,12 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
"pass.nul.ie" =
|
"sso.${lib.my.pubDomain}" = {
|
||||||
|
locations."/".proxyPass = config.my.nginx-sso.includes.endpoint;
|
||||||
|
useACMEHost = lib.my.pubDomain;
|
||||||
|
};
|
||||||
|
|
||||||
|
"pass.${lib.my.pubDomain}" =
|
||||||
let
|
let
|
||||||
upstream = "http://vaultwarden-ctr.${config.networking.domain}";
|
upstream = "http://vaultwarden-ctr.${config.networking.domain}";
|
||||||
in
|
in
|
||||||
@ -79,14 +101,14 @@ in
|
|||||||
locations = mkMerge [
|
locations = mkMerge [
|
||||||
{
|
{
|
||||||
"/".proxyPass = "http://chatterbox-ctr.${config.networking.domain}:8008";
|
"/".proxyPass = "http://chatterbox-ctr.${config.networking.domain}:8008";
|
||||||
"= /".return = "301 https://element.nul.ie";
|
"= /".return = "301 https://element.${lib.my.pubDomain}";
|
||||||
}
|
}
|
||||||
wellKnown
|
wellKnown
|
||||||
];
|
];
|
||||||
useACMEHost = lib.my.pubDomain;
|
useACMEHost = lib.my.pubDomain;
|
||||||
};
|
};
|
||||||
|
|
||||||
"element.nul.ie" =
|
"element.${lib.my.pubDomain}" =
|
||||||
let
|
let
|
||||||
headers = ''
|
headers = ''
|
||||||
add_header X-Frame-Options SAMEORIGIN;
|
add_header X-Frame-Options SAMEORIGIN;
|
||||||
|
@ -13,5 +13,6 @@
|
|||||||
vms = ./vms.nix;
|
vms = ./vms.nix;
|
||||||
network = ./network.nix;
|
network = ./network.nix;
|
||||||
pdns = ./pdns.nix;
|
pdns = ./pdns.nix;
|
||||||
|
nginx-sso = ./nginx-sso.nix;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ in
|
|||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
{
|
{
|
||||||
networking = {
|
networking = {
|
||||||
domain = mkDefault "int.nul.ie";
|
domain = mkDefault "int.${lib.my.pubDomain}";
|
||||||
useDHCP = false;
|
useDHCP = false;
|
||||||
enableIPv6 = mkDefault true;
|
enableIPv6 = mkDefault true;
|
||||||
useNetworkd = mkDefault true;
|
useNetworkd = mkDefault true;
|
||||||
|
127
nixos/modules/nginx-sso.nix
Normal file
127
nixos/modules/nginx-sso.nix
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
{ lib, pkgs, config, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkIf mkMerge getBin mapAttrsToList;
|
||||||
|
inherit (lib.my) mkOpt' mkBoolOpt';
|
||||||
|
|
||||||
|
includeOpts = { ... }: {
|
||||||
|
options = with lib.types; {
|
||||||
|
auth = {
|
||||||
|
path = mkOpt' str "/sso-auth" "HTTP path for SSO auth.";
|
||||||
|
redirect = mkOpt' str "$scheme://$http_host$request_uri" "URL to redirect to upon successful login.";
|
||||||
|
};
|
||||||
|
logout = {
|
||||||
|
path = mkOpt' str "/sso-logout" "HTTP path for SSO logout.";
|
||||||
|
redirect = mkOpt' str "$scheme://$http_host/" "URL to redirect to upon successful logout.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
cfg = config.my.nginx-sso;
|
||||||
|
|
||||||
|
pkg = getBin cfg.package;
|
||||||
|
baseConfig = pkgs.writeText "nginx-sso.yaml" (builtins.toJSON cfg.configuration);
|
||||||
|
runCfg = "/run/nginx-sso/config.yaml";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.my.nginx-sso = with lib.types; {
|
||||||
|
enable = mkBoolOpt' true "Whether to enable custom nginx-sso.";
|
||||||
|
package = mkOpt' package pkgs.nginx-sso "nginx-sso package to use.";
|
||||||
|
configuration = mkOpt' (attrsOf unspecified) { } "nginx-sso configuration.";
|
||||||
|
extraConfigFile = mkOpt' (nullOr str) null "Path to configuration (e.g. for secrets).";
|
||||||
|
|
||||||
|
includes = {
|
||||||
|
endpoint = mkOpt' str "http://localhost:8082" "Upstream for proxied auth requests.";
|
||||||
|
baseURL = mkOpt' str null "Base URL for redirects.";
|
||||||
|
|
||||||
|
instances = mkOpt' (attrsOf (submodule includeOpts)) { } "nginx includes instances.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !config.services.nginx.sso.enable;
|
||||||
|
message = "Stock nginx-sso cannot be used with this module.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.etc = with cfg.includes; mkMerge (mapAttrsToList (n: i: {
|
||||||
|
"nginx/includes/sso/server-${n}.conf".text = ''
|
||||||
|
location ${i.auth.path} {
|
||||||
|
# Do not allow requests from outside
|
||||||
|
internal;
|
||||||
|
|
||||||
|
# Access /auth endpoint to query login state
|
||||||
|
proxy_pass ${endpoint}/auth;
|
||||||
|
|
||||||
|
# Do not forward the request body (nginx-sso does not care about it)
|
||||||
|
proxy_pass_request_body off;
|
||||||
|
proxy_set_header Content-Length "";
|
||||||
|
|
||||||
|
# Set custom information for ACL matching: Each one is available as
|
||||||
|
# a field for matching: X-Host = x-host, ...
|
||||||
|
proxy_set_header X-Origin-URI $request_uri;
|
||||||
|
proxy_set_header X-Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define where to send the user to login and specify how to get back
|
||||||
|
location @error401 {
|
||||||
|
return 302 ${baseURL}/login?go=${i.auth.redirect};
|
||||||
|
}
|
||||||
|
|
||||||
|
# If the user is lead to /logout redirect them to the logout endpoint
|
||||||
|
# of ngninx-sso which then will redirect the user to / on the current host
|
||||||
|
location ${i.logout.path} {
|
||||||
|
return 302 ${baseURL}/logout?go=${i.logout.redirect};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
"nginx/includes/sso/location-${n}.conf".text = ''
|
||||||
|
# Protect this location using the auth_request
|
||||||
|
auth_request ${i.auth.path};
|
||||||
|
|
||||||
|
# Redirect the user to the login page when they are not logged in
|
||||||
|
error_page 401 = @error401;
|
||||||
|
|
||||||
|
# Automatically renew SSO cookie on request
|
||||||
|
auth_request_set $cookie $upstream_http_set_cookie;
|
||||||
|
add_header Set-Cookie $cookie;
|
||||||
|
'';
|
||||||
|
}) instances);
|
||||||
|
|
||||||
|
users = {
|
||||||
|
groups.nginx-sso = {};
|
||||||
|
users.nginx-sso = {
|
||||||
|
group = "nginx-sso";
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.nginx-sso = {
|
||||||
|
description = "Nginx SSO Backend";
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
umask 066
|
||||||
|
${if (cfg.extraConfigFile != null) then ''
|
||||||
|
${pkgs.yq-go}/bin/yq -P '. *= load("${cfg.extraConfigFile}")' "${baseConfig}" > ${runCfg}
|
||||||
|
'' else ''
|
||||||
|
${pkgs.yq-go}/bin/yq -P "${baseConfig}" > ${runCfg}
|
||||||
|
''}
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
RuntimeDirectory = "nginx-sso";
|
||||||
|
User = "nginx-sso";
|
||||||
|
Group = "nginx-sso";
|
||||||
|
ExecStart = [
|
||||||
|
# Specify twice to clear original value
|
||||||
|
""
|
||||||
|
''${pkg}/bin/nginx-sso --frontend-dir ${pkg}/share/frontend --config ${runCfg}''
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
BIN
secrets/nginx-sso.yaml.age
Normal file
BIN
secrets/nginx-sso.yaml.age
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user