move encrypted secrets near relevant files

This commit is contained in:
Noah Masur
2025-03-09 17:09:33 +00:00
parent f59ac536a2
commit 37d1d7724a
60 changed files with 27 additions and 94 deletions

View File

@ -0,0 +1,17 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IE1nSGFPdyBkckt3
c1NtVEo1bm1XREk2ZE9PL1FkOFd0LzQ1R0J4TXN4VGd2clVrZ25NCjZKenFTdHFK
MWVZSXI0NXdVTkhJQXRFRFBRRnIxRHZaOHY1UWVDYW9vTm8KLT4gc3NoLWVkMjU1
MTkgWXlTVU1RIHBmRERwcXdGanBVV0JOczg0Q0hOa1dVM09EeGMxWmJDMm9YU2Mx
djhxQkUKS2U2aHVza2JNdzltRW5wcWhqaTVPUEZoZGNWN2szQXlVYjZ3eXpwc2ZE
OAotPiBzc2gtZWQyNTUxOSBuanZYNUEgbWU0WXA4RjVZWFdPcXZ5M1UwT3lON1JD
cGhlRXZ2NEhWMHdEMitLWERqRQpKRGgwMUhISWE1Uk1ka1dteGo0dlhZcmNjVjN6
QmJBQWo0Mko4aE5jUm9rCi0+IHNzaC1lZDI1NTE5IENxSU9VQSBLaU9sSmRzMlFG
NjBYYTBYeFErNXJwZGVtZ1kvVmVCOXBDZWVoNWhDZ2hrCnE2dkJJSk8rbDRvSHYy
bEVTdXg0VTg1RzZUNi84K1ZvOVB2aUJzNHVPRkEKLT4gc3NoLWVkMjU1MTkgejFP
Y1p3IEM0Mnlockc2SlA1bXJhdnpQNXFnQ2w5bzFSTWpIajJybTBIM3VuNTN5bFUK
bXNIUVVhTzlRMUJTSEpJUURUMXZjRU5zczNjYnBUVVFmMDVEZllONjFjWQotLS0g
NXdIUWduN2Q2eXFzNlFueFR6OWxITVBranpsNTdXaktiSFZ0TTBxRFNlNAr9JzVO
Rhx5rG7CSGdYfeMcuzye4jyE2yiVKi5TVr/qp3vbDpyDQKZLlAUSF/K0rTY9K7Rm
ocY+y/V9ffh3LO2m1Y6BkRqWRJ7v4wcsc3jNGjDHlSB7EqnOwMCXyQAg
-----END AGE ENCRYPTED FILE-----

View File

@ -0,0 +1,154 @@
# This module is necessary for hosts that are serving through Cloudflare.
# Cloudflare is a CDN service that is used to serve the domain names and
# caching for my websites and services. Since Cloudflare acts as our proxy, we
# must allow access over the Internet from Cloudflare's IP ranges.
# We also want to validate our HTTPS certificates from Caddy. We'll use Caddy's
# DNS validation plugin to connect to Cloudflare and automatically create
# validation DNS records for our generated certificates.
{
config,
pkgs,
lib,
...
}:
let
cfg = config.nmasur.presets.services.cloudflare;
cloudflareIpRanges = [
# Cloudflare IPv4: https://www.cloudflare.com/ips-v4
"173.245.48.0/20"
"103.21.244.0/22"
"103.22.200.0/22"
"103.31.4.0/22"
"141.101.64.0/18"
"108.162.192.0/18"
"190.93.240.0/20"
"188.114.96.0/20"
"197.234.240.0/22"
"198.41.128.0/17"
"162.158.0.0/15"
"104.16.0.0/13"
"104.24.0.0/14"
"172.64.0.0/13"
"131.0.72.0/22"
# Cloudflare IPv6: https://www.cloudflare.com/ips-v6
"2400:cb00::/32"
"2606:4700::/32"
"2803:f800::/32"
"2405:b500::/32"
"2405:8100::/32"
"2a06:98c0::/29"
"2c0f:f248::/32"
];
in
{
options.nmasur.presets.services.cloudflare = {
enable = lib.mkEnableOption "Cloudflare proxy configuration";
noProxyDomains = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "Domains to use for dyndns without CDN proxying.";
default = [ ];
};
};
config = lib.mkIf cfg.enable {
# Forces Caddy to error if coming from a non-Cloudflare IP
nmasur.presets.services.caddy.cidrAllowlist = cloudflareIpRanges;
# Tell Caddy to use Cloudflare DNS for ACME challenge validation
services.caddy.package = pkgs.caddy.withPlugins {
plugins = [ "github.com/caddy-dns/cloudflare@v0.0.0-20250228175314-1fb64108d4de" ];
hash = "sha256-3nvVGW+ZHLxQxc1VCc/oTzCLZPBKgw4mhn+O3IoyiSs=";
};
nmasur.presets.services.caddy.tlsPolicies = [
{
issuers = [
{
module = "acme";
email = "acme@${config.nmasur.presets.programs.msmtp.domain}";
account_key = "{env.ACME_ACCOUNT_KEY}";
challenges = {
dns = {
provider = {
name = "cloudflare";
api_token = "{env.CLOUDFLARE_API_TOKEN}";
};
resolvers = [ "1.1.1.1" ];
};
};
}
];
}
];
# Allow Caddy to read Cloudflare API key for DNS validation
systemd.services.caddy.serviceConfig.EnvironmentFile = [
config.secrets.cloudflare-api.dest
config.secrets.letsencrypt-key.dest
];
# Private key is used for LetsEncrypt
secrets.letsencrypt-key = {
source = ./letsencrypt-key.age;
dest = "${config.secretsDirectory}/letsencrypt-key";
owner = "caddy";
group = "caddy";
};
# API key must have access to modify Cloudflare DNS records
secrets.cloudflare-api = {
source = ./cloudflare-api.age;
dest = "${config.secretsDirectory}/cloudflare-api";
owner = "caddy";
group = "caddy";
};
# Wait for secret to exist
systemd.services.caddy = {
after = [
"cloudflare-api-secret.service"
"letsencrypt-key-secret.service"
];
requires = [
"cloudflare-api-secret.service"
"letsencrypt-key-secret.service"
];
};
# Allows Nextcloud to trust Cloudflare IPs
services.nextcloud.settings.trusted_proxies = cloudflareIpRanges;
# Allows Transmission to trust Cloudflare IPs
services.transmission.settings.rpc-whitelist = builtins.concatStringsSep "," (
[ "127.0.0.1" ] ++ cloudflareIpRanges
);
# Using dyn-dns instead of ddclient because I can't find a way to choose
# between proxied and non-proxied records for Cloudflare using just
# ddclient.
services.cloudflare-dyndns =
lib.mkIf ((builtins.length config.services.cloudflare-dyndns.domains) > 0)
{
enable = true;
proxied = true;
deleteMissing = true;
apiTokenFile = config.secrets.cloudflare-api.dest;
};
# Wait for secret to exist to start
systemd.services.cloudflare-dyndns = lib.mkIf config.services.cloudflare-dyndns.enable {
after = [ "cloudflare-api-secret.service" ];
requires = [ "cloudflare-api-secret.service" ];
};
};
}

View File

@ -0,0 +1,21 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IE1nSGFPdyBCc2hS
RUw4Y200allVODI0QTYxdXlHSHRiS1pWWHg5SW9tZ0tGVmc2ajJZCitXeEd0dk9K
MmRkZlRYd253RWFzNXpUR0xuTXI2dWVhNFZpQnNlU0VFdEUKLT4gc3NoLWVkMjU1
MTkgWXlTVU1RIHNScVkwd1RmVGhNcFVSRTlxQzlvSUc2cGxNWUc0YVJ5RjRydk9J
RG1peDQKVU5iN1ZmWEJyOXBiNWdiRFlnNFFKR09vaFB4SWZWK0x3VWJwMDZtYlBj
MAotPiBzc2gtZWQyNTUxOSBuanZYNUEgSXR5OEk5cWZHUEZ3WmFCUTVFeTBnTG5h
cmNxVWFLV2JhUTRBaUJGWERncwpYMFBIN0kySXdjOE5YcS85bXRCRnRsK3NyMHY4
N0JKelFyeHB6T1dEZ2VnCi0+IHNzaC1lZDI1NTE5IENxSU9VQSAyQVJYRXJ1cFVl
dldaa0Qydlc3MzlFYnN5YUx0amdWZm5PcWovRm1MaVg0CkJsSFZRdGJIZzA1T0Ny
bUNnL0Zxa05ubHluSVBUenVCZTZpYlA5UUFEMDQKLT4gc3NoLWVkMjU1MTkgejFP
Y1p3IDFPQU5HZm5mRFl5NnNLVHUvdUlmTEtyS0djNWZaMWg5VDl1ZldNTkVWbXMK
RkVBTzNUa0d6c3NJUHQrazdKWXNZY3NIRzRndGdRNjFjMXZCSEhIQnIyYwotLS0g
VzNOa3dXS0hrMWxNUlJ4UzAxNlkzSXM4RWc1RGFzQjFyb1dGZXFnL3RCVQoq002V
S5MQqBjKKOacO4OWgn5KpmU2D7zJWJjNMxH80L6HFNoyOj4wNa+8TA0Q7MTn3bKN
YvAuwbDAGjjDt8vZFKOiZB0xAex+H7A1MVvuGIA8xQa6iNBMwj7nWTLif5pCbVk+
9aAAprcJVDJx4TeFXlNF6XtcQ3J8abwi6TDqNFpfwwBb/wruyzutgvlOiz1XSBX0
xlCGckq/BCnItLURIb7zhqRMqk/JODPjOKArmP86nCq25Wm+W5JQ8ViQ7LHJyoFj
zbiwabqeBJZgqoVdVMj8Glz+91RVodn6f9VwQcHINgHxmkd6j2z75AmWZecwD2ic
pUMnikqIMI0B3zW5H38t2cJv+aIMTl7lH5Hf1P5jEn3NPw==
-----END AGE ENCRYPTED FILE-----