Skip to content

Instantly share code, notes, and snippets.

@blueforesticarus
Created March 30, 2023 07:25
Show Gist options
  • Save blueforesticarus/b4125319d5afbf1db6ebdc5466011c55 to your computer and use it in GitHub Desktop.
Save blueforesticarus/b4125319d5afbf1db6ebdc5466011c55 to your computer and use it in GitHub Desktop.
A nix config for apache, with all php files being sent to php-fpm. Also Perl. Also simple user/pass protection.
{ config, pkgs, lib, ... }:
let
test_domain = "test.example.com";
perlEnv = pkgs.perl.withPackages (p: with p; [
CGI
]);
in
{
environment.systemPackages = [
pkgs.php
perlEnv
];
# This is the lets encrypt config, this what generates free ssl certs
# https://nixos.org/manual/nixos/stable/#module-security-acme-nginx
security.acme = {
acceptTerms = true;
defaults = {
email = "foo@whatever.com";
# This would enable a larger, more secure key, but I'm disabling for fear of older browsers
# default is ex256 https://go-acme.github.io/lego/usage/cli/options/
# keyType = "ec384";/* */
};
};
# setup apache, the webserver
# nginx is less cringe, but doesn't support .htaccess files, which myapp seems to need
services.httpd = {
enable = true;
enablePHP = false; # use php-fpm
enablePerl = true;
# run apache as example user to simplify permissions (XXX fixme)
user = "example";
extraModules = [
"proxy"
"proxy_fcgi"
{ name = "fastcgi"; path = "${pkgs.apacheHttpdPackages.mod_fastcgi}/modules/mod_fastcgi.so"; }
"actions"
];
virtualHosts = {
# Lets make a second virtualhost for testing
"${test_domain}" = rec {
enableACME = true; # use Let's Encrypt config above for ssl cert
forceSSL = true; # force https (http will NOT load if true)
documentRoot = "/home/example/${test_domain}/public_html"; # seperate folder for the test site
# put the test version of the site behind a password
# apache is cringe https://httpd.apache.org/docs/2.4/howto/auth.html#gettingitworking
# note that for password it must be a Directory section NOT a Location section
extraConfig = ''
<Directory "${documentRoot}">
AuthType Basic
AuthName "Restricted Content"
AuthBasicProvider file
AuthUserFile "/etc/httpd/passwd"
Require valid-user
Options +ExecCGI
AddHandler cgi-script .cgi
DirectoryIndex index.html index.cgi index.php
AllowOverride All
</Directory>
# linode of all places: https://www.linode.com/docs/guides/install-php-fpm-and-apache-on-debian-8/#before-you-begin
# I could not get the more often suggested ProxyMatch method to work
AddType application/x-httpd-fastphp8 .php
Action application/x-httpd-fastphp8 /php8-fcgi
Alias /php8-fcgi ${documentRoot}/php_dummy
FastCgiExternalServer ${documentRoot}/php_dummy -socket ${config.services.phpfpm.pools.myapp.socket} -pass-header Authorization
<Directory ${documentRoot}/php_dummy>
Require all granted
</Directory>
'';
};
};
};
# put perl and sendmail in apache's search PATH
# TODO sendmail
systemd.services.httpd.path = [ perlEnv ];
# login for test site password
# create with `htpasswd -c /etc/httpd/ admin`
environment.etc."httpd/passwd".text = "admin:<insert hash>";
systemd.services."phpfpm-myapp" = {
serviceConfig.ProtectHome = lib.mkForce false;
};
# forward all myapp php requests to phpfpm
services.phpfpm.pools.myapp = {
user = "example"; #run myapp as example, simple but not very advisable
group = "users";
settings = {
pm = "dynamic";
"listen.owner" = config.services.httpd.user;
# https://spot13.com/pmcalculator/
"pm.max_children" = 2;
"pm.start_servers" = 1;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 1;
"pm.max_requests" = 500;
"catch_workers_output" = 1;
"php_flag[display_errors]" = "on";
#"php_admin_value[error_log]" = "/var/log/php-fpm/default/error.log";
"php_admin_flag[log_errors]" = "on";
};
phpEnv."PATH" = lib.makeBinPath [ pkgs.php ];
};
# Tweak to ensure web server starts AFTER networking is initialized, likely unnessecary
systemd.services.httpd = {
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
};
# install mysql (mariadb should be compatible)
services.mysql = {
enable = true;
package = pkgs.mariadb;
ensureUsers = [
{
# create mysql user with same name as our primary user "example"
# we don't set a password. apache/myapp.php run as example; mysql will allow passwordless local access if the username matches
name = "example";
ensurePermissions = {
"*.*" = "ALL PRIVILEGES";
};
#TODO restrict permissions on other tables
}
];
# create db "myappdb" if it does not exist
ensureDatabases = ["myappdb"];
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment