230 lines
6.3 KiB
Nix
230 lines
6.3 KiB
Nix
|
{ lib, config, ... }:
|
||
|
with lib;
|
||
|
let
|
||
|
vm-module = {
|
||
|
options = {
|
||
|
ip = mkOption { type = types.str; };
|
||
|
ssh = mkOption {
|
||
|
type = types.nullOr types.ints.unsigned;
|
||
|
default = null;
|
||
|
};
|
||
|
aliases = mkOption {
|
||
|
type = types.listOf types.str;
|
||
|
default = [ ];
|
||
|
};
|
||
|
port-forward = mkOption {
|
||
|
type = types.listOf types.ints.unsigned;
|
||
|
default = [ ];
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
hypervisor-module =
|
||
|
{ config, name, ... }:
|
||
|
{
|
||
|
options = {
|
||
|
ip = mkOption { type = types.str; };
|
||
|
host = mkOption { type = types.str; };
|
||
|
vms = mkOption { type = types.attrsOf (types.submodule vm-module); };
|
||
|
|
||
|
domain-list = mkOption {
|
||
|
type = types.listOf types.str;
|
||
|
internal = true;
|
||
|
readOnly = true;
|
||
|
};
|
||
|
ports = mkOption {
|
||
|
type = types.listOf types.ints.unsigned;
|
||
|
internal = true;
|
||
|
readOnly = true;
|
||
|
};
|
||
|
redirects = mkOption {
|
||
|
type = types.unspecified;
|
||
|
internal = true;
|
||
|
readOnly = true;
|
||
|
};
|
||
|
};
|
||
|
config = rec {
|
||
|
domain-list = flatten (
|
||
|
mapAttrsToList (main: vm: [
|
||
|
main
|
||
|
vm.aliases
|
||
|
]) config.vms
|
||
|
);
|
||
|
ports = flatten (
|
||
|
mapAttrsToList (_: vm: vm.port-forward ++ optional (!isNull vm.ssh) vm.ssh) config.vms
|
||
|
);
|
||
|
redirects = {
|
||
|
stream = flatten (
|
||
|
mapAttrsToList (
|
||
|
_: vm:
|
||
|
optional (!isNull vm.ssh) {
|
||
|
input = vm.ssh;
|
||
|
out = 22;
|
||
|
ip = vm.ip;
|
||
|
}
|
||
|
++ map (port: {
|
||
|
input = port;
|
||
|
out = port;
|
||
|
ip = vm.ip;
|
||
|
}) vm.port-forward
|
||
|
) config.vms
|
||
|
);
|
||
|
http = mapAttrs (_: vm: { inherit (vm) ip aliases; }) config.vms;
|
||
|
domain-list = domain-list;
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
entry-module =
|
||
|
{ config, name, ... }:
|
||
|
{
|
||
|
options = {
|
||
|
host = mkOption { type = types.str; };
|
||
|
hypervisors = mkOption { type = types.attrsOf (types.submodule hypervisor-module); };
|
||
|
|
||
|
redirects = mkOption {
|
||
|
type = types.unspecified;
|
||
|
internal = true;
|
||
|
readOnly = true;
|
||
|
};
|
||
|
hosts-redirects = mkOption {
|
||
|
type = types.unspecified;
|
||
|
internal = true;
|
||
|
readOnly = true;
|
||
|
};
|
||
|
};
|
||
|
config = rec {
|
||
|
redirects = {
|
||
|
stream = flatten (
|
||
|
mapAttrsToList (
|
||
|
_: hyp:
|
||
|
map (port: {
|
||
|
input = port;
|
||
|
out = port;
|
||
|
ip = hyp.ip;
|
||
|
}) hyp.ports
|
||
|
) config.hypervisors
|
||
|
);
|
||
|
http = mapAttrs (_: hyp: {
|
||
|
ip = hyp.ip;
|
||
|
aliases = hyp.domain-list;
|
||
|
}) config.hypervisors;
|
||
|
domain-list = flatten (
|
||
|
mapAttrsToList (fqdn: hyp: [ fqdn ] ++ hyp.redirects.domain-list) config.hypervisors
|
||
|
);
|
||
|
};
|
||
|
hosts-redirects =
|
||
|
mergeAttrs
|
||
|
(listToAttrs (
|
||
|
mapAttrsToList (main: hyp: {
|
||
|
name = hyp.host;
|
||
|
value = {
|
||
|
fqdn = main;
|
||
|
inherit (hyp.redirects) stream http domain-list;
|
||
|
};
|
||
|
}) config.hypervisors
|
||
|
))
|
||
|
{
|
||
|
${config.host} = {
|
||
|
fqdn = name;
|
||
|
inherit (redirects) stream http domain-list;
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
|
||
|
cfg = config.kat-proxies;
|
||
|
|
||
|
hosts-redirects = zipAttrsWith (_: vals: mergeAttrsList vals) (
|
||
|
mapAttrsToList (_: entry: entry.hosts-redirects) cfg.entries
|
||
|
);
|
||
|
hostname = config.networking.hostName;
|
||
|
redirects = hosts-redirects.${hostname};
|
||
|
in
|
||
|
{
|
||
|
options.kat-proxies = {
|
||
|
enable = mkEnableOption "nginx configuration of proxies";
|
||
|
entries = mkOption { type = types.attrsOf (types.submodule entry-module); };
|
||
|
internal-webroot = mkOption { type = types.package; };
|
||
|
};
|
||
|
|
||
|
config = mkIf cfg.enable {
|
||
|
security.acme.certs.${redirects.fqdn}.extraDomainNames = redirects.domain-list;
|
||
|
networking.firewall.allowedTCPPorts = [
|
||
|
80
|
||
|
443
|
||
|
] ++ map ({ input, ... }: input) redirects.stream;
|
||
|
services.nginx = {
|
||
|
enable = true;
|
||
|
virtualHosts =
|
||
|
mapAttrs (
|
||
|
_:
|
||
|
{ aliases, ip }:
|
||
|
{
|
||
|
useACMEHost = redirects.fqdn;
|
||
|
forceSSL = true;
|
||
|
acmeFallbackHost = ip;
|
||
|
acmeFallbackRecommendedProxySettings = true;
|
||
|
serverAliases = aliases;
|
||
|
locations = {
|
||
|
"/.${hostname}" = {
|
||
|
extraConfig = ''
|
||
|
internal;
|
||
|
error_page 404 =418 /.${hostname}/error/418.html;
|
||
|
'';
|
||
|
root = cfg.internal-webroot;
|
||
|
};
|
||
|
"/" = {
|
||
|
recommendedProxySettings = true;
|
||
|
proxyPass = "https://${ip}/";
|
||
|
extraConfig = ''
|
||
|
proxy_set_header Connection ''';
|
||
|
proxy_http_version 1.1;
|
||
|
chunked_transfer_encoding off;
|
||
|
proxy_buffering off;
|
||
|
proxy_cache off;
|
||
|
error_page 502 =599 "/.${hostname}/error/599.html";
|
||
|
'';
|
||
|
};
|
||
|
};
|
||
|
}
|
||
|
) redirects.http
|
||
|
// {
|
||
|
${redirects.fqdn} = {
|
||
|
default = true;
|
||
|
enableACME = true;
|
||
|
addSSL = true;
|
||
|
locations = {
|
||
|
"/.${hostname}" = {
|
||
|
extraConfig = ''
|
||
|
internal;
|
||
|
error_page 404 =418 /.${hostname}/error/418.html;
|
||
|
'';
|
||
|
root = cfg.internal-webroot;
|
||
|
};
|
||
|
"/" = {
|
||
|
extraConfig = ''
|
||
|
return 418;
|
||
|
error_page 418 =418 /.${hostname}/error/418.html;
|
||
|
'';
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
streamConfig = concatStringsSep "\n" (
|
||
|
map (
|
||
|
{
|
||
|
input,
|
||
|
ip,
|
||
|
out,
|
||
|
}:
|
||
|
''
|
||
|
server {
|
||
|
listen ${toString input};
|
||
|
proxy_pass ${ip}:${toString out};
|
||
|
}
|
||
|
''
|
||
|
) redirects.stream
|
||
|
);
|
||
|
};
|
||
|
};
|
||
|
}
|