Compare commits

...

38 commits

Author SHA1 Message Date
7a0588afd2 added dynamic lan client hostname resolution to dnsmasq and unbound 2024-11-20 19:36:08 -08:00
b35b792cef unbound updates 2024-11-20 18:39:26 -08:00
675db09c39 updated kernel settings to allow lan interace to accept route advertisements for ipv6
updated firewall to support dhcp ipv6 requests
fixed caddy systemd directives
2024-11-20 18:28:32 -08:00
420250037f disabled ad blocking in Unbound, leaving it to adguard
updated nftables to support ipvt router advertisements
updated dnsmasq to send router advertisements
2024-11-20 17:28:31 -08:00
0c4c5a0fa0 wireguard working with nftables 2024-11-20 01:43:03 -08:00
6a3d739322 * Moved fully to nftables
* Moved to drop policy, blocked all traffic to WAN other than 443 and wireguard
2024-11-20 01:14:35 -08:00
f5cf3559e2 * Use domain in caddy bind
* Update caddy to require Unbound
2024-11-18 00:43:01 -08:00
24d95b3e75 * Minor caddy tweaks 2024-11-17 23:50:17 -08:00
3240c35efb unbound conf cleanup 2024-11-17 22:04:22 -08:00
596cc1e2e7 Caddy config
dont use 10.0.0.1 for public domains

added wireguard to unbound
2024-11-17 17:25:46 -08:00
7acfc92e7a * proxied-hosts config
* ipv6 working
* adguard pointing at unbound
2024-11-17 13:42:49 -08:00
622e85c129 * Updated wireguard configuration
* Updated internal network to 10.0.0.0/24
* Updated ddclient to support multiple zones
2024-11-15 21:22:20 -08:00
e21105f241 module-based DNS and DHCP config 2024-11-14 21:34:03 -08:00
81436b4192 * SOPS cert update
* Caddy updates
2024-11-13 15:59:36 -08:00
Ellis Rahhal
cf2f29a73f * Several updates to support NUC hardware 2024-11-13 15:25:54 -08:00
42bc948c3c removed disk config 2024-11-12 17:40:37 -08:00
8327f85db5 updated flake lock 2024-11-12 17:36:04 -08:00
ec9cb51b00 added disko 2024-11-12 17:35:44 -08:00
030430becd hardware updates 2024-11-12 17:29:43 -08:00
e610efa978 updated boot config 2024-11-12 11:18:57 -08:00
8cbdeda943 moved to unstable 2024-11-11 22:26:30 -08:00
1898aca426 added example system flake; updated readme 2024-11-06 14:09:00 -08:00
Ellis Rahhal
4749662b6a * Added Unifi
* Added Adguard Home
2024-11-06 11:35:15 -08:00
Ellis Rahhal
441ba09c48 added nvim config; added wireguard config 2024-11-03 00:05:50 -07:00
Ellis Rahhal
62e086cf4a added gitea; updated authentik setup; added sops config scripts 2024-11-02 10:23:44 -07:00
Ellis Rahhal
53b4cc2ee5 added link to haproxy config 2024-11-02 00:59:53 -07:00
Ellis Rahhal
fb758ef3c4 updated caddy config; updated sops generation scripts 2024-11-02 00:53:55 -07:00
Ellis Rahhal
d425f537b5 moved to a module to be imported by other flakes 2024-10-29 11:57:40 -07:00
Ellis Rahhal
cffcd62b3c added vaultwarden 2024-10-26 15:49:19 -07:00
Ellis Rahhal
9d6d46229a updated ddclient config 2024-10-26 14:41:16 -07:00
Ellis Rahhal
82817412fc updated ddclient config 2024-10-26 14:40:35 -07:00
Ellis Rahhal
c8ca72d834 upgraded to NixOS 24.05; doc update; minor tweaks 2024-09-19 21:40:20 -07:00
Ellis Rahhal
761c4e7c7d further auth setup 2024-04-28 19:34:35 -07:00
Ellis Rahhal
0540b3d641 fixed broken ldap import; temporarily disabled http to https caddy redirect 2024-04-28 16:12:46 -07:00
Ellis Rahhal
29877e469d authentik ldap working; home-assistant auth with LDAP working 2024-04-25 23:49:16 -07:00
Ellis Rahhal
1a2597de28 authentik working 2024-04-24 22:45:20 -07:00
Ellis Rahhal
25c07a884a Sops integrated; home assistant functioning; authentik integrated but not yet working 2024-04-24 09:22:04 -07:00
Ellis Rahhal
94b2c86208 ddclient update; added home-assistant 2024-03-14 23:50:10 -07:00
59 changed files with 4118 additions and 563 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
build
result
*.qcow2
homefree-image
lan-client-image

20
.sops.yaml Normal file
View file

@ -0,0 +1,20 @@
# see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
keys:
- &user_homefree 06321D7F20335A7E08595BA905D137EE114BA2C2
- &server_homefree 0BC4F8FF51F3167F06683FFB19008821C072983E
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree
- path_regex: secrets/homefree/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree

View file

@ -12,3 +12,66 @@ liberate you from giant cloud providers.
― Neal Stephenson, The Diamond Age: Or, a Young Lady's Illustrated Primer
## Don't suckle the Feed. Cultivate the Seed.
## Installing
Install NixOS directly, or use a deployment system such as [NixOS Anywhere](https://github.com/nix-community/nixos-anywhere)
Update system's configuration to look like something in [example-flake.nix](./example-flake.nix)
Configure system by setting up values as defined in the [HomeFree module](./module.nix)
## Adding a secret
```
nix-shell -p sops --run "sops secrets/authentik.yaml"
```
Then add a key or keys, e.g.
```
env-vars: |
VAR1 = abc
VAR2 = def
```
Then reference in Nix config as follows:
```
config.sops.secrets.app.env-vars.path
```
Or point directly to the path, e.g.
```
sops.secrets."app" = {
owner = "homefree";
path = "/run/secrets/app/env-vars";
restartUnits = [ "app.service" ];
};
```
and reference the path in config
## Getting server key
After starting the vm using `make run`, run `make generate-sops-config`
Then, within the VM:
```
cd ~/nixcfg
make build
```
## Initializing Authentik
Browse to:
http://ha.homefree.lan:9000/if/flow/initial-setup/
## Changing password for Authentik
ak create_recovery_key 10 akadmin
## Setting up HAProxy to transparently proxy TSL requests to Caddy
https://forum.opnsense.org/index.php?topic=18538.msg84958#msg84958

View file

@ -1,84 +0,0 @@
TODOS
=====
* setup VLANs
* https://wiki.nftables.org/wiki-nftables/index.php/Main_Page
* https://serverfault.com/questions/858556/transparent-firewall-with-nftables-and-vlans
* https://serverfault.com/questions/1057819/route-untagged-vlan-to-a-tagged-vlan-with-nftables
* setup ipv6
* Make a flake that sets up host machine for dev
* Figure out how to use host id_rsa.pub in build, rather than hard-coded key
* maybe share ~/.ssh/ida_rsa.pub into machine? would cause problems if share failed
* Determine if there are any problems with disabling "wait for network" to get rid of error
* Look into microvm instead of qemu, which has been difficult to work with
* https://github.com/astro/microvm.nix
* setup DNSSEC for dnsmasq, IPV6
* https://blog.josefsson.org/2015/10/26/combining-dnsmasq-and-unbound/
### Solutions
* Firewall
* https://www.jjpdev.com/posts/home-router-nixos/
* NAS
* https://github.com/reckenrode/nixos-configs/tree/c556206df2611af2f9ea83954aae1b51461e44c5/hosts/x86_64-linux/meteion
* https://www.reddit.com/r/NixOS/comments/yr21p1/offtheshelf_nas_supporting_nixos/
* reverse proxy
* Traefik
* HAProxy
* nginx-proxy-manager
* caddy
* ad block
* Unbound
* AdGuard
* PiHole
* intrusion protection
* https://www.redhat.com/sysadmin/security-intrusion-detection
* fail2ban
* Suricata
* OSSEC-HIDS
* Snort
* Zeek
* Tripwire
* certs
* certbot
* caddy
* SSO
* https://github.com/greenpau/caddy-security
* authentik
* authelia
* keycloak
* VPN
* Tailscale/Headscale
* https://tailscale.com/kb/1136/tailnet
* https://github.com/tailscale/golink
* https://github.com/tailscale-dev/tclip
* wireguard
* openvpn
* Backup
* bacula
* kopia
* restic
* Hypervisor
* https://vpsadminos.org/
### VyOS comparison
* Netfiter - does it use nftables? How does its config language map to Netfilter? Can it be extracted?
* FRR - high performance IP routing suite - any use for this in a home router?
* strongSwan - IPsec VPN. IPsec is slower than Wireguard but clients are built into OSes
* Acel-PPP - VPN/tunnel server for PPPoE, PPtP, L2TPv2, SSTP, IPoE
* FastNetMon - DDoS detection
* Squid - caching proxy
* PowerDNS - commercial grade DNS server
### DONE
* Setup host so default network virbr0 starts at boot
* currently need to us "sudo virsh net-start --network default"
* Does hardware-configuration.nix root disk device need to be updated with each build?
* Get VM starting from command line
* Figure out way to setup SSH without needing to lookup IP in virt-manager
* Use user network, map SSH to unused port
* https://unix.stackexchange.com/questions/489891/qemu-access-guest-vm-from-the-host-machine
* better though would be to add hostname, e.g. homefree-vm
* Share folder automatically setup by qemu xml?
* https://www.debugpoint.com/share-folder-virt-manager/

View file

@ -12,8 +12,8 @@ build_image() {
HOST=$1
nix build .#nixosConfigurations.${HOST}.config.formats.qcow
mkdir -p ./build
mv ./result ./${HOST}.qcow2
rsync -L ./${HOST}.qcow2 ./build/${HOST}.qcow2
mv ./result ./${HOST}-image
rsync -L ./${HOST}-image/nixos.qcow2 ./build/${HOST}.qcow2
chmod 750 ./build/${HOST}.qcow2
}

View file

@ -1,6 +1,17 @@
{ ...}:
{ homefree-inputs, ... }:
{
_module.args.homefree-inputs = homefree-inputs;
imports = [
./configuration.nix
homefree-inputs.nixos-generators.nixosModules.all-formats
homefree-inputs.nixos-hardware.nixosModules.common-cpu-intel
homefree-inputs.nixos-hardware.nixosModules.common-pc-laptop
homefree-inputs.disko.nixosModules.disko
homefree-inputs.sops-nix.nixosModules.sops
homefree-inputs.authentik-nix.nixosModules.default
homefree-inputs.nixvim.nixosModules.nixvim
./modules/ddclient-multi.nix
./module.nix
./hosts/homefree/configuration.nix
];
}

32
example-system-flake.nix Normal file
View file

@ -0,0 +1,32 @@
{
description = "HomeFree Instance";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
homefree.url = "github:erahhal/HomeFree";
};
outputs = {
self,
...
}@inputs:
{
nixosConfigurations = {
homefree =
let
system = "x86_64-linux";
in
inputs.nixpkgs.lib.nixosSystem {
system = system;
modules = [
inputs.homefree.nixosModules.default
];
specialArgs = {
inherit inputs;
inherit system;
};
};
};
};
}

608
flake.lock generated
View file

@ -3,17 +3,17 @@
"adblock-unbound": {
"inputs": {
"adblockStevenBlack": "adblockStevenBlack",
"flake-utils": "flake-utils",
"lancache-domains": "lancache-domains",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1688055723,
"narHash": "sha256-8WtkSAr4qYA3o6kiOCESK3rHJmIsa6TMBrT3/Cbfvro=",
"lastModified": 1704832551,
"narHash": "sha256-6xS/ANMIh3b4Ia3Ubl9rtb3LVw9QldihnP3IvuG9zwQ=",
"owner": "MayNiklas",
"repo": "nixos-adblock-unbound",
"rev": "9356ccd526fdcf91bfee7f0ebebae831349d43cc",
"rev": "a5d3731836b1c2ca65834e07be03c02daca5b434",
"type": "github"
},
"original": {
@ -38,56 +38,172 @@
"type": "github"
}
},
"agenix": {
"authentik-nix": {
"inputs": {
"darwin": "darwin",
"home-manager": "home-manager",
"authentik-src": "authentik-src",
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"flake-utils": "flake-utils",
"napalm": "napalm",
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix",
"systems": "systems"
},
"locked": {
"lastModified": 1703433843,
"narHash": "sha256-nmtA4KqFboWxxoOAA6Y1okHbZh+HsXaMPFkYHsoDRDw=",
"owner": "ryantm",
"repo": "agenix",
"rev": "417caa847f9383e111d1397039c9d4337d024bf0",
"lastModified": 1730763915,
"narHash": "sha256-+UPc6ZJHWJ9fQFAeqSqsQDCFDIXT+f3HiIOnHFIyrig=",
"owner": "erahhal",
"repo": "authentik-nix",
"rev": "daba454bd25cea9796e525d225f06fb0782abba6",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"owner": "erahhal",
"ref": "no-docs",
"repo": "authentik-nix",
"type": "github"
}
},
"darwin": {
"authentik-src": {
"flake": false,
"locked": {
"lastModified": 1730315123,
"narHash": "sha256-UYOdBlkGeIGCG/pCGLANWv1bKTdBEUp6jTiLG7BpY7E=",
"owner": "goauthentik",
"repo": "authentik",
"rev": "e8b5e4c1272151f4a3666e53754f7deefb8e2fb3",
"type": "github"
},
"original": {
"owner": "goauthentik",
"ref": "version/2024.8.4",
"repo": "authentik",
"type": "github"
}
},
"devshell": {
"inputs": {
"nixpkgs": [
"agenix",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1700795494,
"narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
"lastModified": 1728330715,
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
"owner": "numtide",
"repo": "devshell",
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1731274291,
"narHash": "sha256-cZ0QMpv5p2a6WEE+o9uu0a4ma6RzQDOQTbm7PbixWz8=",
"owner": "nix-community",
"repo": "disko",
"rev": "486250f404f4a4f4f33f8f669d83ca5f6e6b7dfc",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"revCount": 57,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1726153070,
"narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1730504689,
"narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "506278e768c2a08bec68eb62932193e341f55c90",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": [
"authentik-nix",
"systems"
]
},
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
@ -96,19 +212,89 @@
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"nixvim",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"nixvim",
"nixpkgs"
],
"nixpkgs-stable": [
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1730302582,
"narHash": "sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"nixvim",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"agenix",
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1703113217,
"narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=",
"lastModified": 1731235328,
"narHash": "sha256-NjavpgE9/bMe/ABvZpyHIUeYF1mqR5lhaep3wB79ucs=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1",
"rev": "60bb110917844d354f3c18e05450606a435d2d10",
"type": "github"
},
"original": {
@ -117,17 +303,108 @@
"type": "github"
}
},
"ixx": {
"inputs": {
"flake-utils": [
"nixvim",
"nuschtosSearch",
"flake-utils"
],
"nixpkgs": [
"nixvim",
"nuschtosSearch",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729958008,
"narHash": "sha256-EiOq8jF4Z/zQe0QYVc3+qSKxRK//CFHMB84aYrYGwEs=",
"owner": "NuschtOS",
"repo": "ixx",
"rev": "9fd01aad037f345350eab2cd45e1946cc66da4eb",
"type": "github"
},
"original": {
"owner": "NuschtOS",
"ref": "v0.0.6",
"repo": "ixx",
"type": "github"
}
},
"lancache-domains": {
"flake": false,
"locked": {
"lastModified": 1679999806,
"narHash": "sha256-oDZ2pSf8IgofRS4HaRppGcd4kHQj48AC9dkS++avYy8=",
"owner": "uklans",
"repo": "cache-domains",
"rev": "31b2ba1e0a7c419327cb97f589b508d78b9aecbf",
"type": "github"
},
"original": {
"owner": "uklans",
"repo": "cache-domains",
"type": "github"
}
},
"napalm": {
"inputs": {
"flake-utils": [
"authentik-nix",
"flake-utils"
],
"nixpkgs": [
"authentik-nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1725806412,
"narHash": "sha256-lGZjkjds0p924QEhm/r0BhAxbHBJE1xMOldB/HmQH04=",
"owner": "willibutz",
"repo": "napalm",
"rev": "b492440d9e64ae20736d3bec5c7715ffcbde83f5",
"type": "github"
},
"original": {
"owner": "willibutz",
"ref": "avoid-foldl-stack-overflow",
"repo": "napalm",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1730448474,
"narHash": "sha256-qE/cYKBhzxHMtKtLK3hlSR3uzO1pWPGLrBuQK7r0CHc=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "683d0c4cd1102dcccfa3f835565378c7f3cbe05e",
"type": "github"
},
"original": {
"owner": "lnl7",
"repo": "nix-darwin",
"type": "github"
}
},
"nix-editor": {
"inputs": {
"nixpkgs": "nixpkgs_2",
"utils": "utils"
},
"locked": {
"lastModified": 1681140879,
"narHash": "sha256-eyLPtopt7lRvmRDJx7gSBYUtYGfOSVXarf0KbLbw/Sw=",
"lastModified": 1703105021,
"narHash": "sha256-Ne9NG7x45a8aJyAN+yYWbr/6mQHBVVkwZZ72EZHHRqw=",
"owner": "vlinkz",
"repo": "nix-editor",
"rev": "ab2a7e94ca176589c1e8236ce31cd89044e4818f",
"rev": "b5017f8d61753ce6a3a1a2aa7e474d59146a8ae3",
"type": "github"
},
"original": {
@ -136,13 +413,35 @@
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"authentik-nix",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1703863825,
"narHash": "sha256-rXwqjtwiGKJheXB43ybM8NwWB8rO2dSRrEqes0S7F5Y=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "5163432afc817cf8bd1f031418d1869e4c9d5547",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nixlib": {
"locked": {
"lastModified": 1693701915,
"narHash": "sha256-waHPLdDYUOHSEtMKKabcKIMhlUOHPOOPQ9UyFeEoovs=",
"lastModified": 1729990941,
"narHash": "sha256-hUP9oxmnOmNnKcDOf5Y55HQ+NnoT0+bLWHLQWLLw9Ks=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "f5af57d3ef9947a70ac86e42695231ac1ad00c25",
"rev": "7d68864343650322045894951602d6e82b5296d7",
"type": "github"
},
"original": {
@ -159,11 +458,11 @@
]
},
"locked": {
"lastModified": 1696058303,
"narHash": "sha256-eNqKWpF5zG0SrgbbtljFOrRgFgRzCc4++TMFADBMLnc=",
"lastModified": 1731341898,
"narHash": "sha256-2L34Jrd73o6q6t8xMVVK7cj4qk5uHYbSIh72qtIKWWs=",
"owner": "nix-community",
"repo": "nixos-generators",
"rev": "150f38bd1e09e20987feacb1b0d5991357532fb5",
"rev": "3a5bf194b6b25ee27a2c61afed039f3cb3a0fbfc",
"type": "github"
},
"original": {
@ -174,11 +473,11 @@
},
"nixos-hardware": {
"locked": {
"lastModified": 1700559156,
"narHash": "sha256-gL4epO/qf+wo30JjC3g+b5Bs8UrpxzkhNBBsUYxpw2g=",
"lastModified": 1731332224,
"narHash": "sha256-0ctfVp27ingWtY7dbP5+QpSQ98HaOZleU0teyHQUAw0=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "c3abafb01cd7045dba522af29b625bd1e170c2fb",
"rev": "184687ae1a3139faa4746168baf071f60d0310c8",
"type": "github"
},
"original": {
@ -190,11 +489,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1703013332,
"narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=",
"lastModified": 1726937504,
"narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6",
"rev": "9357f4f23713673f310988025d9dc261c20e70c6",
"type": "github"
},
"original": {
@ -204,49 +503,30 @@
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1725233747,
"narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1705033721,
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=",
"lastModified": 1730602179,
"narHash": "sha256-efgLzQAWSzJuCLiCaQUCDu4NudNlHdg2NzGLX5GYaEY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea",
"rev": "3c2f1c4ca372622cb2f9de8016c9a0b1cbd0f37c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-trunk": {
"locked": {
"lastModified": 1700973916,
"narHash": "sha256-4W1xIjy67P/8ZcZMZxysTNgjNu9G8DegkI4ac+cnRYY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1744e3fa0103321e7d21d6b907eeff6965adf964",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1700794826,
"narHash": "sha256-RyJTnTNKhO0yqRpDISk03I/4A67/dp96YRxc86YOPgU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5a09cb4b393d58f9ed0d9ca1555016a8543c2ac8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
@ -269,27 +549,27 @@
},
"nixpkgs_3": {
"locked": {
"lastModified": 1700787330,
"narHash": "sha256-4VIBCyfqnEsdVP/SgKZ3rudwzxGdEqpKfgoWETs/I6k=",
"lastModified": 1731139594,
"narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7f7851dfc570812c305d89438681b715a4f7beba",
"rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1704842529,
"narHash": "sha256-OTeQA+F8d/Evad33JMfuXC89VMetQbsU4qcaePchGr4=",
"lastModified": 1730272153,
"narHash": "sha256-B5WRZYsRlJgwVHIV6DvidFN7VX7Fg9uuwkRW9Ha8z+w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "eabe8d3eface69f5bb16c18f8662a702f50c20d5",
"rev": "2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53",
"type": "github"
},
"original": {
@ -299,16 +579,98 @@
"type": "github"
}
},
"nixvim": {
"inputs": {
"devshell": "devshell",
"flake-compat": "flake-compat_2",
"flake-parts": "flake-parts_2",
"git-hooks": "git-hooks",
"home-manager": "home-manager",
"nix-darwin": "nix-darwin",
"nixpkgs": [
"nixpkgs"
],
"nuschtosSearch": "nuschtosSearch",
"treefmt-nix": "treefmt-nix_2"
},
"locked": {
"lastModified": 1731356813,
"narHash": "sha256-w0TJwJwZd9so/chWYFFEtOQdnXTCvmNXIHs1FWJDlMM=",
"owner": "nix-community",
"repo": "nixvim",
"rev": "c892aa20732f982d4cc2b3ef2e2276a2a9a4d45b",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixvim",
"type": "github"
}
},
"nuschtosSearch": {
"inputs": {
"flake-utils": "flake-utils_2",
"ixx": "ixx",
"nixpkgs": [
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1731347683,
"narHash": "sha256-BcSWCEUBShuB32LPif+EG0XGXyUi2jyjCSpGE1rbOws=",
"owner": "NuschtOS",
"repo": "search",
"rev": "135d202e032be70c93b6d7d53592ef4799d6efde",
"type": "github"
},
"original": {
"owner": "NuschtOS",
"repo": "search",
"type": "github"
}
},
"poetry2nix": {
"inputs": {
"flake-utils": [
"authentik-nix",
"flake-utils"
],
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"authentik-nix",
"nixpkgs"
],
"systems": [
"authentik-nix",
"systems"
],
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1727169160,
"narHash": "sha256-m/3tT0Wvk2V4H15riZC/yT7i7t+8V58HTDpthKV5yhk=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "7624b3e0275d9b52dbdda46ef7ffee66b36ff823",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"root": {
"inputs": {
"adblock-unbound": "adblock-unbound",
"agenix": "agenix",
"authentik-nix": "authentik-nix",
"disko": "disko",
"nix-editor": "nix-editor",
"nixos-generators": "nixos-generators",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs_3",
"nixpkgs-trunk": "nixpkgs-trunk",
"nixpkgs-unstable": "nixpkgs-unstable",
"nixvim": "nixvim",
"sops-nix": "sops-nix"
}
},
@ -318,11 +680,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1705201153,
"narHash": "sha256-y0/a4IMDZrc7lAkR7Gcm5R3W2iCBiARHnYZe6vkmiNE=",
"lastModified": 1731364708,
"narHash": "sha256-HC0anOL+KmUQ2hdRl0AtunbAckasxrkn4VLmxbW/WaA=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "70dd0d521f7849338e487a219c1a07c429a66d77",
"rev": "4c91d52db103e757fc25b58998b0576ae702d659",
"type": "github"
},
"original": {
@ -332,6 +694,21 @@
}
},
"systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@ -346,6 +723,49 @@
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"authentik-nix",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719749022,
"narHash": "sha256-ddPKHcqaKCIFSFc/cvxS14goUhCOAwsM1PbMr0ZtHMg=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "8df5ff62195d4e67e2264df0b7f5e8c9995fd0bd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": [
"nixvim",
"nixpkgs"
]
},
"locked": {
"lastModified": 1730321837,
"narHash": "sha256-vK+a09qq19QNu2MlLcvN4qcRctJbqWkX7ahgPZ/+maI=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "746901bb8dba96d154b66492a29f5db0693dbfcc",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"locked": {
"lastModified": 1667395993,

View file

@ -2,17 +2,20 @@
description = "HomeFree Self-Hosting Platform";
inputs = {
# Use stable for main
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
# Trails trunk - latest packages with broken commits filtered out
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
# Very latest packages - some commits broken
nixpkgs-trunk.url = "github:NixOS/nixpkgs";
# nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
nixvim = {
# url = "github:nix-community/nixvim/nixos-24.05";
url = "github:nix-community/nixvim";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-generators = {
url = "github:nix-community/nixos-generators";
inputs.nixpkgs.follows = "nixpkgs";
@ -20,8 +23,6 @@
nix-editor.url = "github:vlinkz/nix-editor";
agenix.url = "github:ryantm/agenix";
sops-nix.url = "github:Mic92/sops-nix";
adblock-unbound = {
@ -29,6 +30,18 @@
inputs.nixpkgs.follows = "nixpkgs";
};
authentik-nix = {
# url = "github:nix-community/authentik-nix/version/2024.8.3";
# url = "github:nix-community/authentik-nix";
url = "github:erahhal/authentik-nix/no-docs";
## optional overrides. Note that using a different version of nixpkgs can cause issues, especially with python dependencies
# inputs.flake-parts.follows = "flake-parts";
};
styx = {
url = "github:styx-static/styx";
};
# notnft = {
# url = "github:chayleaf/notnft";
# inputs.nixpkgs.follows = "nixpkgs";
@ -40,56 +53,33 @@
# };
};
outputs = {
self,
nixos-generators,
nixos-hardware,
nixpkgs,
agenix,
sops-nix,
...
}@inputs:
outputs = { self, ... } @ inputs:
let
system = "x86_64-linux";
# Can't use name "inputs" as it gets overridden by parent flakes that define inputs.nixpkgs.lib.nixosSystem
homefree-inputs = inputs;
# versionInfo = import ./version.nix;
# version = versionInfo.version + (inputs.nixpkgs.lib.optionalString (!versionInfo.released) "-dirty");
in
{
nixosModules = rec {
homefree = import ./default.nix { inherit homefree-inputs; inherit system; };
imports = [ ];
default = homefree;
lan-client = import ./lan-client.nix { inherit homefree-inputs; inherit system; };
};
nixosConfigurations = {
homefree =
let
system = "x86_64-linux";
in
inputs.nixpkgs.lib.nixosSystem {
homefree-test = inputs.nixpkgs.lib.nixosSystem {
system = system;
modules = [
nixos-hardware.nixosModules.common-cpu-intel
nixos-hardware.nixosModules.common-pc-laptop
agenix.nixosModules.default
sops-nix.nixosModules.sops
# inputs.nixos-router.nixosModules.default
# inputs.notnft.lib.${system}
(import ./hosts/homefree/configuration.nix)
self.nixosModules.homefree
];
specialArgs = {
inherit inputs;
inherit system;
inherit agenix;
inherit sops-nix;
};
};
lan-client =
let
system = "x86_64-linux";
in
inputs.nixpkgs.lib.nixosSystem {
lan-client = inputs.nixpkgs.lib.nixosSystem {
system = system;
modules = [
nixos-hardware.nixosModules.common-cpu-intel
nixos-hardware.nixosModules.common-pc-laptop
(import ./hosts/lan-client/configuration.nix)
self.nixosModules.lan-client
];
specialArgs = {
inherit inputs;
inherit system;
};
};
};
};

23
generate-sops-config-host.sh Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env bash
## Import the user's SSH key into GPG
cp ~/.ssh/id_rsa /tmp/id_rsa
ssh-keygen -p -N "" -f /tmp/id_rsa
USER_GPG_FINGERPRINT=$(nix-shell --quiet -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --quiet" 2>&1)
echo "${USER_GPG_FINGERPRINT}"
rm /tmp/id_rsa
# set ultimate trust level
# nix-shell --quiet -p gnupg --run "echo -e 'trust\n5\ny\n' | gpg --command-fd 0 --edit-key ${USER_GPG_FINGERPRINT}"
nix-shell --quiet -p gnupg --run "echo \"${USER_GPG_FINGERPRINT}:6:\" | gpg --import-ownertrust"
## Import the homefree host SSH key into GPG
# remove key from known_hosts
ssh-keygen -R "[localhost]:2223"
# Get GPG fingerprint of server RSA key
SERVER_GPG_FINGERPRINT=$(nix-shell --quiet -p gnupg -p ssh-to-pgp --run "ssh -o LogLevel=ERROR -o StrictHostKeychecking=no -p 2223 homefree@localhost \"sudo cat /etc/ssh/ssh_host_rsa_key\" | ssh-to-pgp -private-key | gpg --import --allow-non-selfsigned-uid --quiet" 2>&1 | head -n 1)
echo "${SERVER_GPG_FINGERPRINT}"
# set ultimate trust level
nix-shell --quiet -p gnupg --run "echo \"${SERVER_GPG_FINGERPRINT}:6:\" | gpg --import-ownertrust"

52
generate-sops-config-old.sh Executable file
View file

@ -0,0 +1,52 @@
#!/usr/bin/env bash
## Import the user's SSH key into GPG
cp ~/.ssh/id_rsa /tmp/id_rsa
ssh-keygen -p -N "" -f /tmp/id_rsa
USER_GPG_FINGERPRINT=$(nix-shell --quiet -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --quiet" 2>&1)
echo "${USER_GPG_FINGERPRINT}"
rm /tmp/id_rsa
# set ultimate trust level
# nix-shell --quiet -p gnupg --run "echo -e 'trust\n5\ny\n' | gpg --command-fd 0 --edit-key ${USER_GPG_FINGERPRINT}"
nix-shell --quiet -p gnupg --run "echo \"${USER_GPG_FINGERPRINT}:6:\" | gpg --import-ownertrust"
## Import the homefree host SSH key into GPG
# remove key from known_hosts
ssh-keygen -R "[localhost]:2223"
# Get GPG fingerprint of server RSA key
SERVER_GPG_FINGERPRINT=$(nix-shell --quiet -p gnupg -p ssh-to-pgp --run "ssh -o LogLevel=ERROR -o StrictHostKeychecking=no -p 2223 homefree@localhost \"sudo cat /etc/ssh/ssh_host_rsa_key\" | ssh-to-pgp -private-key | gpg --import --allow-non-selfsigned-uid --quiet" 2>&1 | head -n 1)
# set ultimate trust level
nix-shell --quiet -p gnupg --run "echo \"${SERVER_GPG_FINGERPRINT}:6:\" | gpg --import-ownertrust"
# This example uses YAML anchors which allows reuse of multiple keys
# without having to repeat yourself.
# Also see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
# for a more complex example.
cat > .sops.yaml << EOF
# see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
keys:
- &user_homefree $USER_GPG_FINGERPRINT
- &server_homefree $SERVER_GPG_FINGERPRINT
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree
- path_regex: secrets/homefree/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree
EOF
for config in $(find secrets -name '*.yaml'); do
nix-shell -p sops --run "sops updatekeys $config"
done

58
generate-sops-config-server.sh Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env bash
## @TODO: consolidate to single script that works on host or on guest
## @TODO: If no user key on guest, complain and abort
## @TODO: Fix error messages that mess with getting fingerprit
## @TODO: Make sure uid matches <curruser>@localhost, as it tells sops where to look for keyring
## https://www.reddit.com/r/GnuPG/comments/m76to1/is_there_a_way_to_change_the_name_on_a_key_pair/
## Import the user's SSH key into GPG
# cp ~/.ssh/id_rsa /tmp/id_rsa
# ssh-keygen -p -N "" -f /tmp/id_rsa
# USER_GPG_FINGERPRINT=$(nix-shell --quiet -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --allow-non-selfsigned-uid --quiet" 2>&1 | head -2 | tail -1)
# echo "${USER_GPG_FINGERPRINT}"
# rm /tmp/id_rsa
# # set ultimate trust level
# # nix-shell --quiet -p gnupg --run "echo -e 'trust\n5\ny\n' | gpg --command-fd 0 --edit-key ${USER_GPG_FINGERPRINT}"
# nix-shell --quiet -p gnupg --run "echo \"${USER_GPG_FINGERPRINT}:6:\" | gpg --import-ownertrust"
#
# ## Import the homefree host SSH key into GPG
#
# # remove key from known_hosts
# ssh-keygen -R "[localhost]:2223"
# # Get GPG fingerprint of server RSA key
# SERVER_GPG_FINGERPRINT=$(nix-shell --quiet -p gnupg -p ssh-to-pgp --run "sudo cat /etc/ssh/ssh_host_rsa_key | ssh-to-pgp -private-key | gpg --import --allow-non-selfsigned-uid --quiet" 2>&1 | head -2 | tail -1)
# # set ultimate trust level
# nix-shell --quiet -p gnupg --run "echo \"${SERVER_GPG_FINGERPRINT}:6:\" | gpg --import-ownertrust"
#
# # This example uses YAML anchors which allows reuse of multiple keys
# # without having to repeat yourself.
# # Also see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
# # for a more complex example.
# cat > .sops.yaml << EOF
# # see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
# keys:
# - &user_homefree $USER_GPG_FINGERPRINT
# - &server_homefree $SERVER_GPG_FINGERPRINT
# creation_rules:
# - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
# key_groups:
# - pgp:
# - *user_homefree
# - *server_homefree
# - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
# key_groups:
# - pgp:
# - *user_homefree
# - *server_homefree
# - path_regex: secrets/homefree/[^/]+\.(yaml|json|env|ini)$
# key_groups:
# - pgp:
# - *user_homefree
# - *server_homefree
# EOF
for config in $(find secrets -name '*.yaml'); do
nix-shell -p sops --run "sops updatekeys $config"
done

View file

@ -1,30 +1,9 @@
#!/usr/bin/env bash
cp ~/.ssh/id_rsa /tmp/id_rsa
ssh-keygen -p -N "" -f /tmp/id_rsa
USER_GPG_FINGERPRINT=$(nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i ~/.ssh/id_rsa | gpg --import --quiet" 2>&1)
rm /tmp/id_rsa
ssh -o LogLevel=ERROR -o StrictHostKeychecking=no -p 2223 homefree@localhost "mkdir -p ~/.ssh"
scp -P 2223 ~/.ssh/id_rsa homefree@localhost:/home/homefree/.ssh/id_rsa
scp -P 2223 ~/.ssh/id_rsa.pub homefree@localhost:/home/homefree/.ssh/id_rsa.pub
ssh-keygen -R "[localhost]:2223"
SERVER_GPG_FINGERPRINT=$(ssh -o StrictHostKeychecking=no -p 2223 homefree@localhost "sudo cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o homefree.asc" 2>&1)
ssh -o LogLevel=ERROR -o StrictHostKeychecking=no -p 2223 homefree@localhost "cd ~/nixcfg/HomeFree; ./generate-sops-config-server.sh"
# This example uses YAML anchors which allows reuse of multiple keys
# without having to repeat yourself.
# Also see https://github.com/Mic92/dotfiles/blob/master/nixos/.sops.yaml
# for a more complex example.
cat > .sops.yaml << EOF
keys:
- &user_homefree $USER_GPG_FINGERPRINT
- &server_homefree $SERVER_GPG_FINGERPRINT
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree
- path_regex: secrets/homefree/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *user_homefree
- *server_homefree
EOF
./generate-sops-config-host.sh

View file

@ -1,15 +1,27 @@
{ inputs, ... }:
{ config, lib, ... }:
{
imports = [
inputs.nixos-generators.nixosModules.all-formats
../../profiles/agenix.nix
../../profiles/adguardhome.nix
../../profiles/authentik.nix
../../profiles/common.nix
../../profiles/config-editor.nix
../../profiles/ddclient.nix
../../profiles/dnsmasq.nix
../../profiles/home-assistant
../../profiles/git.nix
../../profiles/gitea.nix
../../profiles/hardware-configuration.nix
../../profiles/hosting.nix
../../profiles/nixvim.nix
../../profiles/postgres.nix
# ../../profiles/radvd.nix
../../profiles/router.nix
../../profiles/virtual-machine.nix
../../profiles/traffic-shaping.nix
../../profiles/unbound.nix
../../profiles/unifi.nix
../../profiles/vaultwarden.nix
../../profiles/wireguard.nix
];
# --------------------------------------------------------------------------------------
@ -46,19 +58,20 @@
# --------------------------------------------------------------------------------------
# @TODO: Make this UI configurable
time.timeZone = "America/Los_Angeles";
## Must be forced due to Authentik hard coding a value of UTC
time.timeZone = lib.mkForce config.homefree.system.timeZone;
networking = {
# @TODO: Make this UI configurable
hostName = "homefree";
hostName = config.homefree.system.hostName;
## NetworkManager disabled in favor of networkd
useNetworkd = true;
wireless = {
# Disable wpa_supplicant
enable = false;
};
# wireless = {
# # Disable wpa_supplicant
# enable = false;
# };
interfaces = {
ens3.useDHCP = true;
"${config.homefree.network.wan-interface}".useDHCP = true;
};
};

View file

@ -1,8 +1,7 @@
{ inputs, ... }:
{ ... }:
{
imports = [
inputs.nixos-generators.nixosModules.all-formats
../../profiles/common.nix
../../profiles/hardware-configuration.nix
];

4
import-ssh-keys-to-gpg.sh Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
nix-shell --quiet -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i ~/.ssh/id_rsa | gpg --import --quiet"
nix-shell --quiet -p gnupg -p ssh-to-pgp --run "ssh -o StrictHostKeychecking=no -p 2223 homefree@localhost \"sudo cat /etc/ssh/ssh_host_rsa_key\" | ssh-to-pgp -private-key | gpg --import --quiet"

11
lan-client.nix Normal file
View file

@ -0,0 +1,11 @@
{ homefree-inputs, system, ... }:
{
_module.args.homefree-inputs = homefree-inputs;
imports = [
homefree-inputs.nixos-generators.nixosModules.all-formats
homefree-inputs.nixos-hardware.nixosModules.common-cpu-intel
homefree-inputs.nixos-hardware.nixosModules.common-pc-laptop
./hosts/lan-client/configuration.nix
];
}

373
module.nix Normal file
View file

@ -0,0 +1,373 @@
## @TODO: Look at the following for a VM test setup
## https://github.com/nix-community/disko/blob/master/module.nix
{ config, lib, extendModules, ... }:
# let
# vmVariantWithHomefree = extendModules {
# modules = [
# ./lib/interactive-vm.nix
# ];
# };
# in
{
options.homefree = {
system = {
hostName = lib.mkOption {
type = lib.types.str;
default = "homefree";
description = "Hostname for the system";
};
## @TODO: Detect during setup
timeZone = lib.mkOption {
type = lib.types.str;
default = "America/Los_Angeles";
description = "Timezone for the system";
};
## @TODO: Detect during setup
defaultLocale = lib.mkOption {
type = lib.types.str;
default = "en_US.UTF-8";
description = "Default locale for the system";
};
localDomain = lib.mkOption {
type = lib.types.str;
## @TODO: Should this be "local"?
default = "localdomain";
description = "local lan domain";
};
## @TODO: Deduplicate this with localDomain
## recursive?
searchDomainsLocal = lib.mkOption {
type = lib.types.listOf lib.types.str;
## @TODO: Should this be "local"?
default = [ "localdomain" ];
description = "Search domain for the system";
};
domain = lib.mkOption {
type = lib.types.str;
default = "homefree.host";
description = "Domain for the system";
};
additionalDomains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Additional zones for the system";
};
adminUsername = lib.mkOption {
type = lib.types.str;
default = "homefree";
description = "Username for the system admin";
};
adminHashedPassword = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Hashed password for the system admin
Generate with:
mkpasswd -m sha-512
'';
};
authorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "SSH authorized keys for the system admin";
};
};
## @TODO: Add default subnet and gateway config, e.g. 10.0.0.0/24, 10.0.0.1
## @TODO: This section doesn't make sense. Some network config is in "system" above
## and some is in separate services, e.g. unbound and ddns
network = {
## @TODO: Detect during setup
wan-interface = lib.mkOption {
type = lib.types.str;
default = "ens3";
description = "External interface to the internet";
};
wan-bitrate-mbps-down = lib.mkOption {
type = lib.types.int;
description = "WAN download bitrate in Mbit/s";
};
wan-bitrate-mbps-up = lib.mkOption {
type = lib.types.int;
description = "WAN upload bitrate in Mbit/s";
};
## @TODO: Detect during setup
lan-interface = lib.mkOption {
type = lib.types.str;
default = "ens5";
description = "Internal interface to the local network";
};
static-ip-expiration = lib.mkOption {
type = lib.types.str;
default = "3d";
description = "Expiration time of static IPs";
};
static-ips = lib.mkOption {
default = [];
description = "Static IP mappings";
type = with lib.types; listOf (submodule {
options = {
mac-address = lib.mkOption {
type = lib.types.str;
description = "MAC address to assign IP to";
};
hostname = lib.mkOption {
type = lib.types.str;
description = "Hostname to assign to IP";
};
ip = lib.mkOption {
type = lib.types.str;
description = "IP Address";
};
};
});
};
## @TODO: Make type for dns override entry
dns-overrides = lib.mkOption {
description = "dns hostname to IP overrides";
default = [];
type = with lib.types; listOf (submodule {
options = {
hostname = lib.mkOption {
type = lib.types.str;
description = "Hostname of override";
};
domain = lib.mkOption {
type = lib.types.str;
description = "Domain of override";
};
ip = lib.mkOption {
type = lib.types.str;
description = "IP Address";
};
};
});
};
enable-adblock = lib.mkOption {
type = lib.types.bool;
default = false;
description = "enable ad blocking";
};
blocked-domains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "list of domains to block";
};
};
dynamic-dns = {
interval = lib.mkOption {
type = lib.types.str;
default = "10m";
description = "Interval for dynamic DNS client";
};
usev4 = lib.mkOption {
type = lib.types.str;
default = "webv4, webv4=ipinfo.io/ip";
description = "Use format for obtaining ipv4 for dynamic DNS client";
};
usev6 = lib.mkOption {
type = lib.types.str;
default = "webv6, webv6=v6.ipinfo.io/ip";
description = "Use format for obtaining ipv6 for dynamic DNS client";
};
zones = lib.mkOption {
description = "Dynamic DNS Zone Config";
default = [];
type = with lib.types; listOf (submodule {
options = {
disable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "disable dynamic dns for zone";
};
## @TODO: validate against network.domain and network.additionalDomains?
zone = lib.mkOption {
type = lib.types.str;
default = "homefree.host";
description = "Zone for dynamic DNS client";
};
protocol = lib.mkOption {
type = lib.types.str;
default = "hetzner";
description = "Protocol for dynamic DNS client";
};
username = lib.mkOption {
type = lib.types.str;
default = "erahhal";
description = "Username for dynamic DNS client";
};
domains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "@" "*" "www" "dev" ];
description = "Domains for dynamic DNS client";
};
passwordFile = lib.mkOption {
type = lib.types.str;
description = "String path to password file";
};
};
});
};
};
wireguard = {
listenPort = lib.mkOption {
type = lib.types.int;
default = 64210;
description = "External listening port for clients";
};
peers = lib.mkOption {
description = "List of wireguard peers";
example = ''
[
# List of allowed peers.
{ # Feel free to give a meaning full name
# Public key of the peer (not a file path).
publicKey = "{client public key}";
# List of IPs assigned to this peer within the tunnel subnet. Used to configure routing.
allowedIPs = [ "10.100.0.2/32" ];
}
{ # John Doe
publicKey = "{john doe's public key}";
allowedIPs = [ "10.100.0.3/32" ];
}
];
'';
type = with lib.types; listOf (submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
default = "";
description = "Name of peer";
};
publicKey = lib.mkOption {
type = lib.types.str;
default = "";
description = "Public key for peer";
};
allowedIPs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "List of IPs assigned to this peer within the tunnel subnet. Used to configure routing.";
};
};
});
};
};
proxied-hosts = lib.mkOption {
description = "List of hosts on lan to proxy";
type = with lib.types; listOf (submodule {
options = {
label = lib.mkOption {
type = lib.types.str;
default = "";
description = "label of proxy config";
};
description = lib.mkOption {
type = lib.types.str;
default = "";
description = "description of proxy config";
};
subdomains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "list of subdomains";
};
http-domains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "list of http domains";
};
https-domains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "list of https domains";
};
host = lib.mkOption {
type = lib.types.str;
default = "10.0.0.1";
description = "host name or address of service to proxy";
};
port = lib.mkOption {
type = lib.types.int;
description = "port of service on lan network";
};
public = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to expose on WAN interface";
};
ssl = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether upstream service is using TLS";
};
ssl-no-verify = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to verify certificate of upstream service";
};
};
});
};
};
# options.virtualisation.vmVariantWithHomefree = lib.mkOption {
# description = ''
# Machine configuration to be added for the vm script available at `.system.build.vmWithHomefree`.
# '';
# inherit (vmVariantWithHomefree) type;
# default = { };
# visible = "shallow";
# };
#
# config = {
# system.build = {
# testVms = lib.mkDefault config.virtualisation.vmVariantWithHomefree.system.build.vmWithHomefree;
# };
# };
}

275
modules/ddclient-multi.nix Normal file
View file

@ -0,0 +1,275 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.ddclient-multi;
boolToStr = bool: if bool then "yes" else "no";
dataDir = "/var/lib/ddclient";
StateDirectory = builtins.baseNameOf dataDir;
RuntimeDirectory = StateDirectory;
configFile' = pkgs.writeText "ddclient.conf" (''
# This file can be used as a template for configFile or is automatically generated by Nix options.
cache=${dataDir}/ddclient.cache
foreground=YES
quiet=${boolToStr cfg.quiet}
verbose=${boolToStr cfg.verbose}
${lib.optionalString (cfg.use != "") "use=${cfg.use}"}
${lib.optionalString (cfg.use == "" && cfg.usev4 != "") "usev4=${cfg.usev4}"}
${lib.optionalString (cfg.use == "" && cfg.usev6 != "") "usev6=${cfg.usev6}"}
${cfg.extraConfig}
'' + lib.concatMapStrings (zoneCfg: ''
login=${zoneCfg.username}
password=${if zoneCfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/${zoneCfg.zone}/ddclient.key" else "@${zoneCfg.zone}_password_placeholder@"}
protocol=${zoneCfg.protocol}
ssl=${boolToStr zoneCfg.ssl}
wildcard=YES
${lib.optionalString (zoneCfg.script != "") "script=${zoneCfg.script}"}
${lib.optionalString (zoneCfg.server != "") "server=${zoneCfg.server}"}
${lib.optionalString (zoneCfg.extraConfig != "") zoneCfg.extraConfig}
${lib.optionalString (zoneCfg.zone != "") "zone=${zoneCfg.zone}"}
${lib.concatStringsSep "," zoneCfg.domains}
'') (lib.filter (zone: zone.disable == false) cfg.zones));
configFile = if (cfg.configFile != null) then cfg.configFile else configFile';
preStart = ''
install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf
${lib.optionalString (cfg.configFile == null) (lib.concatMapStrings (zoneCfg:
if (zoneCfg.protocol == "nsupdate") then ''
install --mode=600 --owner=$USER ${zoneCfg.passwordFile} /run/${RuntimeDirectory}/${zoneCfg.zone}/ddclient.key
'' else if (zoneCfg.passwordFile != null) then ''
"${pkgs.replace-secret}/bin/replace-secret" "@${zoneCfg.zone}_password_placeholder@" "${zoneCfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf"
'' else ''
sed -i '/^password=@${zoneCfg.zone}_password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf
'') cfg.zones)}
'';
in
with lib;
{
imports = [
(mkRemovedOptionModule [ "services" "ddclient-multi" "homeDir" ] "")
(mkRemovedOptionModule [ "services" "ddclient-multi" "password" ] "Use services.ddclient-multi.passwordFile instead.")
(mkRemovedOptionModule [ "services" "ddclient-multi" "ipv6" ] "")
];
###### interface
options = {
services.ddclient-multi = with lib.types; {
enable = mkOption {
default = false;
type = bool;
description = ''
Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
'';
};
package = mkOption {
type = package;
default = pkgs.ddclient;
defaultText = lib.literalExpression "pkgs.ddclient";
description = ''
The ddclient executable package run by the service.
'';
};
interval = mkOption {
default = "10min";
type = str;
description = ''
The interval at which to run the check and update.
See {command}`man 7 systemd.time` for the format.
'';
};
configFile = mkOption {
default = null;
type = nullOr path;
description = ''
Path to configuration file.
When set this overrides the generated configuration from module options.
'';
example = "/root/nixos/secrets/ddclient.conf";
};
quiet = mkOption {
default = false;
type = bool;
description = ''
Print no messages for unnecessary updates.
'';
};
use = lib.mkOption {
default = "";
type = str;
description = ''
Method to determine the IP address to send to the dynamic DNS provider.
'';
};
usev4 = lib.mkOption {
default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '";
type = str;
description = ''
Method to determine the IPv4 address to send to the dynamic DNS provider. Only used if `use` is not set.
'';
};
usev6 = lib.mkOption {
default = "webv6, webv6=checkipv6.dyndns.com/, webv6-skip='Current IP Address: '";
type = str;
description = ''
Method to determine the IPv6 address to send to the dynamic DNS provider. Only used if `use` is not set.
'';
};
verbose = mkOption {
default = false;
type = bool;
description = ''
Print verbose information.
'';
};
extraConfig = mkOption {
default = "";
type = lines;
description = ''
Extra configuration. Contents will be added verbatim to the configuration file.
::: {.note}
`daemon` should not be added here because it does not work great with the systemd-timer approach the service uses.
:::
'';
};
zones = mkOption {
default = [];
description = ''
Config per zone.
'';
type = with lib.types; listOf (submodule {
options = rec {
disable = mkOption {
default = false;
type = bool;
description = ''
Disable zone
'';
};
domains = mkOption {
default = [ "" ];
type = listOf str;
description = ''
Domain name(s) to synchronize.
'';
};
username = mkOption {
# For `nsupdate` username contains the path to the nsupdate executable
default = lib.optionalString (protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate";
defaultText = "";
type = str;
description = ''
User name.
'';
};
passwordFile = mkOption {
default = null;
type = nullOr str;
description = ''
A file containing the password or a TSIG key in named format when using the nsupdate protocol.
'';
};
protocol = mkOption {
default = "dyndns2";
type = str;
description = ''
Protocol to use with dynamic DNS provider (see https://ddclient.net/protocols.html ).
'';
};
server = mkOption {
default = "";
type = str;
description = ''
Server address.
'';
};
ssl = mkOption {
default = true;
type = bool;
description = ''
Whether to use SSL/TLS to connect to dynamic DNS provider.
'';
};
script = mkOption {
default = "";
type = str;
description = ''
script as required by some providers.
'';
};
zone = mkOption {
default = "";
type = str;
description = ''
zone as required by some providers.
'';
};
extraConfig = mkOption {
default = "";
type = lines;
description = ''
Extra configuration for zone. Contents will be added verbatim to the zone-specific config.
'';
};
};
});
};
};
};
###### implementation
config = mkIf config.services.ddclient-multi.enable {
systemd.services.ddclient-multi = {
description = "Dynamic DNS Client";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
restartTriggers = optional (cfg.configFile != null) cfg.configFile;
path = lib.optional (lib.hasPrefix "if," cfg.use) pkgs.iproute2;
serviceConfig = {
DynamicUser = true;
RuntimeDirectoryMode = "0700";
inherit RuntimeDirectory;
inherit StateDirectory;
Type = "oneshot";
ExecStartPre = [ "!${pkgs.writeShellScript "ddclient-prestart" preStart}" ];
ExecStart = "${lib.getExe cfg.package} -file /run/${RuntimeDirectory}/ddclient.conf";
};
};
systemd.timers.ddclient-multi = {
description = "Run ddclient";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = cfg.interval;
OnUnitInactiveSec = cfg.interval;
};
};
};
}

158
profiles/adguardhome.nix Normal file
View file

@ -0,0 +1,158 @@
{ config, lib, pkgs, ... }:
{
#-----------------------------------------------------------------------------------------------------
# Ad blocking
#-----------------------------------------------------------------------------------------------------
services.adguardhome = {
enable = true;
openFirewall = true;
port = 3000;
settings = {
http = {
address = "0.0.0.0:3000";
session_ttl = "720h";
};
users = [
{
name = config.homefree.system.adminUsername;
password = "$2a$10$Tt4QvbLQxnspv2TbcLMP7ug8eJ0NqMsGyVPbpEqtmkyCVrFpvh4GS";
# password = config.homefree.system.adminHashedPassword;
}
];
auth_attempts = 5;
block_auth_min = 15;
theme = "auto";
dns = {
bind_hosts = [ "0.0.0.0" ];
port = 53;
anonymize_client_ip = false;
ratelimit = 20;
ratelimit_subnet_len_ipv4 = 24;
ratelimit_subnet_len_ipv6 = 56;
ratelimit_whitelist = [];
refuse_any = true;
upstream_dns = [
"10.0.0.1:53530"
# "https://dns10.quad9.net/dns-query"
];
bootstrap_dns = [
"9.9.9.10"
"149.112.112.10"
"2620:fe::10"
"2620:fe::fe:10"
];
upstream_mode = "load_balance";
fastest_timeout = "1s";
blocked_hosts = [
"version.bind"
"id.server"
"hostname.bind"
];
trusted_proxies = [
"127.0.0.0/8"
"::1/128"
];
cache_size = 4194304;
cache_ttl_min = 0;
cache_ttl_max = 0;
cache_optimistic = false;
aaaa_disabled = false;
enable_dnssec = false;
edns_client_subnet = {
custom_ip = "";
enabled = false;
use_custom = false;
};
max_goroutines = 300;
handle_ddr = true;
ipset = [];
ipset_file = "";
bootstrap_prefer_ipv6 = false;
upstream_timeout = "10s";
private_networks = [];
use_private_ptr_resolvers = true;
local_ptr_upstreams = [];
use_dns64 = false;
dns64_prefixes = [];
serve_http3 = false;
use_http3_upstreams = false;
serve_plain_dns = true;
hostsfile_enabled = true;
};
filters = [
{
enabled = true;
url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt";
name = "AdGuard DNS filter";
id = 1;
}
{
enabled = false;
url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt";
name = "AdAway Default Blocklist";
id = 2;
}
];
whitelist_filters = [];
user_rules = [];
dhcp = {
enabled = false;
};
filtering = {
blocking_ipv4 = "";
blocking_ipv6 = "";
blocked_services = {
schedule = {
time_zone = "Local";
};
ids = [];
};
protection_disabled_until = null;
safe_search = {
enabled = false;
bing = true;
duckduckgo = true;
google = true;
pixabay = true;
yandex = true;
youtube = true;
};
blocking_mode = "default";
parental_block_host = "family-block.dns.adguard.com";
safebrowsing_block_host = "standard-block.dns.adguard.com";
rewrites = [];
safebrowsing_cache_size = 1048576;
safesearch_cache_size = 1048576;
parental_cache_size = 1048576;
cache_time = 30;
filters_update_interval = 24;
blocked_response_ttl = 10;
filtering_enabled = true;
parental_enabled = false;
safebrowsing_enabled = false;
protection_enabled = true;
};
clients = {
runtime_sources = {
whois = true;
arp = true;
rdns = true;
dhcp = true;
hosts = true;
};
persistent = [];
};
log = {
file = "";
max_backups = 0;
max_size = 100;
max_age = 3;
compress = false;
local_time = false;
verbose = false;
};
schema_version = 28;
};
};
}

View file

@ -1,4 +1,4 @@
{ agenix, options, system, ... }:
{ config, agenix, options, system, ... }:
{
environment.systemPackages = [
agenix.packages.${system}.default
@ -9,6 +9,6 @@
# default path is /etc/ssh/ssh_host_rsa_key
age.identityPaths = options.age.identityPaths.default ++ [
"/home/homefree/.ssh/id_rsa"
"/home/${config.system.homefree.adminUsername}/.ssh/id_rsa"
];
}

76
profiles/authentik.nix Normal file
View file

@ -0,0 +1,76 @@
{ config, pkgs, ... }:
{
environment.systemPackages = with pkgs; [
openldap
];
services.authentik = {
enable = true;
# Deployed SOPS file
environmentFile = "/run/secrets/authentik/authentik-env";
## @TODO: make these configurable from module
settings = {
email = {
host = "smtp.homefree.host";
port = 587;
username = "authentik@homefree.host";
use_tls = true;
use_ssl = false;
from = "authentik@homefree.host";
};
disable_startup_analytics = true;
avatars = "initials";
};
};
services.authentik-ldap = {
enable = true;
# Deployed SOPS file
environmentFile = "/run/secrets/authentik/authentik-ldap-env";
};
networking.firewall.allowedTCPPorts = [
# 3389 # LDAP
9000 # Web GUI
];
sops.secrets = {
"authentik/authentik-env" = {
format = "yaml";
# @TODO: Move secrets to this folder
sopsFile = ../secrets/authentik.yaml;
owner = config.homefree.system.adminUsername;
path = "/run/secrets/authentik/authentik-env";
restartUnits = [ "authentik.service" ];
};
"authentik/authentik-ldap-env" = {
format = "yaml";
# @TODO: Move secrets to this folder
sopsFile = ../secrets/authentik.yaml;
owner = config.homefree.system.adminUsername;
path = "/run/secrets/authentik/authentik-ldap-env";
restartUnits = [ "authentik-ldap.service" ];
};
"authentik/postgres-password" = {
format = "yaml";
# @TODO: Move secrets to this folder
sopsFile = ../secrets/authentik.yaml;
};
};
# # Set the authentik postgresql password
# systemd.services.postgresql.postStart = let
# password_file_path = config.sops.secrets."authentik/postgres-password".path;
# in ''
# $PSQL -tA <<'EOF'
# DO $$
# DECLARE password TEXT;
# BEGIN
# password := trim(both from replace(pg_read_file('${password_file_path}'), E'\n', '''));
# EXECUTE format('ALTER ROLE authentik WITH PASSWORD '''%s''';', password);
# END $$;
# EOF
# '';
}

View file

@ -1,4 +1,4 @@
{ config, pkgs, inputs, system, ...}:
{ config, homefree-inputs, pkgs, system, ...}:
{
# --------------------------------------------------------------------------------------
@ -11,7 +11,7 @@
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "23.11"; # Did you read the comment?
system.stateVersion = "24.11"; # Did you read the comment?
# @TODO: Could this be useful for auto-upgrading systems out there?
# system.autoUpgrade = {
@ -27,10 +27,11 @@
# };
nix = {
nixPath = [ "nixpkgs=${inputs.nixpkgs}" "nixos-config=/home/homefree/nixcfg" ];
nixPath = [ "nixpkgs=${homefree-inputs.nixpkgs}" "nixos-config=/home/${config.homefree.system.adminUsername}/nixcfg" ];
# Which package collection to use system-wide.
package = pkgs.nixFlakes;
package = pkgs.nixVersions.stable;
# package = pkgs.nixFlakes;
settings = {
# sets up an isolated environment for each build process to improve reproducibility.
@ -73,7 +74,7 @@
keep-outputs = true
'';
registry.nixpkgs.flake = inputs.nixpkgs;
# registry.nixpkgs.flake = homefree-inputs.nixpkgs;
# Garbage collection - deletes all unreachable paths in Nix store.
gc = {
@ -96,14 +97,13 @@
# User config
# --------------------------------------------------------------------------------------
users.users.homefree = {
users.users."${config.homefree.system.adminUsername}" = {
isNormalUser = true;
home = "/home/homefree";
description = "Homefree User";
home = "/home/${config.homefree.system.adminUsername}";
description = "Homefree Admin";
extraGroups = [ "wheel" ];
# @TODO: Make this dynamic, not hard coded
openssh.authorizedKeys.keys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNvmGn1/uFnfgnv5qsec0GC04LeVB1Qy/G7WivvvUZVBBDzp8goe1DsE8M8iqnBSin56gQZDWsd50co2MbFAWuqH2HxY7OGay7P/V2q+SziTYFva85WGl84qWvYMmdB+alAFBT3L4eH5cegC5NhNp+OGsQuq32RdojgXXQt6vyZnaOypuz90k3rqV6Rt+iBTLz6VziasCLcYydwOvi9f1q6YQwGPLKaupDrV6gxvoX9bXLdopqwnXPSE/Eqczxgwc3PefvAJPSd6TOqIXvbtpv/B3Evt5SPe2gq+qASc5K0tzgra8KAe813kkpq4FuKJzHbT+EmO70wiJjru7zMEhd erahhal@nfml-erahhalQFL" ];
hashedPassword = "$6$5.6V9H0g5F47ubUm$e0N.GXZ9eoqmvpO9MjZlCISC9IIxKKcf0xtnuFyuXSQEQlfaazrS4kBhplDB6GCsQgwpOxdrX2DmcwbMiX/h30";
openssh.authorizedKeys.keys= config.homefree.system.authorizedKeys;
hashedPassword = config.homefree.system.adminHashedPassword;
};
security.sudo.extraRules = [
@ -119,22 +119,6 @@
nixpkgs = {
hostPlatform = system;
config = {
## Allow proprietary packages.
# allowUnfree = true;
## Allow broken packages.
# allowBroken = true;
packageOverrides = pkgs: {
unstable = import inputs.nixpkgs-unstable {
config = config.nixpkgs.config;
inherit system;
};
trunk = import inputs.nixpkgs-trunk {
config = config.nixpkgs.config;
inherit system;
};
};
};
};
# --------------------------------------------------------------------------------------
@ -145,6 +129,15 @@
# Nix mounts read-write automatically when it needs to write to it.
boot.readOnlyNixStore = true;
boot.kernelPackages = pkgs.linuxPackages_latest;
# --------------------------------------------------------------------------------------
# Hardware
# --------------------------------------------------------------------------------------
hardware.enableRedistributableFirmware = true;
hardware.enableAllFirmware = true;
# --------------------------------------------------------------------------------------
# Services
# --------------------------------------------------------------------------------------
@ -155,10 +148,6 @@
# Setting to true will kill things like tmux on logout
services.logind.killUserProcesses = false;
# network locator e.g. scanners and printers
services.avahi.enable = true;
services.avahi.nssmdns = true;
services.gvfs.enable = true; # SMB mounts, trash, and other functionality
services.tumbler.enable = true; # Thumbnail support for images
@ -169,7 +158,17 @@
services.printing.drivers = [ pkgs.brlaser ];
# Enable the OpenSSH daemon.
services.openssh.enable = true;
services.openssh = {
enable = true;
settings = {
## This doesn't work, can't SSH at all
# AllowUsers = [
# "*@192.168.*.*"
# "*@10.0.0.1"
# "erahhal@10.0.0.1"
# ];
};
};
# This will save you money and possibly your life!
services.thermald.enable = true;
@ -182,6 +181,15 @@
powertop.enable = true;
};
# Eternal Terminal
services.eternal-terminal.enable = true;
# et port
networking.firewall.allowedTCPPorts = [ 2022 ];
environment.variables = {
ET_NO_TELEMETRY = "1";
};
# --------------------------------------------------------------------------------------
# i18n
# --------------------------------------------------------------------------------------
@ -201,28 +209,15 @@
programs.nix-ld.enable = true;
programs.command-not-found.enable = true;
programs.command-not-found.dbPath = "${inputs.nixpkgs}/programs.sqlite";
programs.mosh.enable = true;
# environment.variables.EDITOR = "neovim";
programs.neovim = {
enable = true;
defaultEditor = true;
};
environment.interactiveShellInit = ''
alias vi='nvim'
alias vim='nvim'
'';
environment.systemPackages = with pkgs; [
at-spi2-core
backblaze-b2
bashmount
bfg-repo-cleaner
bind
btop
ccze # readable parsed system logs
cpufrequtils
distrobox
@ -238,6 +233,7 @@
git
git-lfs
gnumake
gnupg
htop
hwinfo
iftop
@ -254,8 +250,7 @@
memtest86plus
minicom
neofetch
neovim
unstable.nil
nil
nix-index
openssl
# openjdk16-bootstrap

View file

@ -1,6 +1,6 @@
{ inputs, pkgs, system, ... }:
{ homefree-inputs, system, ... }:
{
environment.systemPackages = with pkgs; [
inputs.nix-editor.packages.${system}.default
environment.systemPackages = [
homefree-inputs.nix-editor.packages.${system}.default
];
}

36
profiles/ddclient.nix Normal file
View file

@ -0,0 +1,36 @@
{ config, inputs, pkgs, lib, ... }:
let
cfg = config.homefree.dynamic-dns;
in
{
#-----------------------------------------------------------------------------------------------------
# Dynamic DNS
#-----------------------------------------------------------------------------------------------------
services.ddclient-multi = {
enable = true;
interval = cfg.interval;
usev4 = cfg.usev4;
usev6 = cfg.usev6;
verbose = true;
zones = lib.map (zone: {
protocol = zone.protocol;
username = zone.username;
zone = zone.zone;
domains = zone.domains;
passwordFile = zone.passwordFile;
}) cfg.zones;
};
## @TODO: Move to host config
sops.secrets = {
"ddclient/ddclient-password" = {
format = "yaml";
sopsFile = ../secrets/ddclient.yaml;
owner = config.homefree.system.adminUsername;
path = "/run/secrets/ddclient/ddclient-password";
restartUnits = [ "ddclient.service" ];
};
};
}

96
profiles/dnsmasq.nix Normal file
View file

@ -0,0 +1,96 @@
{ config, lib, pkgs, ... }:
let
lan-interface = config.homefree.network.lan-interface;
wan-interface = config.homefree.network.wan-interface;
localDomain = config.homefree.system.localDomain;
dhcp-script = pkgs.writeShellScript "dhcp-script" ''
# $1 = action (add, del, old)
# $2 = MAC address
# $3 = IP address
# $4 = hostname
if [ "$1" = "add" ]; then
${pkgs.dnsutils}/bin/nsupdate -l <<EOF
server 127.0.0.1
zone ${localDomain}
update delete $4.${localDomain} A
update add $4.${localDomain} 3600 A $3
send
EOF
${pkgs.dnsutils}/bin/nsupdate -l <<EOF
server 127.0.0.1
update delete $4 A
update add $4 3600 A $3
send
EOF
fi
'';
in
{
services.dnsmasq = {
enable = true;
settings = {
## @TODO
## @WARNING - changes to this do not clear out old entries from /etc/dnsmasq-conf.conf
## Only DHCP server on network
dhcp-authoritative = true;
## Don't listen to anything on wan interface
except-interface = wan-interface;
## Don't send bogus requests to internet
bogus-priv = true;
## Enable Router Advertising for ipv6
enable-ra = true;
## Ipv6
## ra-param = "${lan-interface},0,0"; ## This disables router-advertisements
ra-param = "${lan-interface},10,300";
## DNS servers to pass to clients
## @TODO: Make this based on configured gateway IP
server = [ "10.0.0.1" ];
## Which interfaces to bind to
interface = [
# "${lan-interface}.${builtins.toString vlan-lan-id}"
# "${lan-interface}.${builtins.toString vlan-iot-id}"
# "${lan-interface}.${builtins.toString vlan-guest-id}"
lan-interface
];
## IP ranges to hand out
dhcp-range = [
# "lan,10.0.0.100,10.0.0.254,255.255.255.0,8h"
# "iot,10.2.1.100,10.2.1.254,255.255.255.0,8h"
# "guest,10.3.1.100,10.3.1.254,255.255.255.0,8h"
"tag:${lan-interface},::1,constructor:${lan-interface},ra-names,slaac,12h" # ipv6
"${lan-interface},10.0.0.100,10.0.0.254,255.255.255.0,8h" # ipv4
];
## Disable DNS, since Unbound is handling DNS
port = 0;
cache-size = 500;
## Additional DHCP options
dhcp-option = [
"option6:dns-server,[::]" # @TODO: point this at Unbound when ipv6 is setup
"option:dns-server,10.0.0.1"
];
dhcp-host = lib.map (ip-config:
"${ip-config.mac-address},${ip-config.hostname},${ip-config.ip},${config.homefree.network.static-ip-expiration}")
config.homefree.network.static-ips;
dhcp-script = "${dhcp-script}";
};
};
## dhcpd6 is obsolete
# services.dhcpd6 = {};
}

69
profiles/git.nix Normal file
View file

@ -0,0 +1,69 @@
{ config, pkgs, ... }:
{
programs.git = {
enable = true;
lfs.enable = true;
config = {
checkout = {
defaultRemote = "origin";
};
color = {
ui = "auto";
};
core = {
# Can't specify "${pkgs.neovim}/bin/nvim" because programs.neovim
# wraps neovim-unwrapped in a special way to load plugins, so must
# expect nvim to be in $PATH here
editor = "nvim";
excludesfile = "~/.gitignore_global";
};
delta = {
enable = true;
};
# filter = {
# lfs = {
# clean = "${pkgs.git-lfs}/bin/git-lfs clean -- %f";
# smudge = "${pkgs.git-lfs}/bin/git-lfs smudge --skip -- %f";
# process = "${pkgs.git-lfs}/bin/git-lfs filter-process --skip";
# required = true;
# };
# };
push = {
default = "simple";
};
rerere = {
enabled = true;
};
include = {
path = "~/.gitconfig.local";
};
#==========================
# Diff settings
#==========================
pager = {
difftool = true;
};
#-------------------
## nvim
# - text-based
#------------------
diff = {
tool = "nvimdiff";
};
difftool = {
prompt = true;
};
merge = {
tool = "nvimdiff";
trustExitCode = false;
};
mergetool = {
trustExitCode = false;
};
};
};
}

20
profiles/gitea.nix Normal file
View file

@ -0,0 +1,20 @@
{ config, ... }:
{
services.gitea = {
enable = true;
database = {
## @TODO: move to postgresql
type = "sqlite3";
};
settings = {
server = {
HTTP_PORT = 3001;
DOMAIN = "git.${config.homefree.system.domain}";
MINIMUM_KEY_SIZE_CHECK = false;
START_SSH_SERVER = true;
SSH_PORT = 3022;
};
};
};
}

View file

@ -8,30 +8,11 @@
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_blk" ];
boot.initrd.availableKernelModules = [ "ata_piix" "ahci" "xhci_pci" "nvme" "virtio_pci" "virtio_blk" "floppy" "sd_mod" "sr_mod" "usb_storage" "usbhid" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.kernelModules = [ "kvm-amd" "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
fileSystems."/efi" =
{ device = "systemd-1";
fsType = "autofs";
};
fileSystems."/home/homefree/nixcfg" =
{ device = "mount_homefree_source";
fsType = "virtiofs";
options = [ "nofail" ];
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
@ -40,4 +21,5 @@
# networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -0,0 +1,2 @@
[
]

View file

@ -0,0 +1,413 @@
{ config, inputs, pkgs, ... }:
let
# automations = import ./automations.nix;
scenes = import ./scenes.nix;
scripts = import ./scripts.nix;
groups = import ./groups.nix;
in
{
#-----------------------------------------------------------------------------------------------------
# Home Assistant
#-----------------------------------------------------------------------------------------------------
imports = [
# On first run, this has to be commented out, and a single user created.
# Afterward, it can be re-included
## @TODO: Auto-initializatin for HA
## See: https://github.com/home-assistant/core/issues/16554
./ldap.nix
./trusted-networks.nix
./weather.nix
];
services.home-assistant = {
enable = true;
# Enable Postgres
package = (pkgs.home-assistant.override {
extraPackages = py: with py; [ psycopg2 ];
}).overrideAttrs (oldAttrs: {
doInstallCheck = false;
});
config.recorder.db_url = "postgresql://@/hass";
extraComponents = [
# Components required to complete the onboarding
"adguard"
"backup"
"brother"
"ecobee"
"enphase_envoy"
"esphome"
"flume"
"iaqualink"
"jellyfin"
"litterrobot"
"met"
"mqtt"
"radio_browser"
"roborock"
"schlage"
"snapcast"
"synology_dsm"
"unifi"
"usgs_earthquakes_feed"
"volumio"
"wake_on_lan"
"yamaha_musiccast"
"zwave_js"
];
customComponents = with pkgs.home-assistant-custom-components; [
frigate
smartthinq-sensors
];
customLovelaceModules = with pkgs.home-assistant-custom-lovelace-modules; [
button-card
card-mod
decluttering-card
lg-webos-remote-control
light-entity-card
mini-graph-card
mini-media-player
multiple-entity-row
mushroom
valetudo-map-card
];
config = {
# Includes dependencies for a basic setup
# https://www.home-assistant.io/integrations/default_config/
default_config = {};
# "automation manual" = automations;
"automation ui" = "!include automations.yaml";
"scene manual" = scenes;
"scene ui" = "!include scenes.yaml";
"script manual" = scripts;
"script ui" = "!include scripts.yaml";
"group manual" = groups;
# "group ui" = "!include groups.yaml";
http = {
# @TODO: Make this a passed-in var
base_url = "ha.homefree.lan";
use_x_forwarded_for = true;
trusted_proxies = [
# @TODO: Make this a passed-in var
"127.0.0.1"
"10.0.0.1"
"10.0.2.15"
];
};
## enable with empty top level key
wake_on_lan = {};
switch = [
{
platform = "wake_on_lan";
mac = "B4-B2-91-52-DE-DF";
name = "LGwebOSTV";
host = "10.0.0.40";
}
{
platform = "template";
switches = {
lg_tv = {
unique_id = "lg-tv";
friendly_name = "LG CX 65\" TV";
value_template = "{{ is_state('media_player.lg_webos_smart_tv', 'on') }}";
turn_on = {
service = "switch.turn_on";
target = {
entity_id = "switch.lgwebostv";
};
};
turn_off = {
service = "media_player.turn_off";
target = {
entity_id = "media_player.lg_webos_smart_tv";
};
};
};
};
}
];
command_line = [
{
switch = {
name = "msi_desktop";
unique_id = "msi-desktop";
command_state = "ping -c 2 -i 1 msi-desktop.localdomain";
## Hibernate
# command_off = "ssh -i /run/agenix/msi-desktop-id_rsa -o 'StrictHostKeyChecking=no' erahhal@msi-desktop rundll32.exe powrprof.dll, SetSuspendState Sleep";
command_on = "wakeonlan 2c:f0:5d:72:ac:ab";
## Suspend
command_off = "ssh -i /config/certs/msi_desktop-id_rsa -o ConnectTimeout=30 -o 'StrictHostKeyChecking=no' erahhal@msi-desktop.localdomain 'cd \"/mnt/c/Program Files/PSTools\"; ./psshutdown.exe -accepteula -d -t 0'";
};
}
{
switch = {
name = "stream_vr";
unique_id = "steam-vr";
command_on = "ssh -i /config/certs/msi_desktop-id_rsa -o ConnectTimeout=30 -o 'StrictHostKeyChecking=no' erahhal@msi-desktop.localdomain 'cd \"/mnt/c/Program Files (x86)/Steam/steamapps/common/SteamVR/bin/win64\"; ./vrstartup.exe'";
};
}
];
# media_player = [
# {
# platform = "yamaha";
# host = "10.0.0.41";
# source_names = {
# HDMI1 = "PC HDMI";
# };
# zone_names = {
# Main_Zone = "Family Room";
# };
# }
# ];
sensor = [
# See: https://github.com/home-assistant/core/issues/64839
{
platform = "template";
sensors = {
# ZWaveJS Node Stats
zwavejs_node_statistics = {
friendly_name = "ZwaveJS Node Statistics";
icon_template = ''
{%- if states | selectattr('entity_id', 'search', '_node_status') | selectattr('state', 'in', 'dead, unknown') | list | count > 0 -%}
mdi:emoticon-sad
{%- elif states | selectattr('entity_id', 'search', '_node_status') | rejectattr('state', 'in', 'alive, asleep, dead, unknown') | list | count > 0 -%}
mdi:help-circle
{%- else -%}
mdi:z-wave
{%- endif -%}
'';
value_template = "{{ states | selectattr('entity_id', 'search', '_node_status') | list | count }}";
attribute_templates = {
Alive = "{{ states | selectattr('entity_id', 'search', '_node_status') | selectattr('state', 'in', 'alive') | list | count }}";
Sleeping = "{{ states | selectattr('entity_id', 'search', '_node_status') | selectattr('state', 'in', 'asleep') | list | count }}";
Dead = "{{ states | selectattr('entity_id', 'search', '_node_status') | selectattr('state', 'in', 'dead, unknown') | list | count }}";
};
};
};
}
# {
# platform = "hp_ilo";
# host = "10.0.0.9";
# username = "Administrator";
# # @TODO: REMOVE
# password = "CHANGEME";
# monitored_variables = [
# {
# name = "CPU fanspeed";
# sensor_type = "server_health";
# unit_of_measurement = "%";
# value_template = "{{ ilo_data.fans[\"Fan 1\"].speed[0] }}";
# }
# {
# name = "Fan 2";
# sensor_type = "server_health";
# unit_of_measurement = "%";
# value_template = "{{ ilo_data.fans[\"Fan 2\"].speed[0] }}";
# }
# {
# name = "Fan 3";
# sensor_type = "server_health";
# unit_of_measurement = "%";
# value_template = "{{ ilo_data.fans[\"Fan 3\"].speed[0] }}";
# }
# {
# name = "Server Health";
# sensor_type = "server_health";
# value_template = "{{ ilo_data.health_at_a_glance }}";
# }
# {
# name = "Server Power Readings (raw)";
# sensor_type = "server_power_readings";
# }
# {
# name = "Server Power Status (raw)";
# sensor_type = "server_power_status";
# }
# {
# name = "Server health (raw)";
# sensor_type = "server_health";
# }
# {
# name = "Inlet temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"01-Inlet Ambient\"].currentreading[0] }}";
# }
# {
# name = "Inlet temperature (raw)";
# sensor_type = "server_health";
# value_template = "{{ ilo_data.temperature[\"01-Inlet Ambient\"] }}";
# }
# {
# name = "CPU 1 temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"02-CPU 1\"].currentreading[0] }}";
# }
# {
# name = "P1 DIMM 1-4 temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"03-P1 DIMM 1-4\"].currentreading[0] }}";
# }
# {
# name = "HD Max temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"04-HD Max\"].currentreading[0] }}";
# }
# {
# name = "Chipset temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"05-Chipset\"].currentreading[0] }}";
# }
# {
# name = "VR P1 temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"07-VR P1\"].currentreading[0] }}";
# }
# {
# name = "SuperCAP Max temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"08-Supercap Max\"].currentreading[0] }}";
# }
# {
# name = "iLO Zone temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"09-iLO Zone\"].currentreading[0] }}";
# }
# {
# name = "LOM Zon temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"11-LOM Zone\"].currentreading[0] }}";
# }
# {
# name = "PCI 2 temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"13-PCI 2\"].currentreading[0] }}";
# }
# {
# name = "PCI 1 Zone temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"14-PCI 1 Zone\"].currentreading[0] }}";
# }
# {
# name = "PCI 2 Zone temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"15-PCI 2 Zone\"].currentreading[0] }}";
# }
# {
# name = "System Board temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"16-System Board\"].currentreading[0] }}";
# }
# {
# name = "Sys Exhaust temperature";
# sensor_type = "server_health";
# unit_of_measurement = "°C";
# value_template = "{{ ilo_data.temperature[\"17-Sys Exhaust\"].currentreading[0] }}";
# }
# ];
# }
# {
# # This includes the config necessary for a washer and dryer.
# # Code for dishwasher and mini-washer may be commented out as-needed,
# # and are presented only for reference purposes.
#
# # NOTE: YOUR ENTITY NAMES MAY BE DIFFERENT, THEN THIS WON'T WORK WITHOUT TWEAKING
# # =================================================================================
# # If your washer doesn't have entities named sensor.washer, sensor.washer_run_state
# # (and similar for dryer), you have to change the names throughout here!
#
# # NOTE: THIS CODE EXPECTS YOUR THINQ INTEGRATION TO RETURN ENGLISH STRINGS
# # =================================================================================
# # if your LG account is in another region/language, this will probably break unless
# # you take the time to change out state strings like "Standby". Do this by watching
# # your machine's entities change states during a run, or look at its history.
#
# platform = "template";
# sensors = {
# front_load_washer_door_lock = {
# friendly_name = "Washer Door Lock";
# value_template = "{{ state_attr('sensor.front_load_washer','door_lock') }}";
# };
# front_load_washer_time_display = {
# friendly_name = "Washer Time Display";
# value_template = ''
# {% if is_state('sensor.front_load_washer_run_state', '-') %}
# {% elif is_state('sensor.front_load_washer_run_state', 'unavailable') %}
# {% elif is_state('sensor.front_load_washer_run_state', 'Standby') %}
# -:--
# {% else %}
# {{ state_attr("sensor.front_load_washer","remain_time").split(":")[:-1] | join(':') }}
# {% endif %}
# '';
# };
#
# dryer_time_display = {
# friendly_name = "Dryer Time Display";
# value_template = ''
# {% if is_state('sensor.dryer_run_state', '-') %}
# {% elif is_state('sensor.dryer_run_state', 'unavailable') %}
# {% elif is_state('sensor.dryer_run_state', 'Standby') %}
# -:--
# {% else %}
# {{ state_attr("sensor.dryer","remain_time").split(":")[:-1] | join(':') }}
# {% endif %}
# '';
# };
#
# blank = {
# friendly_name = "Blank Sensor";
# value_template = "";
# };
# };
# }
];
};
};
# Make sure UI-based config files exist in case they haven't been created yet
systemd.tmpfiles.rules = [
"f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass"
"f ${config.services.home-assistant.configDir}/scenes.yaml 0755 hass hass"
"f ${config.services.home-assistant.configDir}/scripts.yaml 0755 hass hass"
];
# HTTP port
networking.firewall.allowedTCPPorts = [ 8123 ];
# Database
services.postgresql = {
ensureDatabases = [ "hass" ];
ensureUsers = [{
name = "hass";
ensureDBOwnership = true;
}];
};
}

View file

@ -0,0 +1,2 @@
[
]

View file

@ -0,0 +1,63 @@
{
stdenv,
fetchFromGitHub,
makeWrapper,
openldap,
coreutils,
gnused,
gnugrep,
lib,
}:
stdenv.mkDerivation {
name = "ldap-auth-sh";
src = fetchFromGitHub {
owner = "efficiosoft";
repo = "ldap-auth-sh";
rev = "93b2c00413942908139e37c7432a12bcb705ac87";
sha256 = "1pymp6ki353aqkigr89g7hg5x1mny68m31c3inxf1zr26n5s2kz8";
};
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
mkdir -p $out/etc
cat > $out/etc/home-assistant.cfg << 'EOF'
CLIENT="ldapsearch"
SERVER="ldap://localhost:3389"
USERDN="cn=$(ldap_dn_escape "$username"),ou=users,dc=ldap,dc=goauthentik,dc=io"
PW=$password
BASEDN="dc=ldap,dc=goauthentik,dc=io"
SCOPE="subtree"
FILTER="(&(objectClass=user)(sAMAccountName=$(ldap_dn_escape "$username")))"
# USERNAME_PATTERN='^[a-z|A-Z|0-9|_|-|.|@]+$'
on_auth_success() {
# print the meta entries for use in HA
if echo "$output" | grep -qE '^(dn|DN):: '; then
# ldapsearch base64 encodes non-ascii
output=$(echo "$output" | sed -n -e "s/^\(dn\|DN\)\s*::\s*\(.*\)$/\2/p" | base64 -d)
else
output=$(echo "$output" | sed -n -e "s/^\(dn\|DN\)\s*:\s*\(.*\)$/\2/p")
fi
name=$(echo "$output" | sed -nr 's/^cn=([^,]+).*/\1/Ip')
[ -z "$name" ] || echo "name=$name"
}
on_auth_failure() {
echo "$output"
}
EOF
install -D -m755 ldap-auth.sh $out/bin/ldap-auth.sh
wrapProgram $out/bin/ldap-auth.sh \
--prefix PATH : ${
lib.makeBinPath [
openldap
coreutils
gnused
gnugrep
]
} \
--add-flags "$out/etc/home-assistant.cfg"
'';
}

View file

@ -0,0 +1,13 @@
{ pkgs, ... }:
let
ldap-auth-sh = pkgs.callPackage ./ldap-auth-sh.nix {};
in
{
services.home-assistant.config.homeassistant.auth_providers = [
{
type = "command_line";
command = "${ldap-auth-sh}/bin/ldap-auth.sh";
meta = true;
}
];
}

View file

@ -0,0 +1,2 @@
[
]

View file

@ -0,0 +1,2 @@
[
]

View file

@ -0,0 +1,14 @@
{ pkgs, ... }:
let
ldap-auth-sh = pkgs.callPackage ./ldap-auth-sh.nix {};
in
{
services.home-assistant.config.homeassistant.auth_providers = [
{
type = "trusted_networks";
trusted_networks = [
"10.0.0.0/8"
];
}
];
}

View file

@ -0,0 +1,61 @@
{
services.home-assistant.extraComponents = [
"openweathermap"
];
services.home-assistant.config = {
conversation.intents = {
WeatherToday = [
"(What's|What is|How's|How is) the (weather|temperature) (like|outside) (today|right now|now|outside)"
"(What's|What is|How's|How is) the (weather|temperature) (like|outside|right now|now|today)"
];
WeatherTomorrow = [
"(What's|What is|How's|How is) the (weather|temperature) (like|outside) (tomorrow|tomorrow morning|tomorrow afternoon|tomorrow evening)"
"(What's|What is|How's|How is) the (weather|temperature) (tomorrow|tomorrow morning|tomorrow afternoon|tomorrow evening)"
];
};
intent_script = {
WeatherToday.speech.text = ''
The weather is currently {{ states('sensor.openweathermap_temperature') | round(0) }} degrees outside and {{ states('sensor.openweathermap_condition') }}.
'';
WeatherTomorrow.speech.text = ''
Tomorrow will be {{ state_attr('weather.openweathermap', 'forecast')[1]["temperature"] | round(0) }} degrees and {{ state_attr('weather.openweathermap', 'forecast')[1]["condition"] }} with a low of {{ state_attr('weather.openweathermap', 'forecast')[1]["templow"] | round(0) }} degrees.
'';
};
input_boolean.rain_notified_today = {
name = "Rain notified today";
icon = "mdi:weather-cloudy";
};
weather = { };
automation = [
{
alias = "rainy/snowy day notification";
trigger = {
platform = "state";
entity_id = "weather.openweathermap";
};
condition = {
condition = "template";
value_template = ''{{ states.input_boolean.rain_notified_today.state == "off" }}'';
};
action = [
{
service = "python_script.weather";
}
];
}
{
alias = "Reset rain notified today";
trigger = {
platform = "time";
at = "00:07:00";
};
action = [
{
service = "input_boolean.turn_off";
entity_id = "input_boolean.rain_notified_today";
}
];
}
];
};
}

View file

@ -1,9 +1,16 @@
{ pkgs, ... }:
{ config, lib, pkgs, ... }:
let
hostConfig = ''
respond "Hello, world! I am being accessed from {scheme}."
'';
proxiedHostConfig = config.homefree.proxied-hosts;
in
{
imports = [
../apps/radicale.nix
];
systemd.services.caddy = {
after = [ "network.target" "network-online.target" "unbound.service" ];
requires = [ "network-online.target" "unbound.service" ];
};
services.caddy = {
enable = true;
@ -11,16 +18,58 @@
## reload config while running instead of restarting. true by default.
enableReload = true;
virtualHosts."localhost" = {
extraConfig = ''
respond "Hello, my world!"
'';
};
## Temporarily set to staging
# acmeCA = "https://acme-staging-v02.api.letsencrypt.org/directory";
virtualHosts = lib.listToAttrs (lib.map (entry:
let
http-urls = lib.flatten (lib.map (subdomain: (lib.map (domain: "http://${subdomain}.${domain}") entry.http-domains)) entry.subdomains);
https-urls = lib.flatten (lib.map (subdomain: (lib.map (domain: "https://${subdomain}.${domain}") entry.https-domains)) entry.subdomains);
urls = http-urls ++ https-urls;
host-string = lib.concatStringsSep ", " urls;
in {
name = host-string;
value = {
logFormat = ''
output file ${config.services.caddy.logDir}/access-${entry.label}.log
'';
## @TODO: Remove headers and check if still works
extraConfig = ''
# header {
# Strict-Transport-Security "max-age=31536000; includeSubdomains"
# X-XSS-Protection "1; mode=block"
# X-Content-Type-Options "nosniff"
# X-Frame-Options "SAMEORIGIN"
# Referrer-Policy "same-origin"
# }
'' + (if entry.public == false then ''
bind 10.0.0.1 192.168.2.1
'' else ''
bind 10.0.0.1 192.168.2.1 ${config.homefree.system.domain}
'')
+ (if entry.ssl == true && entry.ssl-no-verify then ''
reverse_proxy https://${entry.host}:${toString entry.port} {
transport http {
tls
tls_insecure_skip_verify
}
}
'' else ''
reverse_proxy ${if entry.ssl == true then "https" else "http"}://${entry.host}:${toString entry.port}
'');
};
}
) proxiedHostConfig);
## With both http and https set, caddy won't redirect http to https
## REMOVE THIS IN PROD
# virtualHosts."http://localhost, https://localhost, https://${config.homefree.system.domain}, https://www.${config.homefree.system.domain}" = {
# # Nix config mangles the log name, so set it manually
# logFormat = ''
# output file ${config.services.caddy.logDir}/access-localhost.log
# '';
# extraConfig = hostConfig;
# };
virtualHosts."http://radicale.homefree.lan" = {
extraConfig = ''
reverse_proxy :5232
'';
};
};
}

View file

@ -0,0 +1,81 @@
{ homefreeLib, modulesPath, config, lib, ... }:
let
vm_homefree = (homefreeLib.prepareHomefreeConfig config homefreeLib.devices).homefree;
cfg_ = (lib.evalModules {
modules = lib.singleton {
# _file = toString input;
imports = lib.singleton { homefree.devices = vm_homefree.devices; };
options = {
homefree.devices = lib.mkOption {
type = homefreeLib.toplevel;
};
homefree.testMode = lib.mkOption {
type = lib.types.bool;
default = true;
};
};
};
}).config;
disks = lib.attrValues cfg_.homefree.devices.disk;
rootDisk = {
name = "root";
file = ''"$tmp"/${lib.escapeShellArg (builtins.head disks).name}.qcow2'';
driveExtraOpts.cache = "writeback";
driveExtraOpts.werror = "report";
deviceExtraOpts.bootindex = "1";
deviceExtraOpts.serial = "root";
};
otherDisks = map
(disk: {
name = disk.name;
file = ''"$tmp"/${lib.escapeShellArg disk.name}.qcow2'';
driveExtraOpts.werror = "report";
})
(builtins.tail disks);
diskoBasedConfiguration = {
# generated from disko config
virtualisation.fileSystems = cfg_.disko.devices._config.fileSystems;
boot = cfg_.disko.devices._config.boot or { };
swapDevices = cfg_.disko.devices._config.swapDevices or [ ];
};
hostPkgs = config.virtualisation.host.pkgs;
in
{
imports = [
(modulesPath + "/virtualisation/qemu-vm.nix")
diskoBasedConfiguration
];
disko.testMode = true;
disko.imageBuilder.copyNixStore = false;
disko.imageBuilder.extraConfig = {
disko.devices = cfg_.disko.devices;
};
disko.imageBuilder.imageFormat = "qcow2";
virtualisation.useEFIBoot = config.disko.tests.efi;
virtualisation.memorySize = lib.mkDefault config.disko.memSize;
virtualisation.useDefaultFilesystems = false;
virtualisation.diskImage = null;
virtualisation.qemu.drives = [ rootDisk ] ++ otherDisks;
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
boot.zfs.forceImportAll = true;
boot.zfs.forceImportRoot = lib.mkForce true;
system.build.vmWithDisko = hostPkgs.writers.writeDashBin "disko-vm" ''
set -efux
export tmp=$(${hostPkgs.coreutils}/bin/mktemp -d)
trap 'rm -rf "$tmp"' EXIT
${lib.concatMapStringsSep "\n" (disk: ''
${hostPkgs.qemu}/bin/qemu-img create -f qcow2 \
-b ${config.system.build.diskoImages}/${lib.escapeShellArg disk.name}.qcow2 \
-F qcow2 "$tmp"/${lib.escapeShellArg disk.name}.qcow2
'') disks}
set +f
${config.system.build.vm}/bin/run-*-vm
'';
}

503
profiles/nixvim.nix Normal file
View file

@ -0,0 +1,503 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
ripgrep
];
environment.interactiveShellInit = ''
alias vi='nvim'
alias vim='nvim'
'';
programs.nixvim = {
enable = true;
defaultEditor = true;
## ------------------------------------------------
## Options
## ------------------------------------------------
globals = {
mapleader = " "; # global
maplocalleader = " "; # per buffer, e.g. can change behavior per filetype
};
opts = {
number = true; # Show line numbers
relativenumber = true; # Show relative line numbers
ruler = true; # displays line, column, and cursor position at bottom
wrap = false; # don't wrap lines
signcolumn = "yes"; # always show two column sign column on left
cursorline = true; # Highlight line cursor sits on
undodir.__raw = "vim.fs.normalize('~/.local/share/nvim/undo/')";
undofile = true;
# -----------------------------------------------------
# Backspace settings
# indent allow backspacing over autoindent
# eol allow backspacing over line breaks (join lines)
# start allow backspacing over the start of insert; CTRL-W and CTRL-U
# 0 same as ":set backspace=" (Vi compatible)
# 1 same as ":set backspace=indent,eol"
# 2 same as ":set backspace=indent,eol,start"
# -----------------------------------------------------
bs = "2";
# -----------------------------------------------------
# Indentation settings
# -----------------------------------------------------
tabstop = 4; # number of spaces a tab counts for
shiftwidth = 4; # control how many columns text is indented with the reindent operations (<< and >>) and automatic C-style indentation.
expandtab = true; # Insert spaces when entering <Tab>
softtabstop = 4; # Number of spaces that a <Tab> counts for while performing editing operations, like inserting a <Tab> or using <BS>. It "feels" like a tab though
ai = true; # auto indent
};
keymaps = [
# -----------------------------------------------------
# nvim-tree
# -----------------------------------------------------
## Go to current buffer's file in nvim-tree
{
mode = [ "n" ];
key = ",n";
action = ":NvimTreeFindFile<CR>";
}
## Toggle nvim-tree visibility
{
mode = [ "n" ];
key = ",m";
action = ":NvimTreeToggle<CR>";
}
# -----------------------------------------------------
# buffer manipulation
# -----------------------------------------------------
## Next Buffer
{
key = "<Tab>";
action = ":bn<CR>";
options = { noremap = true; };
}
## Previous Buffer
{
key = "<S-Tab>";
action = ":bp<CR>";
options = { noremap = true; };
}
## Close Buffer
{
key = "<leader><Tab>";
action = ":bd<CR>";
options = { noremap = true; };
}
## Force Close Buffer
{
key = "<leader><S-Tab>";
action = ":bd!<CR>";
options = { noremap = true; };
}
## New Tab
{
key = "<leader>t";
action = ":tabnew split<CR>";
options = { noremap = true; };
}
# -----------------------------------------------------
# Telescope
# -----------------------------------------------------
## Lists files in your current working directory, respects .gitignore
{
mode = [ "n" ];
key = "<leader>ff";
action = "<cmd>Telescope find_files<cr>";
options = { noremap = true; };
}
## Finds files by filename
{
mode = [ "n" ];
key = "<c-p>";
action = "<cmd>Telescope find_files<cr>";
options = { noremap = true; };
}
## Search for a string in your current working directory and get results live as you type, respects .gitignore. (Requires ripgrep)
{
mode = [ "n" ];
key = "<leader>fg";
action = "<cmd>Telescope live_grep<cr>";
options = { noremap = true; };
}
## Search file contents
{
mode = [ "n" ];
key = "<c-s>";
action = "<cmd>Telescope live_grep<cr>";
options = { noremap = true; };
}
## Lists open buffers in current neovim instance
{
mode = [ "n" ];
key = "<leader>db";
action = "<cmd>Telescope buffers<cr>";
options = { noremap = true; };
}
## Lists available help tags and opens a new window with the relevant help info on <cr>
{
mode = [ "n" ];
key = "<leader>fh";
action = "<cmd>Telescope help_tags<cr>";
options = { noremap = true; };
}
## Lists manpage entries, opens them in a help window on <cr>
{
mode = [ "n" ];
key = "<leader>fm";
action = "<cmd>Telescope man_pages<cr>";
options = { noremap = true; };
}
## Lists previously open files
{
mode = [ "n" ];
key = "<leader>fp";
action = "<cmd>Telescope oldfiles<cr>";
options = { noremap = true; };
}
## Lists previously open files, Maps to ctrl-/
{
mode = [ "n" ];
key = "<c-_>";
action = "<cmd>Telescope oldfiles<cr>";
options = { noremap = true; };
}
## Lists spelling suggestions for the current word under the cursor, replaces word with selected suggestion on <cr>
{
mode = [ "n" ];
key = "<leader>fs";
action = "<cmd>Telescope spell_suggest<cr>";
options = { noremap = true; };
}
## Lists LSP references for iword under the cursor
{
mode = [ "n" ];
key = "<leader>fr";
action = "<cmd>Telescope lsp_references<cr>";
options = { noremap = true; };
}
## Lists LSP incoming calls for word under the cursor
{
mode = [ "n" ];
key = "<leader>fi";
action = "<cmd>Telescope lsp_incoming_calls<cr>";
options = { noremap = true; };
}
## Lists LSP outgoing calls for word under the cursor
{
mode = [ "n" ];
key = "<leader>fo";
action = "<cmd>Telescope lsp_outgoing_calls<cr>";
options = { noremap = true; };
}
## Dynamically Lists LSP for all workspace symbols
{
mode = [ "n" ];
key = "<leader>fw";
action = "<cmd>Telescope lsp_dynamic_workspace_symbols<cr>";
options = { noremap = true; };
}
## Goto the definition of the word under the cursor, if there's only one, otherwise show all options in Telescope
{
mode = [ "n" ];
key = "<leader>fd";
action = "<cmd>Telescope lsp_definitions<cr>";
options = { noremap = true; };
}
## Other Telescope options:
## git_files search only files in git, respects .gitignore
## oldfiles previously opened files
## command_history
## search_history
## man_pages
## resume lists the results including multi-selections of the previous
## picker
# -----------------------------------------------------
# Diff
# -----------------------------------------------------
{
mode = [ "n" ];
key = ",d";
## @TODO: This doesn't work
action = ''
function()
if next(require('diffview.lib').views) == nil then
vim.cmd('DiffviewOpen origin')
else
vim.cmd('DiffviewClose')
end
end
'';
options = { noremap = true; };
}
# -----------------------------------------------------
# Bufferline
# -----------------------------------------------------
{
mode = [ "n" ];
key = "<A-h>";
action = ":BufferLineCyclePrev<CR>";
options = { noremap = true; silent = true; };
}
{
mode = [ "n" ];
key = "<A-l>";
action = ":BufferLineCycleNex<CR>";
options = { noremap = true; silent = true; };
}
{
mode = [ "n" ];
key = "<A-c>";
action = ":bdelete!<CR>";
options = { noremap = true; silent = true; };
}
];
autoCmd = [
## Close nvim on last buffer closed, not leaving neovim-tree open
{
event = [ "BufEnter" ];
pattern = [ "NvimTree_*" ];
callback = {
__raw = ''
function()
local layout = vim.api.nvim_call_function("winlayout", {})
if layout[1] == "leaf" and vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(layout[2]), "filetype") == "NvimTree" and layout[3] == nil then vim.cmd("confirm quit") end
end
'';
};
}
## Go to same line in file next time it is open
{
event = [ "BufReadPost" ];
pattern = [ "*" ];
callback = {
__raw = ''
function()
if vim.fn.line("'\"") > 1 and vim.fn.line("'\"") <= vim.fn.line("$") then
vim.api.nvim_exec("normal! g'\"",false)
end
end
'';
};
}
## Highlight tabs and trailing whitespace
{
event = [ "BufEnter" ];
pattern = [ "*" ];
callback = {
__raw = ''
function()
vim.cmd([[
if exists('w:extratabs')
call matchdelete(w:extratabs)
unlet w:extratabs
endif
if exists('w:trailingwhitespace')
call matchdelete(w:trailingwhitespace)
unlet w:trailingwhitespace
endif
highlight ExtraTabs ctermbg=red guibg=red
highlight TrailingWhitespace ctermbg=red guibg=red
if &ft != 'help'
let w:extratabs=matchadd('ExtraTabs', '\t\+')
let w:trailingwhitespace=matchadd('TrailingWhitespace', '\s\+$')
endif
]])
end
'';
};
}
## Trim tailing whitespace on save
{
event = [ "BufWritePre" ];
pattern = [ "*" ];
callback = {
__raw = ''
function()
vim.cmd([[
if &ft =~ 'javascript\|html\|jade\|json\|css\|less\|php\|python\|sh\|c\|cpp\|markdown\|yaml\|vim\|nix'
:%s/\s\+$//e
elseif expand('%:t') =~ '\.gltf$' || expand('%:t') =~ '\.glsl$'
:%s/\s\+$//e
endif
]])
end
'';
};
}
];
## ------------------------------------------------
## Theme
## ------------------------------------------------
colorschemes.tokyonight.enable = true;
# colorschemes.gruvbox.enable = true;
## Or:
# extraPlugins = [ pkgs.vimPlugins.gruvbox ];
# colorscheme = "gruvbox";
## ------------------------------------------------
## Included Plugins
## ------------------------------------------------
plugins.bufferline = {
enable = true;
# extraOptions = {
settings = {
options = {
tabpages = true;
sidebar_filetypes = {
NvimTree = true;
};
diagnostics = "nvim_lsp";
always_show_bufferline = true;
};
highlights = {
buffer_selected = {
# fg = "#ffffff";
bold = true;
};
};
};
};
plugins.comment.enable = true;
plugins.diffview = {
enable = true;
};
plugins.fugitive.enable = true;
plugins.gitsigns.enable = true;
plugins.lightline.enable = true;
plugins.lualine.enable = true;
plugins.nvim-autopairs.enable = true;
plugins.nvim-tree = {
enable = true;
extraOptions = {
actions = {
remove_file = {
close_window = false;
};
};
## Keep tree open if already open when opening a tab
tab = {
sync = {
open = true;
close = true;
};
};
view = {
width = 30;
};
renderer = {
group_empty = true;
};
git = {
enable = true;
ignore = false;
timeout = 500;
};
};
};
plugins.rainbow-delimiters.enable = true;
plugins.sleuth.enable = true;
plugins.telescope = {
enable = true;
extensions.ui-select.enable = true;
settings = {
defaults = {
mappings = {
i = {
# One instead of two esc taps to exit telescope
"<esc>" = {
__raw = "require('telescope.actions').close";
};
# Ctrl-space is used by Tmux, so remap to Ctrl-e
"<c-e>" = {
__raw = "require('telescope.actions').to_fuzzy_refine";
};
# "<c-o>" = {
# __raw = "require('trouble.sources.telescope').open";
# };
};
n = {
# "<c-o>" = {
# __raw = "require('trouble.sources.telescope').open";
# };
};
};
};
};
};
plugins.treesitter.enable = true;
plugins.tmux-navigator.enable = true;
plugins.trouble.enable = true;
# ## Needed for telescope, nvim-tree, trouble, diffview, bufferline, and other plugins
# ## Only on unstable at the moment
plugins.web-devicons.enable = true;
## ------------------------------------------------
## Extra Plugins
## ------------------------------------------------
extraPlugins = with pkgs.vimPlugins; [
vim-nix
{
plugin = vim-signify;
config = ''
let g:signify_vcs_cmds = { 'git': 'git diff --no-color --no-ext-diff -U0 master -- %f' }
let g:signify_priority = 1
highlight SignColumn ctermbg=237
'';
}
vim-surround
## focus-nvim only in unstable
# (pkgs.vimUtils.buildVimPlugin {
# name = "focus-nvim";
# src = pkgs.fetchFromGitHub {
# owner = "nvim-focus";
# repo = "focus.nvim";
# rev = "3841a38df972534567e85840d7ead20d3a26faa6";
# sha256 = "sha256-mgHk4u0ab2uSUNE+7DU22IO/xS5uop9iATfFRk6l6hs=";
# };
# })
];
};
}

19
profiles/postgres.nix Normal file
View file

@ -0,0 +1,19 @@
{ lib, pkgs, ... }:
{
services.postgresql = {
enable = true;
# Authentik sets an older package for some reason
package = lib.mkForce pkgs.postgresql_16;
enableTCPIP = true;
authentication = pkgs.lib.mkOverride 10 ''
#type database DBuser auth-method
local all all trust
#type database DBuser origin-address auth-method
# ipv4
host all all 127.0.0.1/32 trust
# ipv6
host all all ::1/128 trust
'';
};
}

33
profiles/radvd.nix Normal file
View file

@ -0,0 +1,33 @@
{ config, ... }:
let
lan-interface = config.homefree.network.lan-interface;
in
{
services.radvd = {
enable = true;
config = ''
interface ${lan-interface}
{
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
AdvDefaultPreference low;
AdvHomeAgentFlag off;
prefix ::/64
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr off;
AdvPreferredLifetime 120;
AdvValidLifetime 300;
};
# Next line has the IPv6 address of a DNS server:
RDNSS 2001:4860:4860::8888
{
AdvRDNSSLifetime 30;
};
};
'';
};
}

View file

@ -1,18 +1,14 @@
{ config, inputs, pkgs, ... }:
{ config, homefree-inputs, pkgs, ... }:
let
# @TODO: How to determine interface names?
# wan-interface = "ens5";
wan-interface = "ens3";
# lan-interface = "ens6";
lan-interface = "ens5";
wan-interface = config.homefree.network.wan-interface;
lan-interface = config.homefree.network.lan-interface;
wireguard-port = config.homefree.wireguard.listenPort;
vlan-wan-id = 100;
vlan-lan-id = 200;
vlan-iot-id = 201;
vlan-guest-id = 202;
# lan-interface = "ens3";
dns-servers = [ "1.1.1.1" "1.0.0.1" ];
adlist = inputs.adblock-unbound.packages.${pkgs.system};
in
{
@ -28,21 +24,47 @@ in
#-----------------------------------------------------------------------------------------------------
boot.kernel.sysctl = {
# if you use ipv4, this is all you need
# enable ipv4 forwarding
"net.ipv4.conf.all.forwarding" = true;
# If you want to use it for ipv6
# enable ipv6 forwarding
"net.ipv6.conf.all.forwarding" = true;
# source: https://github.com/mdlayher/homelab/blob/master/nixos/routnerr-2/configuration.nix#L52
# source: https://github.com/mdlayher/homelab/blob/master/nixos/routnerr-3/configuration.nix#L46[]
# 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;
# "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.${wan-interface}.accept_ra" = 2;
"net.ipv6.conf.${wan-interface}.autoconf" = 1;
"net.ipv6.conf.${lan-interface}.accept_ra" = 2;
"net.ipv6.conf.${lan-interface}.autoconf" = 1;
};
systemd.network = {
networks = {
"01-${lan-interface}" = {
name = lan-interface;
networkConfig = {
Description = "LAN link";
Address = "10.0.0.1/24";
LinkLocalAddressing = "yes";
IPv6AcceptRA = "no";
# Announce a prefix here and act as a router.
IPv6SendRA = "yes";
# Use a DHCPv6-PD delegated prefix (DHCPv6PrefixDelegation.SubnetId)
# from the pool and assigns one /64 to this network.
DHCPPrefixDelegation = "yes";
};
ipv6SendRAConfig = {
# Currently dnsmasq manages DNS servers.
EmitDNS = "no";
EmitDomains = "no";
};
};
};
};
networking = {
@ -51,7 +73,11 @@ in
#-----------------------------------------------------------------------------------------------------
useDHCP = false;
nameservers = dns-servers;
## @TODO: Base on config for lan gateway
nameservers = [ "10.0.0.1" ];
# resolvconf = {
# };
## Define VLANS
## https://www.breakds.org/post/vlan-configuration-by-examples/
@ -75,16 +101,19 @@ in
# };
interfaces = {
# Don't request DHCP on the physical interfaces
${wan-interface} = {
# useDHCP = false;
useDHCP = true;
};
${lan-interface} = {
useDHCP = false;
ipv4.addresses = [{
address = "10.1.1.1";
address = "10.0.0.1";
prefixLength = 24;
}];
# ipv6.addresses = [{
# address = "2001:DB8::";
# prefixLength = 64;
# }];
};
# Handle the VLANs
@ -93,7 +122,7 @@ in
# };
# lan = {
# ipv4.addresses = [{
# address = "10.1.1.1";
# address = "10.0.0.1";
# prefixLength = 24;
# }];
# };
@ -115,7 +144,10 @@ in
# Firewall
#-----------------------------------------------------------------------------------------------------
nat.enable = false;
## @TODO: Evaluate this
# nat.enable = false;
## @TODO: Evaluate this
firewall.enable = false;
## @TODO: Look into nftables Nix DSL: https://github.com/chayleaf/notnft
@ -125,35 +157,69 @@ in
ruleset = ''
flush ruleset
# table ip filter {
# # allow all packets sent by the firewall machine itself
# chain output {
# type filter hook output priority 100; policy accept;
# }
#
# # allow LAN to firewall, disallow WAN to firewall
# chain input {
# type filter hook input priority 0; policy accept;
# iifname "${lan-interface}" accept
# iifname "${wan-interface}" drop
# }
#
# # allow packets from LAN to WAN, and WAN to LAN if LAN initiated the connection
# chain forward {
# type filter hook forward priority 0; policy drop;
# iifname "${lan-interface}" oifname "${wan-interface}" accept
# iifname "${wan-interface}" oifname "${lan-interface}" ct state related,established accept
# }
# }
## "inet" indicates both ipv4 and ipv6
table inet filter {
## allow all packets sent by the firewall machine itself
chain output {
type filter hook output priority 100; policy accept;
}
## allow LAN to firewall, disallow WAN to firewall
chain input {
type filter hook input priority 0; policy drop;
## Allow for web traffic
tcp dport { https } ct state new accept;
## Allow wireguard connections
udp dport { ${toString wireguard-port} } ct state new accept;
## Allow for ipv6 route advertisements
icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit, nd-router-advert, mld-listener-query } accept;
# DHCPv6
ip6 saddr fe80::/10 ip6 daddr fe80::/10 udp sport 547 udp dport 546 accept
iifname { "lo" } accept comment "Allow localhost to access the router"
iifname { "${lan-interface}" } accept comment "Allow local network to access the router"
iifname { "wg0" } accept comment "Allow wireguard network to access the router"
iifname "${wan-interface}" ct state { established, related } accept comment "Allow established traffic"
iifname "${wan-interface}" icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP"
iifname "${wan-interface}" counter drop comment "Drop all other unsolicited traffic from wan"
}
## allow packets from LAN to WAN, and WAN to LAN if LAN initiated the connection
chain forward {
type filter hook forward priority 0; policy drop;
## LAN-WAN
iifname { "${lan-interface}" } oifname { "${wan-interface}" } accept comment "Allow trusted LAN to WAN"
iifname { "${wan-interface}" } oifname { "${lan-interface}" } ct state established, related accept comment "Allow established back to LANs"
## @TODO: Confirm which, if any, of these are needed.
## Wireguard-WAN
iifname { "wg0" } oifname { "${wan-interface}" } accept comment "Allow trusted wireguard to WAN"
iifname { "${wan-interface}" } oifname { "wg0" } ct state established, related accept comment "Allow established back to wireguard"
## Wireguard-LAN
iifname { "wg0" } oifname { "${lan-interface}" } accept comment "Allow trusted wireguard to LAN"
iifname { "${lan-interface}" } oifname { "wg0" } ct state established, related accept comment "Allow established back to wireguard"
}
}
## only need "ip" (ipv4), not "inet" (ipv4+ipv6) as it breaks ipv6 on clients. NAT is not needed for ipv6.
table ip nat {
chain prerouting {
## Lower priority number indicates higher priority
type nat hook prerouting priority 0; policy accept;
}
# for all packets to WAN, after routing, replace source address with primary IP of WAN interface
chain postrouting {
type nat hook postrouting priority 100; policy accept;
## This handles both wg0 and the lan interface
oifname "${wan-interface}" masquerade
}
}
@ -161,6 +227,27 @@ in
};
};
# systemd.services.block-wan-traffic = {
# wantedBy = [ "multi-user.target" ];
# enable = true;
# serviceConfig = {
# User = "root";
# Group = "root";
# };
# script = ''
# IPTABLES=${pkgs.iptables}/bin/iptables
#
# $IPTABLES -A INPUT -i ${wan-interface} -p tcp -m tcp -m multiport --dports 80,443 -j ACCEPT
# $IPTABLES -A INPUT -i ${wan-interface} -p tcp -m tcp -m multiport --dports ${toString config.homefree.wireguard.listenPort} -j ACCEPT
# $IPTABLES -A INPUT -i ${wan-interface} -m conntrack -j ACCEPT --ctstate RELATED,ESTABLISHED
# $IPTABLES -A INPUT -i ${wan-interface} -j DROP
# # $IPTABLES -A OUTPUT -o ${wan-interface} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# # $IPTABLES -A OUTPUT -o ${wan-interface} -j DROP
# # $IPTABLES -A FORWARD -i ${wan-interface} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# # $IPTABLES -A FORWARD -i ${wan-interface} -j DROP
# '';
# };
#-----------------------------------------------------------------------------------------------------
# Performance Tuning
#-----------------------------------------------------------------------------------------------------
@ -184,26 +271,26 @@ in
smp2=3
rps2=2
ens3_irq=$($GREP ens3 /proc/interrupts | $AWK '{ print $1+0 }')
wan_irq=$($GREP ${wan-interface} /proc/interrupts | $AWK '{ print $1+0 }')
# set balancer for enp1s0
echo $smp1 > /proc/irq/$ens3_irq/smp_affinity
echo $smp1 > /proc/irq/$wan_irq/smp_affinity
# set rps for ens3
echo $rps1 > /sys/class/net/ens3/queues/rx-0/rps_cpus
# set rps for wan interface
echo $rps1 > /sys/class/net/${wan-interface}/queues/rx-0/rps_cpus
ens5_irq=$($GREP ens5 /proc/interrupts | $AWK '{ print $1+0 }')
lan_irq=$($GREP ${lan-interface} /proc/interrupts | $AWK '{ print $1+0 }')
# set balancer for enp2s0
# echo $smp2 > /proc/irq/$ens5_irq/smp_affinity
# echo $smp2 > /proc/irq/$lan_irq/smp_affinity
# set rps for ens5
echo $rps2 > /sys/class/net/ens5/queues/rx-0/rps_cpus
# set rps for lan interface
echo $rps2 > /sys/class/net/${lan-interface}/queues/rx-0/rps_cpus
'';
};
#-----------------------------------------------------------------------------------------------------
# DHCP
# DHCP/DNS
#-----------------------------------------------------------------------------------------------------
# See: https://nixos.wiki/wiki/Systemd-resolved
@ -218,134 +305,6 @@ in
'';
};
services.dnsmasq = {
enable = true;
settings = {
## @TODO
## @WARNING - changes to this do not clear out old entries from /etc/dnsmasq-conf.conf
## Only DHCP server on network
dhcp-authoritative = true;
## Enable Router Advertising for ipv6
enable-ra = true;
## DNS servers to pass to clients
server = dns-servers;
## Which interfaces to bind to
interface = [
# "${lan-interface}.${builtins.toString vlan-lan-id}"
# "${lan-interface}.${builtins.toString vlan-iot-id}"
# "${lan-interface}.${builtins.toString vlan-guest-id}"
lan-interface
];
## IP ranges to hand out
dhcp-range = [
# "lan,10.1.1.100,10.1.1.254,255.255.255.0,8h"
# "iot,10.2.1.100,10.2.1.254,255.255.255.0,8h"
# "guest,10.3.1.100,10.3.1.254,255.255.255.0,8h"
"${lan-interface},10.1.1.100,10.1.1.254,255.255.255.0,8h"
];
## Disable DNS
port = 0;
## Additional DHCP options
dhcp-option = [
"option6:dns-server,[::]" # @TODO: point this at Unbound when ipv6 is setup
"option:dns-server,10.1.1.1"
];
cache-size = 500;
};
};
#-----------------------------------------------------------------------------------------------------
# DNS
#-----------------------------------------------------------------------------------------------------
## @TODO - Setup Unbound
## See: https://blog.josefsson.org/2015/10/26/combining-dnsmasq-and-unbound/
services.unbound = {
enable = true;
user = "root";
resolveLocalQueries = true;
settings = {
server = {
include = [
"\"${adlist.unbound-adblockStevenBlack}\""
];
interface = [
"127.0.0.1"
"::1"
"10.1.1.1"
];
access-control = [
"127.0.0.1/8 allow"
"::1 allow"
"10.1.1.1/8 allow"
# @TODO: need ipv6 address
];
outgoing-interface = [
## @TODO: should be WAN IP - how to get this automatically?
"10.0.2.15"
# @TODO: need ipv6 address
];
local-zone = [
"\"homefree.lan.\" static"
];
local-data = [
"\"radicale.lan. IN A 10.1.1.1\""
];
local-data-ptr = [
"\"10.1.1.1 radicale.lan\""
];
};
forward-zone = [
{
name = ".";
forward-addr = [
"1.1.1.1@853#cloudflare-dns.com"
"1.0.0.1@853#cloudflare-dns.com"
];
forward-tls-upstream = "yes";
}
# {
# name = "example.org.";
# forward-addr = [
# "1.1.1.1@853#cloudflare-dns.com"
# "1.0.0.1@853#cloudflare-dns.com"
# ];
# }
];
remote-control.control-enable = true;
};
};
#-----------------------------------------------------------------------------------------------------
# Dynamic DNS
#-----------------------------------------------------------------------------------------------------
# @TODO: https://discourse.nixos.org/t/ddclient-options/20935
services.ddclient = {
enable = true;
interval = "10m";
# protocol = "zoneedit1";
# username = "erahhal";
# zone = "homefree.host";
# passwordFile = config.age.secrets.ddclient.path;
# verbose = true;
configFile = config.age.secrets.ddclient-conf.path;
};
#-----------------------------------------------------------------------------------------------------
# Service Discovery
#-----------------------------------------------------------------------------------------------------
@ -361,7 +320,7 @@ in
];
# network locator e.g. scanners and printers
nssmdns = true;
nssmdns4 = true;
};
#-----------------------------------------------------------------------------------------------------

View file

@ -0,0 +1,170 @@
{ config, pkgs, lib, ... }:
## @TODO: Convert to work with nftables
## @TODO: Test with ethernet cable using the following: https://www.waveform.com/tools/bufferbloat
let
wan-interface = config.homefree.network.wan-interface;
wan-bitrate-mbps-down = config.homefree.network.wan-bitrate-mbps-down;
wan-bitrate-mbps-down-95-percent = builtins.floor (builtins.mul wan-bitrate-mbps-down 0.95);
wan-bitrate-mbps-down-20-percent = builtins.floor (builtins.mul wan-bitrate-mbps-down 0.20);
in
{
## OPNSense Config
## ---------------
## Pipe
## Name: Downstream Pipe
## Bandwidth: 950 Mbit/s
## Scheduler: FlowQueue-CoDel
## CoDel enabled
## Pipe
## Name: Upstream Pipe
## Bandwidth: 950 Mbit/s
## Scheduler: FlowQueue-CoDel
## CoDel enabled
## Queue
## Name: Upstream Queue
## Pipe: Upstream Pipe
## Weight: 1
## Queue
## Name: Downstream Queue
## Pipe: Downstream Pipe
## Weight: 1
## Queue
## Name: High Priority Queue
## Pipe: Upstream Pipe
## Weight: 10
## Rule
## Name: DNS High Priority
## Sequence: 11
## Interface: WAN
## Protocol: UDP
## Source: 10.0.0.1 (lan gateway)
## Src-port: any
## Destination: any
## Dst-port: 53
## Target: High Priority Queue
## Rule
## Name: Upstream traffic
## Sequence: 12
## Interface: WAN
## Protocol: IP
## Source: 10.0.0.0/24 (lan)
## Src-port: any
## Destination: any
## Dst-port: any
## Target: Upstream queue
## Rule
## Name: ACK High Priority
## Sequence: 13
## Interface: WAN
## Protocol: tcp (ACK packets only)
## Source: 10.0.0.0/24 (lan)
## Src-port: any
## Destination: any
## Dst-port: any
## Target: High Priority Queue
## Rule
## Name: Downstream traffic
## Sequence: 14
## Interface: WAN
## Protocol: ip
## Source: any
## Src-port: any
## Destination: 10.0.0.0/24 (lan)
## Dst-port: any
## Target: Downstream queue
## Disable TCP offload engine, which bypasses traffic shaper to save on CPU
systemd.services.disable-transmit-segment-offload = {
wantedBy = [ "multi-user.target" ];
enable = true;
serviceConfig = {
User = "root";
Group = "root";
};
# script = builtins.readFile ../scripts/tune_router_performance.sh;
script = ''
ETHTOOL=${pkgs.ethtool}/bin/ethtool
$ETHTOOL -K ${wan-interface} tso off
$ETHTOOL -K ${wan-interface} gso off
'';
};
systemd.services.traffic-shaper = {
wantedBy = [ "multi-user.target" ];
enable = true;
serviceConfig = {
User = "root";
Group = "root";
};
# script = builtins.readFile ../scripts/tune_router_performance.sh;
script = ''
TC=${pkgs.iproute2}/bin/tc
IPTABLES=${pkgs.iptables}/bin/iptables
# CoDel Active Queue Management (AQM) algorithm
# https://www.bufferbloat.net/projects/codel/wiki/
# fq_codel only supported by certain network drivers
# https://www.bufferbloat.net/projects/bloat/wiki/BQL_enabled_drivers/
# Reference for rules below
# https://wiki.archlinux.org/title/Advanced_traffic_control
# https://www.linuxquestions.org/questions/linux-networking-3/traffic-shaping-with-tc-on-a-server-4175690429-print/
# Remove existing queues
# redirect errors to /dev/null in case the qdisc doesn't exist
$TC qdisc del dev ${wan-interface} ingress 2>/dev/null || true
$TC qdisc del dev ${wan-interface} root 2>/dev/null || true
# 1) Add/Replace root qdisc of eth0 with an HTB instance,
# specify handle so it can be referred to by other rules,
# set default class for all unclassified traffic
$TC qdisc replace dev ${wan-interface} root handle 1: htb default 30
# 2) Create single top level class with handle 1:1 which limits
# total traffic to slightly less than the path max
# Limit to 95% of maximum bandwidth
$TC class add dev ${wan-interface} parent 1: classid 1:1 htb rate ${toString wan-bitrate-mbps-down-95-percent}mbit
# 3) Create child classes for different uses:
# Class 1:10 is our outgoing highest priority path, outgoing SSH/SFTP in this example
# Class 1:20 is our next highest priority path, web admin traffic for example
# Class 1:30 is default and has lowest priority but highest total bandwidth - bulk web traffic for example
$TC class add dev ${wan-interface} parent 1:1 classid 1:10 htb rate 1mbit ceil ${toString wan-bitrate-mbps-down-20-percent}mbit prio 1
$TC class add dev ${wan-interface} parent 1:1 classid 1:20 htb rate 1mbit ceil ${toString wan-bitrate-mbps-down-20-percent}mbit prio 2
$TC class add dev ${wan-interface} parent 1:1 classid 1:30 htb rate 1mbit ceil ${toString wan-bitrate-mbps-down-95-percent}mbit prio 3
# 4) Attach a leaf qdisc to each child class
# HTB by default attaches pfifo as leaf so this is optional.
# fq_codel is said to be worth the effort.
$TC qdisc add dev ${wan-interface} parent 1:10 fq_codel
$TC qdisc add dev ${wan-interface} parent 1:20 fq_codel
$TC qdisc add dev ${wan-interface} parent 1:30 fq_codel
# 5) Add filters for priority traffic
$TC filter add dev ${wan-interface} parent 1: handle 100 fw classid 1:10
$TC filter add dev ${wan-interface} parent 1: handle 200 fw classid 1:20
# Shape traffic
## -t mangle mangle table, used for modifying packet headers
## -A OUTPUT Append to OUTPUT chain (OUTPUT being packets leaving the system)
## -p tcp TCP protocol
## --match multiport Match multiple ports
## --dports 53 Match destination port of 53 (DNS)
## -j MARK What to do on match. Jump to MARK (to set the netfilter mark value associated with the packet. It is only valid in the mangle table)
## --set-mark 200 Set connection mark of 200, which matches the tc filter above
## DNS High Priority
$IPTABLES -t mangle -A OUTPUT -p tcp --match multiport --dports 53 -j MARK --set-mark 100
## ACK High Priority
$IPTABLES -t mangle -A OUTPUT -p tcp --match tcp --tcp-flags ACK ACK -j MARK --set-mark 100
'';
};
}

251
profiles/unbound.nix Normal file
View file

@ -0,0 +1,251 @@
{ homefree-inputs, config, lib, pkgs, ... }:
let
adlist = homefree-inputs.adblock-unbound.packages.${pkgs.system};
zones = [config.homefree.system.domain] ++ config.homefree.system.additionalDomains;
preStart = ''
touch /run/unbound/include.conf
cat > /run/unbound/dynamic.zone<< EOF
\$ORIGIN ${config.homefree.system.localDomain}.
\$TTL 3600
@ IN SOA localhost. root.localhost. (
2023100101 ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
86400 ; minimum
)
IN NS localhost.
EOF
# cp /run/unbound/dynamic.zone /tmp
'';
in
{
## See: https://blog.josefsson.org/2015/10/26/combining-dnsmasq-and-unbound/
## Unbound is a caching resolver, not meant to be used as authoritative.
## nbound does support simple authoritative hosting with local-zone config.
## For a proper authoritative DNS, look at NSD.
systemd.services.unbound = {
serviceConfig = {
ExecStartPre = [ "!${pkgs.writeShellScript "unbound-prestart" preStart}" ];
};
};
services.unbound = {
enable = true;
user = "root";
resolveLocalQueries = true;
settings = {
server = {
include = [
## Leave ad-blocking to AdGuard, as it can be disabled by the client
# "\"${adlist.unbound-adblockStevenBlack}\""
## Include run-time config, such as WAN ip mappings
## @TODO: Update this with ddclient scripts
## @TODO: Remove WAN entries from bare hostname maps below
"\"/run/unbound/include.conf\""
];
port = 53530;
interface = [
"127.0.0.1"
"::1"
"10.0.0.1"
"192.168.2.1" # wireguard
];
access-control = [
"127.0.0.1/24 allow"
"::1 allow"
"10.0.0.1/24 allow"
"192.168.2.1/24 allow"
];
# outgoing-interface = [
# ## @TODO: should be WAN IP - how to get this automatically?
# "10.0.2.15"
# # @TODO: need ipv6 address
# ];
local-zone = [
"\"homefree.lan\" static"
"\"homefree.host\" transparent"
"\"rahh.al\" transparent"
];
## @TODO: Add config.homefree.network.blocked-domains as such:
# local-zone: "example.org" always_nxdomain
## Record format:
## NAME CLASS (default: IN) TYPE RDATA
## localhost IN A 127.0.0.1
local-data =
[
"\"localhost A 127.0.0.1\""
"\"localhost AAAA ::1\""
]
++
## add localhost.<zone> for all configured zones
(lib.map (zone: "\"localhost.${zone} IN A 127.0.0.1\"") zones)
++
## add <hostname>.<zone> for all configured zones
(lib.map (zone: "\"${config.homefree.system.hostName}.${zone} IN A 127.0.0.1\"") zones)
++
# Add DNS overrides
(lib.map (local-data-config:
if builtins.hasAttr "domain" local-data-config then
"\"${local-data-config.hostname}.${local-data-config.domain} IN A ${local-data-config.ip}\""
else
"\"${local-data-config.hostname} IN A ${local-data-config.ip}\""
) config.homefree.network.dns-overrides
)
++
# Point proxy URLs to internal IP when on LAN
(lib.map
(fqn: "\"${fqn} IN A 10.0.0.1\"")
## Flatten to single list
## e.g. [ "hij.lmnop" "hij".xyz" "abc.lmnop" "abc.xyz" "def.lmnop" "def.xyz" ]
(lib.flatten
## Map across all proxy configs
## creating list of lists
## e.g. [ [ "hij.lmnop" "hij".xyz" ] [ "abc.lmnop" "abc.xyz" "def.lmnop" "def.xyz" ] ]
(lib.map
(proxy-config:
## Flatten subdomain-domain combinations for individual proxy into single list
## e.g. [ "abc.lmnop" "abc.xyz" "def.lmnop" "def.xyz" ]
lib.flatten
## Create all subdomain-domain combinations, grouped by subdomain
## e.g. [ [ "abc.lmnop" "abc.xyz" ] [ "def.lmnop" "def.xyz" ]]
(lib.map
(subdomain:
# Create <subdomain>.<domain> fqn string
(lib.map
(domain: "${subdomain}.${domain}")
(proxy-config.http-domains ++ proxy-config.https-domains)
)
)
proxy-config.subdomains
)
)
## @TODO: Get rid of this filter
## See: https://caddy.community/t/caddy-not-handling-requests-when-listening-on-all-interfaces-serving-a-hostname-mapped-to-an-internal-ip/26384
# (lib.filter (proxy-config: proxy-config.public == false) config.homefree.proxied-hosts)
config.homefree.proxied-hosts
)
)
)
++
## router lan ip with public domains
(lib.map (zone: "\"${config.homefree.system.hostName}.${zone} IN A 10.0.0.1\"") zones)
++
## router vpn ip with public domains
(lib.map (zone: "\"${config.homefree.system.hostName}.${zone} IN A 192.168.2.1\"") zones)
++
## @TODO: Move to config for gateway IP
[
## router lan IP
"\"${config.homefree.system.hostName} IN A 10.0.0.1\""
## router lan IP with local domain
"\"${config.homefree.system.hostName}.${config.homefree.system.localDomain} IN A 10.0.0.1\""
## router vpn IP
"\"${config.homefree.system.hostName} IN A 192.168.2.1\""
## router vpn IP with local domain
"\"${config.homefree.system.hostName}.${config.homefree.system.localDomain} IN A 192.168.2.1\""
]
++
## @TODO: How to configure these at runtime?
## router wan IP with public domain
(lib.map (zone: "\"${config.homefree.system.hostName}.${zone} IN A 104.182.229.64\"") zones)
++
## Bare hostname maps
[
## router wan IP - @TODO - THIS NEEDS TO BE DYNAMIC
"\"${config.homefree.system.hostName} IN A 104.182.229.64\""
## router wan ipv6 IP - @TODO - THESE ARE WRONG
"\"${config.homefree.system.hostName} IN AAAA 2600:1700:ab00:4650:2e0:67ff:fe22:3e62\""
## ??? @TODO - WHAT IS THIS?
"\"${config.homefree.system.hostName} IN AAAA 2600:1700:ab00:465f:2e0:67ff:fe22:3e63\""
]
++
## router wan IPv6 with public domain
(lib.map (zone: "\"${config.homefree.system.hostName}.${zone} IN AAAA 2600:1700:ab00:4650:2e0:67ff:fe22:3e62\"") zones)
++
(lib.map (zone: "\"${config.homefree.system.hostName}.${zone} IN AAAA 2600:1700:ab00:465f:2e0:67ff:fe22:3e64\"") zones)
++
(lib.map (ip-config:
"\"${ip-config.hostname} IN A ${ip-config.ip}\"")
config.homefree.network.static-ips)
++
(lib.map (ip-config:
"\"${ip-config.hostname}.${config.homefree.system.localDomain} IN A ${ip-config.ip}\"")
config.homefree.network.static-ips)
;
local-data-ptr = [
"\"::1 localhost\""
"\"127.0.0.1 localhost\""
]
++
(lib.map (ip-config:
"\"${ip-config.ip} ${ip-config.hostname}\"")
config.homefree.network.static-ips)
++
(lib.map (ip-config:
"\"${ip-config.ip} ${ip-config.hostname}.${config.homefree.system.localDomain}\"")
config.homefree.network.static-ips)
## @TODO: Add caddy domains to zones, e.g.:
## "10.0.0.1 auth.rahh.al"
;
hide-identity = true;
hide-version = true;
# Based on recommended settings in https://doc.pi-hole.net/guides/dns/unbound/#configure-unbound
harden-glue = true;
harden-dnssec-stripped = true;
use-caps-for-id = false;
prefetch = true;
edns-buffer-size = 1232;
};
#
# range-lan = {
# start = "10.0.0.200";
# end = "10.0.0.254";
# domain = "localdomain";
# };
forward-zone = [
{
name = ".";
forward-addr = [
"9.9.9.9#dns.quad9.net"
"1.1.1.1@853#cloudflare-dns.com"
"1.0.0.1@853#cloudflare-dns.com"
];
forward-tls-upstream = "yes";
}
# {
# name = "example.org.";
# forward-addr = [
# "1.1.1.1@853#cloudflare-dns.com"
# "1.0.0.1@853#cloudflare-dns.com"
# ];
# }
];
## Enable dynamic updates from dnsmasq
auth-zone = {
name = "\"${config.homefree.system.localDomain}\"";
master = "yes";
allow-notify = "no";
for-downstream = "no";
for-upstream = "yes";
zonefile = "\"/run/unbound/dynamic.zone\"";
};
remote-control.control-enable = true;
};
};
}

12
profiles/unifi.nix Normal file
View file

@ -0,0 +1,12 @@
{ pkgs, ... }:
{
nixpkgs.config.allowUnfree = true;
services.unifi = {
enable = true;
openFirewall = true;
unifiPackage = pkgs.unifi8;
mongodbPackage = pkgs.mongodb-7_0;
};
}

14
profiles/vaultwarden.nix Normal file
View file

@ -0,0 +1,14 @@
{ ... }:
{
services.vaultwarden = {
enable = true;
dbBackend = "sqlite"; # "sqlite", "mysql", "postgresql"
## @TODO: Setup proper backup
backupDir = "/var/backup/vaultwarden";
config = {
ROCKET_ADDRESS = "10.0.0.1";
ROCKET_PORT = 8222;
};
};
}

View file

@ -1,23 +0,0 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
libvirt
virtiofsd
];
systemd.mounts = [
{
what = "mount_homefree_source";
where = "/home/homefree/nixcfg";
type = "virtiofs";
wantedBy = [ "multi-user.target" ];
enable = true;
}
];
boot.extraModprobeConfig = "options kvm_intel nested=1";
boot.kernelParams = [
"intel_iommu=on"
"cgroup_enable=freezer"
];
}

44
profiles/wireguard.nix Normal file
View file

@ -0,0 +1,44 @@
{ config, pkgs, ... }:
let
listenPort = config.homefree.wireguard.listenPort;
in
{
# enable NAT
networking.nat.enable = true;
networking.nat.externalInterface = config.homefree.network.wan-interface;
networking.nat.internalInterfaces = [ "wg0" ];
networking.firewall = {
allowedUDPPorts = [ listenPort ];
};
networking.wireguard.interfaces = {
# "wg0" is the network interface name. You can name the interface arbitrarily.
wg0 = {
# Determines the IP address and subnet of the server's end of the tunnel interface.
ips = [ "192.168.2.1/24" ];
# The port that WireGuard listens to. Must be accessible by the client.
listenPort = listenPort;
# Path to the private key file.
#
# Note: The private key can also be included inline via the privateKey option,
# but this makes the private key world-readable; thus, using privateKeyFile is
# recommended.
privateKeyFile = "/run/secrets/wireguard/server-private-key";
peers = config.homefree.wireguard.peers;
};
};
## @TODO: Move to host config
sops.secrets = {
"wireguard/server-private-key" = {
format = "yaml";
sopsFile = ../secrets/wireguard.yaml;
owner = config.homefree.system.adminUsername;
path = "/run/secrets/wireguard/server-private-key";
};
};
}

10
run.sh
View file

@ -80,10 +80,17 @@ sudo $VIRTIOFSD --socket-path /tmp/vhostqemu --shared-dir ./ --cache auto &
pids[1]=$!
# -netdev tap,id=enp1s0,br=hfbr0,helper=$QEMU_BRIDGE_HELPER \
# -device e1000,netdev=enp1s0,mac=52:53:54:55:56:01 \
# Port 8885: Caddy default HTTP
# Port 8445: Caddy default HTTPS
# Port 8123: Home Assistant
# Port 8222: Vaultwarden
# Port 9000: Authentik
sudo -E qemu-kvm \
$GUI_FLAG \
-cpu host \
-enable-kvm \
-display gtk,show-cursor=on \
-chardev socket,id=char0,path=/tmp/vhostqemu \
-device vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=mount_homefree_source \
-object memory-backend-file,id=mem,size=8G,mem-path=/dev/shm,share=on \
@ -94,7 +101,7 @@ sudo -E qemu-kvm \
-smp 4 \
-m 8G \
-net nic \
-net user,hostfwd=tcp::2223-:22,hostfwd=tcp::8445-:443,hostfwd=tcp::8885-:80 \
-net user,hostfwd=tcp::2223-:22,hostfwd=tcp::8445-:443,hostfwd=tcp::8885-:80,hostfwd=tcp::8123-:8123,hostfwd=tcp::8222-:8222,hostfwd=tcp::9000-:9000 \
-netdev bridge,br=hfbr0,id=hn1,helper=$QEMU_BRIDGE_HELPER \
-device virtio-net,netdev=hn1,mac=e6:c8:ff:09:76:88 \
&
@ -105,6 +112,7 @@ sudo -E qemu-kvm \
$GUI_FLAG \
-cpu host \
-enable-kvm \
-display gtk,show-cursor=on \
-drive file=$OVMF_CODE,if=pflash,format=raw,unit=0,readonly=on \
-drive file=./build/OVMF_VARS.fd,if=pflash,format=raw,unit=1 \
-hda ./build/lan-client.qcow2 \

View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
ssh-keygen -R "[localhost]:2223"
ssh -o StrictHostKeychecking=no -p 2223 homefree@localhost "sudo cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o homefree-server.asc"

View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
# Adds to ~/.gnupg/pubring.kbx
# List with: gpg -k
nix-shell -p gnupg -p ssh-to-pgp --run "ssh-to-pgp -private-key -i $HOME/.ssh/id_rsa | gpg --import --quiet"
# Exports public key from private key
# nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i $HOME/.ssh/id_rsa -o $USER.asc"

View file

@ -0,0 +1,28 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
xsFNBAAAAAABEADLkq41ZqnTM4Ak5nDDodYsm7VthYyBbzw+uzLgnvJY/TDfse5V
s9GZPz5YSNEUiWQ3P3jQgbfY8P6Kvlr++TLcj8i5EkKRGo1ItO2yWDnT8hd3f9Hi
nlBzDHd8aQVzJ4CE2EZo6oSFXrX8Pazp9z/KaLm5LLEcwWZ1JBFTn6vAB6M1tRji
lgLptLER7sKPb9DHjMo0aVv8Vh9tDXk6nuzS+/3u5z17DT+89tsI8paiNrj4dame
Dhg/uT3ko8n8kUFcwLch98taWLYT1y6/e+qCLVXosfIWVTD55U2e+gKu+aSGQ3+8
43j2nIoycmzhj/tQY8Pcz2EQaPwsB2Hp+tnxNBDrETXKSDdLASfmJBNFeQTGnagE
26nu+by/qCNWMC7XJzBvUBVqPH9X6242O4lbD1+c2iIM/T66+9je+rnG/YfxnJ10
5fnMFuGwSzFnCGgWxV3VwEK931GySDs+lNbVA5HTDwD/33JQz9t5hEDIogTWp6UN
QQyU5Uj2jMyRFrGi2As7kmQ+NvuhYIJ5ZXWpGX5PCFt6Fy1H0l1IXQkcwEeMNZLv
Y14jpy5vrp4iRakRq9z5EGHyrHsUb9xpWc8kbwTuy0Xw3xQE4OnQujGhgS1dU3qK
W2zvNn2iWGy4CkhxcJxFAjP1JLOUVd6L1lUPef0IIPlfSBf8y7/ZpW5lGwARAQAB
zSlyb290IChJbXBvcnRlZCBmcm9tIFNTSCkgPHJvb3RAbG9jYWxob3N0PsLBYgQT
AQgAFgUCAAAAAAkQSRgIZw+lWbICGw8CGQEAAEQ5EACTqTJJbLXWAMfLGkAkp1OH
hi7H2I3guHRiHFpVzqF/mu3PcAiCk5ITX2W7LUOjXCkPpUwSpdSq3BqUbOs1oCya
EapHl9pkb5EyvnVcsF+fNLETw7WrFWX1Q6+5nrVDUfL+cjsAoGaljWArxI1gsPx5
+Hbz8TgcE7cXA7pTaQO7bOMVahk0O8GFbMrK/62KfZvw9drCzkF+t/3SsEen19kz
Uuxozf2IpVMabyFm6DUVtfmIizcQOe6TINDvdTm3P1gqlWEmkwDnwUZ0cvdn4GRs
RM29RQv90npa8g4CPWK+Yq8ooUFuOtZBczMrObguQfIbW5q14MCoS6DEl+sK4MdL
EmITwW9awhrEBARPHlxRifmG9N9Pry75eJ8ve1Lgo2I8JM2WW9E6gtZn3vaEfjtc
/7XB0i1h9U4J4uGP/5eSS6U9rbDusaaF+3a1A4wqqG5P0UFmdGAgribwcAWmC6Cu
cORoNmhR07mAbGDZfLN1epdsICVxxLq2VM+G4ihSWLeT0VvOqcqOg23ym1qSj9Yg
Rtg696dq5MRr1WN25xyGLzlDbEdUq/6FWgR5Vp1NcARmo+3sa+c5U5hY47OgfFKm
TUNbaHQjmlyLeKQs3/W3hE4lW+NzJ3OfPu3SlLErPGQ/qXi+6zCnjrQe7DUovpUw
IOtP32FUg8jiuEv1HPU7lA==
=iAZK
-----END PGP PUBLIC KEY BLOCK-----

51
secrets/authentik.yaml Normal file
View file

@ -0,0 +1,51 @@
authentik:
postgres-password: ENC[AES256_GCM,data:LqZbIvmQ7D4Wi18Tb9U=,iv:gMHkdS2xcccK3iBjUnU11EE+n5WJDALmBYvi/IgWbyo=,tag:8/QutMS/EIPFywaU84HPjA==,type:str]
authentik-env: ENC[AES256_GCM,data:6Hs8FldGKG6AZHMAe0KX94CwXGWu2mrEDkCif6q9uYSdYwFTwneV5uyjkoOIgMI8/x5GEBNsMTc+NbuVtQSTaajA+RqJVNwsrSGDmNdSyLLSPAN7MhjpNSOezkjBsuQJVFRuPEjUjRZus06yy2vxPUc4hWygGyC8JwByKp+iD+GhTr05evDbeQYaNjRphZjU2gcpEUAbjA07XMJZgKo+smD/SVpIVRLluqYUD4s6C5Av41n68LLTCYCeu3tPrXXrV7cUVVwiDzcNPwewQN3oBK08fQmAhscuVbr5TTQALhgSAN/VvUo+KTR7eHJR2XkxcS2U4iDAbcL7pXGl8c8iXUH/4tqHqqSfJQI09CQ8yoTpwxfpTU1jyaDKnJ59wD/xNkF69CiK85zZy4u/3abDpNuW9sLYah906XyporFJw8M0iYgP4MEo7HGID0XVyIsdjr6Z21PI4Bv/OJDOr8aihSlSaomM8G5hZLxgQp0gwfI0Z7arVlq0/0wlDeWxOrn9V1GaBKxjcw41JpEWFQIa/S8JA9wy,iv:lZfm4LFPrk5Sa4kSPN5hTQv8ii2rd7EAsrevDjFwCgg=,tag:tqCNiQXPZskLwRz9s+Ad+w==,type:str]
authentik-ldap-env: ENC[AES256_GCM,data:obxbXZPDdlKRIAkZCPkUF6NweaVC+8do7oPe9CdrKpAuDuK14WAwiwlFgN/uOokLS1990P3yeF/dsHghSCY4P0vYwI9obqtPOXf8P+Pyo29sgSBwgbkMlYmLzlSWC5OuDejlDY9vwbRXKMPnk26PdozzMsE/U0IvBg4nX17OGUqV1aV/Ve4G5+4=,iv:IGGMQ2mIVAONdQyY8U3QF5RoxY5I7Hjdrohp7zut4Sg=,tag:+IXNEQFB848UOmNgwH5jpg==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2024-09-20T03:22:05Z"
mac: ENC[AES256_GCM,data:EqC7gfouu7FQXZy2ZLEx0940lrTq1HWVbVwCnNC/x3S0ForK47ByrSqboHBYap2XP4HMJ+B7xYNH9jKkg3q5aL0CCaGA/6XcWLSRDa2u4bxvhCgV+bqoRUMZVeXGK7lhB+z4mzhzwM0LbnRL/ZzDiXylJlRn4rW+hQM1YY5Likw=,iv:V5B9n7MVl1e/RNDcqJr2Sf5gL9OwN+GtbluyFMe66Lc=,tag:4lOcJ6YxZVmzCynKGrSWNw==,type:str]
pgp:
- created_at: "2024-11-13T23:45:26Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQEMAwXRN+4RS6LCAQf/fI9eHGnRrnWNMjXEayMydMDaB6JLr0eKZOdiLKr5Bsb1
F2Yaliq/rhMIj9YgOAHIjitRZICkhUmaq3Ce6NKYigSTH6YG6xOhRS5m/W5PIXVZ
VyrLiEyptEjyL3FqUOBcf3aEEDDuvR5H/wojOd79M5emfBlcaShBHTRyVG9lcOKM
el4AlVIHK7qVQ73VyhAiFFH8r0wPJCIetHotCU6lGLDUyMSu1ny4SFfyK6LYT3Pk
lznTRvQdJB2Nl4jyaP7F/D7TzF9aGNuzZKCTSBwzVCNM9K7UXgMkk5EKgTtmZshp
TEAqzpeS6sLGyyeObCwOQEFu8RdVH0hFTh+rXKgundRmAQkCEPYvyEH9OJjl6aQV
hP1myCWkMasnnQQ2YNBiznvzLUL0AcJle1uLrhjiCJml0amkj5bo/7HvjoEfJyKT
zmepSVIHXJ31rXOZMZFRZF57+Pi4JBHKKvjxKsRhUhSX8JOgxBq6
=p3oE
-----END PGP MESSAGE-----
fp: 06321D7F20335A7E08595BA905D137EE114BA2C2
- created_at: "2024-11-13T23:45:26Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMAxkAiCHAcpg+ARAAnvW2+Tu4/W+bPkeoBAyi3HdBtfmLOJ1h+3UbqN9P21Wz
5BDth/8B6JD93OXeL/1ZCnXUWuR4Bi0+r1a+8XdXfyp5LyyEbDfHY+ALOwIdEupI
+XX/axzs91Flmqp/qdjwu+9RNCN6+VG4Tn1AYGjCKVfN9TRsmDIgeeoc2+oO8+SL
zG3uve7EeVovI0eYMroqCHNwD5iRUY/BCR6N4ihBAzawF/DKkW2tX4L2bRTBUZdy
Tp7QAWm5Z6MjbNyEi9sjKhXcc2fGmDZrwtfdwOfdxIA6VwZW6DPWU+Y09XlZbTP1
phSgTdASGD1NcePHb3CZ4b71AMQi077abF4gJWaVF0IZvUi66i/Kc5R1XoSxBgMk
UTMxVC0ml9ZsUC4PBK8f4ALihok/wU1Qtg0KXKHMOOe4UlIyaaqwUwumRHZhhx8p
EMIv7gTl97+HlRK4dXu5DQKUxd9UsY2TokoCWOAumTOcw4U6AklJ870T3uBieoYf
znOO7yTv5XSnPvvHvmSYlpJDuEhZsqs7LsDiddPBpNFKDhdIWpLObRkNr1cStSHb
S5qGAczdcM4AjUG+W2ClUAbBx5a7QwF9WtEDQok6y+SIcuMfS+3IeTK7XaN3GQyP
HfF7UzAHzTu5Q5JhfpHbauOOKCkTmF4sKPfx9yIeGE75sb/SovMXw0Ik0EcAcmbU
ZgEJAhBplpG4gl1nvD0kE/gfK6hcYITXfbsi0zbMxK5IMR4k3443sOZ2lIV29DSz
gbBTO1kY0JmT0lKw9AQy+XWgSm9J8KUsyZdlrOuDyuUJ4HyccNL7Kne7fxxIA9KG
mBegvVG/aw==
=YNXS
-----END PGP MESSAGE-----
fp: 0BC4F8FF51F3167F06683FFB19008821C072983E
unencrypted_suffix: _unencrypted
version: 3.8.1

Binary file not shown.

49
secrets/ddclient.yaml Normal file
View file

@ -0,0 +1,49 @@
ddclient:
ddclient-password: ENC[AES256_GCM,data:dAYjEwzbJSSTLpTaDBdC7k9IyYjIx7YKgOr4wewpfJc=,iv:s5cUiHl4QBtxYJin0iFtuba1FWCdxCNej7igyd12ayw=,tag:3Zzd5Tmo44sD800rjlWEKg==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2024-10-26T19:47:13Z"
mac: ENC[AES256_GCM,data:4Z20ynsDSJKXyeXkc/QnW6oqRZ1LCsQYrZhKG2Bv0S+e8SANFGUuGUCTszU2MXvjDVyQFmd2m8PsXJ7ATHBZr1oLLXcGZE82Yk01Vyl0GlgfmVFmaTEqPvGX/iug4QE9O+w2P7S22vhFrXuyz8FA1VUzy4ae8zw3MKfl/kIFdWA=,iv:cYh1Z3KR3cMZUolF9Y9Z39DSVI0k/bWUX+EgO3p2YDU=,tag:e5hR9pilQ7Yh095Plp6oRw==,type:str]
pgp:
- created_at: "2024-11-13T23:45:29Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQEMAwXRN+4RS6LCAQgAqAeI1oNLpmJgDaDx1fzOpUIEDAzNyw2f/OHYX6mnK9g5
wXcRE5gMmrzTShLXmldwhNeQHoOc1F4kusZsMSpU3Rpj3L4OhhHb3KhWQ7iVfn6d
XMzWSHMsKvmpjcPRxbt7utuh80XqRhUJeW704ScwGSrejCTMfUFZr3X3C2cICVoc
eMj7AohgJ6PD/pJhsW6Ln+CINXC0ed+6mH1kg76cp1RmZ7rtYbnIyGeC74qxNjGT
JQa4bw3Ywj+y8H3K7/FGay44ZQ6Y+fja9FsRGMq4ZReOLs0eT0YGb2CibhLzZUJM
IxU42wp3DTKb+oF/0lZsTFisQ5yvYjHOsT0qH2kS6dRmAQkCEGzhyzQarGgqOx0h
E2/NiPZ7KdOq2YjhVv2hKTqQuKHZ0mrteZXB0rMltYdNkHykeazYRmwaw/gGI5ap
EqC48Jm+gp0BxgIH46gchl/uZVTzmS2KyPmUNYkGqXAi7WjkYASi
=zCFi
-----END PGP MESSAGE-----
fp: 06321D7F20335A7E08595BA905D137EE114BA2C2
- created_at: "2024-11-13T23:45:29Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMAxkAiCHAcpg+ARAAmIN8Tt0Ea0Y6i1vaECocyWeLAXrwXAXGcp4UevAFE22i
WFnPFDpKWFyUwSSDvXJ45rFdPtLOM4KdyPHaEunryx9bSpWFohuil5nxK0QM+mvX
Mfrs92mRs0ZUdDdBAzVZZXfzeb3hywKhCHS3wobWW28/35y4xt9HGxtoWUS+L3WX
63NpFkgG07JeZ6AtVTxdGcdogFD4jKOU7HAZZbc8GO4rvZg+0+8kBiU5DJbjE0Rb
i2l70+AO14qw9NH5dM07RFnBcrwMMAifr1Q3K7FTSYIT5XYV3yGLe7timWUJtJt5
vrJteROcYuMlCU9VUHfo4acHZcksEHpqC2aH8HCN2m5lTExnmOufQqaRkSVPd9U4
rde6Qg0gS3LzaQs4hcMPtYSLeeO7Pt8QrNXtERKhQ1WVSt2L0z0w7QFVgwqFtKeH
S0tFZDg9nEqlf85Nxjo0bZIOkiamCzhNOLPE1KNqXTcxM/QpXZtZNdFUJSJKJOzr
J2w9FuQYyhHl7HMKoj9jK5SJe0Xe7WINBMFIBo0ezQv9giczp/YtD1j8NS3tEa5y
cnm/aBui3upEZnQn6XG5otOx+Yrlrb9VVCZXP/8vgFW4dpRuKaR090d3Zf4g8bHw
2oZiGPsrjSvKPQ1ewmgs/4oSI0YvJvQfSK62a/lIHC1mrhhGPbVnm8dTfhqO7xHU
ZgEJAhB6JfUjkcKhJFU3tmZz0kgC5qxrioOuPuzBDPuF2jhEQ3uL/FUBGsm/xSR3
Exm8pMvqpHInzcNZVlNnJE+cN/qW5num4YkU0FWiaEWmfl0Nu1Aw7JBITF7ft93v
SMQonXoFCQ==
=rQsJ
-----END PGP MESSAGE-----
fp: 0BC4F8FF51F3167F06683FFB19008821C072983E
unencrypted_suffix: _unencrypted
version: 3.9.1

50
secrets/wireguard.yaml Normal file
View file

@ -0,0 +1,50 @@
wireguard:
server-private-key: ENC[AES256_GCM,data:Q1zWInF562M0EUePbj6mXC4794EekBX+MGjgiuNUhYyFODI6NMZPgvUA7Wg=,iv:PEkVReP8fRD6tNXi9jfuns9BR1hAVDZDJ9XwaJm4P6M=,tag:KJxaPgZjdJ70Il3sJWJu7Q==,type:str]
server-public-key: ENC[AES256_GCM,data:6QenG4zAJUZNmnS168SbmDSgfKr9eiFSXPDZhG6yVAtaRZnq3CHM0AHOo2U=,iv:fWckJlgDfiluOfGUXLJ5oN/PAjFfKrsnH++ABYTubbQ=,tag:bh0Ad+Q0nxNRk1ip3WyLDw==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2024-11-16T04:26:17Z"
mac: ENC[AES256_GCM,data:YkZbA5S8icRLxgxrDmeM35WRsX69bY5+IonD+4LDDGCVsOH8JA8jW7i9pxbWBdcLf4RktAGWcoBDx6KD47588AJNs1vWounw+9Q9VnfZSQbUDJzrUe0gUxNYsWhGv5E1KnCxNPWuwr3/NatYvmtHuGKTnB5k9LHfu74ijqPeBp4=,iv:FDZcKoeKy0/ruWLFCyolIcj7Glz0eP7SbADj1AcNdX4=,tag:1RZfJ0YXpBafnfI0IgZM0A==,type:str]
pgp:
- created_at: "2024-11-16T04:22:14Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQEMAwXRN+4RS6LCAQgApK6la0ZtWfwFHz8sIiKpX3aE6tblG+V0L+/Ju0eQPopz
hInbCSIRdvBHV6xuYl+8KvtGfur170Gz9DVNZ6mdVudN+32GqX31l8LzCVVn/iRY
9QHkB6wY+3IggFqAZY9XtHU5MXgtH+27i1RzGp2Z8WIxw79649hUz+DaYs71BY+k
GV4EqN8jp6KmzMC9Omdz532sTTukyvLC3KY7gPUSRdJsrt5U+gs2n1JwoXlwIrdm
ieVAgJE+2KaAYpHjRskH3OZBBIbj0TWhQb92FBgzYP5kYR/Mw4gu3DWBLR8nvq4m
UHCAanV71H/GQWcZUE92b5PM6MMuNqLi/7bfBstGuNRoAQkCEMdijK/jGn9geFtl
xlvd6DwXL0TkWC0aUT3YxEDFl/KHs3dFTXktMRO+C6TBdm8tAuYMZvHDDIne9nQE
KWXOrt5rCsWfLF53OXttdtkoTuUnyl8mdyN8QAQ6ySmGuBrfXLxc6zM=
=K0On
-----END PGP MESSAGE-----
fp: 06321D7F20335A7E08595BA905D137EE114BA2C2
- created_at: "2024-11-16T04:22:14Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMAxkAiCHAcpg+AQ//al96/lbZoXjasxIVCbFpV4UMb9RRDdE0GN7V66hGkxnX
Bqo5DpUuuV4cakFownILu1naIbxrppRurXWG/xa4pUyDJSEx9SS3e1qE8nKtUF7L
FtElYaX7G+v41IpizHYMRLuBspVk8hE3OHvWcd3BfuG4BO0ckd71DFgrhkDa20sw
499KW/SM4ccJEOESBxbntGZDYBJWDZ1M7zncz9mDhDGgd/78rPVh/I3b55hZCp0m
Nd92IxFhIFURGjTWAnVtSdEYsHOzli4UhzDNhlgQV4mFH7UDN4KLB3YZF4+hmKqq
Xq8amebGMwTTgs+svzrzOUPsDCgQS47VK8T+8UMIUsfkc3D7FMtiE9la+Io0A5LZ
k6Getq2FZuqKtBlg2Ypcsj33mSMDRzvYjiIC8T2gbEiPtTb9k2vYMYKdWo2FO1nk
HrEMtWwb8wgDPCcZRCL8LRl5IAcGWaHxFgxomaODKG5u5xHuTCpBwGkL5vHJSYyq
a4zcK8qNJMxGhQuVUFiSS5z4M4r/py8vNNA1AI+dFKqEro1uR+msGN9YL3BWNdYu
e7jWOAElbLa3J2nNmZk8NCuVk9Jww0dlT5Urrv07g6k//LIt2TTpU/4SCyjB34vm
wfjvHSFJM+zSnzuDPCRLFC+ccA4TmGNVwTW/T8EbT/4jRI2mOjdQ08Ba6Jf9pCvU
aAEJAhAqEAY20AY62A07hBZHnchG0ihYgM9k5crmgYDpYm5PQg1skoeAJS76rg9r
HG+IqHquvY5bp8gKOoCapBxb1XRh9w6xdiXpMqFd0oZxzq9XZGvyqm0KByhfWpMO
tRV5IxAvcAAH
=bEBX
-----END PGP MESSAGE-----
fp: 0BC4F8FF51F3167F06683FFB19008821C072983E
unencrypted_suffix: _unencrypted
version: 3.9.1

1
version.nix Normal file
View file

@ -0,0 +1 @@
{ version = "0.0.1"; released = false; }