Self-host your own digital island

support providing certificates with Eon's ACME interface

+3 -3
flake.lock
···
"opam-repository": "opam-repository"
},
"locked": {
-
"lastModified": 1713007178,
-
"narHash": "sha256-091K5GMtbNZE78gSviME6ARITr6AL94f3naLzt2DabM=",
+
"lastModified": 1718122335,
+
"narHash": "sha256-ooeplCUj5dY2KT840ecFtR+iDq1V2iB5rDsFqjbdFSs=",
"owner": "RyanGibb",
"repo": "eon",
-
"rev": "d2b108c439bbd6e8a315630d730037ef940d6c74",
+
"rev": "87b7ec1cd6cb7dc0f950d8d37a91845465780faf",
"type": "github"
},
"original": {
+1
flake.nix
···
./modules/default.nix
nixos-mailserver.nixosModule
eon.nixosModules.default
+
eon.nixosModules.acme
({ pkgs, config, ... }: {
nixpkgs.overlays = [
(final: prev: {
+16
modules/acme-eon.nix
···
+
{ pkgs, config, lib, ... }:
+
+
with lib;
+
let cfg = config.eilean;
+
in {
+
options.eilean.acme-eon = mkEnableOption "acme-eon";
+
+
config = mkIf cfg.acme-eon {
+
assertions = [{
+
assertion = cfg.services.dns.server == "eon";
+
message = ''
+
If config.eilean.acme-eon is enabled config.eilean.services.dns.server must be "eon".
+
'';
+
}];
+
};
+
}
+4 -1
modules/default.nix
···
{
imports = [
+
./acme-eon.nix
./services/dns/default.nix
./mastodon.nix
./mailserver.nix
···
config = {
# TODO install manpage
environment.systemPackages = [ ];
-
security.acme.defaults.email =
+
security.acme.defaults.email = lib.mkIf (!config.eilean.acme-eon)
+
"${config.eilean.username}@${config.networking.domain}";
+
security.acme-eon.defaults.email = lib.mkIf config.eilean.acme-eon
"${config.eilean.username}@${config.networking.domain}";
networking.firewall.allowedTCPPorts = mkIf config.services.nginx.enable [
80 # HTTP
+3 -1
modules/gitea.nix
···
};
config = mkIf cfg.gitea.enable {
+
security.acme-eon.nginxCerts = [ subdomain ];
+
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts."${subdomain}" = {
-
enableACME = true;
+
enableACME = lib.mkIf (!cfg.acme-eon) true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${
+21 -2
modules/mailserver.nix
···
};
config = mkIf cfg.mailserver.enable {
+
security.acme-eon.certs."${subdomain}" = lib.mkIf cfg.acme-eon {
+
group = "turnserver";
+
reloadServices = [ "postfix.service" "dovecot.service" ];
+
};
+
mailserver = {
enable = true;
fqdn = subdomain;
···
# Use Let's Encrypt certificates. Note that this needs to set up a stripped
# down nginx and opens port 80.
-
certificateScheme = "acme-nginx";
-
+
certificateScheme = if cfg.acme-eon then "manual" else "acme-nginx";
+
certificateFile = lib.mkIf cfg.acme-eon "${
+
config.security.acme-eon.certs.${subdomain}.directory
+
}/fullchain.pem";
+
keyFile = lib.mkIf cfg.acme-eon
+
"${config.security.acme-eon.certs.${subdomain}.directory}/key.pem";
localDnsResolver = false;
};
···
services.nginx.virtualHosts."${config.mailserver.fqdn}".extraConfig = ''
return 301 $scheme://${domain}$request_uri;
'';
+
+
systemd.services.dovecot2 = lib.mkIf cfg.acme-eon {
+
wants = [ "acme-eon-${subdomain}.service" ];
+
after = [ "acme-eon-${subdomain}.service" ];
+
};
+
+
systemd.services.postfix = lib.mkIf cfg.acme-eon {
+
wants = [ "acme-eon-${subdomain}.service" ];
+
after = [ "acme-eon-${subdomain}.service" ];
+
};
services.postfix.config = {
smtpd_tls_protocols =
+3 -1
modules/mastodon.nix
···
users.groups.${config.services.mastodon.group}.members =
[ config.services.nginx.user ];
+
security.acme-eon.nginxCerts = lib.mkIf cfg.acme-eon [ subdomain ];
+
services.nginx = {
enable = true;
recommendedProxySettings = true;
···
"${subdomain}" = {
root = "${config.services.mastodon.package}/public/";
forceSSL = true;
-
enableACME = true;
+
enableACME = lib.mkIf (!cfg.acme-eon) true;
locations."/system/".alias = "/var/lib/mastodon/public-system/";
+4 -2
modules/matrix/synapse.nix
···
LC_CTYPE = "C";
'';
+
security.acme-eon.nginxCerts = lib.mkIf cfg.acme-eon [ domain subdomain ];
+
services.nginx = {
enable = true;
# only recommendedProxySettings and recommendedGzipSettings are strictly required,
···
# i.e. to delegate from the host being accessible as ${domain}
# to another host actually running the Matrix homeserver.
"${domain}" = {
-
enableACME = true;
+
enableACME = lib.mkIf (!cfg.acme-eon) true;
forceSSL = true;
locations."= /.well-known/matrix/server".extraConfig = let
···
# Reverse proxy for Matrix client-server and server-server communication
"${subdomain}" = {
-
enableACME = true;
+
enableACME = lib.mkIf (!cfg.acme-eon) true;
forceSSL = true;
# Or do a redirect instead of the 404, or whatever is appropriate for you.
+1 -1
modules/services/dns/default.nix
···
enable = mkEnableOption "DNS server";
server = mkOption {
type = types.enum [ "bind" "eon" ];
-
default = "bind";
+
default = if config.eilean.acme-eon then "eon" else "bind";
};
openFirewall = mkOption {
type = types.bool;
+2
modules/services/dns/eon.nix
···
in lib.mkIf (cfg.enable && cfg.server == "eon") {
services.eon = {
enable = true;
+
application = "capd";
+
capnpAddress = lib.mkDefault config.networking.domain;
zoneFiles = let
mapZonefile = zonename: zone:
"${
+35 -23
modules/turn.nix
···
options.eilean.turn = { enable = mkEnableOption "TURN server"; };
config = mkIf cfg.turn.enable {
-
services.coturn =
-
let certDir = config.security.acme.certs.${subdomain}.directory;
-
in {
-
enable = true;
-
no-cli = true;
-
no-tcp-relay = true;
-
secure-stun = true;
-
use-auth-secret = true;
-
static-auth-secret-file = staticAuthSecretFile;
-
realm = subdomain;
-
relay-ips = with config.eilean; [ serverIpv4 serverIpv6 ];
-
cert = "${certDir}/fullchain.pem";
-
pkey = "${certDir}/key.pem";
-
};
+
security.acme-eon.certs."${subdomain}" = lib.mkIf cfg.acme-eon {
+
group = "turnserver";
+
reloadServices = [ "coturn" ];
+
};
+
+
services.coturn = let
+
certDir = if cfg.acme-eon then
+
config.security.acme-eon.certs.${subdomain}.directory
+
else
+
config.security.acme.certs.${subdomain}.directory;
+
in {
+
enable = true;
+
no-cli = true;
+
no-tcp-relay = true;
+
secure-stun = true;
+
use-auth-secret = true;
+
static-auth-secret-file = staticAuthSecretFile;
+
realm = subdomain;
+
relay-ips = with config.eilean; [ serverIpv4 serverIpv6 ];
+
cert = "${certDir}/fullchain.pem";
+
pkey = "${certDir}/key.pem";
+
};
systemd.services = {
coturn-static-auth-secret-generator = {
···
serviceConfig.RemainAfterExit = true;
};
"coturn" = {
-
after = [ "coturn-static-auth-secret-generator.service" ];
+
after = [ "coturn-static-auth-secret-generator.service" ]
+
++ lib.lists.optional cfg.acme-eon "acme-eon-${subdomain}.service";
requires = [ "coturn-static-auth-secret-generator.service" ];
+
wants = lib.lists.optional cfg.acme-eon "acme-eon-${subdomain}.service";
};
};
···
allowedUDPPortRanges = [ turn-range ];
};
-
security.acme.certs.${config.services.coturn.realm} = {
-
postRun =
-
"systemctl reload nginx.service; systemctl restart coturn.service";
-
group = "turnserver";
-
};
-
services.nginx.enable = true;
-
services.nginx.virtualHosts = {
+
security.acme.certs.${config.services.coturn.realm} =
+
lib.mkIf (!cfg.acme-eon) {
+
postRun =
+
"systemctl reload nginx.service; systemctl restart coturn.service";
+
group = "turnserver";
+
};
+
services.nginx.enable = lib.mkIf (!cfg.acme-eon) true;
+
services.nginx.virtualHosts = lib.mkIf (!cfg.acme-eon) {
"${config.services.coturn.realm}" = {
forceSSL = true;
enableACME = true;
};
};
-
users.groups."turnserver".members = [ config.services.nginx.user ];
+
users.groups."turnserver".members =
+
lib.mkIf (!cfg.acme-eon) [ config.services.nginx.user ];
eilean.dns.enable = true;
eilean.services.dns.zones.${config.networking.domain}.records = [{
+2 -1
template/configuration.nix
···
# TODO replace this with domain
networking.domain = "example.org";
-
security.acme.acceptTerms = true;
+
security.acme.acceptTerms = lib.mkIf (!config.eilean.acme-eon) true;
+
security.acme-eon.acceptTerms = lib.mkIf config.eilean.acme-eon true;
# TODO select internationalisation properties
i18n.defaultLocale = "en_GB.UTF-8";