Allow using existing ACME certificates
Add a certificate scheme for using an existing ACME certificate without setting up Nginx. Also use names instead of magic numbers for certificate schemes.
This commit is contained in:
parent
42c5564791
commit
a948c49ca7
7 changed files with 49 additions and 35 deletions
50
default.nix
50
default.nix
|
@ -48,7 +48,11 @@ in
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
example = [ "imap.example.com" "pop3.example.com" ];
|
example = [ "imap.example.com" "pop3.example.com" ];
|
||||||
default = [];
|
default = [];
|
||||||
description = "Secondary domains and subdomains for which it is necessary to generate a certificate.";
|
description = ''
|
||||||
|
({option}`mailserver.certificateScheme` == `acme-nginx`)
|
||||||
|
|
||||||
|
Secondary domains and subdomains for which it is necessary to generate a certificate.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
messageSizeLimit = mkOption {
|
messageSizeLimit = mkOption {
|
||||||
|
@ -448,19 +452,26 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
certificateScheme = mkOption {
|
certificateScheme = let
|
||||||
type = types.enum [ 1 2 3 ];
|
schemes = [ "manual" "selfsigned" "acme-nginx" "acme" ];
|
||||||
default = 2;
|
translate = i: warn "setting mailserver.certificateScheme by number is deprecated, please use names instead"
|
||||||
|
(builtins.elemAt schemes (i - 1));
|
||||||
|
in mkOption {
|
||||||
|
type = with types; coercedTo (enum [ 1 2 3 ]) translate (enum schemes);
|
||||||
|
default = "selfsigned";
|
||||||
description = ''
|
description = ''
|
||||||
Certificate Files. There are three options for these.
|
The scheme to use for managing TLS certificates:
|
||||||
|
|
||||||
1) You specify locations and manually copy certificates there.
|
1. `manual`: you specify locations via {option}`mailserver.certificateFile` and
|
||||||
2) You let the server create new (self signed) certificates on the fly.
|
{option}`mailserver.keyFile` and manually copy certificates there.
|
||||||
3) You let the server create a certificate via `Let's Encrypt`. Note that
|
2. `selfsigned`: you let the server create new (self-signed) certificates on the fly.
|
||||||
this implies that a stripped down webserver has to be started. This also
|
3. `acme-nginx`: you let the server request certificates from [Let's Encrypt](https://letsencrypt.org)
|
||||||
implies that the FQDN must be set as an `A` record to point to the IP of
|
via NixOS' ACME module. By default, this will set up a stripped-down Nginx server for
|
||||||
the server. In particular port 80 on the server will be opened. For details
|
{option}`mailserver.fqdn` and open port 80. For this to work, the FQDN must be properly
|
||||||
on how to set up the domain records, see the guide in the readme.
|
configured to point to your server (see the [setup guide](setup-guide.rst) for more information).
|
||||||
|
4. `acme`: you already have an ACME certificate set up (for example, you're already running a TLS-enabled
|
||||||
|
Nginx server on the FQDN). This is better than `manual` because the appropriate services will be reloaded
|
||||||
|
when the certificate is renewed.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -468,8 +479,9 @@ in
|
||||||
type = types.path;
|
type = types.path;
|
||||||
example = "/root/mail-server.crt";
|
example = "/root/mail-server.crt";
|
||||||
description = ''
|
description = ''
|
||||||
Scheme 1)
|
({option}`mailserver.certificateScheme` == `manual`)
|
||||||
Location of the certificate
|
|
||||||
|
Location of the certificate.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -477,8 +489,9 @@ in
|
||||||
type = types.path;
|
type = types.path;
|
||||||
example = "/root/mail-server.key";
|
example = "/root/mail-server.key";
|
||||||
description = ''
|
description = ''
|
||||||
Scheme 1)
|
({option}`mailserver.certificateScheme` == `manual`)
|
||||||
Location of the key file
|
|
||||||
|
Location of the key file.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -486,8 +499,9 @@ in
|
||||||
type = types.path;
|
type = types.path;
|
||||||
default = "/var/certs";
|
default = "/var/certs";
|
||||||
description = ''
|
description = ''
|
||||||
Scheme 2)
|
({option}`mailserver.certificateScheme` == `selfsigned`)
|
||||||
This is the folder where the certificate will be created. The name is
|
|
||||||
|
This is the folder where the self-signed certificate will be created. The name is
|
||||||
hardcoded to "cert-DOMAIN.pem" and "key-DOMAIN.pem" and the
|
hardcoded to "cert-DOMAIN.pem" and "key-DOMAIN.pem" and the
|
||||||
certificate is valid for 10 years.
|
certificate is valid for 10 years.
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -81,7 +81,7 @@ these should be the most common ones.
|
||||||
|
|
||||||
# Use Let's Encrypt certificates. Note that this needs to set up a stripped
|
# Use Let's Encrypt certificates. Note that this needs to set up a stripped
|
||||||
# down nginx and opens port 80.
|
# down nginx and opens port 80.
|
||||||
certificateScheme = 3;
|
certificateScheme = "acme-nginx";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,22 +21,22 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# cert :: PATH
|
# cert :: PATH
|
||||||
certificatePath = if cfg.certificateScheme == 1
|
certificatePath = if cfg.certificateScheme == "manual"
|
||||||
then cfg.certificateFile
|
then cfg.certificateFile
|
||||||
else if cfg.certificateScheme == 2
|
else if cfg.certificateScheme == "selfsigned"
|
||||||
then "${cfg.certificateDirectory}/cert-${cfg.fqdn}.pem"
|
then "${cfg.certificateDirectory}/cert-${cfg.fqdn}.pem"
|
||||||
else if cfg.certificateScheme == 3
|
else if cfg.certificateScheme == "acme" || cfg.certificateScheme == "acme-nginx"
|
||||||
then "${config.security.acme.certs.${cfg.fqdn}.directory}/fullchain.pem"
|
then "${config.security.acme.certs.${cfg.fqdn}.directory}/fullchain.pem"
|
||||||
else throw "Error: Certificate Scheme must be in { 1, 2, 3 }";
|
else throw "unknown certificate scheme";
|
||||||
|
|
||||||
# key :: PATH
|
# key :: PATH
|
||||||
keyPath = if cfg.certificateScheme == 1
|
keyPath = if cfg.certificateScheme == "manual"
|
||||||
then cfg.keyFile
|
then cfg.keyFile
|
||||||
else if cfg.certificateScheme == 2
|
else if cfg.certificateScheme == "selfsigned"
|
||||||
then "${cfg.certificateDirectory}/key-${cfg.fqdn}.pem"
|
then "${cfg.certificateDirectory}/key-${cfg.fqdn}.pem"
|
||||||
else if cfg.certificateScheme == 3
|
else if cfg.certificateScheme == "acme" || cfg.certificateScheme == "acme-nginx"
|
||||||
then "${config.security.acme.certs.${cfg.fqdn}.directory}/key.pem"
|
then "${config.security.acme.certs.${cfg.fqdn}.directory}/key.pem"
|
||||||
else throw "Error: Certificate Scheme must be in { 1, 2, 3 }";
|
else throw "unknown certificate scheme";
|
||||||
|
|
||||||
passwordFiles = let
|
passwordFiles = let
|
||||||
mkHashFile = name: hash: pkgs.writeText "${builtins.hashString "sha256" name}-password-hash" hash;
|
mkHashFile = name: hash: pkgs.writeText "${builtins.hashString "sha256" name}-password-hash" hash;
|
||||||
|
|
|
@ -23,6 +23,6 @@ in
|
||||||
config = with cfg; lib.mkIf enable {
|
config = with cfg; lib.mkIf enable {
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
dovecot opendkim openssh postfix rspamd
|
dovecot opendkim openssh postfix rspamd
|
||||||
] ++ (if certificateScheme == 2 then [ openssl ] else []);
|
] ++ (if certificateScheme == "selfsigned" then [ openssl ] else []);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
{ config, pkgs, lib, ... }:
|
{ config, lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.mailserver;
|
cfg = config.mailserver;
|
||||||
|
@ -31,7 +31,7 @@ in
|
||||||
++ lib.optional enablePop3 110
|
++ lib.optional enablePop3 110
|
||||||
++ lib.optional enablePop3Ssl 995
|
++ lib.optional enablePop3Ssl 995
|
||||||
++ lib.optional enableManageSieve 4190
|
++ lib.optional enableManageSieve 4190
|
||||||
++ lib.optional (certificateScheme == 3) 80;
|
++ lib.optional (certificateScheme == "acme-nginx") 80;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ let
|
||||||
acmeRoot = "/var/lib/acme/acme-challenge";
|
acmeRoot = "/var/lib/acme/acme-challenge";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (cfg.enable && cfg.certificateScheme == 3) {
|
config = lib.mkIf (cfg.enable && (cfg.certificateScheme == "acme" || cfg.certificateScheme == "acme-nginx")) {
|
||||||
services.nginx = {
|
services.nginx = lib.mkIf (cfg.certificateScheme == "acme-nginx") {
|
||||||
enable = true;
|
enable = true;
|
||||||
virtualHosts."${cfg.fqdn}" = {
|
virtualHosts."${cfg.fqdn}" = {
|
||||||
serverName = cfg.fqdn;
|
serverName = cfg.fqdn;
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
let
|
let
|
||||||
cfg = config.mailserver;
|
cfg = config.mailserver;
|
||||||
certificatesDeps =
|
certificatesDeps =
|
||||||
if cfg.certificateScheme == 1 then
|
if cfg.certificateScheme == "manual" then
|
||||||
[]
|
[]
|
||||||
else if cfg.certificateScheme == 2 then
|
else if cfg.certificateScheme == "selfsigned" then
|
||||||
[ "mailserver-selfsigned-certificate.service" ]
|
[ "mailserver-selfsigned-certificate.service" ]
|
||||||
else
|
else
|
||||||
[ "acme-finished-${cfg.fqdn}.target" ];
|
[ "acme-finished-${cfg.fqdn}.target" ];
|
||||||
|
@ -29,7 +29,7 @@ in
|
||||||
{
|
{
|
||||||
config = with cfg; lib.mkIf enable {
|
config = with cfg; lib.mkIf enable {
|
||||||
# Create self signed certificate
|
# Create self signed certificate
|
||||||
systemd.services.mailserver-selfsigned-certificate = lib.mkIf (cfg.certificateScheme == 2) {
|
systemd.services.mailserver-selfsigned-certificate = lib.mkIf (cfg.certificateScheme == "selfsigned") {
|
||||||
after = [ "local-fs.target" ];
|
after = [ "local-fs.target" ];
|
||||||
script = ''
|
script = ''
|
||||||
# Create certificates if they do not exist yet
|
# Create certificates if they do not exist yet
|
||||||
|
|
Loading…
Reference in a new issue