ACME: Difference between revisions

imported>Onny
Add usage infos
m add citation for nginx acmeroot setting advice
 
(17 intermediate revisions by 11 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 =
 
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 11: Line 87:
   certs."mx1.example.org" = {
   certs."mx1.example.org" = {
     dnsProvider = "inwx";
     dnsProvider = "inwx";
     # Suplying password files like this will make your credentials world-readable
     # 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.
     # in the Nix store. This is for demonstration purpose only, do not use this in production.
     credentialsFile = "${pkgs.writeText "inwx-creds" ''
     environmentFile = "${pkgs.writeText "inwx-creds" ''
       INWX_USERNAME=xxxxxxxxxx
       INWX_USERNAME=xxxxxxxxxx
       INWX_PASSWORD=yyyyyyyyyy
       INWX_PASSWORD=yyyyyyyyyy
Line 21: 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.


== Usage ==
=== With Cloudflare as DNS provider ===


After successfull generation, certificates can be found in the directory <code>/var/lib/acme</code>. To use certificates in other applications, permissions can be adjusted by setting a group name as a string or reference it.
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>
 
== 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">
<syntaxhighlight lang="nix">
security.acme.certs."mx1.example.org".group = config.services.maddy.group;
security.acme.certs.${domainName}.reloadServices = [
  "prosody"
  "coturn"
  "nginx"
  "dovecot2"
  "postfix"
];
</syntaxhighlight>
</syntaxhighlight>


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