Define .htaccess files declaratively

High respected community,

I need to configure CGI on Apache Web Server, running on NIxOS. I have successfully loaded CGI module into Apache, but I can’t access scripts, which CGI have to execute.

When trying to access https://mydomain.com/cgi-bin/someScript

I obtain HTTP response 403(Forbidden)

As far as I understand, I have to configure .htaccess file, but I haven’t found any appropriate option in httpd module.

Please advice, how should I configure .htaccess file - declaratively, with Nix language

.htaccess files are mainly for people who don’t have access to the Apache config. You’re the sysadmin in this situation so you don’t need a .htaccess file. Can you let know what you’re trying to do and what nix configuration you have so far?

Here is my config:

{ pkgs, ... }:
{
  services = {
    httpd = {
      enable = true;
      adminAddr = "mail@mydomain.com";
      user = "wwwrun";
      group = "wwwrun";
      mpm = "event";
      extraModules = 
      [
        "cgid"
      ];
      virtualHosts = {
        "mydomain.com" = {
          listen = [ { port = 1256; ssl = true; } ];
          forceSSL = true;
          enableACME = true;
	  acmeRoot = "/var/lib/acme/acme-challenge";
          http2 = true;
	};
      };
      extraConfig = ''
ScriptAlias "/cgi-bin/" "/var/cgi/scripts"
      '';
    };
  };
  users.users."wwwrun" = {
    extraGroups = [ "acmerecievers" ];
  };
}

As you can see, /var/cgi/scripts is aliased to https://mydomain.com/cgi-bin/

Let’s assume, that I have hello shell script, that placed in /var/cgi/scripts I need this script to be executed, when I open https://mydomain.com:1256/cgi-bin/hello, but instead, I receive 403 HTTP response.

I ensured, that /var/cgi/scripts belongs to wwwrun user.

When I’m trying to trigger helloscript, I’m getting this message in apache error log

[Thu Nov 12 15:58:21.801910 2020] [authz_core:error] [pid 8915:tid 139815394662144] [client xx.xx.xx.xx:xxxxx] AH01630: client denied by server configuration: /var/cgi/scripts

Add the following to your extraConfig:

<Directory /var/cgi/scripts>
  Require all granted
</Directory>

Unrelated but you can remove cgid from extraModules as mentioned in the other thread - this comes out of the box :+1:

Unfortunately, nothing changed :frowning: Apache continues to return 403 response

Sure I’ll hop on a computer today at some point and get you the exact solution.

Here is the solution I came up with:

  services.httpd.enable = true;
  services.httpd.adminAddr = "webmaster@example.org";
  services.httpd.virtualHosts.localhost = {
    extraConfig = ''
      ScriptAlias /cgi-bin/ /var/cgi/scripts/

      <Directory /var/cgi/scripts>
        DirectoryIndex index.pl
        Require all granted
      </Directory>
    '';
  };

If you want a perfectly (one time) reproducible solution you can test in a NixOS VM take the above and add this very dirty hack:

  systemd.tmpfiles.rules =
    let
      script = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] ''
        #! ${pkgs.perl}/bin/perl
        print "Content-type: text/html\n\n";
        print "<html><body><h1>Hello World!";
        print "</h1></body></html>\n";
      '';
    in
      [
        "d /var/cgi"
        "d /var/cgi/scripts"
        "f /var/cgi/scripts/index.pl 0755 - - - ${script}"
      ];

From here:

~> curl localhost:80/cgi-bin/                                                                                                                                                   
<html><body><h1>Hello World!</h1></body></html>

In summary:

  • make sure index.pl (or whatever your cgi program is) has executable bit set
  • make sure your scripts directory has an apache <Directory> directive with Require all granted (or equivalent, depending on your needs)
  • under no situation ever should you or anyone use the systemd.tmpfiles.rules hack I have written here, other than for a disposable VM test if the apache configuration alone I provided doesn’t work

Let me know if you have any issues.

1 Like

Thanks for the solution. Unfortunately, it does not seems to worrk for me. I’ve rewritten my config, to include those solution, you provided me. Here’s what I have now:

{ pkgs, ... }:
{
  systemd.tmpfiles.rules =
    let
      script = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] ''
        #! ${pkgs.perl}/bin/perl
        print "Content-type: text/html\n\n";
        print "<html><body><h1>Hello World!";
        print "</h1></body></html>\n";
      '';
    in
      [
        "d /var/cgi"
        "d /var/cgi/scripts"
        "f /var/cgi/scripts/index.pl 0755 - - - ${script}"
      ];
  services = {
    httpd = {
      enable = true;
      adminAddr = "mail@mydomain.com";
      user = "wwwrun";
      group = "wwwrun";
      mpm = "event";
      extraModules = 
      [
        "cgid"
      ];
      virtualHosts = {
        "mydomain.com" = {
          listen = [ { port = 1256; ssl = true; } ];
          forceSSL = true;
          enableACME = true;
          acmeRoot = "/var/lib/acme/acme-challenge";
          http2 = true;
        };
      };
      extraConfig = ''
ScriptAlias "/cgi-bin/" "/var/cgi/scripts"
<Directory /var/cgi/scripts>
  Require all granted
  DirectoryIndex index.pl
</Directory>
      '';
    };
  };
  users.users."wwwrun" = {
    extraGroups = [ "acmerecievers" ];
  };
}

but nothing seems have changed. I still receive 403 HTTP response and httpd writes this to error log:

[Thu Nov 12 21:48:50.263303 2020] [authz_core:error] [pid 23265:tid 140630193964800] [client xx.xx.xx.xx:xxxxx] AH01630: client denied by server configuration: /var/cgi/scriptsindex.pl

But what bothers me in this situation is that apache writes strange path to the error log:

/var/cgi/scriptsindex.pl

(there’s no / between scripts and index.pl)

Use

instead of ScriptAlias "/cgi-bin/" "/var/cgi/scripts"

1 Like

Yup. Nail on the head there. Missing the trailing slash.

Thanks you all for help, investigation and feedback.
CGI is now working :slight_smile:

Great! Just be sure to delete that tmpfiles monstrosity and never use it again :laughing: