Add DNS accounts, Let's Encrypt, password change, reboot, HTTP redirect
- router.dnsProviders: named accounts at DNS providers, used by dynamic DNS and certificates (ddclient and lego provider lists in pkgs/router-webui/providers.json). Schema 2 migrates router.ddns. - router.acme: a Let's Encrypt certificate for the web UI via security.acme (DNS-01, resolver 1.1.1.1), served and reloaded by the web UI; Certificate page. - Web UI: password change (web UI and root), reboot button, HTTP to HTTPS redirect (router.webui.httpPort), grouped menu. - Latest kernel (linuxPackages_latest); 7.3 and 7.3.0 are the same kernel for the reboot hint. - SPECS.md: wanted later (REST API, GraphQL, MCP). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
+108
-20
@@ -580,6 +580,111 @@ in
|
||||
);
|
||||
};
|
||||
|
||||
dnsProviders = mkOption {
|
||||
default = { };
|
||||
description = ''
|
||||
Accounts at DNS providers, by name. Dynamic DNS (`ddns.provider`) and
|
||||
certificates (`acme.provider`) use them, so a token is kept once.
|
||||
'';
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options = {
|
||||
type = mkOption {
|
||||
type = types.str;
|
||||
example = "cloudflare";
|
||||
description = ''
|
||||
The provider. For dynamic DNS the ddclient protocol
|
||||
(<https://ddclient.net/protocols.html>); for certificates the
|
||||
provider code of lego (<https://go-acme.github.io/lego/dns>).
|
||||
The names are the same where both know the provider.
|
||||
The lists are in `pkgs/router-webui/providers.json`.
|
||||
'';
|
||||
};
|
||||
username = mkOption {
|
||||
type = types.str;
|
||||
default = "token";
|
||||
description = "Login for dynamic DNS; `token` for an API token (Cloudflare).";
|
||||
};
|
||||
server = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "dynupdate.no-ip.com";
|
||||
description = "The provider's update server, for protocols that need one (`dyndns2`).";
|
||||
};
|
||||
secretFile = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "/var/lib/router/dns-secrets/cloudflare";
|
||||
description = ''
|
||||
File with the password or API token (dynamic DNS; for
|
||||
certificates through cloudflare, desec, digitalocean, duckdns
|
||||
and hetzner the same token). Never a path in the Nix store.
|
||||
'';
|
||||
};
|
||||
environmentFile = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "/var/lib/router/dns-secrets/route53.env";
|
||||
description = ''
|
||||
File with `VARIABLE=value` lines for lego, for certificates
|
||||
through providers other than those above; the variables are
|
||||
listed at <https://go-acme.github.io/lego/dns>. Never a path in the Nix store.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
acme = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
A certificate from Let's Encrypt for the web UI, proven by a DNS
|
||||
record (DNS-01, no port open to the internet), renewed automatically.
|
||||
'';
|
||||
};
|
||||
provider = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "cloudflare";
|
||||
description = "The name of a connection in `dnsProviders` that can change the records of the domains.";
|
||||
};
|
||||
domains = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "router.example.org" ];
|
||||
description = "Names in the certificate; the first is its name. A wildcard like `*.home.example.org` is possible.";
|
||||
};
|
||||
email = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Where Let's Encrypt writes about expiring certificates.";
|
||||
};
|
||||
dnsResolver = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "1.1.1.1:53";
|
||||
example = "9.9.9.9:53";
|
||||
description = ''
|
||||
Resolver (`address:port`) that lego asks to find the zone's name
|
||||
servers and to check that the challenge record has spread. Without
|
||||
one it would use the router's own Unbound, which answers the local
|
||||
domain itself and caches. `null`: the router's resolver.
|
||||
'';
|
||||
};
|
||||
staging = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Use the staging service: certificates that browsers do not trust, for trying it out.";
|
||||
};
|
||||
acceptTerms = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Accept the Let's Encrypt subscriber agreement.";
|
||||
};
|
||||
};
|
||||
|
||||
ddns = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
@@ -590,28 +695,11 @@ in
|
||||
uplink address changes.
|
||||
'';
|
||||
};
|
||||
protocol = mkOption {
|
||||
type = types.str;
|
||||
default = "cloudflare";
|
||||
example = "dyndns2";
|
||||
description = "ddclient protocol of the provider, see <https://ddclient.net/protocols.html>.";
|
||||
};
|
||||
server = mkOption {
|
||||
provider = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "dynupdate.no-ip.com";
|
||||
description = "The provider's update server, for protocols that need one (`dyndns2`).";
|
||||
};
|
||||
username = mkOption {
|
||||
type = types.str;
|
||||
default = "token";
|
||||
description = "Login; `token` for a Cloudflare API token.";
|
||||
};
|
||||
passwordFile = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "/var/lib/router/ddns-secret";
|
||||
description = "File with the password or API token. Never a path in the Nix store.";
|
||||
example = "cloudflare";
|
||||
description = "The name of a connection in `dnsProviders` to update the records through.";
|
||||
};
|
||||
zone = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
|
||||
Reference in New Issue
Block a user