feat(caddy): move to new package
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-12-29 01:28:33 -05:00
parent 83fe089c5e
commit 98833bdc6f
14 changed files with 367 additions and 19 deletions

View file

@ -1,6 +1,7 @@
{ {
buildApp, buildApp,
callPackage, callPackage,
go,
nix-update, nix-update,
nodejs_latest, nodejs_latest,
prefetch-npm-deps, prefetch-npm-deps,
@ -11,6 +12,7 @@ buildApp {
npmDepsHash = "sha256-TU7HoUGeCXUKwm2s4Np6NQahk6gWBN9WC5vob3zw7Ns="; npmDepsHash = "sha256-TU7HoUGeCXUKwm2s4Np6NQahk6gWBN9WC5vob3zw7Ns=";
runtimeInputs = [ runtimeInputs = [
go
nix-update nix-update
nodejs_latest nodejs_latest
prefetch-npm-deps prefetch-npm-deps

View file

@ -10,6 +10,7 @@ import updateNodeModules from './node-modules';
import { import {
runNixUpdate, runNixUpdate,
updateCaddyPlugins,
updateCustomPackage, updateCustomPackage,
updateVuetorrent, updateVuetorrent,
} from './misc'; } from './misc';
@ -66,6 +67,10 @@ const main = async() => {
console.log(runNixUpdate('homepage')); console.log(runNixUpdate('homepage'));
} }
if (args['cp'] || args['caddy-plugins']) {
console.log(updateCaddyPlugins());
}
if (args['a'] || args['all']) { if (args['a'] || args['all']) {
// Update this first because of nix run cmd // Update this first because of nix run cmd
const firefoxOutput = updateFirefoxAddons(); const firefoxOutput = updateFirefoxAddons();
@ -93,6 +98,11 @@ const main = async() => {
console.log(vuetorrentOutput); console.log(vuetorrentOutput);
const caddyPluginsOutput = updateCaddyPlugins();
console.log(caddyPluginsOutput);
// This doesn't need to be added to commit msgs // This doesn't need to be added to commit msgs
console.log(updateCustomPackage( console.log(updateCustomPackage(
'scopedPackages.x86_64-linux.lovelace-components.custom-sidebar', 'scopedPackages.x86_64-linux.lovelace-components.custom-sidebar',
@ -144,6 +154,9 @@ const main = async() => {
if (vuetorrentOutput.length > 5) { if (vuetorrentOutput.length > 5) {
output.push(`Misc Sources:\n${indentOutput(vuetorrentOutput)}\n\n`); output.push(`Misc Sources:\n${indentOutput(vuetorrentOutput)}\n\n`);
} }
if (caddyPluginsOutput.length > 5) {
output.push(`Caddy Plugins:\n${indentOutput(caddyPluginsOutput)}\n\n`);
}
if (nixUpdateOutputs.length > 5) { if (nixUpdateOutputs.length > 5) {
output.push(`nix-update executions:\n${indentOutput(nixUpdateOutputs)}\n`); output.push(`nix-update executions:\n${indentOutput(nixUpdateOutputs)}\n`);
} }

View file

@ -1,7 +1,7 @@
import { writeFileSync } from 'node:fs'; import { writeFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
import { parseFetchurl } from './lib'; import { parseFetchurl, replaceInFile } from './lib';
/* Constants */ /* Constants */
@ -71,3 +71,78 @@ export const runNixUpdate = (
stderr: execution.stderr.toString(), stderr: execution.stderr.toString(),
}; };
}; };
const genPluginsText = (
plugins: Record<string, { url: string, version: string }>,
) => `# This file was autogenerated. DO NOT EDIT!
{
plugins = {
${Object.entries(plugins)
.map(([key, value]) => `
${key} = {
url = "${value.url}";
version = "${value.version}";
};
`)
.join('')}
};
hash = "";
}
`;
export const updateCaddyPlugins = () => {
let updates = '';
const dir = `${FLAKE}/configurations/cluster/modules/caddy`;
// Setup workspace
spawnSync(
[
'rm -rf /tmp/update-caddy',
'mkdir -p /tmp/update-caddy',
'cd /tmp/update-caddy || exit 1',
'go mod init temp',
].join('; '),
[],
{ shell: true, cwd: '/tmp' },
);
const plugins = JSON.parse(spawnSync('nix',
['eval', '-f', `${dir}/plugins.nix`, '--json'],
{ shell: true }).stdout.toString()).plugins as Record<string, { url: string, version: string }>;
// Get most recent versions of plugins
Object.entries(plugins).forEach(([key, value]) => {
const NEW_VERSION = spawnSync([
'go mod init temp > /dev/null',
`go get ${value.url} > /dev/null`,
`grep '${value.url}' go.mod`,
].join('; '), [], { shell: true, cwd: '/tmp/update-caddy' })
.stdout
.toString()
.trim()
.replace(' // indirect', '')
.split(' ')[1];
if (plugins[key].version !== NEW_VERSION) {
updates += `${key}: ${plugins[key].version} -> ${NEW_VERSION}`;
plugins[key].version = NEW_VERSION;
}
});
writeFileSync(`${dir}/plugins.nix`, genPluginsText(plugins));
// Get new hash
const caddyPkgAttr = 'nixosConfigurations.thingone.config.services.caddy.package';
const NEW_HASH = spawnSync(
`nix build "$FLAKE#${caddyPkgAttr}" |& sed -n 's/.*got: *//p'`,
[],
{ shell: true },
).stdout.toString().trim();
replaceInFile(/hash = ".*";/, `hash = "${NEW_HASH}";`, `${dir}/plugins.nix`);
return updates;
};

View file

@ -1,36 +1,37 @@
{ {
caddy-plugins,
config, config,
lib,
mainUser, mainUser,
pkgs, pkgs,
self,
... ...
}: let }: let
inherit (lib) attrValues;
inherit (config.sops) secrets; inherit (config.sops) secrets;
inherit (config.networking) hostName; inherit (config.networking) hostName;
caddy = caddy-plugins.packages.${pkgs.system}.default;
in { in {
imports = [caddy-plugins.nixosModules.default]; imports = [self.nixosModules.caddy-plus];
# User stuff
environment.systemPackages = [caddy];
users.users.${mainUser}.extraGroups = ["caddy"]; users.users.${mainUser}.extraGroups = ["caddy"];
boot.kernel.sysctl."net.ipv4.ip_nonlocal_bind" = 1; boot.kernel.sysctl."net.ipv4.ip_nonlocal_bind" = 1;
systemd.services.caddy.serviceConfig = { systemd.services.caddy.serviceConfig = {
EnvironmentFile = secrets.caddy-cloudflare.path; EnvironmentFile = secrets.caddy-cloudflare.path;
# For some reason the service
# doesn't shutdown normally
KillSignal = "SIGKILL";
RestartKillSignal = "SIGKILL";
}; };
services.caddy = { services.caddy = {
enable = true; enable = true;
enableReload = false; enableReload = false;
package = caddy;
package = let
pluginsInfo = import ./plugins.nix;
in
pkgs.caddy.withPlugins {
plugins = map (x: "${x.url}@${x.version}") (attrValues pluginsInfo.plugins);
inherit (pluginsInfo) hash;
};
virtualHosts = let virtualHosts = let
clusterIP = config.services.pcsd.virtualIps.caddy-vip.ip; clusterIP = config.services.pcsd.virtualIps.caddy-vip.ip;

View file

@ -0,0 +1,11 @@
# This file was autogenerated. DO NOT EDIT!
{
plugins = {
cloudflare = {
url = "github.com/caddy-dns/cloudflare";
version = "v0.0.0-20240703190432-89f16b99c18e";
};
};
hash = "sha256-WGV/Ve7hbVry5ugSmTYWDihoC9i+D3Ct15UKgdpYc9U=";
}

View file

@ -1,7 +1,7 @@
{...}: { {...}: {
imports = [ imports = [
./blocky.nix ./blocky.nix
./caddy.nix ./caddy
./headscale ./headscale
./nfs-client.nix ./nfs-client.nix
./pcsd.nix ./pcsd.nix

Binary file not shown.

BIN
flake.nix

Binary file not shown.

View file

@ -73,11 +73,6 @@ let
owner = "matt1432"; owner = "matt1432";
repo = "nixos-pcsd"; repo = "nixos-pcsd";
}; };
caddy-plugins = mkDep {
owner = "matt1432";
repo = "nixos-caddy-cloudflare";
};
}; };
serviviInputs = { serviviInputs = {

View file

@ -0,0 +1,104 @@
self: {
config,
lib,
pkgs,
...
}: let
inherit (lib) types;
inherit (lib.attrsets) attrValues;
inherit (lib.modules) mkIf mkOverride;
inherit (lib.options) mkOption;
inherit (lib.strings) concatMapStringsSep concatStringsSep optionalString stringLength substring toUpper;
cfg = config.services.caddy;
capitalize = str:
toUpper (substring 0 1 str)
+ substring 1 (stringLength str) str;
mkSubDirConf = subOpts:
optionalString (subOpts.reverseProxy != null) (
if subOpts.experimental
then ''
${subOpts.extraConfig}
redir /${subOpts.subDirName} /${subOpts.subDirName}/
route /${subOpts.subDirName}/* {
uri strip_prefix ${subOpts.subDirName}
reverse_proxy ${subOpts.reverseProxy} {
header_up X-Real-IP {remote}
header_up X-${capitalize (subOpts.subDirName)}-Base "/${subOpts.subDirName}"
}
}
''
else ''
${subOpts.extraConfig}
redir /${subOpts.subDirName} /${subOpts.subDirName}/
reverse_proxy /${subOpts.subDirName}/* {
to ${subOpts.reverseProxy}
}
''
);
mkSubDomainConf = hostName: subOpts: ''
@${subOpts.subDomainName} host ${subOpts.subDomainName}.${hostName}
handle @${subOpts.subDomainName} {
${subOpts.extraConfig}
${optionalString (subOpts.reverseProxy != null) "reverse_proxy ${subOpts.reverseProxy}"}
${concatMapStringsSep "\n" mkSubDirConf (attrValues subOpts.subDirectories)}
}
'';
mkVHostConf = hostOpts: let
sslCertDir = config.security.acme.certs.${hostOpts.useACMEHost}.directory;
in ''
${hostOpts.hostName} ${concatStringsSep " " hostOpts.serverAliases} {
${optionalString (hostOpts.listenAddresses != []) "bind ${concatStringsSep " " hostOpts.listenAddresses}"}
${optionalString (hostOpts.useACMEHost != null) "tls ${sslCertDir}/cert.pem ${sslCertDir}/key.pem"}
log {
${hostOpts.logFormat}
}
${hostOpts.extraConfig}
${optionalString (hostOpts.reverseProxy != null) "reverse_proxy ${hostOpts.reverseProxy}"}
${concatMapStringsSep "\n" mkSubDirConf (attrValues hostOpts.subDirectories)}
${concatMapStringsSep "\n" (mkSubDomainConf hostOpts.hostName) (attrValues hostOpts.subDomains)}
}
'';
settingsFormat = pkgs.formats.json {};
configFile =
if cfg.settings != {}
then settingsFormat.generate "caddy.json" cfg.settings
else let
Caddyfile = pkgs.writeTextDir "Caddyfile" ''
{
${cfg.globalConfig}
}
${cfg.extraConfig}
${concatMapStringsSep "\n" mkVHostConf (attrValues cfg.virtualHosts)}
'';
Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" {nativeBuildInputs = [cfg.package];} ''
mkdir -p $out
cp --no-preserve=mode ${Caddyfile}/Caddyfile $out/Caddyfile
caddy fmt --overwrite $out/Caddyfile
'';
in "${
if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform
then Caddyfile-formatted
else Caddyfile
}/Caddyfile";
in {
options.services.caddy.virtualHosts = mkOption {
type = types.attrsOf (types.submodule (import ./vhost-options.nix {inherit cfg;}));
};
# implementation
config = mkIf cfg.enable {
services.caddy.configFile = mkOverride 80 configFile;
};
}

View file

@ -0,0 +1,43 @@
{cfg}: {
lib,
name,
...
}: let
inherit (lib) mkOption types;
in {
options = {
subDirName = mkOption {
type = types.str;
default = name;
description = "The sub directory name to handle.";
};
reverseProxy = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Option to give the parameters to a simple "reverse_proxy" command
appended after extraConfig.
'';
};
experimental = mkOption {
type = types.bool;
default = false;
description = ''
Specify if the app being proxied expects to be under a subdirectory.
If it doesn't, we can attempt to circumvent that but it is not guaranteed
to work for every app.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Additional lines of configuration appended to this sub domain in the
automatically generated `Caddyfile`.
'';
};
};
}

View file

@ -0,0 +1,52 @@
{cfg}: {
lib,
name,
...
}: let
inherit (lib) literalExpression mkOption types;
in {
options = {
subDomainName = mkOption {
type = types.str;
default = name;
description = "The sub domain name to handle.";
};
reverseProxy = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Option to give the parameters to a simple "reverse_proxy" command
appended after extraConfig.
'';
};
subDirectories = mkOption {
type = types.attrsOf (types.submodule (import ./sub-dir-options.nix {inherit cfg;}));
default = {};
example = literalExpression ''
{
headscale = {
appSupport = false;
reverseProxy = "localhost:8080";
extraConfig = '''
encode gzip
''';
};
};
'';
description = ''
Declarative specification of a subdomain's subdirectories served by Caddy.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = ''
Additional lines of configuration appended to this sub domain in the
automatically generated `Caddyfile`.
'';
};
};
}

View file

@ -0,0 +1,51 @@
{cfg}: {lib, ...}: let
inherit (lib) literalExpression mkOption types;
in {
options = {
reverseProxy = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Option to give the parameters to a simple "reverse_proxy" command
appended after extraConfig.
'';
};
subDomains = mkOption {
type = types.attrsOf (types.submodule (import ./sub-domain-options.nix {inherit cfg;}));
default = {};
example = literalExpression ''
{
headscale = {
reverseProxy = "localhost:8080";
extraConfig = '''
encode gzip
''';
}
};
'';
description = ''
Declarative specification of a virtual hosts subdomain served by Caddy.
'';
};
subDirectories = mkOption {
type = types.attrsOf (types.submodule (import ./sub-dir-options.nix {inherit cfg;}));
default = {};
example = literalExpression ''
{
headscale = {
appSupport = false;
reverseProxy = "localhost:8080";
extraConfig = '''
encode gzip
''';
};
};
'';
description = ''
Declarative specification of a subdomain's subdirectories served by Caddy.
'';
};
};
}

View file

@ -2,6 +2,7 @@ self: {
base = import ./base self; base = import ./base self;
base-droid = import ./base/default-droid.nix self; base-droid = import ./base/default-droid.nix self;
borgbackup = import ./borgbackup; borgbackup = import ./borgbackup;
caddy-plus = import ./caddy-plus self;
desktop = import ./desktop self; desktop = import ./desktop self;
docker = import ./docker self.inputs.khepri; docker = import ./docker self.inputs.khepri;
esphome-plus = import ./esphome-plus; esphome-plus = import ./esphome-plus;