nixos/nginx: validate syntax of config file at build time
Shamelessly stolen from nixcloud-webservices: https://github.com/nixcloud/nixcloud-webservices/blob/master/modules/web/webserver/lib/nginx_check_config.nix The nixos test testing the behavior of nginx in case of faulty config would not build with this change (on purpose), so I modified it so that the failure is not syntactic.
This commit is contained in:
@@ -241,7 +241,7 @@ let
|
|||||||
|
|
||||||
configPath = if cfg.enableReload
|
configPath = if cfg.enableReload
|
||||||
then "/etc/nginx/nginx.conf"
|
then "/etc/nginx/nginx.conf"
|
||||||
else configFile;
|
else finalConfigFile;
|
||||||
|
|
||||||
execCommand = "${cfg.package}/bin/nginx -c '${configPath}'";
|
execCommand = "${cfg.package}/bin/nginx -c '${configPath}'";
|
||||||
|
|
||||||
@@ -391,6 +391,38 @@ let
|
|||||||
);
|
);
|
||||||
|
|
||||||
mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
|
mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
|
||||||
|
|
||||||
|
snakeOilCert = pkgs.runCommand "nginx-config-validate-cert" { nativeBuildInputs = [ pkgs.openssl.bin ]; } ''
|
||||||
|
mkdir $out
|
||||||
|
openssl genrsa -des3 -passout pass:xxxxx -out server.pass.key 2048
|
||||||
|
openssl rsa -passin pass:xxxxx -in server.pass.key -out $out/server.key
|
||||||
|
openssl req -new -key $out/server.key -out server.csr \
|
||||||
|
-subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
|
||||||
|
openssl x509 -req -days 1 -in server.csr -signkey $out/server.key -out $out/server.crt
|
||||||
|
'';
|
||||||
|
validatedConfigFile = pkgs.runCommand "validated-nginx.conf" { nativeBuildInputs = [ cfg.package ]; } ''
|
||||||
|
# nginx absolutely wants to read the certificates even when told to only validate config, so let's provide fake certs
|
||||||
|
sed ${configFile} \
|
||||||
|
-e "s|ssl_certificate .*;|ssl_certificate ${snakeOilCert}/server.crt;|g" \
|
||||||
|
-e "s|ssl_trusted_certificate .*;|ssl_trusted_certificate ${snakeOilCert}/server.crt;|g" \
|
||||||
|
-e "s|ssl_certificate_key .*;|ssl_certificate_key ${snakeOilCert}/server.key;|g" \
|
||||||
|
> conf
|
||||||
|
|
||||||
|
LD_PRELOAD=${pkgs.libredirect}/lib/libredirect.so \
|
||||||
|
NIX_REDIRECTS="/etc/resolv.conf=/dev/null" \
|
||||||
|
nginx -t -c $(readlink -f ./conf) > out 2>&1 || true
|
||||||
|
if ! grep -q "syntax is ok" out; then
|
||||||
|
echo nginx config validation failed.
|
||||||
|
echo config was ${configFile}.
|
||||||
|
echo 'in case of false positive, set `services.nginx.validateConfig` to false.'
|
||||||
|
echo nginx output:
|
||||||
|
cat out
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cp ${configFile} $out
|
||||||
|
'';
|
||||||
|
|
||||||
|
finalConfigFile = if cfg.validateConfig then validatedConfigFile else configFile;
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -489,6 +521,15 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
validateConfig = mkOption {
|
||||||
|
default = pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform;
|
||||||
|
defaultText = literalExpression "pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform";
|
||||||
|
type = types.bool;
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
Validate the generated nginx config at build time. The check is not very robust and can be disabled in case of false positives. This is notably the case when cross-compiling or when using `include` with files outside of the store.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
additionalModules = mkOption {
|
additionalModules = mkOption {
|
||||||
default = [];
|
default = [];
|
||||||
type = types.listOf (types.attrsOf types.anything);
|
type = types.listOf (types.attrsOf types.anything);
|
||||||
@@ -1027,7 +1068,7 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
environment.etc."nginx/nginx.conf" = mkIf cfg.enableReload {
|
environment.etc."nginx/nginx.conf" = mkIf cfg.enableReload {
|
||||||
source = configFile;
|
source = finalConfigFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
# This service waits for all certificates to be available
|
# This service waits for all certificates to be available
|
||||||
@@ -1046,7 +1087,7 @@ in
|
|||||||
# certs are updated _after_ config has been reloaded.
|
# certs are updated _after_ config has been reloaded.
|
||||||
before = sslTargets;
|
before = sslTargets;
|
||||||
after = sslServices;
|
after = sslServices;
|
||||||
restartTriggers = optionals (cfg.enableReload) [ configFile ];
|
restartTriggers = optionals (cfg.enableReload) [ finalConfigFile ];
|
||||||
# Block reloading if not all certs exist yet.
|
# Block reloading if not all certs exist yet.
|
||||||
# Happens when config changes add new vhosts/certs.
|
# Happens when config changes add new vhosts/certs.
|
||||||
unitConfig.ConditionPathExists = optionals (sslServices != []) (map (certName: certs.${certName}.directory + "/fullchain.pem") dependentCertNames);
|
unitConfig.ConditionPathExists = optionals (sslServices != []) (map (certName: certs.${certName}.directory + "/fullchain.pem") dependentCertNames);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
|
|||||||
|
|
||||||
specialisation.reloadWithErrorsSystem.configuration = {
|
specialisation.reloadWithErrorsSystem.configuration = {
|
||||||
services.nginx.package = pkgs.nginxMainline;
|
services.nginx.package = pkgs.nginxMainline;
|
||||||
services.nginx.virtualHosts."!@$$(#*%".locations."~@#*$*!)".proxyPass = ";;;";
|
services.nginx.virtualHosts."hello".extraConfig = "access_log /does/not/exist.log;";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user