ACME: Difference between revisions

Tboston (talk | contribs)
Added wildcard example and clarification on the group usage
m add citation for nginx acmeroot setting advice
 
(9 intermediate revisions by 6 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.  


== Setup ==
= Basics =
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.
 
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>.
 
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>. <ref>From [https://nixos.org/manual/nixos/stable/#module-security-acme-config-dns-with-vhosts NixOS Manual: ''Using DNS validation with web server virtual hosts'']. [https://github.com/NixOS/nixpkgs/issues/210807 Issue #210807] provides some detail on why this is needed.</ref>
 
=== 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.
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.
Line 22: Line 97:
</syntaxhighlight>
</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.
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<nowiki> upstream documentation] on available providers and their specific configuration for the </nowiki><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">
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">
Line 41: Line 118:
</syntaxhighlight>
</syntaxhighlight>


== Usage ==
== TLS challenge ==
 
Todo.


After successfull generation, certificates can be found in the directory <code>/var/lib/acme</code>. When using certificates in other applications it may be required to change permissions. The group of the certificate files can be adjusted by setting the <code>group</code> option as a string
= 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">
<syntaxhighlight lang="nix">
security.acme.certs."example.org".group = "nginx";
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>
</syntaxhighlight>


or reference.
== Reload services after renewal ==


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
security.acme.certs."example.org".group = config.services.nginx.group;
security.acme.certs.${domainName}.reloadServices = [
</syntaxhighlight>Resulting in the following files and permissions<syntaxhighlight lang="bash">
  "prosody"
lrwxrwxrwx 1 acme nginx   13 Aug  4 12:57 cert.pem -> fullchain.pem
   "coturn"
-rw-r----- 1 acme nginx 1567 Aug  4 12:57 chain.pem
  "nginx"
-rw-r----- 1 acme nginx 2865 Aug  4 12:57 fullchain.pem
  "dovecot2"
-rw-r----- 1 acme nginx 3092 Aug  4 12:57 full.pem
  "postfix"
-rw-r----- 1 acme nginx  227 Aug  4 12:57 key.pem
];
</syntaxhighlight>
</syntaxhighlight>


== Using Let's Encrypt Staging ==
== 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
For testing your Let's Encrypt configuration it makes sense to use their [https://letsencrypt.org/docs/staging-environment/ staging environment], because it offers less stringent rate limits.


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
Line 69: Line 182:
</syntaxhighlight>
</syntaxhighlight>


== See also ==
= 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]