Compare commits

2 Commits

Author SHA1 Message Date
mjallen18
2c6ea8b9a4 samba 2026-03-17 14:18:57 -05:00
mjallen18
cf40c72e7e reverse proxy 2026-03-17 14:05:50 -05:00
6 changed files with 103 additions and 168 deletions

View File

@@ -50,15 +50,19 @@ rec {
defaultConfig = { defaultConfig = {
# Caddy reverse proxy: when reverseProxy.enable = true, contribute this # Caddy reverse proxy: when reverseProxy.enable = true, contribute this
# service's virtual host block to the Caddy config. The TLS wildcard # service's named-matcher block into the shared wildcard virtual host.
# cert is handled via a (cloudflare_tls) snippet defined in globalConfig. # The TLS block stays in the caddy module itself; all services merge
# services.caddy.virtualHosts.${fqdn} = lib.mkIf cfg.reverseProxy.enable { # their handle blocks into the same "*.${domain}" extraConfig via the
# extraConfig = '' # lines type (which concatenates automatically).
# import cloudflare_tls services.caddy.virtualHosts."*.${cfg.reverseProxy.domain}" = lib.mkIf cfg.reverseProxy.enable {
# reverse_proxy ${upstreamUrl} extraConfig = ''
# ${cfg.reverseProxy.extraCaddyConfig} @${name} host ${fqdn}
# ''; handle @${name} {
# }; reverse_proxy ${upstreamUrl}
${cfg.reverseProxy.extraCaddyConfig}
}
'';
};
# Open firewall # Open firewall
networking.firewall = lib.mkIf cfg.openFirewall { networking.firewall = lib.mkIf cfg.openFirewall {
@@ -112,11 +116,19 @@ rec {
# # "d ${cfg.configDir}/server-files 0775 ${name} ${name} - -" # # "d ${cfg.configDir}/server-files 0775 ${name} ${name} - -"
# # "d ${cfg.configDir}/user-files 0775 ${name} ${name} - -" # # "d ${cfg.configDir}/user-files 0775 ${name} ${name} - -"
# ]; # ];
} };
// moduleConfig;
in in
{ lib, ... }: { lib, ... }:
{ {
imports = [
# defaultConfig and moduleConfig are kept as separate inline modules so
# the NixOS module system handles all merging (mkIf, mkForce, mkMerge,
# etc.) correctly, rather than merging raw attrsets with // or
# recursiveUpdate which can silently clobber mkIf wrappers.
{ config = lib.mkIf cfg.enable defaultConfig; }
{ config = lib.mkIf cfg.enable moduleConfig; }
];
options.${namespace}.${domain}.${name} = lib.mkOption { options.${namespace}.${domain}.${name} = lib.mkOption {
type = lib.types.submodule { type = lib.types.submodule {
options = { options = {
@@ -167,8 +179,6 @@ rec {
}; };
default = { }; default = { };
}; };
config = lib.mkIf cfg.enable defaultConfig;
}; };
# container # container

View File

@@ -42,78 +42,6 @@ let
tls { tls {
dns cloudflare {$CLOUDFLARE_DNS_API_TOKEN} dns cloudflare {$CLOUDFLARE_DNS_API_TOKEN}
} }
@authentik host authentik.mjallen.dev
handle @authentik {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.authentik.port}
}
@cache host cache.mjallen.dev
handle @cache {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.attic.port}
}
@cloud host cloud.mjallen.dev
handle @cloud {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.nextcloud.port} {
header_up Host {upstream_hostport}
}
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Robots-Tag "noindex, nofollow"
}
}
@gitea host gitea.mjallen.dev
handle @gitea {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.gitea.port}
}
@homeassistant host hass.mjallen.dev
handle @homeassistant {
reverse_proxy http://nuc-nixos.local:8123
}
@immich host immich.mjallen.dev
handle @immich {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.immich.port}
}
@jellyfin host jellyfin.mjallen.dev
handle @jellyfin {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.jellyfin.port}
}
@jellyseerr host jellyseerr.mjallen.dev
handle @jellyseerr {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.jellyseerr.port}
}
@lubelogger host lubelogger.mjallen.dev
handle @lubelogger {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.lubelogger.port}
}
@matrix host matrix.mjallen.dev
handle @matrix {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.matrix.port}
}
@ntfy host ntfy.mjallen.dev
handle @ntfy {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.ntfy.port}
}
@office host office.mjallen.dev
handle @office {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.collabora.port}
}
@termix host termix.mjallen.dev
handle @termix {
reverse_proxy http://10.0.1.3:${toString config.${namespace}.services.termix.port}
}
''; '';
}; };
}; };

View File

@@ -7,52 +7,44 @@
with lib; with lib;
let let
cfg = config.${namespace}.samba; cfg = config.${namespace}.samba;
sambaShares =
makeShare =
name: share:
let let
make = isTimeMachine = share.enableTimeMachine;
name: share: baseAttrs = {
nameValuePair "${name}" { path = share.sharePath;
path = share.sharePath; browseable = if share.browseable then "yes" else "no";
public = if share.enableTimeMachine then "no" else "yes"; "read only" = if share.readOnly then "yes" else "no";
browseable = if share.browseable then "yes" else "no"; "guest ok" = if share.guestOk then "yes" else "no";
writable = "yes"; "create mask" = share.createMask;
"force group" = "jallen-nas"; "directory mask" = share.directoryMask;
"read only" = if share.readOnly then "yes" else "no"; }
"guest ok" = if share.guestOk then "yes" else "no"; // optionalAttrs (cfg.forceGroup != "") { "force group" = cfg.forceGroup; };
"create mask" = share.createMask;
"directory mask" = share.directoryMask; timeMachineAttrs = {
"fruit:aapl" = if share.enableTimeMachine then "yes" else "no"; "vfs objects" = "catia fruit streams_xattr";
"fruit:time machine" = if share.enableTimeMachine then "yes" else "no"; "fruit:aapl" = "yes";
"vfs objects" = "catia fruit streams_xattr"; "fruit:time machine" = "yes";
"fruit:time machine max size" = share.timeMachineMaxSize; }
}; // optionalAttrs (share.timeMachineMaxSize != "") {
"fruit:time machine max size" = share.timeMachineMaxSize;
};
in in
mapAttrs' make cfg.shares; nameValuePair name (baseAttrs // optionalAttrs isTimeMachine timeMachineAttrs);
sambaShares = mapAttrs' makeShare cfg.shares;
in in
{ {
imports = [ ./options.nix ]; imports = [ ./options.nix ];
config = mkIf cfg.enable { config = mkIf cfg.enable {
# make shares visible for Windows clients # Make shares visible for Windows clients via WS-Discovery
services.samba-wsdd = { services.samba-wsdd = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
}; };
services.netatalk = {
enable = cfg.enableTimeMachine;
settings = {
time-machine = {
path = cfg.timeMachinePath;
"valid users" = "whoever";
"time machine" = cfg.enableTimeMachine;
};
};
};
networking.firewall.enable = true;
networking.firewall.allowPing = true;
services.samba = { services.samba = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
@@ -60,17 +52,19 @@ in
nmbd.enable = true; nmbd.enable = true;
settings = { settings = {
global = { global = {
"workgroup" = "WORKGROUP"; workgroup = "WORKGROUP";
"server string" = "Jallen-NAS"; "server string" = config.networking.hostName;
"netbios name" = "Jallen-NAS"; "netbios name" = config.networking.hostName;
"security" = "user"; security = "user";
#"use sendfile" = "yes"; "hosts allow" = cfg.hostsAllow;
#"max protocol" = "smb2";
# note: localhost is the ipv6 localhost ::1
"hosts allow" = "10.0.1. 127.0.0.1 localhost";
"hosts deny" = "0.0.0.0/0"; "hosts deny" = "0.0.0.0/0";
"guest account" = "nobody"; "guest account" = "nobody";
"map to guest" = "bad user"; "map to guest" = "bad user";
}
// optionalAttrs cfg.enableTimeMachine {
# Required globals for macOS Time Machine over SMB3
"fruit:aapl" = "yes";
"fruit:model" = "MacSamba";
}; };
} }
// sambaShares; // sambaShares;

View File

@@ -2,69 +2,88 @@
with lib; with lib;
{ {
options.${namespace}.samba = { options.${namespace}.samba = {
enable = mkEnableOption "nas samba service"; enable = mkEnableOption "NAS samba service";
autoStart = mkOption { hostsAllow = mkOption {
type = types.bool; type = types.str;
default = true; default = "127.0.0.1 localhost";
description = "Space-separated list of hosts/subnets allowed to connect (e.g. \"10.0.1. 127.0.0.1 localhost\").";
}; };
forceGroup = mkOption {
type = types.str;
default = "";
description = "If non-empty, force all file creation to use this group.";
};
enableTimeMachine = mkOption { enableTimeMachine = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = "Whether to enable Time Machine support via SMB3.";
}; };
timeMachinePath = mkOption { timeMachinePath = mkOption {
type = types.str; type = types.str;
default = ""; default = "";
}; description = "Path to the Time Machine backup directory (used as the default Time Machine share path).";
hostsAllow = mkOption {
type = types.str;
default = "";
}; };
shares = mkOption { shares = mkOption {
type = types.attrsOf ( type = types.attrsOf (
types.submodule { types.submodule {
options = { options = {
public = mkOption {
type = types.bool;
default = false;
};
sharePath = mkOption { sharePath = mkOption {
type = types.str; type = types.str;
default = ""; default = "";
description = "Absolute path on disk to expose as this share.";
}; };
readOnly = mkOption { readOnly = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = "Whether the share is read-only.";
}; };
browseable = mkOption { browseable = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
description = "Whether the share appears in network browse lists.";
}; };
guestOk = mkOption { guestOk = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
description = "Whether unauthenticated (guest) access is permitted.";
}; };
createMask = mkOption { createMask = mkOption {
type = types.str; type = types.str;
default = "0774"; default = "0664";
description = "Permission mask applied to newly created files.";
}; };
directoryMask = mkOption { directoryMask = mkOption {
type = types.str; type = types.str;
default = "0775"; default = "0775";
description = "Permission mask applied to newly created directories.";
}; };
enableTimeMachine = mkOption { enableTimeMachine = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = "Whether this share is a Time Machine target.";
}; };
timeMachineMaxSize = mkOption { timeMachineMaxSize = mkOption {
type = types.str; type = types.str;
default = "0K"; default = "";
description = "Maximum size for this Time Machine share (e.g. \"1T\"). Empty means unlimited.";
}; };
}; };
} }
); );
default = { }; default = { };
description = "Attribute set of Samba shares to export.";
}; };
}; };
} }

View File

@@ -21,6 +21,7 @@ let
extraOptions = [ "--device=/dev/dri" ]; extraOptions = [ "--device=/dev/dri" ];
volumes = [ volumes = [
"${cfg.configDir}/tunarr:/config/tunarr" "${cfg.configDir}/tunarr:/config/tunarr"
"${cfg.configDir}/tunarr:/root/.local/share/tunarr"
"${cfg.dataDir}/movies:/libraries/movies" "${cfg.dataDir}/movies:/libraries/movies"
"${cfg.dataDir}/tv:/libraries/tv" "${cfg.dataDir}/tv:/libraries/tv"
"${cfg.configDir}/transcode:/transcode" "${cfg.configDir}/transcode:/transcode"

View File

@@ -162,6 +162,7 @@ in
3001 3001
3333 3333
5201 # iperf 5201 # iperf
5432 # postgresql
8400 8400
9200 # elasticsearch / attic 9200 # elasticsearch / attic
9233 9233
@@ -197,42 +198,24 @@ in
# ################################################### # ###################################################
samba = { samba = {
enable = false; enable = true;
hostsAllow = "10.0.1."; hostsAllow = "10.0.1. 127.0.0.1 localhost";
forceGroup = "jallen-nas";
enableTimeMachine = true; enableTimeMachine = true;
timeMachinePath = "/media/nas/main/timemachine"; timeMachinePath = "/media/nas/main/timemachine";
shares = { shares = {
"3d_printer" = { "3d_printer".sharePath = "/media/nas/main/documents/3d-models";
public = true; Backup.sharePath = "/media/nas/main/backup";
sharePath = "/media/nas/main/3d_printer"; Documents.sharePath = "/media/nas/main/documents";
}; isos.sharePath = "/media/nas/main/documents/isos";
Backup = { app_data.sharePath = "/media/nas/main/appdata";
public = true;
sharePath = "/media/nas/main/backup";
};
Documents = {
public = true;
sharePath = "/media/nas/main/documents";
};
isos = {
public = true;
sharePath = "/media/nas/main/isos";
};
TimeMachine = { TimeMachine = {
public = false;
sharePath = "/media/nas/main/timemachine"; sharePath = "/media/nas/main/timemachine";
guestOk = false;
enableTimeMachine = true; enableTimeMachine = true;
timeMachineMaxSize = "1T"; timeMachineMaxSize = "1T";
}; };
app_data = {
public = true;
sharePath = "/media/nas/main/ssd_app_data";
};
nix-config = {
public = true;
sharePath = "/home/matt/nix-config";
};
}; };
}; };