Moving to modules. Structural changes.

This commit is contained in:
kat witch 2021-07-05 22:47:28 +01:00
parent 3903bc1766
commit 060d4c6d1e
No known key found for this signature in database
GPG key ID: 1B477797DCA5EC72
258 changed files with 621 additions and 407 deletions

View file

@ -0,0 +1,10 @@
{ sources, ... }:
{
disabledModules = [ "programs/vim.nix" ];
imports = with (import (sources.nixexprs + "/modules")).home-manager; [ base16 syncplay konawall i3gopher weechat shell ] ++ [
./vim.nix
./deploy-tf
(sources.tf-nix + "/modules/home/secrets.nix")
];
}

View file

@ -0,0 +1,34 @@
{ config, lib, ... }:
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;
};
};
}

194
config/modules/home/vim.nix Normal file
View file

@ -0,0 +1,194 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.programs.vim;
defaultPlugins = [ pkgs.vimPlugins.vim-sensible ];
knownSettings = {
background = types.enum [ "dark" "light" ];
backupdir = types.listOf types.str;
copyindent = types.bool;
directory = types.listOf types.str;
expandtab = types.bool;
hidden = types.bool;
history = types.int;
ignorecase = types.bool;
modeline = types.bool;
mouse = types.enum [ "n" "v" "i" "c" "h" "a" "r" ];
mousefocus = types.bool;
mousehide = types.bool;
mousemodel = types.enum [ "extend" "popup" "popup_setpos" ];
number = types.bool;
relativenumber = types.bool;
shiftwidth = types.int;
smartcase = types.bool;
tabstop = types.int;
undodir = types.listOf types.str;
undofile = types.bool;
};
vimSettingsType = types.submodule {
options =
let
opt = name: type:
mkOption {
type = types.nullOr type;
default = null;
visible = false;
};
in
mapAttrs opt knownSettings;
};
setExpr = name: value:
let
v =
if isBool value then
(if value then "" else "no") + name
else
"${name}=${
if isList value then concatStringsSep "," value else toString value
}";
in
optionalString (value != null) ("set " + v);
plugins =
let
vpkgs = pkgs.vimPlugins;
getPkg = p:
if isDerivation p then
[ p ]
else
optional (isString p && hasAttr p vpkgs) vpkgs.${p};
in
concatMap getPkg cfg.plugins;
in
{
options = {
programs.vim = {
enable = mkEnableOption "Vim";
package = mkOption {
type = types.package;
default = pkgs.vim_configurable;
defaultText = literalExample "pkgs.vim_configurable";
description = "The package to use for the vim binary.";
};
finalPackage = mkOption {
type = types.package;
visible = false;
readOnly = true;
description = "Resulting customized vim package.";
};
plugins = mkOption {
type = with types; listOf (either str package);
default = defaultPlugins;
example = literalExample "[ pkgs.vimPlugins.YankRing ]";
description = ''
List of vim plugins to install. To get a list of supported plugins run:
<command>nix-env -f '&lt;nixpkgs&gt;' -qaP -A vimPlugins</command>.
</para><para>
Note: String values are deprecated, please use actual packages.
'';
};
settings = mkOption {
type = vimSettingsType;
default = { };
example = literalExample ''
{
expandtab = true;
history = 1000;
background = "dark";
}
'';
description = ''
At attribute set of Vim settings. The attribute names and
corresponding values must be among the following supported
options.
<informaltable frame="none"><tgroup cols="1"><tbody>
${concatStringsSep "\n" (mapAttrsToList (n: v: ''
<row>
<entry><varname>${n}</varname></entry>
<entry>${v.description}</entry>
</row>
'') knownSettings)}
</tbody></tgroup></informaltable>
See the Vim documentation for detailed descriptions of these
options. Note, use <varname>extraConfig</varname> to
manually set any options not listed above.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
example = ''
set nocompatible
set nobackup
'';
description = "Custom .vimrc lines";
};
};
};
config = (
let
customRC = ''
${concatStringsSep "\n" (filter (v: v != "") (mapAttrsToList setExpr
(builtins.intersectAttrs knownSettings cfg.settings)))}
${cfg.extraConfig}
'';
vim = cfg.package.customize {
name = "vim";
vimrcConfig = {
inherit customRC;
packages.home-manager.start = plugins;
};
};
in
mkIf cfg.enable {
assertions =
let
packagesNotFound =
filter (p: isString p && (!hasAttr p pkgs.vimPlugins)) cfg.plugins;
in
[{
assertion = packagesNotFound == [ ];
message = "Following VIM plugin not found in pkgs.vimPlugins: ${
concatMapStringsSep ", " (p: ''"${p}"'') packagesNotFound
}";
}];
warnings =
let stringPlugins = filter isString cfg.plugins;
in
optional (stringPlugins != [ ]) ''
Specifying VIM plugins using strings is deprecated, found ${
concatMapStringsSep ", " (p: ''"${p}"'') stringPlugins
} as strings.
'';
home.packages = [ cfg.finalPackage ];
programs.vim = {
finalPackage = vim;
plugins = defaultPlugins;
};
}
);
}

View file

@ -0,0 +1,8 @@
{ ... }:
{
imports = [
./deploy.nix
./network.nix
];
}

View file

@ -0,0 +1,69 @@
{ tf, target, config, lib, ... }:
with lib;
let
cfg = config.deploy.tf;
unmergedValues = types.mkOptionType {
name = "unmergedValues";
merge = loc: defs: map (def: def.value) defs;
};
in
{
options.deploy.target = mkOption {
type = with types; str;
default = "";
};
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 = mkMerge (singleton
{
attrs = [ "out" "attrs" ];
out.set = removeAttrs cfg cfg.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";
};
dns.records."kittywitch_net_${config.networking.hostName}" =
mkIf (config.hexchen.network.enable) {
tld = "kittywit.ch.";
domain = "${config.networking.hostName}.net";
aaaa.address = config.hexchen.network.address;
};
} ++ mapAttrsToList
(_: user:
mapAttrs (_: mkMerge) user.deploy.tf.out.set)
config.home-manager.users);
security.acme.certs."${config.networking.hostName}.net.kittywit.ch" =
mkIf (config.services.nginx.enable && config.hexchen.network.enable) {
domain = "${config.networking.hostName}.net.kittywit.ch";
dnsProvider = "rfc2136";
credentialsFile = config.secrets.files.dns_creds.path;
group = "nginx";
};
_module.args.tf = target.${config.deploy.target};
};
}

View file

@ -0,0 +1,99 @@
{ sources, config, pkgs, lib, ... }: with lib; let
cfg = config.deploy;
meta = config;
tfModule = { lib, ... }: with lib; {
config._module.args = {
pkgs = mkDefault pkgs;
};
};
tfType = types.submoduleWith {
modules = [
tfModule
"${toString sources.tf-nix}/modules"
];
};
in {
imports = [
(toString (sources.tf-nix + "/modules/run.nix"))
] ++ (optional (builtins.pathExists ../../trusted/tf/tf.nix) (../../trusted/tf/tf.nix));
options = {
deploy = {
dataDir = mkOption {
type = types.path;
};
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 = {
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 = [
../../targets/common
];
deps = {
select.allProviders = true;
enable = true;
};
terraform = {
version = "1.0";
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";
terraform.name = "${name}-tf";
};
};
continue.envVar = "TF_NIX_CONTINUE_${replaceStrings [ "-" ] [ "_" ] config.name}";
} ++ map (nodeName: mapAttrs (_: mkMerge) meta.network.nodes.${nodeName}.deploy.tf.out.set) config.nodeNames);
});
in mkOption {
type = types.attrsOf type;
default = { };
};
};
};
config = {
runners = {
run = mkMerge (mapAttrsToList (targetName: target: mapAttrs' (k: run:
nameValuePair run.name run.set
) target.tf.runners.run) cfg.targets);
lazy.run = mkMerge (mapAttrsToList (targetName: target: mapAttrs' (k: run:
nameValuePair run.name run.set
) target.tf.runners.lazy.run) cfg.targets);
};
};
}

View file

@ -0,0 +1,59 @@
{ pkgs, sources, users, profiles, lib, config, ... }: 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");
};
};
nodes = let
nixosModule = { name, config, meta, modulesPath, lib, ... }: with lib; {
config = {
nixpkgs = {
system = mkDefault pkgs.system;
pkgs = mkDefault pkgs;
#inherit (pkgs) config;
};
};
};
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 = { };
};
};
config.network = {
nixos = {
extraModules = [
"${toString sources.home-manager}/nixos"
../../modules/nixos
];
specialArgs = {
inherit (config.network) nodes;
inherit sources profiles users;
meta = config;
};
};
};
}

View file

@ -0,0 +1,21 @@
{ meta, sources, lib, ... }:
{
imports = with (import (sources.nixexprs + "/modules")).nixos; [ base16 base16-shared ] ++ [
./nftables.nix
./fw-abstraction.nix
./deploy-tf.nix
(sources.tf-nix + "/modules/nixos/secrets.nix")
(sources.tf-nix + "/modules/nixos/secrets-users.nix")
(sources.hexchen + "/modules/hexnet")
];
# stubs for hexchens modules, until more generalized
options.hexchen.dns = lib.mkOption { };
options.hexchen.deploy = lib.mkOption { };
# shim
config = {
_module.args.hosts = lib.mapAttrs (_: config: { inherit config; } ) meta.network.nodes;
};
}

View file

@ -0,0 +1,91 @@
{ tf, target, name, meta, config, lib, ... }:
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 {
freeformType = types.attrsOf unmergedValues;
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 = if (meta.deploy.targets ? ${name}) then
(mkDefault name)
else
head (attrNames ((filterAttrs(targetName: target: elem config.networking.hostName target.nodeNames) meta.deploy.targets)));
};
deploy.tf = mkMerge (singleton
{
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";
};
dns.records."kittywitch_net_${config.networking.hostName}" =
mkIf (config.hexchen.network.enable) {
tld = "kittywit.ch.";
domain = "${config.networking.hostName}.net";
aaaa.address = config.hexchen.network.address;
};
} ++ mapAttrsToList
(_: user:
mapAttrs (_: mkMerge) user.deploy.tf.out.set)
config.home-manager.users);
security.acme.certs."${config.networking.hostName}.net.kittywit.ch" =
mkIf (config.services.nginx.enable && config.hexchen.network.enable) {
domain = "${config.networking.hostName}.net.kittywit.ch";
dnsProvider = "rfc2136";
credentialsFile = config.secrets.files.dns_creds.path;
group = "nginx";
};
_module.args.target = mapNullable (targetName: meta.deploy.targets.${targetName}) cfg.targetName;
_module.args.tf = mapNullable (target: target.tf) target;
};
}

View file

@ -0,0 +1,80 @@
{ config, lib, pkgs, ... }:
with lib;
let cfg = config.kw.fw;
in
{
options.kw.fw = {
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 = {
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" ]);
};
}

View file

@ -0,0 +1,134 @@
{ pkgs, lib, config, modulesPath, ... }:
let
fwcfg = config.networking.firewall;
cfg = config.kw.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; {
kw.nftables = {
enable = mkEnableOption "nftables firewall";
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 = {
enable = true;
inherit ruleset;
};
virtualisation.docker = lib.mkIf doDocker {
extraOptions = "--iptables=false";
};
};
}