Possibly functional blowhole configuration

Signed-off-by: Magic_RB <magic_rb@redalder.org>
This commit is contained in:
Magic_RB 2023-06-12 23:25:40 +02:00
parent 558a6e4168
commit fed32ecfca
36 changed files with 4164 additions and 13 deletions

View file

@ -1,5 +1,24 @@
{
"nodes": {
"deploy-rs": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs_7",
"utils": "utils"
},
"locked": {
"lastModified": 1648475189,
"narHash": "sha256-gAGAS6IagwoUr1B0ohE3iR6sZ8hP4LSqzYLC8Mq3WGU=",
"owner": "serokell",
"repo": "deploy-rs",
"rev": "83e0c78291cd08cb827ba0d553ad9158ae5a95c3",
"type": "github"
},
"original": {
"id": "deploy-rs",
"type": "indirect"
}
},
"dwarffs": {
"inputs": {
"nix": "nix",
@ -40,6 +59,37 @@
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1648199409,
"narHash": "sha256-JwPKdC2PoVBkG6E+eWw3j6BMR6sL3COpYWfif7RVb8Y=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "64a525ee38886ab9028e6f61790de0832aa3ef03",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1627913399,
"narHash": "sha256-hY8g6H2KFL8ownSiFeMOjwPC8P0ueXpCVEbxgda3pko=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "12c64ca55c1014cdc1b16ed5a804aa8576601ff2",
"type": "github"
},
"original": {
"id": "flake-compat",
"type": "indirect"
}
},
"flake-compat_3": {
"flake": false,
"locked": {
"lastModified": 1673956053,
@ -55,7 +105,7 @@
"type": "github"
}
},
"flake-compat_2": {
"flake-compat_4": {
"flake": false,
"locked": {
"lastModified": 1673956053,
@ -126,6 +176,20 @@
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1631561581,
"narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
}
},
"flake-utils_3": {
"inputs": {
"systems": "systems_2"
},
@ -143,7 +207,7 @@
"type": "github"
}
},
"flake-utils_3": {
"flake-utils_4": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
@ -180,6 +244,22 @@
"type": "github"
}
},
"gitignore-nix": {
"flake": false,
"locked": {
"lastModified": 1611672876,
"narHash": "sha256-qHu3uZ/o9jBHiA3MEKHJ06k7w4heOhA+4HCSIvflRxo=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "211907489e9f198594c0eb0ca9256a1949c9d412",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": "nixpkgs_2"
@ -231,6 +311,22 @@
"type": "github"
}
},
"lowdown-src_2": {
"flake": false,
"locked": {
"lastModified": 1632468475,
"narHash": "sha256-NNOm9CbdA8cuwbvaBHslGbPTiU6bh1Ao+MpEPx4rSGo=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "6bd668af3fd098bdd07a1bedd399564141e275da",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nil": {
"inputs": {
"flake-utils": "flake-utils",
@ -270,6 +366,24 @@
"type": "indirect"
}
},
"nix_2": {
"inputs": {
"lowdown-src": "lowdown-src_2",
"nixpkgs": "nixpkgs_8"
},
"locked": {
"lastModified": 1633098935,
"narHash": "sha256-UtuBczommNLwUNEnfRI7822z4vPA7OoRKsgAZ8zsHQI=",
"owner": "nixos",
"repo": "nix",
"rev": "4f496150eb4e0012914c11f0a3ff4df2412b1d09",
"type": "github"
},
"original": {
"id": "nix",
"type": "indirect"
}
},
"nixinate": {
"inputs": {
"nixpkgs": "nixpkgs_4"
@ -406,6 +520,19 @@
"type": "github"
}
},
"nixpkgs_10": {
"locked": {
"lastModified": 1676569297,
"narHash": "sha256-2n4C4H3/U+3YbDrQB6xIw7AaLdFISCCFwOkcETAigqU=",
"path": "/nix/store/qhj65h4klgmnkblfly0apznnl3qdir6x-source",
"rev": "ac1f5b72a9e95873d1de0233fddcb56f99884b37",
"type": "path"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1684570954,
@ -488,11 +615,43 @@
},
"nixpkgs_7": {
"locked": {
"lastModified": 1676569297,
"narHash": "sha256-2n4C4H3/U+3YbDrQB6xIw7AaLdFISCCFwOkcETAigqU=",
"path": "/nix/store/qhj65h4klgmnkblfly0apznnl3qdir6x-source",
"rev": "ac1f5b72a9e95873d1de0233fddcb56f99884b37",
"type": "path"
"lastModified": 1648219316,
"narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_8": {
"locked": {
"lastModified": 1632864508,
"narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "82891b5e2c2359d7e58d08849e4c89511ab94234",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-21.05-small",
"type": "indirect"
}
},
"nixpkgs_9": {
"locked": {
"lastModified": 1632495107,
"narHash": "sha256-4NGE56r+FJGBaCYu3CTH4O83Ys4TrtnEPXrvdwg1TDs=",
"owner": "serokell",
"repo": "nixpkgs",
"rev": "be220b2dc47092c1e739bf6aaf630f29e71fe1c4",
"type": "github"
},
"original": {
"id": "nixpkgs",
@ -501,8 +660,8 @@
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils_3",
"flake-compat": "flake-compat_4",
"flake-utils": "flake-utils_4",
"gitignore": "gitignore",
"nixpkgs": [
"tuxedo-rs",
@ -537,6 +696,7 @@
"nixpkgs": "nixpkgs_6",
"nixpkgs-hashicorp": "nixpkgs-hashicorp",
"secret": "secret",
"serokell-nix": "serokell-nix",
"tuxedo-nixos": "tuxedo-nixos",
"tuxedo-rs": "tuxedo-rs",
"udp-over-tcp": "udp-over-tcp",
@ -582,6 +742,30 @@
"type": "path"
}
},
"serokell-nix": {
"inputs": {
"deploy-rs": "deploy-rs",
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils_2",
"gitignore-nix": "gitignore-nix",
"nix": "nix_2",
"nixpkgs": "nixpkgs_9"
},
"locked": {
"lastModified": 1665438610,
"narHash": "sha256-s8/jYo5qseJ4ilyAM2sz1mD5DBybSTrkfd4b9pkgdcU=",
"owner": "serokell",
"repo": "serokell.nix",
"rev": "a4def0b297a0ec69066747df909251a6a7555b1d",
"type": "github"
},
"original": {
"owner": "serokell",
"ref": "magicrb-allow-wildcards-with-no-main",
"repo": "serokell.nix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
@ -630,7 +814,7 @@
},
"tuxedo-nixos": {
"inputs": {
"flake-compat": "flake-compat",
"flake-compat": "flake-compat_3",
"nixpkgs": [
"nixpkgs"
]
@ -651,7 +835,7 @@
},
"tuxedo-rs": {
"inputs": {
"flake-utils": "flake-utils_2",
"flake-utils": "flake-utils_3",
"nixpkgs": [
"nixpkgs"
],
@ -690,7 +874,7 @@
"uterranix": {
"inputs": {
"flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_7",
"nixpkgs": "nixpkgs_10",
"terranix": "terranix"
},
"locked": {
@ -704,6 +888,21 @@
"type": "path"
}
},
"utils": {
"locked": {
"lastModified": 1648297722,
"narHash": "sha256-W+qlPsiZd8F3XkzXOzAoR+mpFqzm3ekQkJNa+PIh1BQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "0f8662f1319ad6abf89b3380dd2722369fc51ade",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"vtermModule": {
"flake": false,
"locked": {

View file

@ -12,6 +12,7 @@
nil.url = "github:oxalica/nil";
uterranix.url = "path:///home/main/uterranix";
dwarffs.url = "github:edolstra/dwarffs";
serokell-nix.url = "github:serokell/serokell.nix?ref=magicrb-allow-wildcards-with-no-main";
tuxedo-rs.url = "github:AaronErhardt/tuxedo-rs";
tuxedo-rs.inputs.nixpkgs.follows = "nixpkgs";
@ -42,6 +43,7 @@
nixos/systems/heater
nixos/systems/toothpick
nixos/systems/liveusb
nixos/systems/blowhole
overlays/udp-over-tcp.nix
overlays/emacsclient-remote
@ -49,10 +51,15 @@
overlays/emacs-rofi
overlays/tree-sitter-grammars.nix
overlays/emacs-master-nativecomp
overlays/zfs-relmount
overlays/ical2org.nix
];
flake.nixosModules = {
hashicorp = nixos/modules/hashicorp.nix;
hashicorp-envoy = nixos/modules/hashicorp-envoy.nix;
telegraf = nixos/modules/telegraf.nix;
grafana = nixos/modules/grafana.nix;
};
flake.apps = inputs.nixpkgs.lib.genAttrs config.systems (system: {
@ -62,6 +69,9 @@
flake.patches = {
hashicorp-nomad.revert-change-consul-si-tokens-to-be-local = patches/0001-Revert-Change-consul-SI-tokens-to-be-local.patch;
hashicorp-nomad.add-nix-integration = patches/0001-Add-Nix-integration.patch;
hostapd.intel_lar-and-noscan = patches/0001-intel_lar-and-noscan.patch;
hostapd.hostapd-2_10-lar = patches/999-hostapd-2.10-lar.patch;
hostapd.hostapd-2_10-lar-2 = patches/hostapd-2.10-lar.patch;
};
systems = [

181
nixos/modules/grafana.nix Normal file
View file

@ -0,0 +1,181 @@
{ options, config, lib, pkgs, ... }:
let
inherit (lib)
mkEnableOption
mkOption
literalExpression
types
mkDefault
mkIf
recursiveUpdate
;
cfg = config.services.grafana-magic;
settingsFile = settingsFormatIni.generate "config.ini" (recursiveUpdate cfg.settings {
paths.provisioning = "/etc/grafana.d/provisioning";
});
provisioningSettingsFormat = pkgs.formats.yaml {};
settingsFormatIni = pkgs.formats.ini {};
in {
options.services.grafana-magic = {
enable = mkEnableOption (lib.mdDoc "grafana");
package = mkOption {
description = lib.mdDoc "Package to use.";
default = pkgs.grafana;
defaultText = literalExpression "pkgs.grafana";
type = types.package;
};
dataDir = mkOption {
description = lib.mdDoc "Data directory.";
default = "/var/lib/grafana";
type = types.path;
};
settings = mkOption {
description = lib.mdDoc ''
Grafana settings. See <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/>
for available options. INI format is used.
'';
type = types.submodule {
freeformType = settingsFormatIni.type;
options = {
paths.provisioning = mkOption {
type = types.submodule {
options =
let
provisioningOption = name: cname:
mkOption {
type = types.submodule {
options = {
apiVersion = mkOption {
type = types.int;
default = 1;
};
"delete${cname}" = mkOption {
type = provisioningSettingsFormat.type;
default = [];
};
"${name}" = mkOption {
type = provisioningSettingsFormat.type;
default = [];
};
};
};
default = {};
};
in
{
datasources = provisioningOption "datasources" "Datasources";
plugins = provisioningOption "plugins" "Plugins";
dashboards = provisioningOption "dashboards" "Dashboards";
notifiers = provisioningOption "notifiers" "Notifiers";
alerting = provisioningOption "alerting" "Alerting";
};
};
default = {};
apply = x:
let
ln = name:
''
mkdir -p $out/${name}
ln -s ${provisioningSettingsFormat.generate "config.yaml" x.${name}} $out/${name}/config.yaml
'';
in
pkgs.runCommand "grafana-provisioning" {} ''
${ln "datasources"}
${ln "notifiers"}
${ln "alerting"}
${ln "plugins"}
${ln "dashboards"}
'';
};
};
};
default = {};
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
services.grafana-magic.settings = {
server = {
static_root_path = "${cfg.package}/share/grafana/public";
http_port = mkDefault 3000;
protocol = mkDefault "http";
};
};
environment.etc."grafana.d/main.ini" = {
source = settingsFile;
};
environment.etc."grafana.d/provisioning" = {
source = cfg.settings.paths.provisioning;
};
systemd.services.grafana = {
description = "Grafana Service Daemon";
wantedBy = [ "multi-user.target" ];
after = [ "networking.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/grafana-server -homepath ${cfg.dataDir} -config ${settingsFile}";
WorkingDirectory = cfg.dataDir;
User = "grafana";
RuntimeDirectory = "grafana";
RuntimeDirectoryMode = "0755";
# Hardening
AmbientCapabilities = lib.mkIf (cfg.settings.server.http_port < 1024) [ "CAP_NET_BIND_SERVICE" ];
CapabilityBoundingSet = if (cfg.settings.server.http_port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
DeviceAllow = [ "" ];
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "full";
RemoveIPC = true;
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
# Upstream grafana is not setting SystemCallFilter for compatibility
# reasons, see https://github.com/grafana/grafana/pull/40176
SystemCallFilter = [
"@system-service"
"~@privileged"
] ++ lib.optional (cfg.settings.server.protocol == "socket") [ "@chown" ];
UMask = "0027";
};
preStart = ''
ln -fs ${cfg.package}/share/grafana/conf ${cfg.dataDir}
ln -fs ${cfg.package}/share/grafana/tools ${cfg.dataDir}
'';
};
users.users.grafana = {
uid = config.ids.uids.grafana;
description = "Grafana user";
home = cfg.dataDir;
createHome = true;
group = "grafana";
};
users.groups.grafana = {};
};
}

View file

@ -0,0 +1,173 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.hashicorp-envoy;
serviceFormat = pkgs.formats.json {};
serviceFile = name: value:
if value.type == "normal" then
serviceFormat.generate "${name}-service.json" { service = value.service; }
else
serviceFormat.generate "${name}-service.json" value.service;
in
{
options.services.hashicorp-envoy = mkOption {
description = mdDoc ''
'';
type = types.attrsOf (types.submodule {
options = {
service = mkOption {
description = mdDoc ''
'';
type = with types; oneOf [ serviceFormat.type (listOf serviceFormat.type) ];
};
type = mkOption {
description = mdDoc ''
'';
type = with types; enum [ "ingress" "terminating" "normal" ];
default = "normal";
};
environment = mkOption {
description = mdDoc ''
'';
type = with types; attrsOf str;
default = {};
};
adminBind = mkOption {
description = mdDoc ''
'';
type = types.str;
};
address = mkOption {
description = mdDoc ''
'';
type = types.str;
default = "0.0.0.0:19000";
};
drainTime = mkOption {
description = mdDoc ''
'';
type = types.int;
default = 15;
};
parentShutdownTime = mkOption {
description = mdDoc ''
'';
type = types.int;
default = 20;
};
hotRestart = mkOption {
description = mdDoc ''
'';
type = types.bool;
default = false;
};
consulPackage = mkOption {
description = mdDoc ''
'';
type = types.package;
default = pkgs.consul;
};
envoyPackage = mkOption {
description = mdDoc ''
'';
type = types.package;
default = pkgs.envoy;
};
};
});
default = {};
};
config = {
systemd.services = flip mapAttrs' cfg (name: value:
nameValuePair
"hashicorp-envoy-${name}"
{
description = name;
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
path = [ value.envoyPackage ];
restartIfChanged = true;
preStart =
if value.type == "normal" then
''
${value.consulPackage}/bin/consul services register ${serviceFile name value}
''
else
''
${value.consulPackage}/bin/consul config write ${serviceFile name value}
'';
postStop =
if value.type == "normal" then
''
${value.consulPackage}/bin/consul services deregister -id=${value.service.id}
''
else
''
${value.consulPackage}/bin/consul config delete -filename ${serviceFile name value}
'';
script =
let
startEnvoy = pkgs.writeShellScript "start_envoy_${name}.sh"
''
exec ${value.consulPackage}/bin/consul connect envoy \
${optionalString (value.type == "normal") ''
-sidecar-for ${value.service.id} \
''} \
${optionalString (value.type == "ingress") ''
-gateway=ingress \
-register \
-service ${value.service.name} \
''} \
-admin-bind ${value.adminBind} \
-address ${value.address} \
${optionalString value.hotRestart ''
-- \
$([[ $RESTART_EPOCH == 0 ]] && printf -- "--use-dynamic-base-id --base-id-path $RUNTIME_DIRECTORY/id") \
$([[ $RESTART_EPOCH == 0 ]] || printf -- "--base-id $(cat $RUNTIME_DIRECTORY/id)") \
--restart-epoch $RESTART_EPOCH \
--drain-time-s ${toString value.drainTime} \
--parent-shutdown-time-s ${toString value.parentShutdownTime}
''}
'';
in
if value.hotRestart then
"exec ${pkgs.python3}/bin/python ${value.envoyPackage.src}/restarter/hot-restarter.py ${startEnvoy}"
else
"exec ${startEnvoy}";
environment = value.environment;
serviceConfig = {
ExecReload = if value.hotRestart then "${pkgs.coreutils}/bin/kill -HUP $MAINPID" else null;
KillMode = "control-group";
KillSignal = "SIGINT";
LimitNOFILE = 65536;
LimitNPROC = "infinity";
OOMScoreAdjust = -1000;
Restart = "always";
RestartSec = 2;
TasksMax = "infinity";
RuntimeDirectory = name;
};
}
);
};
}

View file

@ -0,0 +1,72 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.telegraf-magic;
settingsFormat = pkgs.formats.toml {};
configFile = settingsFormat.generate "config.toml" cfg.settings;
in {
options = {
services.telegraf-magic = {
enable = mkEnableOption (lib.mdDoc "telegraf server");
package = mkOption {
default = pkgs.telegraf;
defaultText = literalExpression "pkgs.telegraf";
description = lib.mdDoc "Which telegraf derivation to use";
type = types.package;
};
settings = mkOption {
default = {};
description = lib.mdDoc "Extra configuration options for telegraf";
type = settingsFormat.type;
example = {
outputs.influxdb = {
urls = ["http://localhost:8086"];
database = "telegraf";
};
inputs.statsd = {
service_address = ":8125";
delete_timings = true;
};
};
};
systemd = mkOption {
default = {};
description = lib.mdDoc "Applied to `systemd.services.telegraf`.";
type = types.unspecified;
};
};
};
config = mkIf cfg.enable {
systemd.services.telegraf = mkMerge [
(cfg.systemd)
{
description = "Telegraf Agent";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
serviceConfig = {
ExecStart="${cfg.package}/bin/telegraf -config ${configFile}";
ExecReload="${pkgs.coreutils}/bin/kill -HUP $MAINPID";
RuntimeDirectory = "telegraf";
User = "telegraf";
Group = "telegraf";
Restart = "on-failure";
# for ping probes
AmbientCapabilities = [ "CAP_NET_RAW" ];
};
}
];
users.users.telegraf = {
uid = config.ids.uids.telegraf;
group = "telegraf";
description = "telegraf daemon user";
};
users.groups.telegraf = {};
};
}

View file

@ -0,0 +1,81 @@
{ lib, pkgs, secret, ... }:
let
inherit (lib)
concatMapStringsSep;
loggingConfig = ''
logging {
${concatMapStringsSep "\n" (x:
''
channel ${x}_file {
file "/var/log/named/${x}.log" versions 3 size 5m;
severity dynamic;
print-time yes;
};
category ${x} { ${x}_file; };
'') [
"default"
"database"
"security"
"config"
"resolver"
"xfer-in"
"xfer-out"
"notify"
"client"
"unmatched"
"queries"
"network"
"update"
"network"
"dispatch"
"dnssec"
"lame-servers"
]}
};
'';
in
{
systemd.tmpfiles.rules = [
"d /var/log/named 0750 named named - -"
];
services.bind = {
enable = true;
forward = "only";
forwarders = [
"127.0.0.1 port 5353"
];
directory = "/var/lib/bind";
zones = {
"in.redalder.org" = {
file = ./zones/in.redalder.org.zone;
master = true;
};
"hosts.in.redalder.org" = {
file = ./zones/hosts.in.redalder.org.zone;
master = true;
};
};
cacheNetworks = [
"127.0.0.0/8"
(secret.network.networks.home.wireless or "")
(secret.network.networks.home.mine or "")
"10.64.99.0/24"
(secret.network.networks.home.amsterdam or "")
(secret.network.networks.vpn or "")
"172.26.64.0/20"
];
extraConfig = loggingConfig;
extraOptions = ''
# recursion yes;
dnssec-validation auto;
'';
};
systemd.services.bind = {
before = [ "network-online.target" ];
};
}

View file

@ -0,0 +1,80 @@
{inputs', lib, config, pkgs, secret, ...}:
let
inherit (lib)
singleton
mkForce;
in
{
services.hashicorp.vault-agent = {
settings.template = singleton {
source = pkgs.writeText "consul.json.vtmpl"
''
{
"encrypt": "{{ with secret "kv/data/homelab-1/blowhole/consul/encryption_key" }}{{ or .Data.data.key "" }}{{ end }}",
"acl": {
"tokens": {
"agent": "{{ with secret "kv/data/homelab-1/blowhole/consul/agent_token" }}{{ or .Data.data.secret "" }}{{ end }}",
"default": "{{ with secret "kv/data/homelab-1/blowhole/consul/anonymous_token" }}{{ or .Data.data.secret "" }}{{ end }}"
}
}
}
'';
destination = "/run/secrets/consul.json";
command = pkgs.writeShellScript "consul-command" ''
sudo systemctl try-reload-or-restart hashicorp-consul.service
'';
};
};
systemd.services.hashicorp-consul.unitConfig = {
ConditionPathExists = "/run/secrets/consul.json";
};
services.hashicorp.consul = {
enable = true;
extraSettingsPaths = singleton "/run/secrets/consul.json";
package = inputs'.nixpkgs-hashicorp.legacyPackages.${pkgs.stdenv.system}.consul;
settings = {
datacenter = "homelab-1";
data_dir = "/var/lib/consul";
log_level = "INFO";
server = true;
bind_addr = secret.network.ips.blowhole.ip or "";
client_addr = secret.network.ips.blowhole.ip or "";
primary_datacenter = "homelab-1";
acl = {
enabled = true;
default_policy = "deny";
enable_token_persistence = true;
};
ports = {
http = 8500;
grpc = 8502;
};
connect.enabled = true;
ca_file = "/var/secrets/consul-ca.crt";
# cert_file = ""
# key_file = ""
verify_incoming = false;
verify_outgoing = false;
verify_server_hostname = false;
ui_config.enabled = true;
domain = "consul.in.redalder.org";
};
};
systemd.services.hashicorp-consul.serviceConfig = {
LimitNOFILE = mkForce "infinity";
LimitNPROC = mkForce "infinity";
};
}

View file

@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: 2022 Richard Brežák <richard@brezak.sk>
#
# SPDX-License-Identifier: LGPL-3.0-or-later
{ inputs, lib, config, ... }:
let
inherit (lib)
flip
mapAttrs
singleton;
config' = config;
in
{
flake.nixosConfigurations.blowhole = inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {
config' = config';
inputs' = inputs;
secret =
if builtins.pathExists "${inputs.secret}/default.nix" then
import inputs.secret { inherit lib; }
else
{};
};
modules = singleton
({ pkgs, config, ... }:
{
imports = [
./bind.nix
./consul.nix
./filesystems.nix
./firewall.nix
./grub.nix
./hardware.nix
./hostapd.nix
./ical2org.nix
./klipper.nix
./monitoring.nix
./nas.nix
./networking.nix
./nfs.nix
./nomad.nix
./uterranix.nix
./vault-agent.nix
./vault.nix
./watchdog.nix
./nixpkgs.nix
./users.nix
../../common/remote_access.nix
inputs.serokell-nix.nixosModules.acme-sh
config'.flake.nixosModules.hashicorp
config'.flake.nixosModules.hashicorp-envoy
config'.flake.nixosModules.telegraf
config'.flake.nixosModules.grafana
];
_module.args.nixinate = {
host = "blowhole.hosts.in.redalder.org";
sshUser = "main";
buildOn = "local";
substituteOnTarget = true;
hermetic = false;
nixOptions = [
"--override-input secret path://$HOME/dotfiles/secret"
];
};
system.stateVersion = "21.05";
});
};
}

View file

@ -0,0 +1,77 @@
{ pkgs, lib, secret, ... }:
let
inherit (lib)
singleton;
in
{
environment.systemPackages = with pkgs; [
sshfs
];
fileSystems =
{
"/boot" = {
device = "/dev/disk/by-uuid/738acc32-3e2e-4986-987c-40264153d5bf";
fsType = "ext4";
};
"/" = {
device = "blowhole-zpool/local/root";
fsType = "zfs";
};
"/nix" = {
device = "blowhole-zpool/local/nix";
fsType = "zfs";
};
"/var/nfs" = {
device = "/dev/disk/by-uuid/e06f6d2c-e434-4eec-b00d-b13c1ecc96f0";
fsType = "btrfs";
options = [
"subvol=/nfs"
"noatime"
];
};
"/mnt/cctv" = {
device = "camera@${secret.network.ips.woodchip or ""}:/";
fsType = "fuse.sshfs";
noCheck = true;
options = [
"_netdev"
"noauto"
"x-systemd.automount"
"IdentityFile=/run/secrets/id_ed_camera"
"StrictHostKeyChecking=no"
"allow_other"
"reconnect"
"Port=2522"
];
};
"/old-root" = {
device = "/dev/disk/by-uuid/e06f6d2c-e434-4eec-b00d-b13c1ecc96f0";
fsType = "btrfs";
options = [
"subvol=/arch"
"noatime"
];
};
"/var/lib/nomad" = {
device = "blowhole-zpool/persist/nomad";
fsType = "zfs";
};
"/var/secrets" = {
device = "blowhole-zpool/persist/secrets";
fsType = "zfs";
};
"/var/lib/consul" = {
device = "/old-root/var/lib/consul";
options = singleton "bind";
};
"/var/lib/vault" = {
device = "/old-root/var/lib/vault";
options = singleton "bind";
};
}
// secret.mounts.blowhole or {};
}

View file

@ -0,0 +1,303 @@
{ pkgs, secret, config, lib, ... }:
let
inherit (lib)
mapAttrs
const
mkForce
singleton;
wlan = "wlp10s0";
lan = "enp8s0f1";
wan = "enp3s0";
doVPN = "do_vpn0";
nomad = mapAttrs (const toString) {
inherit (config.services.hashicorp.nomad.settings.client)
min_dynamic_port
max_dynamic_port;
};
in
{
boot.kernel.sysctl = {
# Enable forwarding on IPv4 but disable on IPv6
"net.ipv4.conf.all.forwarding" = true;
"net.ipv6.conf.all.forwarding" = false;
# source: https://github.com/mdlayher/homelab/blob/master/nixos/routnerr-2/configuration.nix#L52
# By default, not automatically configure any IPv6 addresses.
"net.ipv6.conf.all.accept_ra" = 0;
"net.ipv6.conf.all.autoconf" = 0;
"net.ipv6.conf.all.use_tempaddr" = 0;
# On WAN, allow IPv6 autoconfiguration and tempory address use.
# "net.ipv6.conf.${name}.accept_ra" = 2;
# "net.ipv6.conf.${name}.autoconf" = 1;
};
services.dnscrypt-proxy2 = {
enable = true;
upstreamDefaults = true;
settings = {
listen_addresses = singleton "127.0.0.1:5353";
dnscrypt_servers = false;
doh_servers = true;
odoh_servers = false;
block_ipv6 = true;
static."mullvad".stamp = "sdns://AgcAAAAAAAAAAAAPZG9oLm11bGx2YWQubmV0Ci9kbnMtcXVlcnk";
static."meganerd".stamp = "sdns://AQcAAAAAAAAADjEzNi4yNDQuOTcuMTE0ICif6V9M6EF_9Xo_MHwkDN4ZJjERopSJN8hBuUWg9YeMJTIuZG5zY3J5cHQtY2VydC5jaGV3YmFjY2EubWVnYW5lcmQubmw";
sources = {};
};
};
systemd.services.dnscrypt-proxy2 = {
before = [ "network-online.target" ];
};
services.dhcpd4 = {
enable = true;
interfaces = [ "${lan}" "${wlan}" ];
extraConfig = ''
option domain-name-servers ${secret.network.ips.blowhole.ip or ""};
option subnet-mask 255.255.255.0;
${secret.dhcp.blowhole.zones or (const "") { inherit wlan lan; }}
'';
};
networking = {
useDHCP = false;
hostName = "blowhole";
resolvconf.useLocalResolver = false;
nameservers = singleton (secret.network.ips.blowhole.ip or "");
# Disable the in-built iptable based firewall
firewall.enable = mkForce false;
localCommands = ''
ip link add enp4s0 type dummy || true
ip link set enp4s0 up || true
ip addr add ${secret.network.ips.blowhole.ip}/24 dev enp4s0 || true
'';
interfaces = {
# Don't do DHCP on the LAN interface
"${lan}" = {
useDHCP = false;
ipv4.addresses = [{
address = secret.network.ips.blowhole.ip or "";
prefixLength = 24;
}];
};
"${wlan}" = {
useDHCP = false;
ipv4.addresses = [{
address = secret.network.ips.blowhole.wlan or "";
prefixLength = 24;
}];
};
# But do DHCP on the WAN interface
"${wan}".useDHCP = true;
};
wireguard = {
enable = true;
interfaces."${doVPN}" =
secret.wireguard."${config.networking.hostName}" or {} // {
listenPort = 6666;
privateKeyFile = "/var/secrets/${doVPN}.key";
};
};
nftables = {
enable = true;
ruleset = ''
table ip nf_filter {
chain input_out {
ct state { established, related } accept comment "Allow established traffic"
icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP"
}
chain input_doVPN {
tcp dport { 4646, 4647, 4648 } accept comment "Nomad traffic"
tcp dport { 8600, 8500, 8502, 8300, 8301, 8302 } accept comment "Consul traffic"
tcp dport { 8200 } accept comment "Vault traffic"
tcp dport { 111, 2049, 4000, 4001, 4002, 20048 } accept comment "NFS traffic"
tcp dport ${nomad.min_dynamic_port}-${nomad.max_dynamic_port} accept comment "Consul Connect sidecar traffic"
tcp dport { 53 } accept comment "DNS traffic"
tcp dport { 80 } accept comment "HTTP traffic"
udp dport { 8600, 8301, 8302 } comment "Consul traffic"
udp dport { 111, 2049, 4000, 4001, 4002, 20048 } accept comment "NFS traffic"
udp dport ${nomad.min_dynamic_port}-${nomad.max_dynamic_port} accept comment "Consul Connect sidecar traffic"
udp dport { 53 } accept comment "DNS traffic"
}
chain input {
type filter hook input priority 0; policy drop;
tcp dport 22 accept comment "Accept SSH traffic always"
iifname != "lo" tcp dport 5353 drop comment "Drop traffic to dnscrypt-proxy always except for localhost to localhost traffic"
iifname { "nomad", "ve-monitor", "ve-klipper" } oifname { "nomad", "ve-monitor", "ve-klipper" } accept comment "Allow Nomad to do whatever it wants in its interface"
iifname { "${wlan}", "${lan}", "lo" } accept comment "Allow local network to access the router"
iifname { "${wan}", "${doVPN}", "nomad", "docker0", "ve-monitor", "ve-klipper" } jump input_out
iifname { "${doVPN}" } jump input_doVPN
# Allow containers to reach the DNS server
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } tcp dport 53 accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } udp dport 53 accept
# Allow Nomad Containers to reach Nomad
iifname { "nomad" } tcp dport 4646 accept
# Allow proxies to reach consul
iifname { "nomad", "ve-monitor", "ve-klipper" } tcp dport 8500 accept
iifname { "ve-monitor", "ve-klipper" } tcp dport 8502 accept
# Allow containers to reach the NFS server
iifname { "docker0" } tcp dport { 111, 2049, 4000, 4001, 4002, 20048 } accept comment "NFS traffic"
iifname { "docker0" } udp dport { 111, 2049, 4000, 4001, 4002, 20048 } accept comment "NFS traffic"
meta nftrace set 1
}
chain output {
type filter hook output priority 0; policy accept;
# Drop all DNS traffic if leaving through "wan"
# oifname { "${wan}" } tcp dport 53 drop
# oifname { "${wan}" } udp dport 53 drop
# Allow DoT traffic to leave through "wan" if it comes from "lo"
# iifname != { "lo" } oifname { "${wan}" } tcp dport 853 drop
}
chain forward {
type filter hook forward priority 10; policy drop;
# Enable flow offloading for better throughput
# ip protocol { tcp, udp } flow offload @f
# Drop all DNS or DoT traffic if forwarded through "wan"
oifname { "${wan}" } tcp dport 853 drop
oifname { "${wan}" } tcp dport 53 drop
oifname { "${wan}" } udp dport 53 drop
# Allow trusted LAN to WAN"
iifname { "${lan}", "${wlan}" } oifname { "${wan}" } accept
iifname { "${wan}" } oifname { "${lan}", "${wlan}" } ct state established, related accept
iifname { "nomad" } oifname { "${doVPN}", "${lan}", "${wlan}" } accept
iifname { "${doVPN}", "${lan}", "${wlan}" } oifname { "nomad" } accept
iifname { "${doVPN}" } oifname { "${lan}", "${wlan}" } accept
iifname { "${lan}", "${wlan}" } oifname { "${doVPN}" } accept
# Allow containers to reach WAN
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${wan}" } accept
iifname { "${wan}" } oifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } ct state established, related accept
# Allow containers to reach the DNS and NFS server
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip daddr 10.64.2.1 tcp dport { 53 } accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip saddr 10.64.2.1 tcp sport { 53 } accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip daddr 10.64.2.1 tcp dport { 111, 2049, 4000, 4001, 4002, 20048 } accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip saddr 10.64.2.1 tcp sport { 111, 2049, 4000, 4001, 4002, 20048 } accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip daddr 10.64.2.1 udp dport { 53 } accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip saddr 10.64.2.1 udp sport { 53 } accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip daddr 10.64.2.1 udp dport { 111, 2049, 4000, 4001, 4002, 20048 } accept
iifname { "nomad", "docker0", "ve-monitor", "ve-klipper" } oifname { "${lan}" } ip saddr 10.64.2.1 udp sport { 111, 2049, 4000, 4001, 4002, 20048 } accept
# Rules to make CNI happy
meta mark and 0x01 == 0x01 accept
meta nftrace set 1
}
}
table ip nf_nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname "${wan}" masquerade
}
chain prerouting {
type nat hook prerouting priority 100; policy accept;
}
}
table ip6 nf_filter {
chain output {
type filter hook output priority 0; policy drop;
meta nftrace set 1
oifname "lo" icmpv6 type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP"
oifname "lo" ip6 saddr "::1" ip6 daddr "::1" reject
}
chain input {
type filter hook input priority 0; policy drop;
meta nftrace set 1
iifname "lo" icmpv6 type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP"
}
chain forward {
type filter hook forward priority 0; policy drop;
}
}
'';
};
};
systemd.services.nftables = {
serviceConfig =
let
rulesScript = pkgs.writeShellScript "nftables-rules" ''
set -ex
export PATH=${pkgs.nftables}/bin:${pkgs.iptables}/bin:${pkgs.bash}/bin:$PATH
tmpfile="$(mktemp)"
iptables-save -t filter >> $tmpfile
iptables-save -t nat >> $tmpfile
nft flush ruleset
cat $tmpfile | iptables-restore
nft -f "${pkgs.writeText "nftables-rules" config.networking.nftables.ruleset}"
rm $tmpfile
iptables -D FORWARD -j MARK --set-mark 0x01 || true
iptables -D FORWARD -j MARK --set-mark 0x00 || true
iptables -I FORWARD -j MARK --set-mark 0x01
iptables -A FORWARD -j MARK --set-mark 0x00
'';
in {
ExecStart = mkForce rulesScript;
ExecReload = mkForce rulesScript;
ExecStop = mkForce (pkgs.writeShellScript "nftables-flush" ''
set -ex
export PATH=${pkgs.nftables}/bin:${pkgs.iptables}/bin:${pkgs.bash}/bin:$PATH
tmpfile="$(mktemp)"
iptables-save -t filter >> $tmpfile
iptables-save -t nat >> $tmpfile
nft flush ruleset
cat $tmpfile | iptables-restore
rm $tmpfile
iptables -D FORWARD -j MARK --set-mark 0x01 || true
iptables -D FORWARD -j MARK --set-mark 0x00 || true
iptables -I FORWARD -j MARK --set-mark 0x01
iptables -A FORWARD -j MARK --set-mark 0x00
'');
};
};
}

View file

@ -0,0 +1,10 @@
{ ... }:
{
boot.loader = {
systemd-boot.enable = false;
grub = {
enable = true;
devices = [ "/dev/disk/by-id/usb-Verbatim_STORE_N_GO_072124E3712B7287-0:0" ];
};
};
}

View file

@ -0,0 +1,18 @@
{ config, ... }:
{
boot = {
supportedFilesystems = ["zfs"];
initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
"nvme"
];
zfs.enableUnstable = true;
kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
};
hardware.enableRedistributableFirmware = true;
}

View file

@ -0,0 +1,539 @@
{ pkgs, config, lib, config', ... }:
let
inherit (lib)
singleton;
openwrtRepo = pkgs.fetchFromGitHub {
owner = "openwrt";
repo = "openwrt";
rev = "67e8cc07f9bb95984624198ccf02123f348246df";
sha256 = "sha256-rBQDTUG9fqwSLrj+LZ6L1x55Y3gkfUubY5zwX9XK3+s=";
};
in
{
# giturl="https://raw.githubusercontent.com/openwrt/openwrt/75b83e94a395fedeb4d308f42013a72c6fee2df4/package/network/services/hostapd/patches/"
# for patch in *.patch
# do
# nix-prefetch-url "$giturl$patch" 2>/dev/null | \
# sed -e 's~^~{ url = "'"$giturl$patch"'"; sha256 = "~' | sed -e 's~$~"; \}~'
# done
services.hashicorp.vault-agent.settings.template = singleton {
source = pkgs.writeText "hostapd_wpa_psk.vtmpl" ''
{{ with secret "kv/data/homelab-1/blowhole/hostapd/wpa_psk" -}}
{{ range $key, $value := .Data.data -}}
{{ with $data := $value -}}
{{ $data.mac_address }} {{ $data.psk }}
{{ end -}}
{{ end -}}
{{ end -}}
'';
destination = "/run/secrets/hostapd_wpa_psk";
};
systemd.services.hostapd.unitConfig = {
ConditionPathExists = "/run/secrets/hostapd_wpa_psk";
};
services.hostapd = {
interface = "wlp10s0";
driver = "nl80211";
ssid = "nothing";
wpa = false;
hwMode = "g";
channel = 14;
countryCode = "NL";
enable = true;
extraConfig = ''
wpa_psk_file=/run/secrets/hostapd_wpa_psk
#ieee80211d=1
#ieee80211h=1
#ieee80211n=1
# ieee80211ac=1
# intel_lar=1
noscan=0
beacon_int=100
channel=14
# channel=149
# chanlist=149
#ht_capab=[HT40+][LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935][DSSS_CCK-40]
#ht_capab=[VHT_IBSS][RRM][MU_MIMO_AIR_SNIFFER][SCAN_START_TIME][BSS_PARENT_TSF][FILS_STA][FILS_MAX_CHANNEL_TIME][ACCEPT_BCAST_PROBE_RESP][OCE_PROBE_REQ_HIGH_TX_RATE][CONTROL_PORT_OVER_NL80211][TXQS][ENABLE_FTM_RESPONDER][CONTROL_PORT_NO_PREAUTH][PROTECTED_TWT][DEL_IBSS_STA][BEACON_PROTECTION_CLIENT][SCAN_FREQ_KHZ][CONTROL_PORT_OVER_NL80211_TX_STATUS]
ap_isolate=1
preamble=1
wmm_enabled=1
utf8_ssid=1
auth_algs=1
wpa=2
wpa_pairwise=CCMP
wpa_disable_eapol_key_retries=0
wpa_key_mgmt=WPA-PSK
okc=0
disable_pmksa_caching=1
bssid=e0:d0:45:81:50:00
# bss=wlp10s1
# ssid=nothing2
# bssid=e0:d0:45:81:50:01
# hw_mode=g
# channel=9
# vht_capab=
# ht_capab=
# auth_algs=1
# wpa=2
# wpa_pairwise=CCMP
# wpa_disable_eapol_key_retries=0
# wpa_key_mgmt=WPA-PSK
# okc=0
# disable_pmksa_caching=1
# wpa_passphrase=${config.services.hostapd.wpaPassphrase}
'';
};
nixpkgs.overlays = singleton
(final: prev:
{
hostapd = prev.hostapd.overrideAttrs (old: {
buildInputs = old.buildInputs ++ (with pkgs; [
libubox
ubus
]);
src = pkgs.fetchgit {
url = "http://w1.fi/hostap.git";
rev = "bb945b98fefc64887dffb40773a19d77585cee42";
sha256 = "sha256-bDxMWjvgyNdBUH8pXJ+yMl3vBBoz57LOJEZHAGSDyS0=";
};
extraConfig = ''
# Example hostapd build time configuration
#
# This file lists the configuration options that are used when building the
# hostapd binary. All lines starting with # are ignored. Configuration option
# lines must be commented out complete, if they are not to be included, i.e.,
# just setting VARIABLE=n is not disabling that variable.
#
# This file is included in Makefile, so variables like CFLAGS and LIBS can also
# be modified from here. In most cass, these lines should use += in order not
# to override previous values of the variables.
# Driver interface for Host AP driver
#CONFIG_DRIVER_HOSTAP=y
# Driver interface for wired authenticator
CONFIG_DRIVER_WIRED=y
# Driver interface for drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y
# QCA vendor extensions to nl80211
#CONFIG_DRIVER_NL80211_QCA=y
# driver_nl80211.c requires libnl. If you are compiling it yourself
# you may need to point hostapd to your version of libnl.
#
#CFLAGS += -I$<path to libnl include files>
#LIBS += -L$<path to libnl library files>
# Use libnl v2.0 (or 3.0) libraries.
#CONFIG_LIBNL20=y
# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
#CONFIG_LIBNL32=y
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
#CONFIG_DRIVER_BSD=y
#CFLAGS += -I/usr/local/include
#LIBS += -L/usr/local/lib
#LIBS_p += -L/usr/local/lib
#LIBS_c += -L/usr/local/lib
# Driver interface for no driver (e.g., RADIUS server only)
#CONFIG_DRIVER_NONE=y
# IEEE 802.11F/IAPP
CONFIG_IAPP=y
# WPA2/IEEE 802.11i RSN pre-authentication
CONFIG_RSN_PREAUTH=y
# IEEE 802.11w (management frame protection)
#CONFIG_IEEE80211W=y
# Support Operating Channel Validation
#CONFIG_OCV=y
# Integrated EAP server
CONFIG_EAP=y
# EAP Re-authentication Protocol (ERP) in integrated EAP server
CONFIG_ERP=y
# EAP-MD5 for the integrated EAP server
CONFIG_EAP_MD5=y
# EAP-TLS for the integrated EAP server
CONFIG_EAP_TLS=y
# EAP-MSCHAPv2 for the integrated EAP server
CONFIG_EAP_MSCHAPV2=y
# EAP-PEAP for the integrated EAP server
CONFIG_EAP_PEAP=y
# EAP-GTC for the integrated EAP server
CONFIG_EAP_GTC=y
# EAP-TTLS for the integrated EAP server
CONFIG_EAP_TTLS=y
# EAP-SIM for the integrated EAP server
#CONFIG_EAP_SIM=y
# EAP-AKA for the integrated EAP server
#CONFIG_EAP_AKA=y
# EAP-AKA' for the integrated EAP server
# This requires CONFIG_EAP_AKA to be enabled, too.
#CONFIG_EAP_AKA_PRIME=y
# EAP-PAX for the integrated EAP server
#CONFIG_EAP_PAX=y
# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
#CONFIG_EAP_PSK=y
# EAP-pwd for the integrated EAP server (secure authentication with a password)
#CONFIG_EAP_PWD=y
# EAP-SAKE for the integrated EAP server
#CONFIG_EAP_SAKE=y
# EAP-GPSK for the integrated EAP server
#CONFIG_EAP_GPSK=y
# Include support for optional SHA256 cipher suite in EAP-GPSK
#CONFIG_EAP_GPSK_SHA256=y
# EAP-FAST for the integrated EAP server
CONFIG_EAP_FAST=y
# EAP-TEAP for the integrated EAP server
# Note: The current EAP-TEAP implementation is experimental and should not be
# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number
# of conflicting statements and missing details and the implementation has
# vendor specific workarounds for those and as such, may not interoperate with
# any other implementation. This should not be used for anything else than
# experimentation and interoperability testing until those issues has been
# resolved.
#CONFIG_EAP_TEAP=y
# Wi-Fi Protected Setup (WPS)
CONFIG_WPS=y
# Enable UPnP support for external WPS Registrars
#CONFIG_WPS_UPNP=y
# Enable WPS support with NFC config method
#CONFIG_WPS_NFC=y
# EAP-IKEv2
#CONFIG_EAP_IKEV2=y
# Trusted Network Connect (EAP-TNC)
#CONFIG_EAP_TNC=y
# EAP-EKE for the integrated EAP server
#CONFIG_EAP_EKE=y
# PKCS#12 (PFX) support (used to read private key and certificate file from
# a file that usually has extension .p12 or .pfx)
CONFIG_PKCS12=y
# RADIUS authentication server. This provides access to the integrated EAP
# server from external hosts using RADIUS.
#CONFIG_RADIUS_SERVER=y
# Build IPv6 support for RADIUS operations
CONFIG_IPV6=y
# IEEE Std 802.11r-2008 (Fast BSS Transition)
CONFIG_IEEE80211R=y
# Use the hostapd's IEEE 802.11 authentication (ACL), but without
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
#CONFIG_DRIVER_RADIUS_ACL=y
# IEEE 802.11n (High Throughput) support
CONFIG_IEEE80211N=y
# Wireless Network Management (IEEE Std 802.11v-2011)
# Note: This is experimental and not complete implementation.
CONFIG_WNM=y
# IEEE 802.11ac (Very High Throughput) support
CONFIG_IEEE80211AC=y
# IEEE 802.11ax HE support
# Note: This is experimental and work in progress. The definitions are still
# subject to change and this should not be expected to interoperate with the
# final IEEE 802.11ax version.
CONFIG_IEEE80211AX=y
# Remove debugging code that is printing out debug messages to stdout.
# This can be used to reduce the size of the hostapd considerably if debugging
# code is not needed.
#CONFIG_NO_STDOUT_DEBUG=y
# Add support for writing debug log to a file: -f /tmp/hostapd.log
# Disabled by default.
#CONFIG_DEBUG_FILE=y
# Send debug messages to syslog instead of stdout
CONFIG_DEBUG_SYSLOG=y
# Add support for sending all debug messages (regardless of debug verbosity)
# to the Linux kernel tracing facility. This helps debug the entire stack by
# making it easy to record everything happening from the driver up into the
# same file, e.g., using trace-cmd.
#CONFIG_DEBUG_LINUX_TRACING=y
# Remove support for RADIUS accounting
#CONFIG_NO_ACCOUNTING=y
# Remove support for RADIUS
#CONFIG_NO_RADIUS=y
# Remove support for VLANs
#CONFIG_NO_VLAN=y
# Enable support for fully dynamic VLANs. This enables hostapd to
# automatically create bridge and VLAN interfaces if necessary.
CONFIG_FULL_DYNAMIC_VLAN=y
# Use netlink-based kernel API for VLAN operations instead of ioctl()
# Note: This requires libnl 3.1 or newer.
#CONFIG_VLAN_NETLINK=y
# Remove support for dumping internal state through control interface commands
# This can be used to reduce binary size at the cost of disabling a debugging
# option.
CONFIG_NO_DUMP_STATE=y
# Enable tracing code for developer debugging
# This tracks use of memory allocations and other registrations and reports
# incorrect use with a backtrace of call (or allocation) location.
#CONFIG_WPA_TRACE=y
# For BSD, comment out these.
#LIBS += -lexecinfo
#LIBS_p += -lexecinfo
#LIBS_c += -lexecinfo
# Use libbfd to get more details for developer debugging
# This enables use of libbfd to get more detailed symbols for the backtraces
# generated by CONFIG_WPA_TRACE=y.
#CONFIG_WPA_TRACE_BFD=y
# For BSD, comment out these.
#LIBS += -lbfd -liberty -lz
#LIBS_p += -lbfd -liberty -lz
#LIBS_c += -lbfd -liberty -lz
# hostapd depends on strong random number generation being available from the
# operating system. os_get_random() function is used to fetch random data when
# needed, e.g., for key generation. On Linux and BSD systems, this works by
# reading /dev/urandom. It should be noted that the OS entropy pool needs to be
# properly initialized before hostapd is started. This is important especially
# on embedded devices that do not have a hardware random number generator and
# may by default start up with minimal entropy available for random number
# generation.
#
# As a safety net, hostapd is by default trying to internally collect
# additional entropy for generating random data to mix in with the data
# fetched from the OS. This by itself is not considered to be very strong, but
# it may help in cases where the system pool is not initialized properly.
# However, it is very strongly recommended that the system pool is initialized
# with enough entropy either by using hardware assisted random number
# generator or by storing state over device reboots.
#
# hostapd can be configured to maintain its own entropy store over restarts to
# enhance random number generation. This is not perfect, but it is much more
# secure than using the same sequence of random numbers after every reboot.
# This can be enabled with -e<entropy file> command line option. The specified
# file needs to be readable and writable by hostapd.
#
# If the os_get_random() is known to provide strong random data (e.g., on
# Linux/BSD, the board in question is known to have reliable source of random
# data from /dev/urandom), the internal hostapd random pool can be disabled.
# This will save some in binary size and CPU use. However, this should only be
# considered for builds that are known to be used on devices that meet the
# requirements described above.
CONFIG_NO_RANDOM_POOL=y
# Should we attempt to use the getrandom(2) call that provides more reliable
# yet secure randomness source than /dev/random on Linux 3.17 and newer.
# Requires glibc 2.25 to build, falls back to /dev/random if unavailable.
CONFIG_GETRANDOM=y
# Should we use poll instead of select? Select is used by default.
#CONFIG_ELOOP_POLL=y
# Should we use epoll instead of select? Select is used by default.
CONFIG_ELOOP_EPOLL=y
# Should we use kqueue instead of select? Select is used by default.
#CONFIG_ELOOP_KQUEUE=y
# Select TLS implementation
# openssl = OpenSSL (default)
# gnutls = GnuTLS
# internal = Internal TLSv1 implementation (experimental)
# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
# none = Empty template
CONFIG_TLS=openssl
# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
# can be enabled to get a stronger construction of messages when block ciphers
# are used.
#CONFIG_TLSV11=y
# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
# can be enabled to enable use of stronger crypto algorithms.
#CONFIG_TLSV12=y
# Select which ciphers to use by default with OpenSSL if the user does not
# specify them.
#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
# If CONFIG_TLS=internal is used, additional library and include paths are
# needed for LibTomMath. Alternatively, an integrated, minimal version of
# LibTomMath can be used. See beginning of libtommath.c for details on benefits
# and drawbacks of this option.
CONFIG_INTERNAL_LIBTOMMATH=y
#ifndef CONFIG_INTERNAL_LIBTOMMATH
#LTM_PATH=/usr/src/libtommath-0.39
#CFLAGS += -I$(LTM_PATH)
#LIBS += -L$(LTM_PATH)
#LIBS_p += -L$(LTM_PATH)
#endif
# At the cost of about 4 kB of additional binary size, the internal LibTomMath
# can be configured to include faster routines for exptmod, sqr, and div to
# speed up DH and RSA calculation considerably
#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
# Interworking (IEEE 802.11u)
# This can be used to enable functionality to improve interworking with
# external networks.
CONFIG_INTERWORKING=y
# Hotspot 2.0
#CONFIG_HS20=y
# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
#CONFIG_SQLITE=y
# Enable Fast Session Transfer (FST)
#CONFIG_FST=y
# Enable CLI commands for FST testing
#CONFIG_FST_TEST=y
# Testing options
# This can be used to enable some testing options (see also the example
# configuration file) that are really useful only for testing clients that
# connect to this hostapd. These options allow, for example, to drop a
# certain percentage of probe requests or auth/(re)assoc frames.
#
#CONFIG_TESTING_OPTIONS=y
# Automatic Channel Selection
# This will allow hostapd to pick the channel automatically when channel is set
# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in
# similar way.
#
# Automatic selection is currently only done through initialization, later on
# we hope to do background checks to keep us moving to more ideal channels as
# time goes by. ACS is currently only supported through the nl80211 driver and
# your driver must have survey dump capability that is filled by the driver
# during scanning.
#
# You can customize the ACS survey algorithm with the hostapd.conf variable
# acs_num_scans.
#
# Supported ACS drivers:
# * ath9k
# * ath5k
# * ath10k
#
# For more details refer to:
# http://wireless.kernel.org/en/users/Documentation/acs
#
#CONFIG_ACS=y
# Multiband Operation support
# These extentions facilitate efficient use of multiple frequency bands
# available to the AP and the devices that may associate with it.
#CONFIG_MBO=y
# Client Taxonomy
# Has the AP retain the Probe Request and (Re)Association Request frames from
# a client, from which a signature can be produced which can identify the model
# of client device like "Nexus 6P" or "iPhone 5s".
CONFIG_TAXONOMY=y
# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
#CONFIG_FILS=y
# FILS shared key authentication with PFS
#CONFIG_FILS_SK_PFS=y
# Include internal line edit mode in hostapd_cli. This can be used to provide
# limited command line editing and history support.
#CONFIG_WPA_CLI_EDIT=y
# Opportunistic Wireless Encryption (OWE)
# Experimental implementation of draft-harkins-owe-07.txt
#CONFIG_OWE=y
# Airtime policy support
CONFIG_AIRTIME_POLICY=y
# Proxy ARP support
CONFIG_PROXYARP=y
# Override default value for the wpa_disable_eapol_key_retries configuration
# parameter. See that parameter in hostapd.conf for more details.
#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
# uBus IPC/RPC System
# Services can connect to the bus and provide methods
# that can be called by other services or clients.
CONFIG_UBUS=y
# OpenWrt patch 380-disable-ctrl-iface-mib.patch
# leads to the MIB only being compiled in if
# CONFIG_CTRL_IFACE_MIB is enabled.
CONFIG_CTRL_IFACE_MIB=y
'';
patches = singleton config'.flake.patches.hostapd.intel_lar-and-noscan;
prePatch = ''
patchFolder="${openwrtRepo}/package/network/services/hostapd/patches"
for file in $(ls $patchFolder) ; do
if [ $file != "300-noscan.patch" ] ; then
patches+=" $patchFolder/$file"
fi
done
'';
postPatch = ''
cp -RT ${openwrtRepo}/package/network/services/hostapd/src .
'';
});
}
);
}

View file

@ -0,0 +1,32 @@
{ pkgs, secret, ... }:
{
systemd.services.ical-vu-sync = {
serviceConfig.Type = "oneshot";
path = with pkgs; [
bash
ical2orgpy
curl
];
script = ''
rm "${secret.ical2org.orgPath or ""}"
cat <<EOF > "${secret.ical2org.orgPath or ""}"
:PROPERTIES:
:ID: 56ed0bf0-c6d0-4a86-980a-905ccab89345
:END:
#+title: VU Calendar
#+filetags: :project-forced:
EOF
curl '${secret.ical2org.icalUrlRooster or ""}' -o - | ical2orgpy - - >> "${secret.ical2org.orgPath or ""}"
curl '${secret.ical2org.icalUrlCanvas or ""}' -o - | CANVAS_TODO=1 ical2orgpy - - >> "${secret.ical2org.orgPath or ""}"
chown 404:404 "${secret.ical2org.orgPath or ""}"
'';
};
# systemd.timers.ical-vu-sync = {
# wantedBy = [ "timers.target" ];
# partOf = [ "ical-vu-sync.service" ];
# timerConfig = {
# OnCalendar = "*-*-* 3:00:00";
# Unit = "ical-vu-sync.service";
# };
# };
}

View file

@ -0,0 +1,534 @@
{ inputs, lib, pkgs, secret, config, config', ...}:
let
inherit (lib)
concatStringsSep
singleton
concatMapStringsSep
splitString;
in
{
uterranix.config = { tflib, ... }:
let
inherit (tflib)
tf;
in
{
output."envoy_klipper".value = tf "vault_consul_secret_backend_role.envoy-klipper";
};
services.hashicorp.vault-agent =
{
settings.template = [
{
source = pkgs.writeText "envoy-klipper.token.vtmpl" ''
{{ with secret "consul/creds/envoy-klipper" }}{{ .Data.token }}{{ end }}
'';
destination = "/run/secrets/klipper/envoy-klipper.token";
command =
let
serviceList =
[ "hashicorp-envoy-mainsail" ];
in
pkgs.writeShellScript "envoy-mainsail-reload.sh"
''
sudo systemd-run -P --machine klipper /run/current-system/sw/bin/bash -l -c \
'systemctl try-reload-or-restart ${concatStringsSep " " serviceList}'
'';
}
];
};
fileSystems."/var/lib/klipper" = {
device = "blowhole-zpool/persist/klipper";
fsType = "zfs";
};
systemd.services."container@klipper" = {
restartIfChanged = lib.mkForce false;
};
containers.klipper = {
ephemeral = true;
autoStart = true;
privateNetwork = true;
localAddress = "10.64.99.6";
hostAddress = "10.64.99.5";
bindMounts = {
"/run/secrets" = {
hostPath = "/run/secrets/klipper";
isReadOnly = true;
};
"/var/lib/klipper" = {
hostPath = "/var/lib/klipper";
isReadOnly = false;
};
"/var/lib/moonraker/gcodes" = {
hostPath = "/var/lib/klipper/gcodes";
isReadOnly = false;
};
"/dev/serial/by-id/" = {
hostPath = "/dev/serial/by-id/";
isReadOnly = false;
};
};
allowedDevices = singleton {
node = "/dev/serial/by-id/usb-Klipper_lpc1768_13E0FF0C469027AEBAA84A52871E00F5-if00";
modifier = "rwm";
};
config = {
nixpkgs.overlays = config.nixpkgs.overlays;
imports = with config'.flake.nixosModules; [
hashicorp
hashicorp-envoy
];
services.hashicorp-envoy.mainsail = {
service = {
name = "mainsail";
id = "mainsail";
address = "10.64.99.6";
port = 80;
connect.sidecar_service = {};
};
environment = {
"CONSUL_HTTP_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8500";
"CONSUL_GRPC_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8502";
"CONSUL_HTTP_TOKEN_FILE" = "/run/secrets/envoy-klipper.token";
};
address = "10.64.99.6:19000";
adminBind = "127.0.0.1:19100";
hotRestart = false;
};
users.users.klipper = {
home = "/var/lib/klipper";
isSystemUser = true;
group = "klipper";
uid = 321;
};
users.groups.klipper = {
gid = 321;
};
services.klipper = {
enable = true;
user = "klipper";
group = "klipper";
package = pkgs.klipper;
settings =
let
indentGcode = gcode:
"\n" + (concatMapStringsSep "\n" (x: " " + x) (splitString "\n" gcode));
in
{
stepper_x = {
step_pin = "P2.2";
dir_pin = "!P2.6";
enable_pin = "!P2.1";
rotation_distance = "40";
microsteps = "16";
endstop_pin = "P1.29"; # P1.28 for X-max
position_endstop = "0";
position_max = "235";
homing_speed = "50";
};
stepper_y = {
step_pin = "P0.19";
dir_pin = "!P0.20";
enable_pin = "!P2.8";
rotation_distance = "40";
microsteps = "16";
endstop_pin = "P1.27"; # P1.26 for Y-max
position_endstop = "0";
position_max = "235";
homing_speed = "50";
};
stepper_z = {
step_pin = "P0.22";
dir_pin = "P2.11";
enable_pin = "!P0.21";
rotation_distance = "8";
microsteps = "16";
endstop_pin = "P1.25"; # P1.24 for Z-max"
position_min = "-4.5";
position_endstop = "1.290";
position_max = "250";
};
extruder = {
step_pin = "P2.13";
dir_pin = "!P0.11";
enable_pin = "!P2.12";
rotation_distance = "23.291";
gear_ratio = "3:1";
microsteps = "16";
nozzle_diameter = "0.400";
filament_diameter = "1.750";
heater_pin = "P2.7";
sensor_type = "PT1000";
sensor_pin = "P0.24";
control = "pid";
pid_Kp = "22.2";
pid_Ki = "1.08";
pid_Kd = "114";
min_temp = "0";
max_temp = "260";
pressure_advance = "0.92";
};
bed_screws = {
screw1 = "30,35";
screw2 = "200,35";
screw3 = "200,205";
screw4 = "30,205";
};
"heater_fan my_nozzle_fan" = {
pin = "P2.4";
heater = "extruder";
heater_temp = "50.0";
fan_speed = "1.0";
};
heater_bed = {
heater_pin = "P2.5";
sensor_type = "ATC Semitec 104GT-2";
sensor_pin = "P0.23";
control = "watermark";
min_temp = "0";
max_temp = "80";
};
fan = {
pin = "P2.3";
};
mcu = {
serial = "/dev/serial/by-id/usb-Klipper_lpc1768_13E0FF0C469027AEBAA84A52871E00F5-if00";
};
printer = {
kinematics = "cartesian";
max_velocity = "200";
max_accel = "2000";
max_z_velocity = "25";
max_z_accel = "100";
};
virtual_sdcard = {
path = "/var/lib/moonraker/gcodes";
};
### Mainsail
pause_resume = {};
display_status = {};
endstop_phase = {};
"tmc2208 stepper_x" = {
uart_pin = "P1.17";
run_current = "0.475";
hold_current = "0.275";
stealthchop_threshold = "250";
};
"tmc2208 stepper_y" = {
uart_pin = "P1.15";
run_current = "0.475";
hold_current = "0.275";
stealthchop_threshold = "250";
};
"tmc2208 stepper_z" = {
uart_pin = "P1.10";
run_current = "0.475";
hold_current = "0.275";
stealthchop_threshold = "30";
};
"tmc2208 extruder" = {
uart_pin = "P1.8";
run_current = "0.560";
hold_current = "0.360";
stealthchop_threshold = "5";
};
board_pins = {
aliases =
indentGcode
''
# EXP1 header
EXP1_1=P1.30, EXP1_3=P1.18, EXP1_5=P1.20, EXP1_7=P1.22, EXP1_9=<GND>,
EXP1_2=P0.28, EXP1_4=P1.19, EXP1_6=P1.21, EXP1_8=P1.23, EXP1_10=<5V>,
# EXP2 header
EXP2_1=P0.17, EXP2_3=P3.26, EXP2_5=P3.25, EXP2_7=P1.31, EXP2_9=<GND>,
EXP2_2=P0.15, EXP2_4=P0.16, EXP2_6=P0.18, EXP2_8=<RST>, EXP2_10=<NC>
# Pins EXP2_1, EXP2_6, EXP2_2 are also MISO, MOSI, SCK of bus "ssp0"
'';
};
display = {
lcd_type = "st7920";
cs_pin = "EXP1_7";
sclk_pin = "EXP1_6";
sid_pin = "EXP1_8";
encoder_pins = "^EXP1_5, ^EXP1_3";
click_pin = "^!EXP1_2";
};
# "endstop_phase stepper_z" =
# { endstop_phase = "29";
# };
# "endstop_phase stepper_y" =
# { endstop_phase = "57";
# };
# "endstop_phase stepper_x" =
# { endstop_phase = "3";
# };
"gcode_macro M600" = {
gcode =
indentGcode
''
{% set x = params.X|default(50)|float %}
{% set y = params.Y|default(0)|float %}
{% set z = params.Z|default(10)|float %}
SAVE_GCODE_STATE NAME=M600_state
PAUSE
G91
G1 E-.8 F2700
G1 Z{z}
G90
G1 X{x} Y{y} F3000
G91
G1 E-50 F1000
G1 X0.1 F3000
G1 E-50 F1000
G1 X-0.1 F3000
G1 E-50 F1000
G1 X0.1 F3000
G1 E-50 F1000
G1 X-0.1 F3000
G1 E-50 F1000
G1 X0.1 F3000
G1 E-50 F1000
G1 X-0.1 F3000
RESTORE_GCODE_STATE NAME=M600_state
'';
};
"gcode_macro CANCEL_PRINT" = {
rename_existing = "BASE_CANCEL_PRINT";
gcode =
indentGcode
''
TURN_OFF_HEATERS
CLEAR_PAUSE
SDCARD_RESET_FILE
BASE_CANCEL_PRINT
'';
};
"gcode_macro PARK_WAIT" = {
gcode =
indentGcode
''
{% set x = params.X|default(0)|float %}
{% set y = params.Y|default(230)|float %}
{% set z = params.Z|default(10)|float %}
{% set e = params.Z|default(20)|float %}
{% set millis = params.MILLIS|default(5)|float %}
SAVE_GCODE_STATE NAME=PAUSE_state
G91
G1 E-{e} F2100
G1 Z{z}
G90
G1 X{x} Y{y} F6000
G4 P{millis}
G91
G1 E{e} F2100
G90
RESTORE_GCODE_STATE NAME=PAUSE_state MOVE=1
'';
};
"gcode_macro PAUSE" = {
rename_existing = "BASE_PAUSE";
gcode =
indentGcode
''
{% set x = params.X|default(0)|float %}
{% set y = params.Y|default(230)|float %}
{% set z = params.Z|default(10)|float %}
{% set e = params.E|default(20)|float %}
SAVE_GCODE_STATE NAME=PAUSE_state
BASE_PAUSE
G91
G1 E-{e} F2100
G1 Z{z}
G90
G1 X{x} Y{y} F6000
'';
};
"gcode_macro RESUME" = {
rename_existing = "BASE_RESUME";
gcode =
indentGcode
''
{% set e = params.Z|default(20)|float %}
G91
G1 E{e} F2100
G90
RESTORE_GCODE_STATE NAME=PAUSE_state MOVE=1
BASE_RESUME
'';
};
"gcode_macro PRIME_LINE" = {
gcode =
indentGcode
''
G92 E0 # Reset Extruder
G1 Z2.0 F3000 # Move Z Axis up little to prevent scratching of Heat Bed
G1 X0.1 Y20 Z0.3 F5000.0 # Move to start position
G1 X0.1 Y200.0 Z0.3 F1500.0 E15 # Draw the first line
G1 X0.4 Y200.0 Z0.3 F5000.0 # Move to side a little
G1 X0.4 Y20 Z0.3 F1500.0 E30 # Draw the second line
G92 E0 # Reset Extruder
G1 Z2.0 F3000 # Move Z Axis up little to prevent scratching of Heat Bed
G1 X5 Y20 Z0.3 F5000.0 # Move over to prevent blob squish
'';
};
"gcode_macro START_PRINT" = {
gcode =
indentGcode
''
{% set z = params.Z|default(0)|float %}
# Use absolute coordinates
G90
# Reset the G-Code Z offset (adjust Z offset if needed)
SET_GCODE_OFFSET Z={z}
# Home the printer
G28
# Prime line
G0 Z0
PRIME_LINE
'';
};
"gcode_macro END_PRINT" = {
gcode =
indentGcode
''
G91 # Relative positioning
G1 E-2 F2700 # Retract a bit
G1 E-2 Z0.2 F2400 # Retract and raise Z
G1 X5 Y5 F3000 # Wipe out
G1 Z10 #Raise Z more
G90 # Absolute positionning
G1 X0 Y200 # Present print
M106 S0 # Turn-off fan
M104 S0 # Turn-off hotend
M140 S0 # Turn-off bed
M84 X Y E # Disable all steppers but Z
'';
};
};
};
services.moonraker = {
enable = true;
group = "klipper";
settings = {
authorization.trusted_clients = [
"127.0.0.1"
(secret.network.ips.heater or "")
(secret.network.ips.edge.vpn or "")
(secret.network.ips.omen.vpn or "")
];
octoprint_compat = {};
history = {};
};
};
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
recommendedOptimisation = true;
upstreams."apiserver" = {
servers."127.0.0.1:7125" = {};
extraConfig = ''
ip_hash;
'';
};
virtualHosts.${secret.network.ips.blowhole.dns or ""} = {
root = pkgs.mainsail;
locations."/".extraConfig = ''
try_files $uri $uri/ /index.html;
'';
locations."/index.html".extraConfig = ''
add_header Cache-Control "no-store, no-cache, must-revalidate";
'';
locations."/websocket".extraConfig = ''
proxy_pass http://apiserver/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
'';
locations."~ ^/(printer|api|access|machine|server)/".extraConfig = ''
proxy_pass http://apiserver$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
'';
extraConfig = ''
client_max_body_size 512M;
'';
};
};
};
};
}

View file

@ -0,0 +1,533 @@
# SPDX-FileCopyrightText: 2023 Richard Brežák <richard@brezak.sk>
#
# SPDX-License-Identifier: LGPL-3.0-or-later
{ pkgs, roots, lib, inputs', config, secret, config', ... }:
let
inherit (lib)
singleton
nixosTests
concatStringsSep;
in
{
uterranix.config = { tflib, ... }:
let
inherit (tflib)
tf;
in
{
output."envoy_grafana".value = tf "vault_consul_secret_backend_role.envoy-grafana";
output."envoy_blowhole".value = tf "vault_consul_secret_backend_role.envoy-blowhole";
data."influxdb-v2_organization"."redalder" = {
name = "redalder";
};
resource."influxdb-v2_bucket"."metrics_bucket" = {
name = "metrics";
description = "Metrics bucket";
org_id = "\${data.influxdb-v2_organization.redalder.id}";
retention_rules = {
every_seconds = 30 * 24 * 60 * 60; # days * h/d * m/h * s/m
};
};
resource."influxdb-v2_bucket"."logs_bucket" = {
org_id = "\${data.influxdb-v2_organization.redalder.id}";
name = "logs";
description = "Logs bucket";
retention_rules = {
every_seconds = 30 * 24 * 60 * 60; # days * h/d * m/h * s/m
};
};
resource."influxdb-v2_authorization"."telegraf_authorization" = {
org_id = "\${data.influxdb-v2_organization.redalder.id}";
description = "Token for telegraf ingestion";
status = "active";
permissions = [
{
action = "write";
resource = {
id = "\${influxdb-v2_bucket.logs_bucket.id}";
org_id = "\${data.influxdb-v2_organization.redalder.id}";
type = "buckets";
};
}
{
action = "write";
resource = {
id = "\${influxdb-v2_bucket.metrics_bucket.id}";
org_id = "\${data.influxdb-v2_organization.redalder.id}";
type = "buckets";
};
}
];
};
resource."influxdb-v2_authorization"."grafana_authorization" = {
org_id = "\${data.influxdb-v2_organization.redalder.id}";
description = "Token for Grefana";
status = "active";
permissions = [
{
action = "read";
resource = {
id = "\${influxdb-v2_bucket.logs_bucket.id}";
org_id = "\${data.influxdb-v2_organization.redalder.id}";
type = "buckets";
};
}
{
action = "read";
resource = {
id = "\${influxdb-v2_bucket.metrics_bucket.id}";
org_id = "\${data.influxdb-v2_organization.redalder.id}";
type = "buckets";
};
}
];
};
resource."vault_mount"."kv" = {
path = "kv";
type = "kv";
options = { version = 2; };
description = "KV Version 2 secret engine mount";
};
resource."vault_kv_secret_v2"."telegraf_secret" = {
mount = "\${vault_mount.kv.path}";
name = "homelab-1/blowhole/monitor/telegraf";
options = { version = 2; };
data_json = builtins.toJSON {
influxdb_token = "\${influxdb-v2_authorization.telegraf_authorization.token}";
};
};
resource."vault_kv_secret_v2"."grafana_secret" = {
mount = "\${vault_mount.kv.path}";
name = "homelab-1/blowhole/monitor/grafana";
options = { version = 2; };
data_json = builtins.toJSON {
influxdb_token = "\${influxdb-v2_authorization.grafana_authorization.token}";
};
};
};
nixpkgs.overlays = singleton (_: _:
{
telegraf =
pkgs.buildGoModule rec {
pname = "telegraf";
version = "1.25.3";
excludedPackages = "test";
doCheck = false;
subPackages = singleton "cmd/telegraf";
src = pkgs.fetchFromGitHub {
owner = "influxdata";
repo = "telegraf";
rev = "v${version}";
sha256 = "sha256-FUZDS4As9qP2Dn0NSBM/e8udDLMk5OZol4CQSI39T4s=";
};
vendorHash = "sha256-uWoWvS9ZZzhpE+PiJv0fqblMLOAGIrhCdi0ugvF/lQI=";
proxyVendor = true;
ldflags = [
"-w" "-s" "-X main.version=${version}"
];
passthru.tests = { inherit (nixosTests) telegraf; };
meta = with lib; {
description = "The plugin-driven server agent for collecting & reporting metrics";
license = licenses.mit;
homepage = "https://www.influxdata.com/time-series-platform/telegraf/";
maintainers = with maintainers; [ mic92 roblabla timstott ];
};
};
});
services.hashicorp.vault-agent =
{
settings.template = [
{
source = pkgs.writeText "envoy-grafana.token.vtmpl" ''
{{ with secret "consul/creds/envoy-grafana" }}{{ .Data.token }}{{ end }}
'';
destination = "/run/secrets/monitor/envoy-grafana.token";
command =
let
serviceList =
[ "hashicorp-envoy-grafana" "hashicorp-envoy-influx" "hashicorp-envoy-telegraf" ];
in
pkgs.writeShellScript "envoy-grafana-reload.sh" ''
sudo systemd-run -P --machine monitor /run/current-system/sw/bin/bash -l -c \
'systemctl try-reload-or-restart ${concatStringsSep " " serviceList}'
'';
}
{
source = pkgs.writeText "envoy-blowhole.token.vtmpl" ''
{{ with secret "consul/creds/envoy-blowhole" }}{{ .Data.token }}{{ end }}
'';
destination = "/run/secrets/envoy-blowhole.token";
command = pkgs.writeShellScript "envoy-blowhole-reload.sh" ''
sudo systemctl try-reload-or-restart hashicorp-envoy-telegraf
'';
}
{
source = pkgs.writeText "telegraf.env.vtmpl" ''
INFLUXDB_TOKEN={{ with secret "kv/data/homelab-1/blowhole/monitor/telegraf" }}{{ .Data.data.influxdb_token }}{{ end }}
'';
destination = "/run/secrets/monitor/telegraf.env";
command = pkgs.writeShellScript "monitor-telegraf-reload.sh" ''
sudo systemd-run -P --machine monitor /run/current-system/sw/bin/bash -l -c \
'systemctl try-reload-or-restart telegraf'
'';
}
{
source = pkgs.writeText "grafana-influx.token.vtmpl" ''
{{ with secret "kv/data/homelab-1/blowhole/monitor/grafana" }}
{{ .Data.data.influxdb_token }}
{{ end }}
'';
destination = "/run/secrets/monitor/grafana-influx.token";
perms = "0644";
command = pkgs.writeShellScript "monitor-telegraf-reload.sh" ''
sudo systemd-run -P --machine monitor /run/current-system/sw/bin/bash -l -c \
'systemctl try-reload-or-restart grafana'
'';
}
];
};
## There is no way to say, hey, listen on localhost. The listeners option is missing the `address` field
## and the `name` field so it's impossible to configure....
services.hashicorp-envoy.telegraf = {
type = "ingress";
address = "${secret.network.ips.blowhole.ip or ""}:19000";
service = {
kind = "ingress-gateway";
name = "telegraf-blowhole";
listeners = singleton {
port = 8086;
protocol = "tcp";
services = singleton {
name = "telegraf";
};
};
};
environment = {
"CONSUL_HTTP_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8500";
"CONSUL_GRPC_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8502";
"CONSUL_HTTP_TOKEN_FILE" = "/run/secrets/envoy-blowhole.token";
};
adminBind = "127.0.0.1:19100";
hotRestart = false;
};
services.telegraf-magic = {
enable = true;
settings = {
inputs.cpu = {
percpu = true;
totalcpu = true;
tags.host = "blowhole";
tags.bucket = "metrics";
};
inputs.mem = {
tags.host = "blowhole";
tags.bucket = "metrics";
};
inputs.nomad = {
url = "http://${secret.network.ips.blowhole.ip or ""}:4646";
tags.host = "blowhole";
tags.bucket = "metrics";
};
# aggregators.minmax = {
# period = "30s";
# drop_original = true;
# namepass = [ "nomad" ];
# };
inputs.zfs = {
tags.host = "blowhole";
tags.bucket = "metrics";
};
# inputs.tail = [
# {
# files = ["/var/lib/nomad/alloc/*/alloc/logs/*.stdout.*"];
# data_format = "value";
# data_type = "string";
# name_override = "nomad_alloc_log";
# tags.bucket = "logs";
# }
# {
# files = ["/var/lib/nomad/alloc/*/alloc/logs/*.stderr.*"];
# data_format = "value";
# data_type = "string";
# name_override = "nomad_alloc_log";
# tags.bucket = "logs";
# }
# ];
inputs.docker_log = {
tags.bucket = "logs";
};
outputs.influxdb_v2 = [
{
urls = singleton "http://${secret.network.ips.blowhole.ip or ""}:8086";
bucket = "metrics";
tagpass.bucket = singleton "metrics";
}
{
urls = singleton "http://${secret.network.ips.blowhole.ip or ""}:8086";
bucket = "logs";
tagpass.bucket = singleton "logs";
}
];
};
systemd = {
serviceConfig.SupplementaryGroups = [ "docker" ];
};
};
fileSystems."/var/lib/grafana" = {
device = "blowhole-zpool/persist/grafana";
fsType = "zfs";
};
fileSystems."/var/lib/grafana-postgres" = {
device = "blowhole-zpool/persist/grafana-postgres";
fsType = "zfs";
};
fileSystems."/var/lib/grafana-influxdb2" = {
device = "blowhole-zpool/persist/grafana-influxdb2";
fsType = "zfs";
};
systemd.services."container@monitor".serviceConfig.LimitNOFILE = "infinity";
# TODO: split interface name and container name, i.e. rewrite the container module....... again
containers.monitor = {
ephemeral = true;
autoStart = true;
privateNetwork = true;
localAddress = "10.64.99.2";
hostAddress = "10.64.99.1";
extraFlags = [
"--capability=CAP_IPC_LOCK"
];
bindMounts = {
"/run/secrets" = {
hostPath = "/run/secrets/monitor";
isReadOnly = true;
};
"/var/lib/grafana" = {
hostPath = "/var/lib/grafana";
isReadOnly = false;
};
"/var/lib/postgresql" = {
hostPath = "/var/lib/grafana-postgres";
isReadOnly = false;
};
"/var/lib/influxdb2" = {
hostPath = "/var/lib/grafana-influxdb2";
isReadOnly = false;
};
};
config = {
nixpkgs.overlays = config.nixpkgs.overlays;
imports = with config'.flake.nixosModules; [
hashicorp
hashicorp-envoy
telegraf
grafana
];
services.hashicorp-envoy.grafana = {
service = {
name = "grafana";
id = "grafana";
address = "10.64.99.2";
port = 3000;
connect.sidecar_service = {};
};
environment = {
"CONSUL_HTTP_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8500";
"CONSUL_GRPC_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8502";
"CONSUL_HTTP_TOKEN_FILE" = "/run/secrets/envoy-grafana.token";
};
address = "10.64.99.2:19000";
adminBind = "127.0.0.1:19100";
hotRestart = false;
};
services.postgresql = {
enable = true;
ensureDatabases = singleton "grafana";
ensureUsers = singleton {
name = "grafana";
ensurePermissions."DATABASE grafana" = "ALL PRIVILEGES";
};
};
systemd.services.grafana = {
serviceConfig = {
Restart = "always";
RestartSec = "10s";
};
};
services.grafana-magic = {
enable = true;
settings = {
security = {
content_security_policy = true;
disable_gravatar = true;
data_source_proxy_whitelist = concatStringsSep " " [
"127.0.0.1:8086"
];
};
server = {
domain = "grafana.in.redalder.org";
};
system = {
http_addr = "127.0.0.1";
};
database = {
type = "postgres";
host = "/var/run/postgresql";
name = "grafana";
user = "grafana";
};
paths.provisioning = {
datasources.datasources = [
{
name = "InfluxDB";
type = "influxdb";
access = "proxy";
orgId = 1;
uid = "influxdb";
url = "http://127.0.0.1:8086";
jsonData = {
version = "Flux";
organization = "redalder";
defaultBucket = "bucket";
};
secureJsonData = {
token = "$__file{/run/secrets/grafana-influx.token}";
};
}
];
};
};
};
services.hashicorp-envoy.influx = {
service = {
name = "influx";
id = "influx";
address = "10.64.99.2";
port = 8086;
connect.sidecar_service = {};
};
environment = {
"CONSUL_HTTP_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8500";
"CONSUL_GRPC_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8502";
"CONSUL_HTTP_TOKEN_FILE" = "/run/secrets/envoy-grafana.token";
};
address = "10.64.99.2:19001";
adminBind = "127.0.0.1:19101";
hotRestart = false;
};
services.influxdb2 = {
enable = true;
settings = {
http-bind-address = "127.0.0.1:8086";
hardening-enabled = true;
reporting-disabled = true;
};
};
services.hashicorp-envoy.telegraf = {
service = {
name = "telegraf";
id = "telegraf";
address = "10.64.99.2";
port = 8087;
connect.sidecar_service = {};
};
environment = {
"CONSUL_HTTP_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8500";
"CONSUL_GRPC_ADDR" = "http://${secret.network.ips.blowhole.ip or ""}:8502";
"CONSUL_HTTP_TOKEN_FILE" = "/run/secrets/envoy-grafana.token";
};
address = "10.64.99.2:19002";
adminBind = "127.0.0.1:19102";
hotRestart = false;
};
services.telegraf-magic = {
enable = true;
settings = {
inputs.influxdb_v2_listener = {
service_address = "127.0.0.1:8087";
bucket_tag = "bucket";
parser_type = "upstream";
};
inputs.systemd_units = {
unittype = "service";
tags = {
host = "blowhole#monitoring";
bucket = "metrics";
};
};
outputs.influxdb_v2 = singleton {
urls = [ "http://127.0.0.1:8086" ];
token = "\${INFLUXDB_TOKEN}";
organization = "redalder";
bucket_tag = "bucket";
};
};
systemd.serviceConfig = {
EnvironmentFile = "/run/secrets/telegraf.env";
};
};
};
};
}

View file

@ -0,0 +1,108 @@
{ pkgs, ... }:
{
fileSystems."/mnt/cartman" = {
device = "storfa/ds1/cartman";
fsType = "zfs";
};
systemd.services.mnt-kyle-zfs-relmount = {
requires = ["mnt-kyle.mount"];
after = ["mnt-kyle.mount"];
requiredBy = ["local-fs.target"];
path = with pkgs; [zfs util-linux];
serviceConfig = {
RemainAfterExit = true;
Type = "oneshot";
ExecStart = "${pkgs.zfs-relmount}/bin/zfs-relmount mount storfa/ds1/kyle /mnt/kyle";
};
};
fileSystems."/mnt/kyle" = {
device = "storfa/ds1/kyle";
fsType = "zfs";
};
systemd.services.mnt-cartman-zfs-relmount = {
requires = ["mnt-cartman.mount"];
after = ["mnt-cartman.mount"];
requiredBy = ["local-fs.target"];
path = with pkgs; [zfs util-linux];
serviceConfig = {
RemainAfterExit = true;
Type = "oneshot";
ExecStart = "${pkgs.zfs-relmount}/bin/zfs-relmount mount storfa/ds1/cartman /mnt/cartman";
};
};
fileSystems."/mnt/stan" = {
device = "storfa/ds1/stan";
fsType = "zfs";
};
systemd.services.mnt-stan-zfs-relmount = {
requires = ["mnt-stan.mount"];
after = ["mnt-stan.mount"];
requiredBy = ["local-fs.target"];
path = with pkgs; [zfs util-linux];
serviceConfig = {
RemainAfterExit = true;
Type = "oneshot";
ExecStart = "${pkgs.zfs-relmount}/bin/zfs-relmount mount storfa/ds1/stan /mnt/stan";
};
};
fileSystems."/run/restic" = {
fsType = "tmpfs";
options = [ "size=64M" ];
};
services.restic.backups.cartman = {
initialize = true;
timerConfig = {
OnCalendar = "03:00";
};
paths = [ "/run/restic/cartman" ];
backupPrepareCommand = ''
snapshot="$(date +restic%+4Y_%U_%u)"
${pkgs.zfs-relmount}/bin/zfs-relmount snapshot storfa/ds1/cartman "''${snapshot}"
mkdir /run/restic/cartman
${pkgs.zfs-relmount}/bin/zfs-relmount mount-snapshot storfa/ds1/cartman /run/restic/cartman "''${snapshot}"
export RESTIC_PROGRESS_FPS=1
'';
backupCleanupCommand = ''
${pkgs.zfs-relmount}/bin/zfs-relmount umount storfa/ds1/cartman /run/restic/cartman
rm -r /run/restic/cartman
'';
passwordFile = "";
repository = "";
};
systemd.timers."restic-backups-cartman" = {
timerConfig = {
Persistent = true;
WakeSystem = true;
};
};
systemd.services."restic-backups-cartman" = {
path = with pkgs; [
util-linux
zfs
];
serviceConfig = {
Nice = 19;
IOSchedulingClass = "idle";
EnvironmentFile = "/var/secrets/restic-b2";
};
};
}

View file

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2023 Richard Brežák <richard@brezak.sk>
#
# SPDX-License-Identifier: LGPL-3.0-or-later
{ ... }:
{
networking = {
hostName = "blowhole";
useDHCP = false;
interfaces.enp7s0f1.useDHCP = true;
firewall.enable = false;
hostId = "2cb135ac";
};
}

View file

@ -0,0 +1,62 @@
# SPDX-FileCopyrightText: 2023 Richard Brežák <richard@brezak.sk>
#
# SPDX-License-Identifier: LGPL-3.0-or-later
{ lib, ... }:
let
inherit (lib)
;
in
{
systemd.services.nfs-mountd.serviceConfig = {
LimitNOFILE = 8192;
};
services.nfs.server = {
enable = true;
lockdPort = 4001;
mountdPort = 4002;
statdPort = 4000;
exports = ''
/var/nfs/jellyfin/cache 10.64.2.1/32(rw,subtree_check,async,no_root_squash,crossmnt)
/var/nfs/jellyfin/config 10.64.2.1/32(rw,subtree_check,async,no_root_squash,crossmnt)
/var/nfs/jellyfin/media 10.64.2.1/32(rw,subtree_check,async,no_root_squash,crossmnt)
/var/nfs/gitea-data 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/gitea-db 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/hydra-data 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/hydra-nix 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/hydra-db 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/minecraft/atm6 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/ingress-letsencrypt 10.64.0.1(rw,subtree_check,async,no_root_squash)
/var/nfs/Magic_RB 10.64.2.129(rw,subtree_check,async)
/mnt/cartman 10.64.0.8/32(rw,subtree_check,async,no_root_squash,crossmnt) 10.64.2.129(rw,subtree_check,async,crossmnt)
/mnt/kyle 10.64.0.8/32(rw,subtree_check,async,no_root_squash,crossmnt) 10.64.2.129(rw,subtree_check,async,crossmnt)
/mnt/stan 10.64.0.8/32(rw,subtree_check,async,no_root_squash,crossmnt) 10.64.2.129(rw,subtree_check,async,crossmnt)
/var/nfs/home-assistant_hass 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/home-assistant_db 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/home-assistant_mosquitto 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/home-assistant_zigbee2mqtt 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/syncthing/data 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/syncthing/config 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/syncthing/storage 10.64.2.1/32(rw,subtree_check,async,crossmnt)
/var/nfs/dovecot/maildir 10.64.0.8/32(rw,subtree_check,async,no_root_squash) 10.64.2.1/32(rw,subtree_check,async,no_root_squash) 10.64.3.20/32(rw,subtree_check,async,no_root_squash)
/var/nfs/getmail/getmail.d 10.64.0.8/32(rw,subtree_check,async,no_root_squash) 10.64.2.1/32(rw,subtree_check,async,no_root_squash) 10.64.3.20/32(rw,subtree_check,async,no_root_squash)
/var/nfs/mail-configuration 10.64.0.8/32(rw,subtree_check,async,no_root_squash) 10.64.2.1/32(rw,subtree_check,async,no_root_squash) 10.64.3.20/32(rw,subtree_check,async,no_root_squash)
/var/nfs/baikal/specific 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/baikal/config 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/matrix/synapse 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/matrix/postgresql 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/matrix/mautrix-facebook 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
/var/nfs/matrix/registrations 10.64.2.1/32(rw,subtree_check,async,no_root_squash)
'';
};
}

View file

@ -0,0 +1,18 @@
{ inputs', config', config, ... }:
{
imports = [
../../common/nixpkgs.nix
];
nixpkgs.overlays =
(with config'.flake.overlays; [
udp-over-tcp
emacsclient-remote
zfs-relmount
ical2org
])
++
(with inputs'.nixng.overlays; [
default
]);
}

View file

@ -0,0 +1,161 @@
{inputs', lib, config, pkgs, secret, config', ...}:
let
inherit (lib)
singleton;
in
{
services.hashicorp.vault-agent = {
settings.template = singleton {
source = pkgs.writeText "nomad.json.vtmpl" ''
{
"server": {
"encrypt": "{{ with secret "kv/data/homelab-1/blowhole/nomad/encryption_key" }}{{ or .Data.data.key "" }}{{ end }}"
},
"vault": {
"token": "{{ with secret "kv/data/homelab-1/blowhole/nomad/vault_token" }}{{ or .Data.data.secret "" }}{{ end }}"
},
"consul": {
"token": "{{ with secret "kv/data/homelab-1/blowhole/nomad/consul_token" }}{{ or .Data.data.secret "" }}{{ end }}"
}
}
'';
destination = "/run/secrets/nomad.json";
command = pkgs.writeShellScript "nomad-command" ''
sudo systemctl try-reload-or-restart hashicorp-nomad.service
'';
};
};
systemd.services.hashicorp-nomad.unitConfig = {
ConditionPathExists = "/run/secrets/nomad.json";
};
services.hashicorp.nomad = {
enable = true;
extraPackages = with pkgs; [
coreutils
iproute2
iptables
consul
glibc
config.nix.package
git
];
extraSettingsPaths = [
"/run/secrets/nomad.json"
];
package = inputs'.nixpkgs-hashicorp.legacyPackages.${pkgs.stdenv.system}.nomad_1_5.overrideAttrs (old:
{
patches = with config'.flake.patches; [
hashicorp-nomad.revert-change-consul-si-tokens-to-be-local
hashicorp-nomad.add-nix-integration
];
});
settings = {
bind_addr = secret.network.ips.blowhole.ip or "";
server.enabled = true;
tls = {
# http = false # true
# rpc = true
# ca_file = "nomad-ca.pem"
# cert_file = "client.pem"
# key_file = "client-key.pem"
# verify_server_hostname = true
# verify_https_client = true
};
vault = {
enabled = true;
address = "https://${secret.network.ips.vault.dns or ""}:8200";
allow_unauthenticated = true;
create_from_role = "nomad-cluster";
};
consul = {
address = "${secret.network.ips.blowhole.ip or ""}:8500";
auto_advertise = true;
server_auto_join = true;
client_auto_join = true;
};
acl.enabled = true;
telemetry = {
publish_allocation_metrics = true;
publish_node_metrics = true;
};
client = {
cni_path = "${pkgs.cni-plugins}/bin";
min_dynamic_port = 20000;
max_dynamic_port = 32000;
options = {
"docker.privileged.enabled" = "true";
};
host_network."default".cidr = secret.network.networks.home.mine or "";
host_network."mesh".cidr = secret.network.networks.home.mine or "";
network_interface = "enp4s0";
host_volume."jellyfin-mount".path = "/mnt/jellyfin-mount";
host_volume."cctv" = {
path = "/mnt/cctv";
read_only = false;
};
enabled = true;
};
plugin."docker" = {
config = {
allow_caps = [
"CHOWN"
"DAC_OVERRIDE"
"FSETID"
"FOWNER"
"MKNOD"
"NET_RAW"
"SETGID"
"SETUID"
"SETFCAP"
"SETPCAP"
"NET_BIND_SERVICE"
"SYS_CHROOT"
"KILL"
"AUDIT_WRITE"
"SYS_ADMIN"
];
allow_privileged = true;
extra_labels = [
"job_name"
"job_id"
"task_group_name"
"task_name"
"namespace"
"node_name"
"node_id"
];
};
};
disable_update_check = true;
data_dir = "/var/lib/nomad";
datacenter = "homelab-1";
region = "homelab-1";
};
};
virtualisation.docker.enable = true;
virtualisation.docker.daemon.settings.dns = [
(secret.network.ips.blowhole or "")
];
}

View file

@ -0,0 +1,19 @@
{ inputs', config', secret, ... }:
{
imports = [
inputs'.home-manager.nixosModules.default
../../common/users.nix
];
home-manager.useGlobalPkgs = true;
home-manager.extraSpecialArgs = {
config' = config';
inputs' = inputs';
secret = secret;
};
home-manager.users.main = {
imports = [ (inputs'.self + "/home-manager/modules/profiles/server.nix") ];
home.stateVersion = "21.05";
};
}

View file

@ -0,0 +1,66 @@
{ config, inputs', lib, config', pkgs, ... }:
let
inherit (lib)
singleton;
in
{
imports = [ inputs'.uterranix.nixosModules.default ];
uterranix.config = { config, tflib, ... }:
let
inherit (tflib)
tf;
in
{
terraform.required_providers =
config'.flake.uterranix.config.${pkgs.stdenv.system}.terraform.required_providers;
imports = config'.uterranix.modules;
resource."vault_consul_secret_backend_role"."envoy-grafana" = {
name = "envoy-grafana";
backend = "consul";
service_identities = [
"grafana"
"influx"
"telegraf"
];
node_identities = singleton "blowhole:homelab-1";
};
resource."consul_acl_policy"."envoy-blowhole" = {
name = "envoy-blowhole";
datacenters = singleton "homelab-1";
rules = ''
mesh = "write"
'';
};
resource."vault_consul_secret_backend_role"."envoy-blowhole" = {
name = "envoy-blowhole";
backend = "consul";
consul_policies = singleton (tf "consul_acl_policy.envoy-blowhole.name");
service_identities = singleton "telegraf-blowhole";
node_identities = [
"blowhole:homelab-1"
];
};
resource."vault_consul_secret_backend_role"."envoy-klipper" = {
name = "envoy-klipper";
backend = "consul";
service_identities = singleton "mainsail";
node_identities = singleton "blowhole:homelab-1";
};
};
}

View file

@ -0,0 +1,76 @@
{ pkgs, lib, config, tf, inputs', ... }:
let
inherit (lib)
singleton;
in
{
systemd.services.hashicorp-vault-agent =
let
config = pkgs.writeText "hashicorp-vault-agent-tmpfiles.d" ''
d /run/secrets 0750 root root 0
x /run/secrets/monitor 0755 root root -
d /run/secrets/monitor 0755 root root 0
x /run/secrets/klipper 0755 root root -
d /run/secrets/klipper 0755 root root 0
'';
in
{
preStart = "systemd-tmpfiles --create " + config;
postStop = "systemd-tmpfiles --clean " + config;
};
services.hashicorp.vault-agent = {
enable = true;
package = inputs'.nixpkgs-hashicorp.legacyPackages.${pkgs.stdenv.system}.vault;
command = "agent";
extraPackages = with pkgs; [
sudo
getent
];
settings = {
vault = {
address = "https://vault.in.redalder.org:8200";
retry = {
num_retries = 5;
};
};
auto_auth = {
method = singleton {
"approle" = {
mount_path = "auth/approle";
config = {
role_id_file_path = "/var/secrets/approle.roleid";
secret_id_file_path = "/var/secrets/approle.secretid";
remove_secret_id_file_after_reading = false;
};
};
};
sink = singleton {
type = "file";
config = {
path = "/run/secrets/vault-token";
};
};
};
template = [
{
source = pkgs.writeText "id_ed_camera" ''
{{ with secret "kv/data/homelab-1/blowhole/id_ed_camera" }}{{ .Data.data.private }}{{ end }}
'';
destination = "/run/secrets/id_ed_camera";
command = pkgs.writeShellScript "id_ed_camera-command" ''
export PATH=${pkgs.util-linux}/bin:$PATH
chown root:root /run/secrets/id_ed_camera
chmod 600 /run/secrets/id_ed_camera
'';
}
];
};
};
}

View file

@ -0,0 +1,80 @@
{lib, config, pkgs, secret, inputs', ...}:
let
inherit (lib)
mkForce;
certs = config.services.acme-sh.certs;
in
{
services.hashicorp.vault = {
enable = true;
package = inputs'.nixpkgs-hashicorp.legacyPackages.${pkgs.stdenv.system}.vault-bin;
settings = {
backend."file".path = "/var/lib/vault";
ui = true;
listener = [
{
"tcp" = {
address = "localhost:8200";
tls_cert_file = "${certs.vault.certPath}";
tls_key_file = "${certs.vault.keyPath}";
};
}
{
"tcp" = {
address = "${secret.network.ips.blowhole.ip or ""}:8200";
tls_cert_file = "${certs.vault.certPath}";
tls_key_file = "${certs.vault.keyPath}";
};
}
];
storage."raft" = {
path = "/var/lib/vault";
node_id = "blowhole";
};
cluster_addr = "https://${secret.network.ips.blowhole.ip or ""}:8201";
api_addr = "http://${secret.network.ips.blowhole.ip or ""}:8200";
};
};
services.acme-sh.certs.vault = {
production = true;
user = "root";
domains."vault.in.redalder.org" = "dns_hetzner";
mainDomain = "vault.in.redalder.org";
postRun = "systemctl try-reload-or-restart --no-block hashicorp-vault.service";
};
systemd.services."acme-sh-vault" = {
serviceConfig.EnvironmentFile = mkForce "/var/secrets/hetzner.env";
};
services.acme-sh.certs.vault-wildcard = {
production = true;
user = "root";
domains."*.in.redalder.org" = "dns_hetzner";
mainDomain = "*.in.redalder.org";
# Trigger vault to reread certificate files.
postRun = ''
PEM_BUNDLE=$(cat <<EOF
$(cat '${certs.vault-wildcard.statePath}/*.in.redalder.org/ca.cer')
$(cat '${certs.vault-wildcard.keyPath}')
EOF
)
(
exec 44<<<"$PEM_BUNDLE"
VAULT_ADDR="https://vault.in.redalder.org:8200" \
VAULT_TOKEN="$(cat /run/secrets/vault-token)" \
${pkgs.vault}/bin/vault write pki-inra/config/ca pem_bundle=@/proc/self/fd/44
)
'';
};
systemd.services."acme-sh-vault-wildcard" = {
serviceConfig.EnvironmentFile = mkForce "/var/secrets/hetzner.env";
};
}

View file

@ -0,0 +1,7 @@
{ ... }:
{
systemd.watchdog.runtimeTime = "60s";
systemd.watchdog.rebootTime = "10m";
systemd.watchdog.kexecTime = "5m";
systemd.services."emergency".serviceConfig.ExecStartPre = "/bin/sh -c \"read -t 30 || /run/current-system/sw/bin/systemctl reboot\"";
}

View file

@ -0,0 +1,15 @@
;
; BIND data file for example.local
;
$TTL 3600
@ IN SOA ns1.example.local. info.example.local. (
2022092600 ; Serial
7200 ; Refresh
120 ; Retry
2419200 ; Expire
3600) ; Default TTL
;
A 10.64.2.1 ; This means that naughydomain.com gets directed to the designated address
* IN A 10.64.2.1 ; This wildcard entry means that any permutation of xxx.naughtydomain.com gets directed to the designated address
AAAA ::1 ; This means that naughydomain.com gets directed to IPv6 localhost
* IN AAAA ::1 ; This wildcard entry means that any permutation of xxx.naughtydomain.com gets directed to IPv6 localhost

View file

@ -0,0 +1,16 @@
$ORIGIN in.redalder.org.
$TTL 5m
hosts IN SOA ns.in.redalder.org. root.redalder.org. (
14 ; serial
4h ; refresh
15m ; retry
8h ; expire
4m) ; negativa caching TTL
IN NS ns.redalder.org
IN A 10.64.2.1
$ORIGIN hosts.in.redalder.org.
blowhole IN A 10.64.2.1
toothpick IN A 10.64.0.1

View file

@ -0,0 +1,26 @@
$ORIGIN redalder.org.
$TTL 5m
in IN SOA ns.redalder.org. root.redalder.org. (
24 ; serial
4h ; refresh
15m ; retry
8h ; expire
4m) ; negativa caching TTL
IN NS ns.redalder.org
IN A 10.64.2.1
$ORIGIN in.redalder.org.
vault IN A 10.64.2.1
consul IN A 10.64.2.1
nomad IN A 10.64.2.1
grafana IN A 10.64.2.1
hass IN A 10.64.2.1
jellyfin IN A 10.64.2.1
zigbee2mqtt IN A 10.64.2.1
syncthing IN A 10.64.2.1
influx IN A 10.64.2.1
mainsail IN A 10.64.2.1
matrix IN A 10.64.2.1

View file

@ -101,7 +101,7 @@ in
powerManagement.enable = true;
powerManagement.finegrained = true;
prime.reverseSync.enable = true;
prime.reverseSync.enable = false;
prime.offload.enable = true;
modesetting.enable = true;

71
overlays/ical2org.nix Normal file
View file

@ -0,0 +1,71 @@
# SPDX-FileCopyrightText: 2022 Richard Brežák <richard@brezak.sk>
#
# SPDX-License-Identifier: LGPL-3.0-or-later
{ inputs, ... }:
{
flake.overlays.ical2org =
final: prev: {
x-wr-timezone = with prev;
python3.pkgs.buildPythonPackage rec {
pname = "x_wr_timezone";
version = "0.0.5";
src = python3.pkgs.fetchPypi {
inherit pname version;
sha256 = "sha256-wFyzS5tYpGB6eI2whtyuV2ZyjkuU4GcocNxVk6bhP+Y=";
};
propagatedBuildInputs = with python3.pkgs; [
pytz
icalendar
pygments
restructuredtext_lint
pytest
];
meta = with lib; {};
};
recurring-ical-events = with prev;
python3.pkgs.buildPythonPackage rec {
pname = "recurring_ical_events";
version = "1.0.2b0";
src = python3.pkgs.fetchPypi {
inherit pname version;
sha256 = "sha256-aoQU7rxRJvqe3PLHPto5T2rCvFSkmqfiClwCL6SRjk0=";
};
propagatedBuildInputs = with python3.pkgs; [
pytz
python-dateutil
final.x-wr-timezone
tzdata
pytest-cov
pbr
];
meta = with lib; {};
};
ical2orgpy = with prev;
python3.pkgs.buildPythonApplication rec {
pname = "ical2orgpy";
version = "0.4.0";
src = inputs.ical2org;
PBR_VERSION="1.2.3";
propagatedBuildInputs = with python3.pkgs; [
click
future
icalendar
pytz
tzlocal
final.recurring-ical-events
];
meta = with prev.lib; {};
};
};
}

View file

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2022 Richard Brežák <richard@brezak.sk>
#
# SPDX-License-Identifier: LGPL-3.0-or-later
{
flake.overlays.zfs-relmount =
final: prev: {
zfs-relmount =
prev.writeShellScriptBin "zfs-relmount"
(builtins.readFile ./zfs-relmount.sh);
};
}

View file

@ -0,0 +1,82 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2022 Richard Brežák <richard@brezak.sk>
#
# SPDX-License-Identifier: LGPL-3.0-or-later
function recurse_children()
{
local volume="${1}"
local dir="${2}"
local relmount="${3}"
local children="${4}"
local action="${5}"
for child in $children
do
if [ "${child}" == "${volume}" ]
then
continue
fi
recursive_perform "${child}" "${dir}/$(basename "${child}")" "${action}"
done
}
function recursive_perform()
{
local volume="${1}"
local dir="${2}"
local action="${3}"
local relmount="$(zfs get -Ho value :relmount "${volume}")"
local children="$(zfs list -Hrd 1 "${volume}" -o name | tr '\n' ' ')"
if ! [ -z "${relmount}" ] && [ "${relmount}" != "-" ]
then
case "${relmount}" in
"yes")
eval "${action}"
recurse_children "${volume}" "${dir}" "${relmount}" "${children}" "${action}"
;;
"pass")
recurse_children "${volume}" "${dir}" "${relmount}" "${children}" "${action}"
;;
"*")
;;
esac
fi
}
action="${1}"
shift 1
case $action in
"mount")
zfs_src="${1}"
dst_dir="${2}"
recursive_perform "${zfs_src}" "${dst_dir}" 'mount -o X-mount.mkdir -t zfs "${volume}" "${dir}"'
;;
"mount-snapshot")
zfs_src="${1}"
dst_dir="${2}"
snapshot="${3}"
recursive_perform "${zfs_src}" "${dst_dir}" 'mount -o X-mount.mkdir -t zfs "${volume}"@'"${snapshot}"' "${dir}"'
;;
"umount")
zfs_src="${1}"
dst_dir="${2}"
recursive_perform "${zfs_src}" "${dst_dir}" 'umount -t zfs "${dir}"'
;;
"snapshot")
root="${1}"
snapshot="${2}"
recursive_perform "${root}" "${root}" 'zfs snapshot "${volume}"@'"${snapshot}"
;;
"*")
;;
esac

View file

@ -0,0 +1,159 @@
From 3590d38c853ce2fd7c2397f6421171234fb9d0a6 Mon Sep 17 00:00:00 2001
From: Magic_RB <magic_rb@redalder.org>
Date: Sat, 27 May 2023 18:11:58 +0200
Subject: [PATCH] intel_lar and noscan
Signed-off-by: Magic_RB <magic_rb@redalder.org>
---
hostapd/config_file.c | 4 ++++
src/ap/ap_config.h | 1 +
src/ap/hw_features.c | 45 +++++++++++++++++++++++++++++++++++++++---
src/ap/ieee802_11_ht.c | 6 ++++++
4 files changed, 53 insertions(+), 3 deletions(-)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 8e179d151..6fa0849a0 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2874,6 +2874,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, bss->wpa_deny_ptk0_rekey);
return 1;
}
+ } else if (os_strcmp(buf, "noscan") == 0) {
+ conf->noscan = atoi(pos);
} else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
char *endp;
unsigned long val = strtoul(pos, &endp, 0);
@@ -3446,6 +3448,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
if (bss->ocv && !bss->ieee80211w)
bss->ieee80211w = 1;
#endif /* CONFIG_OCV */
+ } else if (os_strcmp(buf, "noscan") == 0) {
+ conf->noscan = atoi(pos);
} else if (os_strcmp(buf, "ieee80211n") == 0) {
conf->ieee80211n = atoi(pos);
} else if (os_strcmp(buf, "ht_capab") == 0) {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 8598602b1..a25cf2cbe 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1061,6 +1061,7 @@ struct hostapd_config {
int ht_op_mode_fixed;
u16 ht_capab;
+ int noscan;
int ieee80211n;
int secondary_channel;
int no_pri_sec_switch;
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 842d9f5ba..30ff7b496 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -24,6 +24,17 @@
#include "beacon.h"
#include "hw_features.h"
+static void ieee80211n_do_nothing(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG,
+ "Scan finished!");
+}
+
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params);
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params);
+
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
size_t num_hw_features)
@@ -82,6 +93,33 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
if (hostapd_drv_none(hapd))
return -1;
+
+
+
+ // scan
+ struct wpa_driver_scan_params params;
+ int ret1;
+
+ os_memset(&params, 0, sizeof(params));
+ ieee80211n_scan_channels_5g(iface, &params);
+
+ ret1 = hostapd_driver_scan(iface->bss[0], &params);
+
+ if (ret1 == -EBUSY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s)!",
+ ret1, strerror(-ret1));
+ }
+
+ if (ret1 == 0) {
+ iface->scan_cb = ieee80211n_do_nothing;
+ wpa_printf(MSG_DEBUG,
+ "Sleeping...");
+ for (int i=0; i<110; i++) {
+ usleep(100000);
+ }
+ }
+
modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
&dfs_domain);
if (modes == NULL) {
@@ -308,7 +346,6 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
sec_chan);
}
-
static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
struct wpa_scan_results *scan_res;
@@ -517,8 +554,10 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
int ret;
/* Check that HT40 is used and PRI / SEC switch is allowed */
- if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
+ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch || iface->conf->noscan) {
+ wpa_printf(MSG_DEBUG, "Not scanning due to noscan?");
return 0;
+ }
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
@@ -967,7 +1006,7 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
if (!hostapd_is_usable_punct_bitmap(iface))
return 0;
- if (!iface->conf->secondary_channel)
+ if (!iface->conf->secondary_channel || iface->conf->noscan)
return 1;
if (hostapd_is_usable_chan(iface, iface->freq +
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 59ecbdce7..99d6e82bc 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -230,6 +230,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
return;
}
+ if (iface->conf->noscan)
+ return;
+
if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
wpa_printf(MSG_DEBUG,
"Ignore too short 20/40 BSS Coexistence Management frame");
@@ -390,6 +393,9 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
return;
+ if (iface->conf->noscan)
+ return;
+
wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
" in Association Request", MAC2STR(sta->addr));
--
2.39.1

View file

@ -0,0 +1,106 @@
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3459,6 +3459,8 @@
conf->noscan = atoi(pos);
} else if (os_strcmp(buf, "ht_coex") == 0) {
conf->no_ht_coex = !atoi(pos);
+ } else if (os_strcmp(buf, "intel_lar") == 0) {
+ conf->intel_lar = atoi(pos);
} else if (os_strcmp(buf, "ieee80211n") == 0) {
conf->ieee80211n = atoi(pos);
} else if (os_strcmp(buf, "ht_capab") == 0) {
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1050,6 +1050,7 @@
u16 ht_capab;
int noscan;
int no_ht_coex;
+ int intel_lar;
int ieee80211n;
int secondary_channel;
int no_pri_sec_switch;
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -26,6 +26,17 @@
#include "beacon.h"
#include "hw_features.h"
+static void ieee80211n_do_nothing(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG,
+ "Scan finished!");
+}
+
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params);
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params);
+
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
size_t num_hw_features)
@@ -82,6 +93,33 @@
if (hostapd_drv_none(hapd))
return -1;
+
+ //if (!iface->conf->noscan) {
+ if (iface->conf->intel_lar && !iface->conf->noscan) {
+ // scan
+ struct wpa_driver_scan_params params;
+ int ret1;
+
+ os_memset(&params, 0, sizeof(params));
+ ieee80211n_scan_channels_5g(iface, &params);
+
+ ret1 = hostapd_driver_scan(iface->bss[0], &params);
+
+ if (ret1 == -EBUSY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s)!",
+ ret1, strerror(-ret1));
+ }
+
+ if (ret1 == 0) {
+ iface->scan_cb = ieee80211n_do_nothing;
+ wpa_printf(MSG_DEBUG,
+ "Sleeping...");
+ for (int i=0; i<110; i++) {
+ usleep(100000);
+ }
+ }
+ }
modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
&dfs_domain);
if (modes == NULL) {
@@ -308,7 +346,6 @@
sec_chan);
}
-
static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
struct wpa_scan_results *scan_res;
@@ -517,8 +554,10 @@
int ret;
/* Check that HT40 is used and PRI / SEC switch is allowed */
- if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
+ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch || iface->conf->noscan) {
+ wpa_printf(MSG_DEBUG, "Not scanning due to noscan?");
return 0;
+ }
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
@@ -916,7 +954,7 @@
if (!hostapd_is_usable_edmg(iface))
return 0;
- if (!iface->conf->secondary_channel)
+ if (!iface->conf->secondary_channel || iface->conf->noscan)
return 1;
if (hostapd_is_usable_chan(iface, iface->freq +

View file

@ -0,0 +1,139 @@
diff -ru a/hostapd/config_file.c b/hostapd/config_file.c
--- a/hostapd/config_file.c 2022-01-16 15:51:29.000000000 -0500
+++ b/hostapd/config_file.c 2022-07-06 15:58:31.206322430 -0500
@@ -2906,6 +2906,8 @@
line, bss->wpa_deny_ptk0_rekey);
return 1;
}
+ } else if (os_strcmp(buf, "noscan") == 0) {
+ conf->noscan = atoi(pos);
} else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
char *endp;
unsigned long val = strtoul(pos, &endp, 0);
@@ -3474,6 +3476,8 @@
if (bss->ocv && !bss->ieee80211w)
bss->ieee80211w = 1;
#endif /* CONFIG_OCV */
+ } else if (os_strcmp(buf, "noscan") == 0) {
+ conf->noscan = atoi(pos);
} else if (os_strcmp(buf, "ieee80211n") == 0) {
conf->ieee80211n = atoi(pos);
} else if (os_strcmp(buf, "ht_capab") == 0) {
diff -ru a/src/ap/ap_config.h b/src/ap/ap_config.h
--- a/src/ap/ap_config.h 2022-01-16 15:51:29.000000000 -0500
+++ b/src/ap/ap_config.h 2022-07-06 15:58:31.206322430 -0500
@@ -1014,6 +1014,7 @@
int ht_op_mode_fixed;
u16 ht_capab;
+ int noscan;
int ieee80211n;
int secondary_channel;
int no_pri_sec_switch;
diff -ru a/src/ap/hw_features.c b/src/ap/hw_features.c
--- a/src/ap/hw_features.c 2022-01-16 15:51:29.000000000 -0500
+++ b/src/ap/hw_features.c 2022-07-06 22:57:53.007315518 -0500
@@ -24,6 +24,17 @@
#include "beacon.h"
#include "hw_features.h"
+static void ieee80211n_do_nothing(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG,
+ "Scan finished!");
+}
+
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params);
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params);
+
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
size_t num_hw_features)
@@ -82,6 +93,33 @@
if (hostapd_drv_none(hapd))
return -1;
+
+
+
+ // scan
+ struct wpa_driver_scan_params params;
+ int ret1;
+
+ os_memset(&params, 0, sizeof(params));
+ ieee80211n_scan_channels_5g(iface, &params);
+
+ ret1 = hostapd_driver_scan(iface->bss[0], &params);
+
+ if (ret1 == -EBUSY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s)!",
+ ret1, strerror(-ret1));
+ }
+
+ if (ret1 == 0) {
+ iface->scan_cb = ieee80211n_do_nothing;
+ wpa_printf(MSG_DEBUG,
+ "Sleeping...");
+ for (int i=0; i<110; i++) {
+ usleep(100000);
+ }
+ }
+
modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
&dfs_domain);
if (modes == NULL) {
@@ -308,7 +346,6 @@
sec_chan);
}
-
static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
struct wpa_scan_results *scan_res;
@@ -517,8 +554,10 @@
int ret;
/* Check that HT40 is used and PRI / SEC switch is allowed */
- if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
+ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch || iface->conf->noscan) {
+ wpa_printf(MSG_DEBUG, "Not scanning due to noscan?");
return 0;
+ }
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
@@ -915,7 +954,7 @@
if (!hostapd_is_usable_edmg(iface))
return 0;
- if (!iface->conf->secondary_channel)
+ if (!iface->conf->secondary_channel || iface->conf->noscan)
return 1;
if (hostapd_is_usable_chan(iface, iface->freq +
diff -ru a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
--- a/src/ap/ieee802_11_ht.c 2022-01-16 15:51:29.000000000 -0500
+++ b/src/ap/ieee802_11_ht.c 2022-07-06 15:58:31.206322430 -0500
@@ -230,6 +230,9 @@
return;
}
+ if (iface->conf->noscan)
+ return;
+
if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
wpa_printf(MSG_DEBUG,
"Ignore too short 20/40 BSS Coexistence Management frame");
@@ -390,6 +393,9 @@
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
return;
+ if (iface->conf->noscan)
+ return;
+
wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
" in Association Request", MAC2STR(sta->addr));