Self-host your own digital island

added manpage

+2 -1
docs/getting_started.md
···
## Further Information
-
See [docs](../docs/).
+
For a list of options, use `man eilean-configuration.nix`.
+
+4 -4
flake.lock
···
"nodes": {
"nixpkgs": {
"locked": {
-
"lastModified": 1703068421,
-
"narHash": "sha256-WSw5Faqlw75McIflnl5v7qVD/B3S2sLh+968bpOGrWA=",
-
"owner": "NixOS",
+
"lastModified": 1710272261,
+
"narHash": "sha256-g0bDwXFmTE7uGDOs9HcJsfLFhH7fOsASbAuOzDC+fhQ=",
+
"owner": "nixos",
"repo": "nixpkgs",
-
"rev": "d65bceaee0fb1e64363f7871bc43dc1c6ecad99f",
+
"rev": "0ad13a6833440b8e238947e47bea7f11071dc2b2",
"type": "github"
},
"original": {
+9 -2
flake.nix
···
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
-
outputs = { self, nixpkgs, ... }@inputs: {
+
outputs = { self, nixpkgs, ... }: rec {
+
packages = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system:
+
let
+
pkgs = nixpkgs.legacyPackages.${system};
+
in {
+
manpage = import ./man { inherit pkgs system; };
+
});
+
nixosModules.default = {
imports = [
./modules/default.nix
-
({ config, ... }: {
+
({ pkgs, config, ... }: {
nixpkgs.overlays = [ (final: prev: {
mautrix-meta = (prev.callPackage ./pkgs/mautrix-meta.nix { });
}) ];
+42
man/default.nix
···
+
{ pkgs, system, ... }:
+
+
with pkgs;
+
let
+
optionsDoc =
+
let
+
eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
+
inherit system;
+
modules = [
+
../modules/default.nix
+
];
+
};
+
in pkgs.nixosOptionsDoc {
+
options = eval.options;
+
# TODO make sure all options have descriptions
+
warningsAreErrors = false;
+
};
+
+
# Generate the `man eliean.nix` package
+
eilean-configuration-manual =
+
runCommand "eilean-reference-manpage"
+
{ nativeBuildInputs = [
+
buildPackages.installShellFiles
+
buildPackages.nixos-render-docs
+
];
+
allowedReferences = [ "out" ];
+
}
+
''
+
# Generate manpages.
+
mkdir -p $out/share/man/man5
+
# filter to only eilean options
+
cat ${optionsDoc.optionsJSON}/share/doc/nixos/options.json \
+
| ${pkgs.jq}/bin/jq 'with_entries(select(.key | test("^eilean")))' \
+
> eilean-options.json
+
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
+
--revision dev \
+
--header ${./eilean-configuration-nix-header.5} \
+
--footer ${./eilean-configuration-nix-footer.5} \
+
eilean-options.json \
+
$out/share/man/man5/eilean-configuration.nix.5
+
'';
+
in eilean-configuration-manual
+3
man/eilean-configuration-nix-footer.5
···
+
.SH "AUTHORS"
+
.PP
+
Eilean contributors
+17
man/eilean-configuration-nix-header.5
···
+
.TH "EILEAN-CONFIGURATION\&.NIX" "5" "01/01/1980" "Home Manager"
+
.\" disable hyphenation
+
.nh
+
.\" disable justification (adjust text to left margin only)
+
.ad l
+
.\" enable line breaks after slashes
+
.cflags 4 /
+
.SH "NAME"
+
\fIeilean\-configuration\&.nix\fP \- Eilean configuration specification
+
.SH "DESCRIPTION"
+
.sp
+
Self-host your own digital island.
+
.sp
+
Eilean extends the NixOS module system with the following options.
+
.sp
+
.SH "OPTIONS"
+
.PP
+5 -2
modules/default.nix
···
-
{ lib, config, ... }:
+
{ pkgs, lib, config, ... }:
with lib;
···
./matrix/mautrix-messenger.nix
./turn.nix
./headscale.nix
-
./wireguard/server.nix
./wireguard/default.nix
];
···
};
config = {
+
# install manpage
+
environment.systemPackages = [
+
(import ../man/default.nix { inherit pkgs; system = config.nixpkgs.hostPlatform.system; })
+
];
security.acme.defaults.email = "${config.eilean.username}@${config.networking.domain}";
networking.firewall.allowedTCPPorts = mkIf config.services.nginx.enable [
80 # HTTP
+3 -1
modules/headscale.nix
···
enable = mkEnableOption "headscale";
zone = mkOption {
type = types.str;
-
default = "${config.networking.domain}";
+
default = config.networking.domain;
+
defaultText = "config.networking.domain";
};
domain = mkOption {
type = types.str;
default = "headscale.${config.networking.domain}";
+
defaultText = "headscale.$${config.networking.domain}";
};
};
+2 -2
modules/mailserver/default.nix
···
if (ip == "0.0.0.0" || ip == "::")
then "127.0.0.1"
else if isIpv6 ip then "[${ip}]" else ip;
-
defaultText = lib.literalDocBook "computed from <option>config.services.redis.servers.rspamd.bind</option>";
+
defaultText = lib.literalMD "computed from <option>config.services.redis.servers.rspamd.bind</option>";
description = ''
Address that rspamd should use to contact redis.
'';
···
start program = "${pkgs.systemd}/bin/systemctl start rspamd"
stop program = "${pkgs.systemd}/bin/systemctl stop rspamd"
'';
-
defaultText = lib.literalDocBook "see source";
+
defaultText = lib.literalMD "see source";
description = ''
The configuration used for monitoring via monit.
Use a mail address that you actively check and set it via 'set alert ...'.
-6
modules/mailserver/dovecot.nix
···
in
{
config = with cfg; lib.mkIf enable {
-
assertions = [
-
{
-
assertion = junkMailboxNumber == 1;
-
message = "nixos-mailserver requires exactly one dovecot mailbox with the 'special use' flag set to 'Junk' (${builtins.toString junkMailboxNumber} have been found)";
-
}
-
];
services.dovecot2 = {
enable = true;
-4
modules/mailserver/users.nix
···
in {
config = lib.mkIf enable {
# assert that all accounts provide a password
-
assertions = (map (acct: {
-
assertion = (acct.password != null || acct.passwordFile != null);
-
message = "${acct.name} must provide either a password or a password file";
-
}) (lib.attrValues loginAccounts));
# warn for accounts that specify both password and file
warnings = (map
+76 -38
modules/wireguard/default.nix
···
};
in mkOption {
type = with types; attrsOf (submodule hostOps);
+
default = {};
};
};
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [ wireguard-tools ];
-
networking = {
-
# populate /etc/hosts with hostnames and IPs
-
extraHosts = builtins.concatStringsSep "\n" (
-
attrsets.mapAttrsToList (
-
hostName: values: "${values.ip} ${hostName}"
-
) cfg.hosts
-
);
+
networking = mkMerge [
+
{
+
# populate /etc/hosts with hostnames and IPs
+
extraHosts = builtins.concatStringsSep "\n" (
+
attrsets.mapAttrsToList (
+
hostName: values: "${values.ip} ${hostName}"
+
) cfg.hosts
+
);
-
firewall = {
-
allowedUDPPorts = [ 51820 ];
-
checkReversePath = false;
-
};
+
firewall = {
+
allowedUDPPorts = [ 51820 ];
+
checkReversePath = false;
+
};
-
wireguard = {
-
enable = true;
-
interfaces.wg0 = let hostName = config.networking.hostName; in {
-
ips =
-
if cfg.hosts ? hostname then
-
[ "${cfg.hosts."${hostName}".ip}/24" ]
-
else [ ];
-
listenPort = 51820;
-
privateKeyFile = cfg.hosts."${hostName}".privateKeyFile;
-
peers =
-
let
-
serverPeers = attrsets.mapAttrsToList
-
(hostName: values:
-
if values.server then
-
{
-
allowedIPs = [ "10.0.0.0/24" ];
-
publicKey = values.publicKey;
-
endpoint = "${values.endpoint}:51820";
-
persistentKeepalive = values.persistentKeepalive;
-
}
-
else {})
-
cfg.hosts;
-
# remove empty elements
-
cleanedServerPeers = lists.remove { } serverPeers;
-
in mkIf (!cfg.server) cleanedServerPeers;
+
wireguard = {
+
enable = true;
+
interfaces.wg0 = let hostName = config.networking.hostName; in {
+
ips =
+
if cfg.hosts ? hostname then
+
[ "${cfg.hosts."${hostName}".ip}/24" ]
+
else [ ];
+
listenPort = 51820;
+
privateKeyFile = cfg.hosts."${hostName}".privateKeyFile;
+
peers =
+
let
+
serverPeers = attrsets.mapAttrsToList
+
(hostName: values:
+
if values.server then
+
{
+
allowedIPs = [ "10.0.0.0/24" ];
+
publicKey = values.publicKey;
+
endpoint = "${values.endpoint}:51820";
+
persistentKeepalive = values.persistentKeepalive;
+
}
+
else {})
+
cfg.hosts;
+
# remove empty elements
+
cleanedServerPeers = lists.remove { } serverPeers;
+
in mkIf (!cfg.server) cleanedServerPeers;
+
};
};
-
};
-
};
+
}
+
+
(mkIf cfg.server {
+
nat = {
+
enable = true;
+
externalInterface = "enp1s0";
+
internalInterfaces = [ "wg0" ];
+
};
+
firewall = {
+
extraCommands = ''
+
iptables -I FORWARD -i wg0 -o wg0 -j ACCEPT
+
'';
+
trustedInterfaces = [ "wg0" ];
+
};
+
+
wireguard.interfaces.wg0 = {
+
# Route from wireguard to public internet, allowing server to act as VPN
+
postSetup = ''
+
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
+
'';
+
+
postShutdown = ''
+
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -o enp1s0 -j MASQUERADE
+
'';
+
+
# add clients
+
peers = with lib.attrsets;
+
mapAttrsToList (
+
hostName: values: {
+
allowedIPs = [ "${values.ip}/32" ];
+
publicKey = values.publicKey;
+
persistentKeepalive = values.persistentKeepalive;
+
}
+
) cfg.hosts;
+
};
+
})
+
];
};
}
-39
modules/wireguard/server.nix
···
-
{ pkgs, config, lib, ... }:
-
-
let cfg = config.wireguard; in
-
{
-
networking = lib.mkIf (cfg.enable && cfg.server) {
-
nat = {
-
enable = true;
-
externalInterface = "enp1s0";
-
internalInterfaces = [ "wg0" ];
-
};
-
firewall = {
-
extraCommands = ''
-
iptables -I FORWARD -i wg0 -o wg0 -j ACCEPT
-
'';
-
trustedInterfaces = [ "wg0" ];
-
};
-
-
wireguard.interfaces.wg0 = {
-
# Route from wireguard to public internet, allowing server to act as VPN
-
postSetup = ''
-
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
-
'';
-
-
postShutdown = ''
-
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -o enp1s0 -j MASQUERADE
-
'';
-
-
# add clients
-
peers = with lib.attrsets;
-
mapAttrsToList (
-
hostName: values: {
-
allowedIPs = [ "${values.ip}/32" ];
-
publicKey = values.publicKey;
-
persistentKeepalive = values.persistentKeepalive;
-
}
-
) cfg.hosts;
-
};
-
};
-
}