* Updated localDomain config

* Added generic static site config
* Added stub admin page
This commit is contained in:
Ellis Rahhal 2024-12-21 18:29:34 -08:00
parent 06e0f0fc0a
commit 306c15c890
48 changed files with 324 additions and 341 deletions

6
.gitignore vendored
View file

@ -3,5 +3,7 @@ result
*.qcow2
homefree-image
lan-client-image
site/node_modules
site/public
site/default-landing-page/node_modules
site/default-landing-page/public
site/admin/node_modules
site/admin/public

View file

@ -6,17 +6,17 @@
../../profiles/config-editor.nix
../../profiles/git.nix
../../profiles/hardware-configuration.nix
../../profiles/hosting.nix
../../profiles/nixvim.nix
../../profiles/podman.nix
../../profiles/router.nix
# ../../profiles/traffic-shaping.nix
../../profiles/traffic-control.nix
../../services/adguardhome.nix
../../services/admin.nix
../../services/authentik.nix
../../services/backup.nix
../../services/baikal.nix
../../services/caddy.nix
../../services/cryptpad.nix
../../services/ddclient.nix
../../services/dnsmasq.nix
@ -27,6 +27,7 @@
../../services/headscale-ui.nix
../../services/immich.nix
../../services/jellyfin.nix
../../services/landing-page.nix
../../services/linkwarden.nix
../../services/matrix.nix
../../services/mqtt.nix

View file

@ -52,8 +52,20 @@
localDomain = lib.mkOption {
type = lib.types.str;
## @TODO: Should this be "local"?
default = "localdomain";
description = "local lan domain";
default = "lan";
description = ''
local lan domain for internal devices and services.
Default is "lan". Don't choose "local", as this can conflict with Multicast DNS (mDNS) services,
such as Apple's Bonjour/Zeroconf. "local" is also a reserved TLD and some tools and browsers
might trigger cert warnings.
Other common localdomains you can use:
"localdomain"
"home"
"private"
"internal"
'';
};
domain = lib.mkOption {
@ -396,7 +408,7 @@
public = lib.mkOption {
type = lib.types.bool;
default = false;
default = true;
description = "Open to public on WAN port";
};
@ -616,6 +628,12 @@
description = "description of proxy config";
};
rootDomain = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Maps to root domain, i.e. no subdomain. Only one service can set this to true.";
};
subdomains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
@ -635,16 +653,23 @@
};
host = lib.mkOption {
type = lib.types.str;
default = "10.0.0.1";
type = lib.types.nullOr lib.types.str;
default = null;
description = "host name or address of service to proxy";
};
port = lib.mkOption {
type = lib.types.int;
type = lib.types.nullOr lib.types.int;
default = null;
description = "port of service on lan network";
};
static-path = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "path to static files to serve. Do not set host or port if using this.";
};
subdir = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "subdir at which service is served";
@ -674,6 +699,12 @@
default = false;
description = "Whether to enable basic auth headers";
};
extraCaddyConfig = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "custom caddy config";
};
};
backup = {
@ -693,6 +724,14 @@
});
};
admin-page = {
public = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Open to public on WAN port (not recommended)";
};
};
landing-page = {
path = lib.mkOption {
type = lib.types.path;
@ -812,6 +851,10 @@
map getDuplicateOriginal duplicateLowerLabels;
duplicateLabels = findDuplicateLabels config.homefree.service-config;
badServiceConfigs = builtins.filter (entry: (entry.reverse-proxy.host != null || entry.reverse-proxy.port != null) && entry.reverse-proxy.static-path != null) config.homefree.service-config;
badServiceConfigLabels = builtins.map (entry: entry.label) badServiceConfigs;
rootDomainConfigs = builtins.filter (entry: (entry.reverse-proxy.rootDomain == true)) config.homefree.service-config;
rootDomainConfigLabels = builtins.map (entry: entry.label) rootDomainConfigs;
in
[
{
@ -819,6 +862,14 @@
assertion = lib.length duplicateLabels == 0;
message = "Multiple homefree.service-config entries with the same label: ${lib.concatStringsSep ", " duplicateLabels}";
}
{
assertion = lib.length badServiceConfigs == 0;
message = "homefree.service-config contains entries with both a host/port and static-path config; can only specify one: ${lib.concatStringsSep ", " badServiceConfigLabels}";
}
{
assertion = lib.length rootDomainConfigs <= 1;
message = "homefree.service-config contains more than one service with rootDomain = true: ${lib.concatStringsSep ", " rootDomainConfigLabels}";
}
];
warnings =

View file

@ -195,7 +195,7 @@
# Networking
# --------------------------------------------------------------------------------------
networking.search = [ "localdomain" ];
networking.search = [ config.homefree.system.localDomain ];
# --------------------------------------------------------------------------------------
# Base Packages

View file

@ -1,170 +0,0 @@
{ 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
'';
};
}

View file

@ -173,8 +173,9 @@
reverse-proxy = {
enable = true;
subdomains = [ "adguard" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 3000;
public = config.homefree.services.adguard.public;
};

26
services/admin.nix Normal file
View file

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
let
homefree-admin = pkgs.callPackage ../site/admin { };
in
{
## add homefree admin page as a package
nixpkgs.overlays = [
(final: prev: {
homefree-admin = homefree-admin;
})
];
homefree.service-config = [
{
label = "admin";
reverse-proxy = {
enable = true;
subdomains = [ "admin" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
static-path = "${pkgs.homefree-admin}/lib/node_modules/homefree-admin";
public = config.homefree.admin-page.public;
};
}
];
}

View file

@ -40,8 +40,9 @@
reverse-proxy = {
enable = true;
subdomains = [ "authentik" "auth" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 9000;
public = config.homefree.services.authentik.public;
};

View file

@ -45,8 +45,9 @@ in
reverse-proxy = {
enable = true;
subdomains = [ "baikal" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 3007;
public = config.homefree.services.baikal.public;
};

View file

@ -1,21 +1,9 @@
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
let
hostConfig = ''
respond "Hello, world! I am being accessed from {scheme}."
'';
proxiedHostConfig = lib.filter (service-config: service-config.reverse-proxy.enable == true) config.homefree.service-config;
homefree-site = pkgs.callPackage ../site { };
headscale-ui-config = lib.elemAt (lib.filter (service-config: service-config.label == "headscale-ui") config.homefree.service-config) 0;
trimTrailingSlash = s: lib.head (lib.match "(.*[^/])[/]*" s);
in
{
## add homefree default site as a package
nixpkgs.overlays = [
(final: prev: {
homefree-site = homefree-site;
})
];
systemd.services.caddy = {
after = [ "network.target" "network-online.target" "unbound.service" ];
requires = [ "network-online.target" "unbound.service" ];
@ -36,7 +24,9 @@ in
reverse-proxy-config = service-config.reverse-proxy;
http-urls = lib.flatten (lib.map (subdomain: (lib.map (domain: "http://${subdomain}.${domain}") reverse-proxy-config.http-domains)) reverse-proxy-config.subdomains);
https-urls = lib.flatten (lib.map (subdomain: (lib.map (domain: "https://${subdomain}.${domain}") reverse-proxy-config.https-domains)) reverse-proxy-config.subdomains);
urls = http-urls ++ https-urls;
http-urls-root-domain = if reverse-proxy-config.rootDomain == true then (lib.map (domain: "http://${domain}") reverse-proxy-config.http-domains) else [];
https-urls-root-domain = if reverse-proxy-config.rootDomain == true then (lib.map (domain: "https://${domain}") reverse-proxy-config.https-domains) else [];
urls = http-urls ++ https-urls ++ http-urls-root-domain ++ https-urls-root-domain;
host-string = lib.concatStringsSep ", " urls;
in {
name = host-string;
@ -68,106 +58,13 @@ in
rewrite / ${trimTrailingSlash reverse-proxy-config.subdir}{uri}
'' else "")
## @TODO: throw an error if more than one host is using the same port
+ ''
reverse_proxy ${if reverse-proxy-config.ssl == true then "https" else "http"}://${reverse-proxy-config.host}:${toString reverse-proxy-config.port} {
''
+ (if reverse-proxy-config.ssl == true && reverse-proxy-config.ssl-no-verify then ''
transport http {
tls
tls_insecure_skip_verify
}
'' else "")
+ (if reverse-proxy-config.basic-auth == true then ''
header_up X-remote-user {http.auth.user.id}
'' else "")
+
''
}
'';
};
}
) proxiedHostConfig))
{
## Needed so as to host ui and headscale enpoint on separate domains
"http://headscale.${config.homefree.system.domain}, https://headscale.${config.homefree.system.domain}" = {
logFormat = ''
output file ${config.services.caddy.logDir}/access-headscale.log
'';
extraConfig = ''
header {
# Add general security headers
Strict-Transport-Security "max-age=31536000; includeSubdomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
X-XSS-Protection "1; mode=block"
}
reverse_proxy /web* http://10.0.0.1:3009
reverse_proxy * http://10.0.0.1:8087
bind 10.0.0.1 ${config.homefree.system.domain}
'';
};
}
# {
# ## Needed so as to host ui and headscale enpoint on separate domains
# "https://headscale.${config.homefree.system.domain}" = {
# logFormat = ''
# output file ${config.services.caddy.logDir}/access-headscale.log
# '';
# extraConfig = ''
# @headscale-options {
# host headscale.${config.homefree.system.domain}
# method OPTIONS
# }
# @headscale-other {
# host headscale.${config.homefree.system.domain}
# }
# handle @headscale-options {
# header {
# Access-Control-Allow-Origin https://headscale-ui.${config.homefree.system.domain}
# Access-Control-Allow-Headers *
# Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE"
# }
# respond 204
# }
# handle @headscale-other {
# reverse_proxy http://10.0.0.1:8087 {
# header_down Access-Control-Allow-Origin https://headscale-ui.${config.homefree.system.domain}
# header_down Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE"
# header_down Access-Control-Allow-Headers *
# }
# ''
# + (if headscale-ui-config.public == false then ''
# bind 10.0.0.1
# '' else ''
# bind 10.0.0.1 ${config.homefree.system.domain}
# '')
# + ''
# }
# '';
# };
# }
## Static root site
{
"http://localhost, https://localhost, https://${config.homefree.system.domain}, https://www.${config.homefree.system.domain}" = {
logFormat = ''
output file ${config.services.caddy.logDir}/access-landing-page.log
'';
extraConfig = ''
bind 10.0.0.1 ${config.homefree.system.domain}
root * ${config.homefree.landing-page.path}
+ (if reverse-proxy-config.static-path != null then ''
root * ${reverse-proxy-config.static-path}
file_server
# Enable Gzip compression
encode gzip
# Matrix Synapse settings
respond /.well-known/matrix/server `{"m.server": "matrix.${config.homefree.system.domain}:443"}`
# respond /.well-known/matrix/client `{"m.homeserver":{"base_url":"https://matrix.${config.homefree.system.domain}"},"m.identity_server":{"base_url":"https://identity.${config.homefree.system.domain}"}}`
respond /.well-known/matrix/client `{"m.homeserver":{"base_url":"https://matrix.${config.homefree.system.domain}"}}`
# HTML files - No caching to ensure fresh content
@html {
file
@ -222,20 +119,26 @@ in
Referrer-Policy "strict-origin-when-cross-origin"
X-XSS-Protection "1; mode=block"
}
'';
'' else (''
reverse_proxy ${if reverse-proxy-config.ssl == true then "https" else "http"}://${reverse-proxy-config.host}:${toString reverse-proxy-config.port} {
''
+ (if reverse-proxy-config.ssl == true && reverse-proxy-config.ssl-no-verify then ''
transport http {
tls
tls_insecure_skip_verify
}
'' else "")
+ (if reverse-proxy-config.basic-auth == true then ''
header_up X-remote-user {http.auth.user.id}
'' else "")
+
''
}
''))
+ (if reverse-proxy-config.extraCaddyConfig != null then reverse-proxy-config.extraCaddyConfig else "");
};
}
) 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;
# };
};
}

View file

@ -20,9 +20,10 @@
label = "cryptpad";
reverse-proxy = {
enable = true;
subdomains = [ "cryptpad" "cryptpad-sandbox" "cryptpad-ui" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
subdomains = [ "docs" "cryptpad" "cryptpad-sandbox" "cryptpad-ui" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 3004;
public = config.homefree.services.cryptpad.public;
};

View file

@ -204,8 +204,9 @@ in
reverse-proxy = {
enable = true;
subdomains = [ "frigate" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 8971;
ssl = true;
ssl-no-verify = true;

View file

@ -30,8 +30,9 @@
reverse-proxy = {
enable = true;
subdomains = [ "git" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 3001;
public = config.homefree.services.gitea.public;
};

View file

@ -26,12 +26,57 @@
# reverse-proxy = {
# enable = true;
# subdomains = [ "headscale-ui" ];
# http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
# http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
# https-domains = [ config.homefree.system.domain ];
# host = "10.0.0.1";
# port = 3009;
# public = config.homefree.services.headscale-ui.public;
# };
# }
# ] else [];
## Reference of what caddy config might look like:
# let
# headscale-ui-config = lib.elemAt (lib.filter (service-config: service-config.label == "headscale-ui") config.homefree.service-config) 0;
# in
# {
# ## Needed so as to host ui and headscale enpoint on separate domains
# "https://headscale.${config.homefree.system.domain}" = {
# logFormat = ''
# output file ${config.services.caddy.logDir}/access-headscale.log
# '';
# extraConfig = ''
# @headscale-options {
# host headscale.${config.homefree.system.domain}
# method OPTIONS
# }
# @headscale-other {
# host headscale.${config.homefree.system.domain}
# }
# handle @headscale-options {
# header {
# Access-Control-Allow-Origin https://headscale-ui.${config.homefree.system.domain}
# Access-Control-Allow-Headers *
# Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE"
# }
# respond 204
# }
# handle @headscale-other {
# reverse_proxy http://10.0.0.1:8087 {
# header_down Access-Control-Allow-Origin https://headscale-ui.${config.homefree.system.domain}
# header_down Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE"
# header_down Access-Control-Allow-Headers *
# }
# ''
# + (if headscale-ui-config.public == false then ''
# bind 10.0.0.1
# '' else ''
# bind 10.0.0.1 ${config.homefree.system.domain}
# '')
# + ''
# }
# '';
# };
# }
}

View file

@ -69,17 +69,21 @@ in
];
};
# homefree.service-config = if config.homefree.services.headscale.enable == true then [
# {
# label = "headscale";
# reverse-proxy = {
# enable = true;
# subdomains = [ "headscale" ];
# http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
# https-domains = [ config.homefree.system.domain ];
# port = 8087;
# public = config.homefree.services.headscale.public;
# };
# }
# ] else [];
homefree.service-config = if config.homefree.services.headscale.enable == true then [
{
label = "headscale";
reverse-proxy = {
enable = true;
subdomains = [ "headscale" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 8087;
public = config.homefree.services.headscale.public;
extraCaddyConfig = ''
reverse_proxy /web* http://10.0.0.1:3009
'';
};
}
] else [];
}

View file

@ -1,4 +1,4 @@
{ config, inputs, pkgs, ... }:
{ config, pkgs, ... }:
let
# automations = import ./automations.nix;
@ -145,19 +145,19 @@ in
switch = {
name = "msi_desktop";
unique_id = "msi-desktop";
command_state = "ping -c 2 -i 1 msi-desktop.localdomain";
command_state = "ping -c 2 -i 1 msi-desktop.${config.homefree.system.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'";
command_off = "ssh -i /config/certs/msi_desktop-id_rsa -o ConnectTimeout=30 -o 'StrictHostKeyChecking=no' erahhal@msi-desktop.${config.homefree.system.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'";
command_on = "ssh -i /config/certs/msi_desktop-id_rsa -o ConnectTimeout=30 -o 'StrictHostKeyChecking=no' erahhal@msi-desktop.${config.homefree.system.localDomain} 'cd \"/mnt/c/Program Files (x86)/Steam/steamapps/common/SteamVR/bin/win64\"; ./vrstartup.exe'";
};
}
];
@ -417,7 +417,7 @@ in
reverse-proxy = {
enable = true;
subdomains = [ "homeassistant" "ha" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
port = 8123;
public = config.homefree.services.homeassistant.public;

View file

@ -23,8 +23,9 @@
reverse-proxy = {
enable = true;
subdomains = [ "photos" "immich" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = config.services.immich.port;
public = config.homefree.services.immich.public;
};

View file

@ -39,9 +39,10 @@
label = "jellyfin";
reverse-proxy = {
enable = true;
subdomains = [ "jellyfin" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
subdomains = [ "jellyfin" "video" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 8096;
public = config.homefree.services.jellyfin.public;
};

33
services/landing-page.nix Normal file
View file

@ -0,0 +1,33 @@
{ config, pkgs, ... }:
let
homefree-site = pkgs.callPackage ../site/default-landing-page { };
in
{
## add homefree default site as a package
nixpkgs.overlays = [
(final: prev: {
homefree-site = homefree-site;
})
];
homefree.service-config = [
{
label = "landing-page";
reverse-proxy = {
enable = true;
rootDomain = true;
subdomains = [ "www" "homefree" ];
http-domains = [ config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
static-path = config.homefree.landing-page.path;
public = true;
extraCaddyConfig = ''
# Matrix Synapse settings
respond /.well-known/matrix/server `{"m.server": "matrix.${config.homefree.system.domain}:443"}`
# respond /.well-known/matrix/client `{"m.homeserver":{"base_url":"https://matrix.${config.homefree.system.domain}"},"m.identity_server":{"base_url":"https://identity.${config.homefree.system.domain}"}}`
respond /.well-known/matrix/client `{"m.homeserver":{"base_url":"https://matrix.${config.homefree.system.domain}"}}`
'';
};
}
];
}

View file

@ -26,9 +26,10 @@ in
label = "linkwarden";
reverse-proxy = {
enable = true;
subdomains = [ "linkwarden" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
subdomains = [ "linkwarden" "links" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 3005;
public = config.homefree.services.linkwarden.public;
};

View file

@ -203,8 +203,9 @@
reverse-proxy = {
enable = true;
subdomains = [ "matrix" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 8008;
public = config.homefree.services.matrix.public;
# basic-auth = true;

View file

@ -146,8 +146,9 @@ in
reverse-proxy = {
enable = true;
subdomains = [ "nextcloud" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 3010;
subdir = "/nextcloud/";
public = config.homefree.services.nextcloud.public;

View file

@ -24,8 +24,9 @@
reverse-proxy = {
enable = true;
subdomains = [ "radicale" "dav" "webdav" "caldav" "carddav" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 5232;
public = config.homefree.services.radicale.public;
# basic-auth = true;

View file

@ -215,7 +215,7 @@ in
# range-lan = {
# start = "10.0.0.200";
# end = "10.0.0.254";
# domain = "localdomain";
# domain = config.homefree.system.localDomain;
# };
forward-zone = [

View file

@ -15,8 +15,9 @@
reverse-proxy = {
enable = true;
subdomains = [ "unifi" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 8443;
ssl = true;
ssl-no-verify = true;

View file

@ -20,8 +20,9 @@ in
reverse-proxy = {
enable = true;
subdomains = [ "vaultwarden" ];
http-domains = [ "homefree.${config.homefree.system.localDomain}" ];
http-domains = [ "homefree.lan" config.homefree.system.localDomain ];
https-domains = [ config.homefree.system.domain ];
host = "10.0.0.1";
port = 8222;
public = config.homefree.services.vaultwarden.public;
};

18
site/admin/default.nix Normal file
View file

@ -0,0 +1,18 @@
{ pkgs, buildNpmPackage, ... }:
buildNpmPackage {
name = "admin";
src = ./.;
# npmDepsHash = lib.fakeHash;
npmDepsHash = "sha256-C7dFRyWooP920Ei4JeK10fL93zJN5XQu85+Tz6oU0fA=";
# The prepack script runs the build script, which we'd rather do in the build phase.
npmPackFlags = [
"--legacy-peer-deps"
"--loglevel=verbose"
];
makeCacheWritable = true;
nodejs = pkgs.nodejs_22;
}

11
site/admin/index.html Normal file
View file

@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>HomeFree Admin</title>
<script type="module" src="./index.js"></script>
</head>
<body>
<homefree-admin></homefree-admin>
</body>
</html>

9
site/admin/index.js Normal file
View file

@ -0,0 +1,9 @@
import XElement from '/node_modules/@netflix/x-element/x-element.js';
class HomeFreeAdmin extends XElement {
static template(html) {
return () => html`<span>HomeFree Admin</span>`;
}
}
customElements.define('homefree-admin', HomeFreeAdmin);

24
site/admin/package-lock.json generated Normal file
View file

@ -0,0 +1,24 @@
{
"name": "homefree-admin",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homefree-admin",
"version": "0.0.1",
"dependencies": {
"@netflix/x-element": "github:netflix/x-element"
}
},
"node_modules/@netflix/x-element": {
"version": "1.1.2",
"resolved": "git+ssh://git@github.com/netflix/x-element.git#aaefbba9b9b754c52ef292234ef751705908b888",
"license": "Apache-2.0",
"engines": {
"node": "22.11.0",
"npm": "10.9.0"
}
}
}
}

12
site/admin/package.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "homefree-admin",
"version": "0.0.1",
"private": true,
"type": "module",
"dependencies": {
"@netflix/x-element": "github:netflix/x-element"
},
"scripts": {
"build": ""
}
}

View file

@ -1,7 +1,7 @@
{ buildNpmPackage, lib, ... }:
{ buildNpmPackage, ... }:
buildNpmPackage {
name = "site";
name = "default-landing-page";
src = ./.;
# npmDepsHash = lib.fakeHash;
npmDepsHash = "sha256-uOLu/MrHS+Et9yUyZO66ANRCzG15hki+7oSTqw4eyT0=";