Skip to content
This repository was archived by the owner on Nov 10, 2025. It is now read-only.

dafitt/schallernetz

Repository files navigation

!Github: This repository is a representation of my server configuration, intended for viewing only. It will not work because secrets have been filtered out.

🚧🏚️

This project has been migrated to clan.lol: https://git.clan.lol/dafitt/schallerclan

Our Snowfall🌨️🍂 NixOS❄️ Network

Overview

Servers Description Software
adguardhome🔗 DNS Blocker. Adguard Home
bitwarden🔗 Password Manager. Vaultwarden
DavidCAL🔗 Calendar & Address book. Radicale
DavidSYNC🔗 File syncronization. Syncthing
forgejo🔗 Private GitHub. Forgejo
haproxy-* Reverse Proxy and Load Balancer. HAProxy
homepage-*🔗 Homepage / Dashboard. homepage
idm🔗 Kanidm is a modern and simple identity management platform written in rust. Kanidm
mail🔗 Mailserver. Simple NixOS Mailserver
MichiSHARE File share. Samba
satisfactory A first-person open-world factory building game. nekowinston/satisfactory-server-flake
searx🔗 Recursive Web Search Engine. SearXNG
unbound Recursive & authoritative DNS. Unbound
uptimekuma🔗 A fancy self-hosted monitoring tool. Uptime Kuma
wireguard-* VPN. WireGuard
Hosts Used for/as
barebonej3160 Gateway, Subnetting, Routing, Firewall, DNS, VPN
minisforumhm80 Always-on host for lightweight containers and experimentation.

Configuration

Network

Some words on networking, since networking is hard.

Read the Networkstructure first.

We need to declare our network with the schallernetz.networking-option. See modules/nixos/networking/default.nix for available options.

My network options like schallernetz.networking.domain, schallernetz.networking.ip6 or schallernetz.networking.subnets must be set the same for every server host in this network. So I would recommend to put those options into a file like e.g. systems/global-configuration.nix and import this file to every server host with imports = [../../global-configuration.nix];.

Physical interfaces: Since physical interfaces are always different, it makes no sense to declare them in a module or globally. So they must be declared in systems/. An example:

systemd.network.networks = {
  "30-enp4s0" = {
    matchConfig.Name = "enp4s0";
    linkConfig.RequiredForOnline = "enslaved";
    vlan = [ "server-vlan" "dmz-vlan" ]; # tagged
    networkConfig = {
      Bridge = "management"; # untagged
      LinkLocalAddressing = "no";
    };
  };
}

As you can see, you don't configure the network directly on the physical interface, you map the network to the interface via vlan or a bridge. If the server needs to be accessable through a network, we also need to give the associated bridge the desired static IPv6. Here the host can be accessed through the management network:

systemd.network.networks = {
  "60-management" = with config.schallernetz.networking.subnets.management; {
      # NOTE completion of bridge
    address = [
      "${ip6.prefix}***REMOVED_IPv6***${ip6.suffix}"
      "***REMOVED_IPv6***/64"
    ];
  };
}

The router has of course slightly more bridge configuration than a normal host. See systems/x86_64-linux/barebonej3160/default.nix for an example.

Identity Management (idm)

Finish server setup

In the kanidm documentation flollow 5.3 > Default Admin Accounts:

  • The URL to the kanidm instance is https://idm.<yourMainDomain>.
  • Instead of the docker commands (mentioned in the docs), go into the nixos-container (systemd-nspawn container) idm and run the kanidmd commands directly.

In the server:

ssh admin@<host>.<yourMainDomain>
sudo nixos-container root-login idm

kanidmd recover-account admin
kanidmd recover-account idm_admin

Setup groups and persons declaratively

modules/nixos/containers/idm/persons.nix:

{
  demo_user = {
    displayName = "Demo User"; # required
    legalName = "Demo User";
    mailAddresses = [ "demo.user@example.com" ];
    groups = [ "demo_group" ];
  };
  # ...
}

modules/nixos/containers/idm/groups.nix:

{
  demo_group.members = [ "demo_user" ];
  idm_people_admins.members = [ "demo_user" ];
  # ...
}

All options: search.nixos.org github:oddlama/kanidm-provision

After rebuilding trigger a credential reset for your users: $ kanidm person credential create-reset-token <USERNAME> [ttl_seconds]

How to use the client tool

From the client:

nix develop .#idm

kanidm login --name idm_admin
kanidm self whoami --name anonymous

kanidm logout --name idm_admin

Usage

Update

Take your time. No rush!

  1. (NixOS-version upgrade) At a new NixOS release manually update inputs in flake.nix. e.g. 24.11 -> 25.05.

  2. nix flake update --commit-lock-file

  3. nix flake check to check for new eval warnings & errors

  4. (optional) nixos-rebuild build --flake .#<host> to check for build errors

  5. For every host nixos-rebuild test --flake .#<host> first (especially the router/firewall) and then nixos-rebuild switch --flake .#<host>. Or if errors occur nixos-rebuild boot --flake .#<host>.

Structure

The flakes structure is similar to my dotfiles, but without home-manager.

Network

I decided to build a IPv6 only network (for now) because

  • global IPv4 addresses are expensive to get nowadays
  • it was easier for me to setup (in comparison to NAT in IPv4)
  • it makes a clear subnet structure (one subnet always /64)
  • from some ISPs you don't get an IPv4 anymore.

I implemented the network with systemd-networkd, because "systemd.network should be preferred over networking.interfaces"

Firewall

nftables is used as the firewall implementation. It also seperates the subnets you declare with schallernetz.networking.subnets.

Outbound is allowed by default. Inbound is dropped by default, but can be configured with schallernetz.networking.subnets.<name>.firewall.inputRules.

Containers

Every service is beeing executed in a seperate NixOS Container.

This has some advanages:

  • Every server has its own IP
  • Processes are sealed off from the host system (more security)
  • Can always be started and stopped, independently from the host

DNS

Split-horizon DNS:

My internal nameserver (modules/nixos/containers/unbound) is serving 'unique local addresses' and the bought public nameserver from the dns provider is serving 'global unique addresses'.

Advantages:

  • When internet is down, i can still access my services from within the network.
  • Private DNS:
    • Improved security and control
    • Better performance
    • Privacy

Since my ISP changes the prefix of my global unicast address frequently, I had to use DDNS (dynamic DNS), which I implemented with services.inadyn.

👀, 🏆 and ❤️

About

My servers declared with Snowfallorg-lib

Resources

Stars

Watchers

Forks

Contributors