How do I merge 2 packages declaratively using a nix expression?

I am trying to build the apache web-server with modsecurity included by default. The modsecurity_standalone package removes the default behavior of copying the library file into apache’s modules folder.
I stumbled upon a way by overriding the attributes passed to mkDerivation:

let
  pkgs = ((import (fetchTarball https://github.com/NixOS/nixpkgs/archive/staging.tar.gz)) {});
  modsecurityApache = pkgs.modsecurity_standalone.overrideAttrs (
    oldAttrs: rec {
      # disable the patch file that removes copying this into
      # apache's modules directory
      patches = [];
    }
  );
in
pkgs.mkShell {
  buildInputs = [ pkgs.apacheHttpd modsecurityApache ];
}

I created this snippet as my-httpd.nix. I then ran nix build -f my-httpd.nix.
It fails when compiling the modsecurityApache derivation:

$ nix build -f innovu-httpd.nix
[12.5/0.0 MiB DL] downloading 'https://github.com/NixOS/nixpkgs/archive/staging.[15.1/0.0 MiB DL] downloading 'https://github.com/NixOS/nixpkgs/archive/staging.[16.3/0.0 MiB DL] downloading 'https://github.com/NixOS/builder for '/nix/store/5s997iwck55mlg2fbv3zg7hzmk05zwc3-modsecurity-2.9.3.drv' failed with exit code 2; last 10 log lines:
  make[3]: Entering directory '/build/modsecurity-2.9.3/apache2'
  Removing unused static libraries...
  install: cannot create regular file '/nix/store/9z4lsps5k2jncsxbv9625hyppy5z8nkd-apache-httpd-2.4.41/modules/mod_security2.so': Permission denied
  make[3]: *** [Makefile:1207: install-exec-hook] Error 1
  make[3]: Leaving directory '/build/modsecurity-2.9.3/apache2'
  make[2]: *** [Makefile:1144: install-exec-am] Error 2
  make[2]: Leaving directory '/build/modsecurity-2.9.3/apache2'
  make[1]: *** [Makefile:1087: install-am] Error 2
  make[1]: Leaving directory '/build/modsecurity-2.9.3/apache2'
  make: *** [Makefile:501: install-recursive] Error 1
cannot build derivation '/nix/store/7xq6cxl021a6g1xg6jjw5p7n2vhzm0v1-nix-shell.drv': 1 dependencies couldn't be built
[2 built (1 failed), 89 copied (419.4 MiB), 127.4 MiB DL]
error: build of '/nix/store/7xq6cxl021a6g1xg6jjw5p7n2vhzm0v1-nix-shell.drv' failed

The key error seems to be this line:

install: cannot create regular file '/nix/store/9z4lsps5k2jncsxbv9625hyppy5z8nkd-apache-httpd-2.4.41/modules/mod_security2.so': Permission denied

Given nix isolates dependencies this would make sense. A later derivation should not be able to mutate a prior one’s directory structure. But I require each package’s directory structure to be merged into one.

  • How would you adjust the above nix expression to achieve what I’m after?
  • Are there better ways of solving this problem using alternative nix packaging hooks?

I think there’s a buildEnv function in Nixpkgs, which does what you want - create a shared tree, where N packages are symlinked together.

It doesn’t sound like you’re using NixOS… but in case you are you can look at services.httpd.extraModules.

Thanks for the tip! I tried using buildEnv with:

let
  pkgs = ((import (fetchTarball https://github.com/NixOS/nixpkgs/archive/staging.tar.gz)) {});
in
pkgs.buildEnv {
  name = "my-httpd";
  paths = [ pkgs.apacheHttpd pkgs.modsecurity_standalone ];
}

nix build -f my-httpd.nix works, and checking result shows the outputs but not merged as I needed. Reading the manual I was unsure how to copy or merge the given paths. I tried setting the pathsToLink and extraOutputsToInstall values as well.

@aanderse correct, I am not using nixOS. I’ll look at those expressions for more help.

I’ll probably end up generating a custom configuration file that specifies the full store path to the modsecurity shared object. That seems easy enough using the postBuild hook.

That is a reasonable idea, and the equivalent of what services.httpd.extraModules does.

1 Like

I ended up with the following in a custom-httpd.nix:

let
  pkgs = ((import (fetchTarball https://github.com/NixOS/nixpkgs/archive/staging.tar.gz))  {});
in
pkgs.buildEnv {
  name = "custom-httpd";
  paths = [ pkgs.apacheHttpd pkgs.modsecurity_standalone ];
  postBuild = ''
    full_path=$(readlink -f $out/lib/mod_security2.so);
    apache_root=$(readlink -f $out/modules/mod_actions.so | sed -e 's/modules.*$//');
    custom_conf=$out/custom-httpd.conf;
    # must use double quotes to substitute values
    echo "ServerRoot $apache_root" >> $custom_conf;
    echo 'LoadModule unique_id_module modules/mod_unique_id.so' >> $custom_conf;
    echo "LoadModule security2_module $full_path" >> $custom_conf;
  '';
}

Then I easily test it with nix build -f custom-httpd.nix and install it using nix-env -f custom-httpd.nix -i.
It creates the custom-httpd.conf with the necessary derivation paths into the root of the users install profile. From there I can imperatively configure the rest of a given non-nixOS linux system.

2 Likes