mirror of
https://github.com/gensokyo-zone/infrastructure.git
synced 2026-02-09 20:39:18 -08:00
refactor: get rid of config folder
This commit is contained in:
parent
2606e1d874
commit
cb3ae5f434
254 changed files with 79 additions and 101 deletions
58
modules/darwin/touchid.nix
Normal file
58
modules/darwin/touchid.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.security.pam;
|
||||
|
||||
# Implementation Notes
|
||||
#
|
||||
# We don't use `environment.etc` because this would require that the user manually delete
|
||||
# `/etc/pam.d/sudo` which seems unwise given that applying the nix-darwin configuration requires
|
||||
# sudo. We also can't use `system.patchs` since it only runs once, and so won't patch in the
|
||||
# changes again after OS updates (which remove modifications to this file).
|
||||
#
|
||||
# As such, we resort to line addition/deletion in place using `sed`. We add a comment to the
|
||||
# added line that includes the name of the option, to make it easier to identify the line that
|
||||
# should be deleted when the option is disabled.
|
||||
mkSudoTouchIdAuthScript = isEnabled:
|
||||
let
|
||||
file = "/etc/pam.d/sudo";
|
||||
option = "security.pam.enableSudoTouchIdAuth";
|
||||
in ''
|
||||
${if isEnabled then ''
|
||||
# Enable sudo Touch ID authentication, if not already enabled
|
||||
if ! grep 'pam_tid.so' ${file} > /dev/null; then
|
||||
sed -i "" '2i\
|
||||
auth sufficient pam_tid.so # nix-darwin: ${option}
|
||||
' ${file}
|
||||
fi
|
||||
'' else ''
|
||||
# Disable sudo Touch ID authentication, if added by nix-darwin
|
||||
if grep '${option}' ${file} > /dev/null; then
|
||||
sed -i "" '/${option}/d' ${file}
|
||||
fi
|
||||
''}
|
||||
'';
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
security.pam.enableSudoTouchIdAuth = mkEnableOption ''
|
||||
Enable sudo authentication with Touch ID
|
||||
When enabled, this option adds the following line to /etc/pam.d/sudo:
|
||||
auth sufficient pam_tid.so
|
||||
(Note that macOS resets this file when doing a system update. As such, sudo
|
||||
authentication with Touch ID won't work after a system update until the nix-darwin
|
||||
configuration is reapplied.)
|
||||
'';
|
||||
};
|
||||
|
||||
config = {
|
||||
system.activationScripts.extraActivation.text = ''
|
||||
# PAM settings
|
||||
echo >&2 "setting up pam..."
|
||||
${mkSudoTouchIdAuthScript cfg.enableSudoTouchIdAuth}
|
||||
'';
|
||||
};
|
||||
}
|
||||
39
modules/home/deploy.nix
Normal file
39
modules/home/deploy.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
/*
|
||||
This module:
|
||||
* Provides in-scope TF config for home-manager.
|
||||
*/
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.deploy.tf;
|
||||
unmergedValues = types.mkOptionType {
|
||||
name = "unmergedValues";
|
||||
merge = loc: defs: map (def: def.value) defs;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
options.deploy.tf = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = types.attrsOf unmergedValues;
|
||||
|
||||
options = {
|
||||
attrs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
out.set = mkOption { type = types.unspecified; };
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
config = {
|
||||
deploy.tf = {
|
||||
attrs = [ "out" "attrs" ];
|
||||
out.set = removeAttrs cfg cfg.attrs;
|
||||
};
|
||||
};
|
||||
}
|
||||
4
modules/home/disables.nix
Normal file
4
modules/home/disables.nix
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{ ... }: {
|
||||
disabledModules = [
|
||||
];
|
||||
}
|
||||
22
modules/home/displays.nix
Normal file
22
modules/home/displays.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{ config, lib, nixos, ... }: with lib; {
|
||||
options.hardware.displays = mkOption {
|
||||
type = with types; attrsOf (submodule ({ config, ... }: {
|
||||
options = {
|
||||
pos = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
res = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
config = mkMerge [
|
||||
{
|
||||
hardware.displays = nixos.hardware.displays;
|
||||
}
|
||||
(mkIf config.wayland.windowManager.sway.enable {
|
||||
wayland.windowManager.sway.config.output = config.hardware.displays;
|
||||
})
|
||||
];
|
||||
}
|
||||
54
modules/home/firewall.nix
Normal file
54
modules/home/firewall.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let cfg = config.network.firewall;
|
||||
in
|
||||
{
|
||||
options.network.firewall = {
|
||||
public.tcp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
public.udp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
private.tcp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
private.udp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
public.tcp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
public.udp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
private.tcp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
private.udp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
public.interfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "Public firewall interfaces";
|
||||
default = [ ];
|
||||
};
|
||||
private.interfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "Private firewall interfaces";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
}
|
||||
77
modules/home/network.nix
Normal file
77
modules/home/network.nix
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
{ config, nixos, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
options.network = {
|
||||
enable = mkEnableOption "Use kat's network module?";
|
||||
addresses = mkOption {
|
||||
type = with types; attrsOf (submodule ({ name, ... }: {
|
||||
options = {
|
||||
enable = mkEnableOption "Is the system a part of the ${name} network?";
|
||||
ipv4 = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
ipv6 = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
prefix = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
out = {
|
||||
identifierList = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = if config.enable then singleton config.domain ++ config.out.addressList else [ ];
|
||||
};
|
||||
addressList = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = if config.enable then concatMap (i: optional i.enable i.address) [ config.ipv4 config.ipv6 ] else [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
tf = {
|
||||
enable = mkEnableOption "Was the system provisioned by terraform?";
|
||||
ipv4_attr = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
ipv6_attr = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
dns = {
|
||||
enable = mkEnableOption "Do you want DNS to be semi-managed through this module?";
|
||||
isRoot = mkEnableOption "Is this system supposed to be the @ for the domain?";
|
||||
email = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
zone = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
dynamic = mkEnableOption "Enable Glauca Dynamic DNS Updater";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
network.addresses = nixos.network.addresses or {};
|
||||
network.tf = nixos.network.tf or {};
|
||||
network.dns = nixos.network.dns or {};
|
||||
};
|
||||
}
|
||||
43
modules/home/secrets.nix
Normal file
43
modules/home/secrets.nix
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{ config, nixos, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
secretType = types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
path = mkOption { type = types.str; };
|
||||
field = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
});
|
||||
repoSecretType = types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
source = mkOption {
|
||||
type = types.path;
|
||||
};
|
||||
text = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
});
|
||||
cfg = config.kw.secrets;
|
||||
in
|
||||
{
|
||||
options.kw = {
|
||||
secrets = {
|
||||
variables = mkOption {
|
||||
type = types.attrsOf secretType;
|
||||
default = { };
|
||||
};
|
||||
repo = mkOption {
|
||||
type = types.attrsOf repoSecretType;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
config = {
|
||||
kw.secrets.repo = nixos.kw.secrets.repo;
|
||||
};
|
||||
}
|
||||
158
modules/home/theme.nix
Normal file
158
modules/home/theme.nix
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
/*
|
||||
This module:
|
||||
* provides a central way to change the font my system uses.
|
||||
*/
|
||||
|
||||
with lib;
|
||||
|
||||
let cfg = config.kw.theme; in
|
||||
{
|
||||
options.kw.theme = {
|
||||
enable = mkEnableOption "kat's theme module";
|
||||
sass = {
|
||||
variables = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = (cfg.base16 // cfg.base16t // {
|
||||
term_font = cfg.font.termName;
|
||||
font = cfg.font.name;
|
||||
font_size = cfg.font.size_css;
|
||||
});
|
||||
};
|
||||
css_style = mkOption {
|
||||
type = types.enum [ "nested" "compressed" "compact" "expanded" ];
|
||||
default = "expanded";
|
||||
};
|
||||
};
|
||||
swaylock = mkEnableOption "use swaylock module";
|
||||
base16 = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
};
|
||||
base16t = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
};
|
||||
alpha = mkOption {
|
||||
type = types.float;
|
||||
};
|
||||
font = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "Iosevka SS10";
|
||||
};
|
||||
termName = mkOption {
|
||||
type = types.str;
|
||||
default = "Iosevka Term SS10";
|
||||
};
|
||||
size = mkOption {
|
||||
type = types.float;
|
||||
default = 9.0;
|
||||
};
|
||||
size_css = mkOption {
|
||||
type = types.str;
|
||||
default = "${toString (cfg.font.size + 3)}px";
|
||||
};
|
||||
};
|
||||
};
|
||||
config = mkIf (cfg.enable) {
|
||||
kw.theme = {
|
||||
base16 = lib.mapAttrs' (k: v: lib.nameValuePair k "#${v.hex.rgb}")
|
||||
(lib.filterAttrs (n: _: lib.hasInfix "base" n) config.lib.arc.base16.schemeForAlias.default);
|
||||
base16t = lib.mapAttrs' (k: v: lib.nameValuePair "${k}t" "rgba(${toString v.rgb.r}, ${toString v.rgb.g}, ${toString v.rgb.b}, ${toString cfg.alpha})")
|
||||
(lib.filterAttrs (n: _: lib.hasInfix "base" n) config.lib.arc.base16.schemeForAlias.default);
|
||||
alpha = 0.7;
|
||||
};
|
||||
|
||||
programs.swaylock = mkIf (cfg.swaylock) {
|
||||
enable = true;
|
||||
package = pkgs.swaylock-effects-develop;
|
||||
args = {
|
||||
screenshots = true;
|
||||
daemonize = true;
|
||||
show-failed-attempts = true;
|
||||
indicator = true;
|
||||
indicator-radius = 110;
|
||||
indicator-thickness = 8;
|
||||
font = cfg.font.name;
|
||||
font-size = cfg.font.size_css;
|
||||
clock = true;
|
||||
datestr = "%F";
|
||||
timestr = "%T";
|
||||
effect-blur = "5x2";
|
||||
fade-in = 0.2;
|
||||
};
|
||||
colors = with cfg.base16; {
|
||||
key-hl = base0C;
|
||||
separator = base01;
|
||||
line = base01;
|
||||
line-clear = base01;
|
||||
line-caps-lock = base01;
|
||||
line-ver = base01;
|
||||
line-wrong = base01;
|
||||
ring = base00;
|
||||
ring-clear = base0B;
|
||||
ring-caps-lock = base09;
|
||||
ring-ver = base0D;
|
||||
ring-wrong = base08;
|
||||
inside = base00;
|
||||
inside-clear = base00;
|
||||
inside-caps-lock = base00;
|
||||
inside-ver = base00;
|
||||
inside-wrong = base00;
|
||||
text = base05;
|
||||
text-clear = base05;
|
||||
text-caps-lock = base05;
|
||||
text-ver = base05;
|
||||
text-wrong = base05;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.services.swayidle = mkIf (cfg.swaylock) {
|
||||
Unit = {
|
||||
Description = "swayidle";
|
||||
Documentation = [ "man:swayidle(1)" ];
|
||||
PartOf = [ "graphical-session.target" ];
|
||||
};
|
||||
Service = {
|
||||
Type = "simple";
|
||||
ExecStart =
|
||||
let
|
||||
lockCommand = config.programs.swaylock.script;
|
||||
in
|
||||
''
|
||||
${pkgs.swayidle}/bin/swayidle -w \
|
||||
timeout 300 '${lockCommand}' \
|
||||
timeout 600 'swaymsg "output * dpms off"' \
|
||||
resume 'swaymsg "output * dpms on"' \
|
||||
before-sleep '${lockCommand}'
|
||||
'';
|
||||
RestartSec = 3;
|
||||
Restart = "always";
|
||||
};
|
||||
Install = { WantedBy = [ "sway-session.target" ]; };
|
||||
};
|
||||
|
||||
lib.kw.sassTemplate = { name, src }:
|
||||
let
|
||||
variables = pkgs.writeText "base-variables.sass" ''
|
||||
${(concatStringsSep "\n" (mapAttrsToList(var: con: "\$${var}: ${con}") cfg.sass.variables))}
|
||||
'';
|
||||
source = pkgs.callPackage
|
||||
({ sass, stdenv }: stdenv.mkDerivation {
|
||||
inherit name src variables;
|
||||
nativeBuildInputs = lib.singleton sass;
|
||||
phases = [ "buildPhase" ];
|
||||
buildPhase = ''
|
||||
cat $variables $src > src-mut.sass
|
||||
sass src-mut.sass $out --sourcemap=none --style=${cfg.sass.css_style}
|
||||
'';
|
||||
})
|
||||
{ };
|
||||
in
|
||||
{
|
||||
inherit source;
|
||||
text = builtins.readFile source;
|
||||
};
|
||||
_module.args = { inherit (config.lib) kw; };
|
||||
};
|
||||
}
|
||||
170
modules/meta/deploy.nix
Normal file
170
modules/meta/deploy.nix
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
{ inputs, config, pkgs, lib, ... }:
|
||||
|
||||
/*
|
||||
This module:
|
||||
* makes tf-nix a part of the meta config
|
||||
* handles the trusted import for tf-nix
|
||||
* provides the target interface
|
||||
* imports the per-host TF config for each target
|
||||
*/
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.deploy;
|
||||
meta = config;
|
||||
tfModule = { lib, ... }: with lib; {
|
||||
config._module.args = {
|
||||
pkgs = mkDefault pkgs;
|
||||
};
|
||||
};
|
||||
tfType = types.submoduleWith {
|
||||
modules = [
|
||||
tfModule
|
||||
"${toString inputs.tf-nix}/modules"
|
||||
];
|
||||
specialArgs = {
|
||||
meta = config;
|
||||
};
|
||||
shorthandOnlyDefinesConfig = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
"${toString inputs.tf-nix}/modules/run.nix"
|
||||
] ++ (optional (builtins.pathExists ../../tf/tf.nix) (../../tf/tf.nix));
|
||||
options = {
|
||||
deploy = {
|
||||
dataDir = mkOption {
|
||||
type = types.path;
|
||||
default = ../../tf;
|
||||
};
|
||||
local = {
|
||||
isRoot = mkOption {
|
||||
type = types.bool;
|
||||
default = builtins.getEnv "HOME_UID" == "0";
|
||||
};
|
||||
hostName = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default =
|
||||
let
|
||||
hostName = builtins.getEnv "HOME_HOSTNAME";
|
||||
in
|
||||
if hostName == "" then null else hostName;
|
||||
};
|
||||
};
|
||||
targets =
|
||||
let
|
||||
type = types.submodule ({ config, name, ... }: {
|
||||
options = {
|
||||
enable = mkEnableOption "Enable the target" // { default = true; };
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
};
|
||||
nodeNames = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
tf = mkOption {
|
||||
type = tfType;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
config.tf = mkMerge (singleton
|
||||
({ ... }: {
|
||||
imports = [
|
||||
../../tf.nix
|
||||
];
|
||||
deploy.gcroot = {
|
||||
name = mkDefault "kw-${config.name}";
|
||||
user = mkIf (builtins.getEnv "HOME_USER" != "") (mkDefault (builtins.getEnv "HOME_USER"));
|
||||
};
|
||||
providers.local = { };
|
||||
deps = {
|
||||
select.allProviders = true;
|
||||
enable = true;
|
||||
/*
|
||||
apply = {
|
||||
doneCommand = ''
|
||||
git -C "${cfg.dataDir}" add -A
|
||||
git -C "${cfg.dataDir}" commit -m "${config.name}: $(date +'%F %T')"
|
||||
git -C "${cfg.dataDir}" push
|
||||
'';
|
||||
};
|
||||
*/
|
||||
};
|
||||
terraform = {
|
||||
version = "1.0";
|
||||
prettyJson = true;
|
||||
logPath = cfg.dataDir + "/terraform-${config.name}.log";
|
||||
dataDir = cfg.dataDir + "/tfdata/${config.name}";
|
||||
environment.TF_CLI_ARGS_apply = "-backup=-";
|
||||
environment.TF_CLI_ARGS_taint = "-backup=-";
|
||||
};
|
||||
state = {
|
||||
file = cfg.dataDir + "/terraform-${config.name}.tfstate";
|
||||
};
|
||||
runners = {
|
||||
lazy = {
|
||||
inherit (meta.runners.lazy) file args;
|
||||
attrPrefix = "deploy.targets.${name}.tf.runners.run.";
|
||||
};
|
||||
run = {
|
||||
apply.name = "${name}-apply-uw";
|
||||
terraform.name = "${name}-tf";
|
||||
myApply = {
|
||||
name = "${name}-apply";
|
||||
command = let
|
||||
path = toString cfg.dataDir;
|
||||
in ''
|
||||
set -e
|
||||
git -C "${path}" pull
|
||||
${config.tf.runners.run.apply.package}/bin/${config.tf.runners.run.apply.executable}
|
||||
git -C "${path}" add -A
|
||||
git -C "${path}" commit -m "${config.name}: $(date +'%F %T')"
|
||||
git -C "${path}" push --force
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
continue.envVar = "TF_NIX_CONTINUE_${replaceStrings [ "-" ] [ "_" ] config.name}";
|
||||
}) ++ map (nodeName: mapAttrs (_: mkMerge) meta.network.nodes.nixos.${nodeName}.deploy.tf.out.set) config.nodeNames);
|
||||
});
|
||||
in
|
||||
mkOption {
|
||||
type = types.attrsOf type;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
config = {
|
||||
deploy.targets =
|
||||
let
|
||||
nodeNames = attrNames config.network.nodes.nixos;
|
||||
targets = config.deploy.targets;
|
||||
explicitlyDefinedHosts = concatLists (mapAttrsToList (targetName: target: remove targetName target.nodeNames) config.deploy.targets);
|
||||
in
|
||||
genAttrs nodeNames (nodeName: {
|
||||
enable = mkDefault (! elem nodeName explicitlyDefinedHosts);
|
||||
nodeNames = singleton nodeName;
|
||||
});
|
||||
|
||||
runners = {
|
||||
run = mkMerge (mapAttrsToList
|
||||
(targetName: target: mapAttrs'
|
||||
(k: run:
|
||||
nameValuePair run.name run.set
|
||||
)
|
||||
target.tf.runners.run)
|
||||
(filterAttrs (_: v: v.enable) cfg.targets));
|
||||
lazy.run = mkMerge (mapAttrsToList
|
||||
(targetName: target: mapAttrs'
|
||||
(k: run:
|
||||
nameValuePair run.name run.set
|
||||
)
|
||||
target.tf.runners.lazy.run)
|
||||
(filterAttrs (_: v: v.enable) cfg.targets));
|
||||
};
|
||||
};
|
||||
}
|
||||
56
modules/meta/imports.nix
Normal file
56
modules/meta/imports.nix
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
{ config, lib, profiles, root, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
options = {
|
||||
lib = mkOption {
|
||||
type = types.attrsOf (types.attrsOf types.unspecified);
|
||||
};
|
||||
network.importing = {
|
||||
nixosImports = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
darwinImports = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
homeImports = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
users = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
config = {
|
||||
network.importing = {
|
||||
nixosImports = mkDefault (map (path: toString path) [
|
||||
(root + "/nodes/nixos/HN.nix")
|
||||
(root + "/nodes/nixos/HN/nixos.nix")
|
||||
(root + "/trusted/nodes/nixos/HN/nixos.nix")
|
||||
]);
|
||||
darwinImports = mkDefault (map (path: toString path) [
|
||||
(root + "/nodes/darwin/HN.nix")
|
||||
(root + "/nodes/darwin/HN/darwin.nix")
|
||||
(root + "/trusted/nodes/darwin/HN/darwin.nix")
|
||||
]);
|
||||
homeImports = mkDefault (map (path: toString path) [
|
||||
(root + "/nodes/nixos/HN/home.nix")
|
||||
(root + "/nodes/darwin/HN/home.nix")
|
||||
(root + "/trusted/nodes/HN/home.nix")
|
||||
]);
|
||||
users = mkDefault (singleton "kat");
|
||||
};
|
||||
lib.kw.nixosImport = hostName: lib.nodeImport {
|
||||
inherit (config.network.importing) nixosImports homeImports users;
|
||||
inherit profiles hostName;
|
||||
};
|
||||
lib.kw.darwinImport = hostName: lib.nodeImport {
|
||||
nixosImports = config.network.importing.darwinImports;
|
||||
profiles = profiles // { base = {}; };
|
||||
inherit (config.network.importing) homeImports users;
|
||||
inherit hostName;
|
||||
};
|
||||
_module.args = { inherit (config.lib) kw; };
|
||||
};
|
||||
}
|
||||
136
modules/meta/network.nix
Normal file
136
modules/meta/network.nix
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
{ pkgs, inputs, lib, meta, config, ... }:
|
||||
|
||||
/*
|
||||
This module:
|
||||
* Makes hosts nixosModules.
|
||||
* Manages module imports and specialArgs.
|
||||
* Builds network.nodes.
|
||||
*/
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
options.network = {
|
||||
nixos = {
|
||||
extraModules = mkOption {
|
||||
type = types.listOf types.unspecified;
|
||||
default = [ ];
|
||||
};
|
||||
specialArgs = mkOption {
|
||||
type = types.attrsOf types.unspecified;
|
||||
default = { };
|
||||
};
|
||||
modulesPath = mkOption {
|
||||
type = types.path;
|
||||
default = toString (pkgs.path + "/nixos/modules");
|
||||
};
|
||||
};
|
||||
darwin = {
|
||||
extraModules = mkOption {
|
||||
type = types.listOf types.unspecified;
|
||||
default = [ ];
|
||||
};
|
||||
specialArgs = mkOption {
|
||||
type = types.attrsOf types.unspecified;
|
||||
default = { };
|
||||
};
|
||||
modulesPath = mkOption {
|
||||
type = types.path;
|
||||
default = toString (inputs.darwin + "/modules");
|
||||
};
|
||||
};
|
||||
nodes.nixos =
|
||||
let
|
||||
nixosModule = { name, config, meta, modulesPath, lib, ... }: with lib; {
|
||||
options = {
|
||||
nixpkgs.crossOverlays = mkOption {
|
||||
type = types.listOf types.unspecified;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
config = {
|
||||
nixpkgs = {
|
||||
system = mkDefault pkgs.system;
|
||||
pkgs =
|
||||
let
|
||||
pkgsReval = import pkgs.path {
|
||||
inherit (config.nixpkgs) localSystem crossSystem crossOverlays;
|
||||
inherit (pkgs) overlays config;
|
||||
};
|
||||
in
|
||||
mkDefault (if config.nixpkgs.config == pkgs.config && config.nixpkgs.system == pkgs.targetPlatform.system then pkgs else pkgsReval);
|
||||
};
|
||||
};
|
||||
};
|
||||
nixosType =
|
||||
let
|
||||
baseModules = import (config.network.nixos.modulesPath + "/module-list.nix");
|
||||
in
|
||||
types.submoduleWith {
|
||||
modules = baseModules
|
||||
++ singleton nixosModule
|
||||
++ config.network.nixos.extraModules;
|
||||
|
||||
specialArgs = {
|
||||
inherit baseModules;
|
||||
inherit (config.network.nixos) modulesPath;
|
||||
} // config.network.nixos.specialArgs;
|
||||
};
|
||||
in
|
||||
mkOption {
|
||||
type = types.attrsOf nixosType;
|
||||
default = { };
|
||||
};
|
||||
nodes.darwin =
|
||||
let
|
||||
darwinModule = { name, config, meta, modulesPath, lib, ... }: with lib; {
|
||||
config = {
|
||||
_module.args.pkgs = pkgs;
|
||||
nixpkgs = {
|
||||
system = mkDefault pkgs.system;
|
||||
};
|
||||
};
|
||||
};
|
||||
darwinType =
|
||||
let
|
||||
baseModules = import (config.network.darwin.modulesPath + "/module-list.nix");
|
||||
in
|
||||
types.submoduleWith {
|
||||
modules = baseModules
|
||||
++ singleton darwinModule
|
||||
++ config.network.darwin.extraModules;
|
||||
|
||||
specialArgs = {
|
||||
inherit baseModules;
|
||||
inherit (config.network.darwin) modulesPath;
|
||||
} // config.network.darwin.specialArgs;
|
||||
};
|
||||
in
|
||||
mkOption {
|
||||
type = types.attrsOf darwinType;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
config.network = {
|
||||
darwin = {
|
||||
extraModules = [
|
||||
inputs.home-manager.darwinModules.home-manager
|
||||
meta.modules.darwin
|
||||
];
|
||||
specialArgs = {
|
||||
inherit (config.network) nodes;
|
||||
inherit inputs meta;
|
||||
};
|
||||
};
|
||||
nixos = {
|
||||
extraModules = [
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
meta.modules.nixos
|
||||
];
|
||||
specialArgs = {
|
||||
inherit (config.network) nodes;
|
||||
inherit inputs meta;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
5
modules/meta/secrets.nix
Normal file
5
modules/meta/secrets.nix
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{ config, lib, ... }: with lib; {
|
||||
options.kw.secrets.command = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
}
|
||||
176
modules/nixos/bird.nix
Normal file
176
modules/nixos/bird.nix
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
bcfg = config.network.bird;
|
||||
cfg = config.network.bird.ospf;
|
||||
in
|
||||
{
|
||||
options.network.bird = {
|
||||
routerId = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Router ID to use. Must be an IPv4 address.";
|
||||
};
|
||||
kernel4Config = mkOption {
|
||||
type = types.lines;
|
||||
default = ''
|
||||
ipv4 {
|
||||
import none;
|
||||
export filter {
|
||||
if source = RTS_STATIC then reject;
|
||||
accept;
|
||||
};
|
||||
};
|
||||
scan time 15;
|
||||
'';
|
||||
};
|
||||
kernel6Config = mkOption {
|
||||
type = types.lines;
|
||||
default = ''
|
||||
ipv6 {
|
||||
import none;
|
||||
export filter {
|
||||
if source = RTS_STATIC then reject;
|
||||
accept;
|
||||
};
|
||||
};
|
||||
scan time 15;
|
||||
'';
|
||||
};
|
||||
staticRoutes4 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
extraStatic4 = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
};
|
||||
staticRoutes6 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
extraStatic6 = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
options.network.bird.ospf = {
|
||||
enable = mkEnableOption "OSPF-based network routing";
|
||||
protocols = mkOption {
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
version = mkOption { type = types.enum [ 2 3 ]; default = 2; };
|
||||
extra = mkOption { type = types.lines; default = ""; };
|
||||
areas = mkOption {
|
||||
description = "areas to configure in bird";
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
extra = mkOption { type = types.lines; default = ""; };
|
||||
networks = mkOption {
|
||||
description = "Definition of area IP ranges. This is used in summary LSA origination.";
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
external = mkOption {
|
||||
description = "Definition of external area IP ranges for NSSAs. This is used for NSSA-LSA translation.";
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
interfaces = mkOption {
|
||||
description = "Interfaces to assign to the area";
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
cost = mkOption { type = types.int; default = 10; };
|
||||
poll = mkOption { type = types.int; default = 20; };
|
||||
retransmit = mkOption { type = types.int; default = 5; };
|
||||
priority = mkOption { type = types.int; default = 1; };
|
||||
deadCount = mkOption { type = types.int; default = 4; };
|
||||
type = mkOption {
|
||||
type = types.enum [
|
||||
null
|
||||
"broadcast"
|
||||
"bcast"
|
||||
"pointopoint"
|
||||
"ptp"
|
||||
"nonbroadcast"
|
||||
"nbma"
|
||||
"pointomultipoint"
|
||||
"ptmp"
|
||||
];
|
||||
default = null;
|
||||
};
|
||||
extra = mkOption { type = types.lines; default = ""; };
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.bird2 = {
|
||||
enable = true;
|
||||
config = ''
|
||||
${optionalString (bcfg.routerId != null) "router id ${bcfg.routerId};"}
|
||||
|
||||
protocol device {
|
||||
scan time 10;
|
||||
}
|
||||
|
||||
protocol kernel kernel4 {
|
||||
${bcfg.kernel4Config}
|
||||
}
|
||||
protocol kernel kernel6 {
|
||||
${bcfg.kernel6Config}
|
||||
}
|
||||
|
||||
protocol static static4 {
|
||||
ipv4 { import all; export none; };
|
||||
${concatMapStringsSep "\n" (x: "route ${x};") bcfg.staticRoutes4}
|
||||
${bcfg.extraStatic4}
|
||||
}
|
||||
protocol static static6 {
|
||||
ipv6 { import all; export none; };
|
||||
${concatMapStringsSep "\n" (x: "route ${x};") bcfg.staticRoutes6}
|
||||
${bcfg.extraStatic6}
|
||||
}
|
||||
|
||||
${concatStringsSep "\n" (mapAttrsToList (protoName: proto: ''
|
||||
protocol ospf v${toString proto.version} ${protoName} {
|
||||
${concatStringsSep "\n" (mapAttrsToList (areaName: area: ''
|
||||
area ${areaName} {
|
||||
${optionalString
|
||||
(area.networks != [])
|
||||
"networks { ${concatStringsSep "\n" (map (x: "${x};") area.networks)} };"}
|
||||
${optionalString
|
||||
(area.external != [])
|
||||
"external { ${concatStringsSep "\n" (map (x: "${x};") area.external)} };"}
|
||||
${concatStringsSep "\n" (mapAttrsToList (ifacePattern: iface: ''
|
||||
interface "${ifacePattern}" {
|
||||
cost ${toString iface.cost};
|
||||
poll ${toString iface.poll};
|
||||
retransmit ${toString iface.retransmit};
|
||||
priority ${toString iface.priority};
|
||||
dead count ${toString iface.deadCount};
|
||||
${optionalString (iface.type != null) "type ${iface.type};"}
|
||||
${iface.extra}
|
||||
};
|
||||
'') area.interfaces)}
|
||||
${area.extra}
|
||||
};
|
||||
'') proto.areas)}
|
||||
${proto.extra}
|
||||
}
|
||||
'') cfg.protocols)}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
84
modules/nixos/deploy.nix
Normal file
84
modules/nixos/deploy.nix
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{ tf, target, name, meta, config, lib, ... }:
|
||||
|
||||
/*
|
||||
This module:
|
||||
* aliases <hostname>.system.build.toplevel to <hostname>.deploy.system for ease of use.
|
||||
* marries meta config to NixOS configs for each host.
|
||||
* provides in-scope TF config in NixOS and home-manager, instead of only as a part of meta config.
|
||||
*/
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.deploy;
|
||||
unmergedValues = types.mkOptionType {
|
||||
name = "unmergedValues";
|
||||
merge = loc: defs: map (def: def.value) defs;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.deploy = {
|
||||
targetName = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
system = mkOption {
|
||||
type = types.unspecified;
|
||||
readOnly = true;
|
||||
};
|
||||
};
|
||||
options.deploy.tf = mkOption {
|
||||
type = types.submodule {
|
||||
inherit (unmerged) freeformType;
|
||||
|
||||
options = {
|
||||
import = mkOption {
|
||||
type = types.attrsOf types.unspecified;
|
||||
default = [ ];
|
||||
};
|
||||
imports = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "Other targets to depend on";
|
||||
default = [ ];
|
||||
};
|
||||
attrs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
out.set = mkOption { type = types.unspecified; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
deploy = {
|
||||
system = config.system.build.toplevel;
|
||||
targetName = let targetsList = attrNames (filterAttrs (_: target: target.enable && elem name target.nodeNames) meta.deploy.targets); in
|
||||
if (builtins.length targetsList == 0) then null
|
||||
else lib.warnIf (builtins.length targetsList > 1) "The host ${name} is assigned to several targets: ${concatMapStrings (x: "${x},") targetsList}." (head targetsList);
|
||||
};
|
||||
deploy.tf = mkMerge (singleton
|
||||
(lib.mkIf (config.deploy.targetName != null) {
|
||||
attrs = [ "import" "imports" "out" "attrs" ];
|
||||
import = genAttrs cfg.tf.imports (target: meta.deploy.targets.${target}.tf);
|
||||
out.set = removeAttrs cfg.tf cfg.tf.attrs;
|
||||
deploy.systems.${config.networking.hostName} =
|
||||
with tf.resources; {
|
||||
isRemote =
|
||||
(config.networking.hostName != builtins.getEnv "HOME_HOSTNAME");
|
||||
nixosConfig = config;
|
||||
connection = tf.resources.${config.networking.hostName}.connection.set;
|
||||
triggers.copy.${config.networking.hostName} =
|
||||
tf.resources.${config.networking.hostName}.refAttr "id";
|
||||
triggers.secrets.${config.networking.hostName} =
|
||||
tf.resources.${config.networking.hostName}.refAttr "id";
|
||||
};
|
||||
}) ++ mapAttrsToList
|
||||
(_: user:
|
||||
mapAttrs (_: mkMerge) user.deploy.tf.out.set)
|
||||
config.home-manager.users);
|
||||
|
||||
_module.args.target = mapNullable (targetName: meta.deploy.targets.${targetName}) cfg.targetName;
|
||||
_module.args.tf = mapNullable (target: target.tf) target;
|
||||
};
|
||||
}
|
||||
2
modules/nixos/disables.nix
Normal file
2
modules/nixos/disables.nix
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{ inputs, ... }: {
|
||||
}
|
||||
14
modules/nixos/displays.nix
Normal file
14
modules/nixos/displays.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{ config, lib, ... }: with lib; {
|
||||
options.hardware.displays = mkOption {
|
||||
type = with types; attrsOf (submodule ({ config, ... }: {
|
||||
options = {
|
||||
pos = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
res = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
}
|
||||
82
modules/nixos/firewall.nix
Normal file
82
modules/nixos/firewall.nix
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.network.firewall;
|
||||
in
|
||||
{
|
||||
options.network.firewall = {
|
||||
public.tcp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
public.udp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
private.tcp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
private.udp.ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
public.tcp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
public.udp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
private.tcp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
private.udp.ranges = mkOption {
|
||||
type = types.listOf (types.attrsOf types.port);
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
public.interfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "Public firewall interfaces";
|
||||
default = [ ];
|
||||
};
|
||||
private.interfaces = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "Private firewall interfaces";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
network.firewall = mkMerge (mapAttrsToList (_: user: user.network.firewall) config.home-manager.users);
|
||||
networking.firewall.interfaces =
|
||||
let
|
||||
fwTypes = {
|
||||
ports = "Ports";
|
||||
ranges = "PortRanges";
|
||||
};
|
||||
|
||||
interfaceDef = visibility:
|
||||
listToAttrs (flatten (mapAttrsToList
|
||||
(type: typeString:
|
||||
map
|
||||
(proto: {
|
||||
name = "allowed${toUpper proto}${typeString}";
|
||||
value = cfg.${visibility}.${proto}.${type};
|
||||
}) [ "tcp" "udp" ])
|
||||
fwTypes));
|
||||
|
||||
interfaces = visibility:
|
||||
listToAttrs
|
||||
(map (interface: nameValuePair interface (interfaceDef visibility))
|
||||
cfg.${visibility}.interfaces);
|
||||
in
|
||||
mkMerge (map (visibility: interfaces visibility) [ "public" "private" ]);
|
||||
};
|
||||
}
|
||||
259
modules/nixos/network.nix
Normal file
259
modules/nixos/network.nix
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
{ config, lib, tf, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.network;
|
||||
in
|
||||
{
|
||||
options.network = {
|
||||
enable = mkEnableOption "Use kat's network module?";
|
||||
addresses = mkOption {
|
||||
type = with types; attrsOf (submodule ({ name, options, config, ... }: {
|
||||
options = {
|
||||
enable = mkEnableOption "Is it a member of the ${name} network?";
|
||||
nixos = {
|
||||
ipv4 = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = options.nixos.ipv4.address.isDefined;
|
||||
};
|
||||
selfaddress = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
ipv6 = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = options.nixos.ipv6.address.isDefined;
|
||||
};
|
||||
selfaddress = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
tf = {
|
||||
ipv4 = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = options.tf.ipv4.address.isDefined;
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
ipv6 = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = options.tf.ipv6.address.isDefined;
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
prefix = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
subdomain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "${config.subdomain}.${cfg.dns.domain}";
|
||||
};
|
||||
target = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "${config.domain}.";
|
||||
};
|
||||
out = {
|
||||
identifierList = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = optionals config.enable (singleton config.domain ++ config.out.addressList);
|
||||
};
|
||||
addressList = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = optionals config.enable (concatMap (i: optional i.enable i.address) [ config.nixos.ipv4 config.nixos.ipv6 ]);
|
||||
};
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
extraCerts = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = { };
|
||||
};
|
||||
tf = {
|
||||
enable = mkEnableOption "Was the system provisioned by terraform?";
|
||||
ipv4_attr = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
ipv6_attr = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
dns = {
|
||||
enable = mkEnableOption "Do you want DNS to be semi-managed through this module?";
|
||||
isRoot = mkEnableOption "Is this system supposed to be the @ for the domain?";
|
||||
email = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
zone = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
networks = cfg.addresses;
|
||||
networksWithDomains = filterAttrs (_: v: v.enable) networks;
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
lib.kw.virtualHostGen = args: virtualHostGen ({ inherit config; } // args);
|
||||
|
||||
network = {
|
||||
dns = {
|
||||
domain = builtins.substring 0 ((builtins.stringLength cfg.dns.zone) - 1) cfg.dns.zone;
|
||||
};
|
||||
addresses = lib.mkMerge [
|
||||
(mkIf (!cfg.tf.enable) (genAttrs [ "private" "public" "yggdrasil" ] (network: {
|
||||
tf = {
|
||||
ipv4.address = mkIf (cfg.addresses.${network}.nixos.ipv4.enable) cfg.addresses.${network}.nixos.ipv4.address;
|
||||
ipv6.address = mkIf (cfg.addresses.${network}.nixos.ipv6.enable) cfg.addresses.${network}.nixos.ipv6.address;
|
||||
};
|
||||
})))
|
||||
(mkIf cfg.tf.enable (genAttrs ["yggdrasil" ] (network: {
|
||||
tf = {
|
||||
ipv4.address = mkIf (cfg.addresses.${network}.nixos.ipv4.enable) cfg.addresses.${network}.nixos.ipv4.address;
|
||||
ipv6.address = mkIf (cfg.addresses.${network}.nixos.ipv6.enable) cfg.addresses.${network}.nixos.ipv6.address;
|
||||
};
|
||||
})))
|
||||
(mkIf cfg.tf.enable {
|
||||
public = {
|
||||
tf = {
|
||||
ipv4.address = mkIf (cfg.tf.ipv4_attr != null) (tf.resources.${config.networking.hostName}.refAttr cfg.tf.ipv4_attr);
|
||||
ipv6.address = mkIf (cfg.tf.ipv6_attr != null) (tf.resources.${config.networking.hostName}.refAttr cfg.tf.ipv6_attr);
|
||||
};
|
||||
nixos = {
|
||||
ipv4.selfaddress = mkIf (tf.state.enable && cfg.tf.ipv4_attr != null) (tf.resources.${config.networking.hostName}.getAttr cfg.tf.ipv4_attr);
|
||||
ipv6.selfaddress = mkIf (tf.state.enable && cfg.tf.ipv6_attr != null) (tf.resources.${config.networking.hostName}.getAttr cfg.tf.ipv6_attr);
|
||||
ipv4.address = mkIf (tf.state.resources ? ${tf.resources.${config.networking.hostName}.out.reference} && cfg.tf.ipv4_attr != null) (tf.resources.${config.networking.hostName}.importAttr cfg.tf.ipv4_attr);
|
||||
ipv6.address = mkIf (tf.state.resources ? ${tf.resources.${config.networking.hostName}.out.reference} && cfg.tf.ipv6_attr != null) (tf.resources.${config.networking.hostName}.importAttr cfg.tf.ipv6_attr);
|
||||
};
|
||||
};
|
||||
})
|
||||
({
|
||||
private = {
|
||||
prefix = "int";
|
||||
subdomain = "${config.networking.hostName}.${cfg.addresses.private.prefix}";
|
||||
};
|
||||
yggdrasil = {
|
||||
enable = cfg.yggdrasil.enable;
|
||||
prefix = "ygg";
|
||||
subdomain = "${config.networking.hostName}.${cfg.addresses.yggdrasil.prefix}";
|
||||
};
|
||||
public = {
|
||||
subdomain = config.networking.hostName;
|
||||
};
|
||||
})
|
||||
(mkIf cfg.yggdrasil.enable {
|
||||
yggdrasil.nixos.ipv6.address = cfg.yggdrasil.address;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
networking.domain = mkDefault (if cfg.addresses.public.enable then cfg.dns.domain
|
||||
else if cfg.addresses.private.enable then "${cfg.addresses.private.prefix}.${cfg.dns.domain}" else "");
|
||||
|
||||
deploy.tf.dns.records =
|
||||
let
|
||||
recordsV4 = mapAttrs'
|
||||
(n: v:
|
||||
nameValuePair "node_${n}_${config.networking.hostName}_v4" {
|
||||
inherit (v.tf.ipv4) enable;
|
||||
inherit (cfg.dns) zone;
|
||||
domain = v.subdomain;
|
||||
a = { inherit (v.tf.ipv4) address; };
|
||||
})
|
||||
networksWithDomains;
|
||||
recordsV6 = mapAttrs'
|
||||
(n: v:
|
||||
nameValuePair "node_${n}_${config.networking.hostName}_v6" {
|
||||
inherit (v.tf.ipv6) enable;
|
||||
inherit (cfg.dns) zone;
|
||||
domain = v.subdomain;
|
||||
aaaa = { inherit (v.tf.ipv6) address; };
|
||||
})
|
||||
networksWithDomains;
|
||||
in
|
||||
mkMerge (map (record: mkIf cfg.dns.enable record) [
|
||||
recordsV4
|
||||
recordsV6
|
||||
(mkIf cfg.dns.isRoot {
|
||||
"node_root_${config.networking.hostName}_v4" = {
|
||||
inherit (cfg.addresses.public) enable;
|
||||
inherit (cfg.dns) zone;
|
||||
a = { inherit (cfg.addresses.public.tf.ipv4) address; };
|
||||
};
|
||||
"node_root_${config.networking.hostName}_v6" = {
|
||||
inherit (cfg.addresses.public) enable;
|
||||
inherit (cfg.dns) zone;
|
||||
aaaa = { inherit (cfg.addresses.public.tf.ipv6) address; };
|
||||
};
|
||||
})
|
||||
]);
|
||||
|
||||
security.acme.certs = mkMerge (map (cert: mkIf cfg.dns.enable cert) [
|
||||
(mkIf config.services.nginx.enable (mapAttrs'
|
||||
(n: v:
|
||||
nameValuePair "${n}_${config.networking.hostName}" {
|
||||
inherit (v) domain;
|
||||
dnsProvider = "rfc2136";
|
||||
credentialsFile = config.secrets.files.dns_creds.path;
|
||||
group = mkDefault "nginx";
|
||||
})
|
||||
networksWithDomains))
|
||||
(mapAttrs'
|
||||
(n: v:
|
||||
nameValuePair "${n}" {
|
||||
domain = v;
|
||||
dnsProvider = "rfc2136";
|
||||
credentialsFile = config.secrets.files.dns_creds.path;
|
||||
group = mkDefault "nginx";
|
||||
})
|
||||
cfg.extraCerts)
|
||||
]);
|
||||
|
||||
services.nginx.virtualHosts = mkMerge (map (host: mkIf cfg.dns.enable host) [
|
||||
(mkIf config.services.nginx.enable (mapAttrs'
|
||||
(n: v:
|
||||
nameValuePair v.domain {
|
||||
useACMEHost = "${n}_${config.networking.hostName}";
|
||||
forceSSL = true;
|
||||
})
|
||||
networksWithDomains))
|
||||
(mapAttrs'
|
||||
(n: v:
|
||||
nameValuePair v {
|
||||
useACMEHost = "${n}";
|
||||
forceSSL = true;
|
||||
})
|
||||
cfg.extraCerts)
|
||||
]);
|
||||
|
||||
_module.args = { inherit (config.lib) kw; };
|
||||
};
|
||||
}
|
||||
131
modules/nixos/nftables.nix
Normal file
131
modules/nixos/nftables.nix
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
{ pkgs, lib, config, modulesPath, ... }:
|
||||
|
||||
let
|
||||
fwcfg = config.networking.firewall;
|
||||
cfg = config.networking.nftables;
|
||||
|
||||
doDocker = config.virtualisation.docker.enable && cfg.generateDockerRules;
|
||||
|
||||
mkPorts = cond: ports: ranges: action: let
|
||||
portStrings = (map (range: "${toString range.from}-${toString range.to}") ranges)
|
||||
++ (map toString ports);
|
||||
in lib.optionalString (portStrings != []) ''
|
||||
${cond} dport { ${lib.concatStringsSep ", " portStrings} } ${action}
|
||||
'';
|
||||
|
||||
ruleset = ''
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority filter
|
||||
policy ${cfg.inputPolicy}
|
||||
|
||||
icmpv6 type { echo-request, echo-reply, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, packet-too-big } accept
|
||||
icmp type echo-request accept
|
||||
|
||||
ct state invalid drop
|
||||
ct state established,related accept
|
||||
|
||||
iifname { ${
|
||||
lib.concatStringsSep "," (["lo"] ++ fwcfg.trustedInterfaces)
|
||||
} } accept
|
||||
|
||||
${mkPorts "tcp" fwcfg.allowedTCPPorts fwcfg.allowedTCPPortRanges "accept"}
|
||||
${mkPorts "udp" fwcfg.allowedUDPPorts fwcfg.allowedUDPPortRanges "accept"}
|
||||
|
||||
${
|
||||
lib.concatStringsSep "\n" (lib.mapAttrsToList (name: ifcfg:
|
||||
mkPorts "iifname ${name} tcp" ifcfg.allowedTCPPorts ifcfg.allowedTCPPortRanges "accept"
|
||||
+ mkPorts "iifname ${name} udp" ifcfg.allowedUDPPorts ifcfg.allowedUDPPortRanges "accept"
|
||||
) fwcfg.interfaces)
|
||||
}
|
||||
|
||||
# DHCPv6
|
||||
ip6 daddr fe80::/64 udp dport 546 accept
|
||||
|
||||
${cfg.extraInput}
|
||||
|
||||
counter
|
||||
}
|
||||
chain output {
|
||||
type filter hook output priority filter
|
||||
policy ${cfg.outputPolicy}
|
||||
|
||||
${cfg.extraOutput}
|
||||
|
||||
counter
|
||||
}
|
||||
chain forward {
|
||||
type filter hook forward priority filter
|
||||
policy ${cfg.forwardPolicy}
|
||||
|
||||
${lib.optionalString doDocker ''
|
||||
oifname docker0 ct state invalid drop
|
||||
oifname docker0 ct state established,related accept
|
||||
iifname docker0 accept
|
||||
''}
|
||||
|
||||
${cfg.extraForward}
|
||||
|
||||
counter
|
||||
}
|
||||
}
|
||||
${lib.optionalString doDocker ''
|
||||
table ip nat {
|
||||
chain docker-postrouting {
|
||||
type nat hook postrouting priority 10
|
||||
iifname docker0 masquerade
|
||||
}
|
||||
}
|
||||
''}
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
in {
|
||||
options = with lib; {
|
||||
networking.nftables = {
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
};
|
||||
extraInput = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
};
|
||||
extraOutput = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
};
|
||||
extraForward = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
};
|
||||
inputPolicy = mkOption {
|
||||
type = types.str;
|
||||
default = "drop";
|
||||
};
|
||||
outputPolicy = mkOption {
|
||||
type = types.str;
|
||||
default = "accept";
|
||||
};
|
||||
forwardPolicy = mkOption {
|
||||
type = types.str;
|
||||
default = "accept";
|
||||
};
|
||||
generateDockerRules = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
networking.firewall.enable = false;
|
||||
networking.nftables = {
|
||||
inherit ruleset;
|
||||
};
|
||||
|
||||
virtualisation.docker = lib.mkIf doDocker {
|
||||
extraOptions = "--iptables=false";
|
||||
};
|
||||
};
|
||||
}
|
||||
51
modules/nixos/policyrouting.nix
Normal file
51
modules/nixos/policyrouting.nix
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.networking.policyrouting;
|
||||
|
||||
ruleOpts = { ... }: {
|
||||
options = {
|
||||
prio = mkOption {
|
||||
type = types.int;
|
||||
};
|
||||
rule = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
networking.policyrouting = {
|
||||
enable = mkEnableOption "Declarative Policy-Routing";
|
||||
rules = mkOption {
|
||||
type = with types; listOf (submodule ruleOpts);
|
||||
default = [ ];
|
||||
};
|
||||
rules6 = mkOption {
|
||||
type = with types; listOf (submodule ruleOpts);
|
||||
default = [ ];
|
||||
};
|
||||
rules4 = mkOption {
|
||||
type = with types; listOf (submodule ruleOpts);
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
networking.policyrouting.rules = [
|
||||
{ rule = "lookup main"; prio = 32000; }
|
||||
];
|
||||
networking.localCommands = ''
|
||||
set -x
|
||||
ip -6 rule flush
|
||||
ip -4 rule flush
|
||||
${concatMapStringsSep "\n" ({ prio, rule }: "ip -6 rule add ${rule} prio ${toString prio}") (cfg.rules ++ cfg.rules6)}
|
||||
${concatMapStringsSep "\n" ({ prio, rule }: "ip -4 rule add ${rule} prio ${toString prio}") (cfg.rules ++ cfg.rules4)}
|
||||
'';
|
||||
};
|
||||
}
|
||||
57
modules/nixos/secrets.nix
Normal file
57
modules/nixos/secrets.nix
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{ config, lib, meta, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
secretType = types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
path = mkOption { type = types.str; };
|
||||
field = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
});
|
||||
repoSecretType = types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
source = mkOption {
|
||||
type = types.path;
|
||||
};
|
||||
text = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
});
|
||||
mcfg = meta.kw.secrets;
|
||||
cfg = config.kw.secrets;
|
||||
in
|
||||
{
|
||||
options.kw = {
|
||||
secrets = {
|
||||
variables = mkOption {
|
||||
type = types.attrsOf secretType;
|
||||
default = { };
|
||||
};
|
||||
repo = mkOption {
|
||||
type = types.attrsOf repoSecretType;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
kw.secrets.variables = lib.mkMerge (mapAttrsToList (username: user: user.kw.secrets.variables) config.home-manager.users);
|
||||
}
|
||||
(mkIf (cfg.variables != { }) {
|
||||
deploy.tf.variables = mapAttrs'
|
||||
(name: content:
|
||||
nameValuePair name ({
|
||||
value.shellCommand = "${mcfg.command} ${content.path}" + optionalString (content.field != "") " -f ${content.field}";
|
||||
type = "string";
|
||||
sensitive = true;
|
||||
})
|
||||
)
|
||||
cfg.variables;
|
||||
})
|
||||
];
|
||||
}
|
||||
145
modules/nixos/yggdrasil.nix
Normal file
145
modules/nixos/yggdrasil.nix
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
{ config, meta, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.network.yggdrasil;
|
||||
calcAddr = pubkey: lib.readFile (pkgs.runCommandNoCC "calcaddr-${pubkey}" { } ''
|
||||
echo '{ SigningPublicKey: "${pubkey}" }' | ${config.services.yggdrasil.package}/bin/yggdrasil -useconf -address | tr -d '\n' > $out
|
||||
'').outPath;
|
||||
in
|
||||
{
|
||||
options.network.yggdrasil = {
|
||||
enable = mkEnableOption "Enable the yggdrasil-based private hexnet";
|
||||
pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = "Public key of this node";
|
||||
};
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
#description = "Main Yggdrasil address. Set automatically";
|
||||
#default = calcAddr cfg.signingPubkey;
|
||||
default = "";
|
||||
};
|
||||
trust = mkOption {
|
||||
type = types.bool;
|
||||
description = "Open Firewall completely for the network";
|
||||
default = false;
|
||||
};
|
||||
listen = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
description = "Allow other hosts in the network to connect directly";
|
||||
default = false;
|
||||
};
|
||||
endpoints = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "Endpoints to listen on";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
tunnel = {
|
||||
localV6 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "v6 subnets to expose";
|
||||
default = [ ];
|
||||
};
|
||||
localV4 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "v4 subnets to expose";
|
||||
default = [ ];
|
||||
};
|
||||
remoteV6 = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "Extra v6 subnets to route";
|
||||
default = { };
|
||||
};
|
||||
remoteV4 = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "Extra v4 subnets to route";
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
extra = {
|
||||
pubkeys = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "Additional hosts to allow into the network. Keys won't be added to definition host.";
|
||||
default = { };
|
||||
example = { host = "0000000000000000000000000000000000000000000000000000000000000000"; };
|
||||
};
|
||||
addresses = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
internal = true;
|
||||
default = mapAttrs (_: c: calcAddr c) cfg.extra.pubkeys;
|
||||
};
|
||||
localV6 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "v6 subnets to expose, but not route";
|
||||
default = [ ];
|
||||
};
|
||||
localV4 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "v4 subnets to expose, but not route";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
extern = {
|
||||
pubkeys = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "Additional hosts to allow into the network. Keys won't be added to definition host.";
|
||||
default = { };
|
||||
example = { host = "0000000000000000000000000000000000000000000000000000000000000000"; };
|
||||
};
|
||||
endpoints = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "Endpoints to listen on";
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (
|
||||
let
|
||||
yggConfigs = filter
|
||||
(
|
||||
c: c.enable && (cfg.pubkey != c.pubkey)
|
||||
)
|
||||
(
|
||||
mapAttrsToList (_: node: node.network.yggdrasil or { enable = false; pubkey = null; }) meta.network.nodes.nixos
|
||||
);
|
||||
pubkeys = flatten ((filter (n: n != "0000000000000000000000000000000000000000000000000000000000000000") (attrValues cfg.extern.pubkeys)) ++ (map (c: [ c.pubkey ] ++ (attrValues c.extra.pubkeys)) yggConfigs));
|
||||
in
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = !(cfg.listen.enable && (cfg.listen.endpoints == [ ]));
|
||||
message = "Specify network.yggdrasil.listen.endpoints";
|
||||
}
|
||||
];
|
||||
|
||||
networking.firewall.trustedInterfaces = mkIf cfg.trust [ "yggdrasil" ];
|
||||
|
||||
services.yggdrasil = {
|
||||
enable = true;
|
||||
persistentKeys = true;
|
||||
config = {
|
||||
AllowedPublicKeys = pubkeys;
|
||||
IfName = "yggdrasil";
|
||||
Listen = cfg.listen.endpoints;
|
||||
Peers = lib.flatten (cfg.extern.endpoints ++ (map (c: c.listen.endpoints) (filter (c: c.listen.enable) yggConfigs)));
|
||||
};
|
||||
};
|
||||
|
||||
system.build.yggdrasilTemplate =
|
||||
let
|
||||
json = builtins.toJSON {
|
||||
inherit (config.services.yggdrasil.config) Peers SessionFirewall TunnelRouting;
|
||||
PublicKey = "";
|
||||
PrivateKey = "";
|
||||
};
|
||||
in
|
||||
pkgs.runCommandNoCC "yggdrasil-template.json" { }
|
||||
"echo '${json}' | ${config.services.yggdrasil.package}/bin/yggdrasil -useconf -normaliseconf > $out";
|
||||
}
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue