Skip to content

Commit

Permalink
feat: support for ACME provisioned certificates, age-secured CF token…
Browse files Browse the repository at this point in the history
… secret, Tailnet certificate generation, and Plex media server
  • Loading branch information
tcarrio committed Nov 25, 2024
1 parent a05667e commit 26b7f2d
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 5 deletions.
3 changes: 3 additions & 0 deletions nixos/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ in
};
};

# Do not allow users to be modified by anything other than NixOS
users.mutableUsers = false;

systemd.tmpfiles.rules = [
"d /nix/var/nix/profiles/per-user/${username} 0755 ${username} root"
"d /mnt/snapshot/${username} 0755 ${username} users"
Expand Down
6 changes: 6 additions & 0 deletions nixos/mixins/permissions/groups/media.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
_: {
users.groups.media-server = {
# Media management group, think Plex or Jellyfin
gid = 1300;
};
}
134 changes: 134 additions & 0 deletions nixos/mixins/services/plex.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
{ lib, pkgs, config, ... }:
let
domain = "media.int.carrio.me";
in
{
# Securely mount Age secret for Cloudflare DNS verification config
age.secrets.cloudflare-dns-verification = {
file = ../../../secrets/services/acme/cloudflare.age;
owner = "root";
group = "root";
mode = "400";
};

# Various media services are granted necessary access to volumes via 'media-server' group
services.plex.group = "media-server";

# ACME NixOS Docs: https://wiki.nixos.org/wiki/ACME
security.acme = {
acceptTerms = true;
defaults.email = "[email protected]";
certs = {
"${domain}" = {
inherit domain;
group = "nginx";
dnsProvider = "cloudflare";

# https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#EnvironmentFile=
environmentFile = config.age.secrets.cloudflare-dns-verification.path;
};
};
};

systemd.services."provision-tailnet-certificate" = {
wants = [ "tailscale.service" ];
path = with pkgs; [ tailscale dnsutils coreutils ];
script = ''
ts_domain="$(${pkgs.dnsutils}/bin/dig @100.100.100.100 +noall +answer +short -x $(${pkgs.tailscale}/bin/tailscale ip -1) | ${pkgs.coreutils}/bin/sed -E 's/\.$//')"
mkdir -p /var/lib/tailscale/certs/
${pkgs.tailscale}/bin/tailscale cert \
--cert-file=/var/lib/tailscale/certs/cert.pem \
--key-file=/var/lib/tailscale/certs/key.pem \
--min-validity=48h \
$ts_domain
'';
};

systemd.timers."provision-tailnet-certificate-cron" = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1m";
OnUnitActiveSec = "12h";
Unit = "provision-tailnet-certificate.service";
};
};

# Plex NixOS Docs: https://nixos.wiki/wiki/Plex
services.nginx = {
enable = true;
# give a name to the virtual host. It also becomes the server name.
virtualHosts."${domain}" = {
# Since we want a secure connection, we force SSL
forceSSL = true;

# http2 can more performant for streaming: https://blog.cloudflare.com/introducing-http2/
http2 = true;

# Provide the ssl cert and key for the vhost
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";

extraConfig = ''
#Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
send_timeout 100m;
# Why this is important: https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/
ssl_stapling on;
ssl_stapling_verify on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
#Intentionally not hardened for security for player support and encryption video streams has a lot of overhead with something like AES-256-GCM-SHA384.
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
# Forward real ip and host to Plex
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $server_addr;
proxy_set_header Referer $server_addr;
proxy_set_header Origin $server_addr;
# Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off.
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml;
gzip_disable "MSIE [1-6]\.";
# Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones.
# Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more
client_max_body_size 100M;
# Plex headers
proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier;
proxy_set_header X-Plex-Device $http_x_plex_device;
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name;
proxy_set_header X-Plex-Platform $http_x_plex_platform;
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version;
proxy_set_header X-Plex-Product $http_x_plex_product;
proxy_set_header X-Plex-Token $http_x_plex_token;
proxy_set_header X-Plex-Version $http_x_plex_version;
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
proxy_set_header X-Plex-Provides $http_x_plex_provides;
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
proxy_set_header X-Plex-Model $http_x_plex_model;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Buffering off send to the client as soon as the data is received from Plex.
proxy_redirect off;
proxy_buffering off;
'';
locations."/" = {
proxyPass = "http://127.0.0.1:32400/";
};
};
};
}
1 change: 1 addition & 0 deletions nixos/mixins/users/tcarrio/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ in
]
++ ifExists [
"docker"
"media-server"
"podman"
"nordvpn"
];
Expand Down
8 changes: 4 additions & 4 deletions nixos/modules/desktop/fonts.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ in
config = lib.mkIf cfg.ultraMode {
fonts = {
fontDir.enable = true;
packages = with pkgs; lib.optional cfg.ultraMode [
packages = with pkgs; lib.optional cfg.ultraMode ([
(nerdfonts.override { fonts = [ "FiraCode" "SourceCodePro" "UbuntuMono" ]; })
fira
fira-go
liberation_ttf
source-serif
ubuntu_font_family
work-sans
] // lib.optional (cfg.ultraMode || cfg.japanese) [
] ++ lib.optional (cfg.ultraMode || cfg.japanese) [
ipafont
kochi-substitute
] // lib.optional (cfg.ultraMode || cfg.emoji) [
] ++ lib.optional (cfg.ultraMode || cfg.emoji) [
joypixels
noto-fonts-emoji
];
]);

# Enable a basic set of fonts providing several font styles and families and reasonable coverage of Unicode.
enableDefaultPackages = cfg.ultraMode;
Expand Down
12 changes: 11 additions & 1 deletion nixos/workstation/glass/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@
../../mixins/hardware/gtx-1080ti.nix
../../mixins/hardware/roccat.nix
../../mixins/hardware/systemd-boot.nix
../../mixins/network-shares/ds418-nfs.nix
# TODO: Fix DS418 network share configuration
# ../../mixins/network-shares/ds418-nfs.nix
../../mixins/permissions/groups/media.nix
../../mixins/services/nordvpn.nix
../../mixins/services/pipewire.nix
../../mixins/services/plex.nix
];

oxc = {
desktop = {
daw.enable = true;
# fonts.ultraMode = true;
ente.enable = true;
logseq.enable = true;
obs-studio.enable = true;
Expand Down Expand Up @@ -99,8 +103,14 @@
package = pkgs.openrgb-with-all-plugins;
};
jellyfin = {
enable = false;
openFirewall = true;
group = "media-server";
};
plex = {
enable = true;
openFirewall = true;
dataDir = "/data/plex/plex/";
};
};

Expand Down
1 change: 1 addition & 0 deletions secrets/secrets.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ in
"services/netbird/token.age".publicKeys = autoMeshSystems;
"services/tailscale/token.age".publicKeys = autoMeshSystems;
"services/jira-cli/token.age".publicKeys = macos;
"services/acme/cloudflare.age".publicKeys = with glass; [ tcarrio host ];

"network-shares/ds418/smb.conf.age".publicKeys = [ glass.tcarrio ];

Expand Down
Binary file added secrets/services/acme/cloudflare.age
Binary file not shown.

0 comments on commit 26b7f2d

Please sign in to comment.