ACME: Difference between revisions
imported>Onny Initial page |
→DNS challenge: mention services.nginx.virtualHosts.<name>.acmeRoot should also be set to null for nginx. |
||
| (19 intermediate revisions by 10 users not shown) | |||
| Line 1: | Line 1: | ||
NixOS supports automatic domain validation & certificate retrieval and renewal using the ACME protocol. Any provider can be used, but by default NixOS uses Let's Encrypt. The alternative ACME client [https://go-acme.github.io/lego/ lego] is used under the hood. | NixOS supports automatic domain validation & certificate retrieval and renewal using the ACME protocol. Any provider can be used, but by default NixOS uses Let's Encrypt. The alternative ACME client [https://go-acme.github.io/lego/ lego] is used under the hood. | ||
= | = Basics = | ||
Following example setup generates certificates using DNS validation. | This process should generate three key files. The naming and usage of | ||
the three key files is common to all programs and services in NixOS. | |||
We let <code>sslCertDir = | |||
config.security.acme.certs.${domainName}.directory;</code> in the | |||
following paragraph. | |||
The three key files and their location are | |||
* <code>sslServerCert = "/var/host.cert";</code> Path to server SSL certificate. Located at <code>"${sslCertDir}/fullchain.pem"</code>. | |||
* <code>sslServerChain = "/var/ca.pem";</code> Path to server SSL chain file. Located at <code>"${sslCertDir}/chain.pem"</code>. | |||
* <code>sslServerKey = "/var/host.key";</code> Path to server SSL certificate key. Located at <code>"${sslCertDir}/key.pem"</code>. | |||
Beginning in late 2024, user @ThinkChaos started working on unifying | |||
modules options to use the same interface for specifying certificates. | |||
Currently, in 2025-09, the <code>useACMEHost</code> option can be used | |||
with a wide variety of services | |||
[https://search.nixos.org/options?channel=25.05&query=useACMEHost], | |||
which simplifies the configuration and enables the automatic checking | |||
of correct private and public key permissions during nixos-rebuild. | |||
= Obtaining a new certificate = | |||
== Basics == | |||
You need to agree to the Terms of Service, provide an email address, | |||
provide a domain name, and, if any, extra domain names. | |||
DNS challenge supports obtaining certificates for wildcard domains, | |||
such as <code>*.example.org</code>. | |||
<syntaxhighlight lang="nix"> | |||
let | |||
domainName = "example.org"; | |||
in | |||
{ | |||
security.acme = { | |||
acceptTerms = true; | |||
defaults.email = "admin@${domainName}"; | |||
certs = { | |||
"${domainName}" = { | |||
group = config.services.nginx.group; | |||
extraDomainNames = [ | |||
"mail.${domainName}" | |||
"www.${domainName}" | |||
]; | |||
}; | |||
}; | |||
}; | |||
} | |||
</syntaxhighlight> | |||
== HTTP challenge == | |||
To use HTTP challenge, you need to have your DNS record pointing to | |||
this computer. You also need to enable a web server and allow | |||
plaintext traffic on port 80. This example is based on the previous section: | |||
<syntaxhighlight lang="nix"> | |||
security.acme = { | |||
defaults.webroot = "/var/lib/acme/acme-challenge/"; | |||
# We are using nginx as webserver, therefore set correct key permissions | |||
certs."${domainName}".group = config.services.nginx.group; | |||
}; | |||
# for acme plain http challenge | |||
networking.firewall.allowedTCPPorts = [ 80 ]; | |||
# webserver for http challenge | |||
services.nginx = { | |||
enable = true; | |||
virtualHosts."${domainName}" = { | |||
forceSSL = true; | |||
useACMEHost = "${domainName}"; | |||
locations."/.well-known/".root = "/var/lib/acme/acme-challenge/"; | |||
}; | |||
}; | |||
</syntaxhighlight> | |||
== DNS challenge == | |||
If you want to use the DNS challenge with nginx, you should also set [https://search.nixos.org/options?show=services.nginx.virtualHosts.%3Cname%3E.acmeRoot service.nginx.virtualHosts.<name>.acmeRoot] to <code>null</code>. | |||
=== With inwx as DNS provider === | |||
Following example setup generates certificates using DNS validation. [https://letsencrypt.org/repository/ Let's Encrypt ToS] has to be accepted. Further the contact mail <code>admin+acme@example.com</code> is defined. | |||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
security.acme = { | security.acme = { | ||
acceptTerms = true; | acceptTerms = true; | ||
defaults.email = "admin+acme@example. | defaults.email = "admin+acme@example.org"; | ||
certs."example. | certs."mx1.example.org" = { | ||
domain = "*.example. | dnsProvider = "inwx"; | ||
# Supplying password files like this will make your credentials world-readable | |||
# in the Nix store. This is for demonstration purpose only, do not use this in production. | |||
environmentFile = "${pkgs.writeText "inwx-creds" '' | |||
INWX_USERNAME=xxxxxxxxxx | |||
INWX_PASSWORD=yyyyyyyyyy | |||
''}"; | |||
}; | |||
}; | |||
</syntaxhighlight> | |||
Certificates are getting generated for the domain | |||
<code>mx1.example.org</code> using the DNS provider | |||
<code>inwx</code>. See [https://go-acme.github.io/lego/dns upstream | |||
documentation] on available providers and their specific configuration | |||
for the <code>credentialsFile</code> option. | |||
=== With Cloudflare as DNS provider === | |||
The next example issues a wildcard certificate and uses Cloudflare for validation. We're also adding the group "nginx" here so that the certificate files can be used by nginx later on.<syntaxhighlight lang="nix"> | |||
security.acme = { | |||
acceptTerms = true; | |||
defaults.email = "admin@example.org"; | |||
certs = { | |||
"example.org" = { | |||
domain = "*.example.org"; | |||
group = "nginx"; | |||
dnsProvider = "cloudflare"; | |||
# location of your CLOUDFLARE_DNS_API_TOKEN=[value] | |||
# https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#EnvironmentFile= | |||
environmentFile = "/home/admin/cloudflare"; | |||
}; | |||
}; | }; | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== See also | == TLS challenge == | ||
Todo. | |||
= Integration with service modules = | |||
== Setting file permission with postRun == | |||
Use the <code>security.acme.certs.*.postRun</code> to set permissions | |||
on the key directory and the key files: | |||
<syntaxhighlight lang="nix"> | |||
security.acme.certs.${domainName}.postRun = '' | |||
# set permission on dir | |||
${pkgs.acl}/bin/setfacl -m \ | |||
u:nginx:rx,u:turnserver:rx,u:prosody:rx,u:dovecot2:rx,u:postfix:rx \ | |||
/var/lib/acme/${domainName} | |||
# set permission on key file | |||
${pkgs.acl}/bin/setfacl -m \ | |||
u:nginx:r,u:turnserver:r,u:prosody:r,u:dovecot2:r,u:postfix:r \ | |||
/var/lib/acme/${domainName}/*.pem | |||
''; | |||
</syntaxhighlight> | |||
== Reload services after renewal == | |||
<syntaxhighlight lang="nix"> | |||
security.acme.certs.${domainName}.reloadServices = [ | |||
"prosody" | |||
"coturn" | |||
"nginx" | |||
"dovecot2" | |||
"postfix" | |||
]; | |||
</syntaxhighlight> | |||
== Using useACMEHost == | |||
Many service modules support obtaining certificates. But if you were | |||
to configure certificate options separately for each service module, | |||
it would be time consuming and risks hitting the certificate renewal | |||
limits of the service provider. | |||
Instead, centrally manage certificate options within the security.acme | |||
module; then point other services to security.acme with | |||
<code>useACMEHost</code> option. | |||
<syntaxhighlight lang="nix"> | |||
security.acme.certs."example.org".extraDomainNames = [ | |||
"syncplay.example.org" | |||
"reposilite.example.org" | |||
"site2.example.org" | |||
]; | |||
services.syncplay.useACMEHost = "example.org"; | |||
services.reposilite.useACMEHost = "example.org"; | |||
services.nginx.virtualHosts."site2.example.org".useACMEHost = "example.org"; | |||
</syntaxhighlight> | |||
= Using Let's Encrypt Staging = | |||
If you'd like to use the Let's Encrypt [https://letsencrypt.org/docs/staging-environment/ staging environment], eg for its less stringent rate limits, set | |||
<syntaxhighlight lang="nix"> | |||
security.acme.defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; | |||
</syntaxhighlight> | |||
= See also = | |||
* NixOS manual on [https://nixos.org/manual/nixos/stable/index.html#module-security-acme SSL/TLS Certificates with ACME] | * NixOS manual on [https://nixos.org/manual/nixos/stable/index.html#module-security-acme SSL/TLS Certificates with ACME] | ||
[[Category: Server]] | |||
[[Category: Networking]] | |||