Something went wrong. Try again.
What happens if I push all of nixpkgs
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653{ config, pkgs, lib, ...}:
with lib;
let cfg = config.services.wordpress; eachSite = cfg.sites; user = "wordpress"; webserver = config.services.${cfg.webserver}; stateDir = hostName: "/var/lib/wordpress/${hostName}";
pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec { pname = "wordpress-${hostName}"; version = src.version; src = cfg.package;
installPhase = '' mkdir -p $out cp -r * $out/
# symlink the wordpress config ln -s ${wpConfig hostName cfg} $out/share/wordpress/wp-config.php # symlink uploads directory ln -s ${cfg.uploadsDir} $out/share/wordpress/wp-content/uploads ln -s ${cfg.fontsDir} $out/share/wordpress/wp-content/fonts
# https://github.com/NixOS/nixpkgs/pull/53399 # # Symlinking works for most plugins and themes, but Avada, for instance, fails to # understand the symlink, causing its file path stripping to fail. This results in # requests that look like: https://example.com/wp-content//nix/store/...plugin/path/some-file.js # Since hard linking directories is not allowed, copying is the next best thing.
# copy additional plugin(s), theme(s) and language(s) ${concatStringsSep "\n" ( mapAttrsToList ( name: theme: "cp -r ${theme} $out/share/wordpress/wp-content/themes/${name}" ) cfg.themes )} ${concatStringsSep "\n" ( mapAttrsToList ( name: plugin: "cp -r ${plugin} $out/share/wordpress/wp-content/plugins/${name}" ) cfg.plugins )} ${concatMapStringsSep "\n" ( language: "cp -r ${language} $out/share/wordpress/wp-content/languages/" ) cfg.languages} ''; };
mergeConfig = cfg: { # wordpress is installed onto a read-only file system DISALLOW_FILE_EDIT = true; AUTOMATIC_UPDATER_DISABLED = true; DB_NAME = cfg.database.name; DB_HOST = "${cfg.database.host}:${ if cfg.database.socket != null then cfg.database.socket else toString cfg.database.port }"; DB_USER = cfg.database.user; DB_CHARSET = "utf8"; # Always set DB_PASSWORD even when passwordFile is not set. This is the # default Wordpress behaviour. DB_PASSWORD = if (cfg.database.passwordFile != null) then { _file = cfg.database.passwordFile; } else ""; } // cfg.settings;
wpConfig = hostName: cfg: let conf_gen = c: mapAttrsToList (k: v: "define('${k}', ${mkPhpValue v});") cfg.mergedConfig; in pkgs.writeTextFile { name = "wp-config-${hostName}.php"; text = '' <?php $table_prefix = '${cfg.database.tablePrefix}';
require_once('${stateDir hostName}/secret-keys.php');
${cfg.extraConfig} ${concatStringsSep "\n" (conf_gen cfg.mergedConfig)}
if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php'); ?> ''; checkPhase = "${pkgs.php}/bin/php --syntax-check $target"; };
mkPhpValue = v: let isHasAttr = s: isAttrs v && hasAttr s v; # "you're escaped" -> "'you\'re escaped'" # https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.single toPhpString = s: "'${escape [ "'" "\\" ] s}'"; in if isString v then toPhpString v # NOTE: If any value contains a , (comma) this will not get escaped else if isList v && strings.isConvertibleWithToString v then toPhpString (concatMapStringsSep "," toString v) else if isInt v then toString v else if isBool v then boolToString v else if isHasAttr "_file" then "trim(file_get_contents(${toPhpString (toString v._file)}))" else if isHasAttr "_raw" then v._raw else abort "The Wordpress config value ${lib.generators.toPretty { } v} can not be encoded.";
secretsVars = [ "AUTH_KEY" "SECURE_AUTH_KEY" "LOGGED_IN_KEY" "NONCE_KEY" "AUTH_SALT" "SECURE_AUTH_SALT" "LOGGED_IN_SALT" "NONCE_SALT" ]; secretsScript = hostStateDir: '' # The match in this line is not a typo, see https://github.com/NixOS/nixpkgs/pull/124839 grep -q "LOOGGED_IN_KEY" "${hostStateDir}/secret-keys.php" && rm "${hostStateDir}/secret-keys.php" if ! test -e "${hostStateDir}/secret-keys.php"; then umask 0177 echo "<?php" >> "${hostStateDir}/secret-keys.php" ${concatMapStringsSep "\n" (var: '' echo "define('${var}', '`tr -dc a-zA-Z0-9 </dev/urandom | head -c 64`');" >> "${hostStateDir}/secret-keys.php" '') secretsVars} echo "?>" >> "${hostStateDir}/secret-keys.php" chmod 440 "${hostStateDir}/secret-keys.php" fi '';
siteOpts = { lib, name, config, ... }: { options = { package = mkPackageOption pkgs "wordpress" { };
uploadsDir = mkOption { type = types.path; default = "/var/lib/wordpress/${name}/uploads"; description = '' This directory is used for uploads of pictures. The directory passed here is automatically created and permissions adjusted as required. ''; };
fontsDir = mkOption { type = types.path; default = "/var/lib/wordpress/${name}/fonts"; description = '' This directory is used to download fonts from a remote location, e.g. to host google fonts locally. ''; };
plugins = mkOption { type = with types; coercedTo (listOf path) ( l: warn "setting this option with a list is deprecated" listToAttrs ( map (p: nameValuePair (p.name or (throw "${p} does not have a name")) p) l ) ) (attrsOf path); default = { }; description = '' Path(s) to respective plugin(s) which are copied from the 'plugins' directory.
::: {.note} These plugins need to be packaged before use, see example. ::: ''; example = literalExpression '' { inherit (pkgs.wordpressPackages.plugins) embed-pdf-viewer-plugin; } ''; };
themes = mkOption { type = with types; coercedTo (listOf path) ( l: warn "setting this option with a list is deprecated" listToAttrs ( map (p: nameValuePair (p.name or (throw "${p} does not have a name")) p) l ) ) (attrsOf path); default = { inherit (pkgs.wordpressPackages.themes) twentytwentyfive; }; defaultText = literalExpression "{ inherit (pkgs.wordpressPackages.themes) twentytwentyfive; }"; description = '' Path(s) to respective theme(s) which are copied from the 'theme' directory.
::: {.note} These themes need to be packaged before use, see example. ::: ''; example = literalExpression '' { inherit (pkgs.wordpressPackages.themes) responsive-theme; } ''; };
languages = mkOption { type = types.listOf types.path; default = [ ]; description = '' List of path(s) to respective language(s) which are copied from the 'languages' directory. ''; example = literalExpression '' [ # Let's package the German language. # For other languages try to replace language and country code in the download URL with your desired one. # Reference https://translate.wordpress.org for available translations and # codes. (pkgs.stdenv.mkDerivation { name = "language-de"; src = pkgs.fetchurl { url = "https://de.wordpress.org/wordpress-''${pkgs.wordpress.version}-de_DE.tar.gz"; # Name is required to invalidate the hash when wordpress is updated name = "wordpress-''${pkgs.wordpress.version}-language-de"; sha256 = "sha256-dlas0rXTSV4JAl8f/UyMbig57yURRYRhTMtJwF9g8h0="; }; installPhase = "mkdir -p $out; cp -r ./wp-content/languages/* $out/"; }) ]; ''; };
database = { host = mkOption { type = types.str; default = "localhost"; description = "Database host address."; };
port = mkOption { type = types.port; default = 3306; description = "Database host port."; };
name = mkOption { type = types.str; default = "wordpress"; description = "Database name."; };
user = mkOption { type = types.str; default = "wordpress"; description = "Database user."; };
passwordFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/keys/wordpress-dbpassword"; description = '' A file containing the password corresponding to {option}`database.user`. ''; };
tablePrefix = mkOption { type = types.str; default = "wp_"; description = '' The $table_prefix is the value placed in the front of your database tables. Change the value if you want to use something other than wp_ for your database prefix. Typically this is changed if you are installing multiple WordPress blogs in the same database.
See <https://codex.wordpress.org/Editing_wp-config.php#table_prefix>. ''; };
socket = mkOption { type = types.nullOr types.path; default = null; defaultText = literalExpression "/run/mysqld/mysqld.sock"; description = "Path to the unix socket file to use for authentication."; };
createLocally = mkOption { type = types.bool; default = true; description = "Create the database and database user locally."; }; };
virtualHost = mkOption { type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix); example = literalExpression '' { adminAddr = "webmaster@example.org"; forceSSL = true; enableACME = true; } ''; description = '' Apache configuration can be done by adapting {option}`services.httpd.virtualHosts`. ''; };
poolConfig = mkOption { type = with types; attrsOf (oneOf [ str int bool ]); default = { "pm" = "dynamic"; "pm.max_children" = 32; "pm.start_servers" = 2; "pm.min_spare_servers" = 2; "pm.max_spare_servers" = 4; "pm.max_requests" = 500; }; description = '' Options for the WordPress PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. ''; };
settings = mkOption { type = types.attrsOf types.anything; default = { }; description = '' Structural Wordpress configuration. Refer to <https://developer.wordpress.org/apis/wp-config-php> for details and supported values. ''; example = literalExpression '' { WP_DEFAULT_THEME = "twentytwentytwo"; WP_SITEURL = "https://example.org"; WP_HOME = "https://example.org"; WP_DEBUG = true; WP_DEBUG_DISPLAY = true; WPLANG = "de_DE"; FORCE_SSL_ADMIN = true; AUTOMATIC_UPDATER_DISABLED = true; } ''; };
mergedConfig = mkOption { readOnly = true; default = mergeConfig config; defaultText = literalExpression '' { DISALLOW_FILE_EDIT = true; AUTOMATIC_UPDATER_DISABLED = true; } ''; description = '' Read only representation of the final configuration. ''; };
extraConfig = mkOption { type = types.lines; default = ""; description = '' Any additional text to be appended to the wp-config.php configuration file. This is a PHP script. For configuration settings, see <https://codex.wordpress.org/Editing_wp-config.php>.
**Note**: Please pass structured settings via `services.wordpress.sites.${name}.settings` instead. ''; example = '' @ini_set( 'log_errors', 'Off' ); @ini_set( 'display_errors', 'On' ); ''; };
};
config.virtualHost.hostName = mkDefault name; };in{ # interface options = { services.wordpress = {
sites = mkOption { type = types.attrsOf (types.submodule siteOpts); default = { }; description = "Specification of one or more WordPress sites to serve"; };
webserver = mkOption { type = types.enum [ "httpd" "nginx" "caddy" ]; default = "httpd"; description = '' Whether to use apache2 or nginx for virtual host management.
Further nginx configuration can be done by adapting `services.nginx.virtualHosts.<name>`. See [](#opt-services.nginx.virtualHosts) for further information.
Further apache2 configuration can be done by adapting `services.httpd.virtualHosts.<name>`. See [](#opt-services.httpd.virtualHosts) for further information. ''; };
}; };
# implementation config = mkIf (eachSite != { }) (mkMerge [ {
assertions = (mapAttrsToList (hostName: cfg: { assertion = cfg.database.createLocally -> cfg.database.user == user; message = ''services.wordpress.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned''; }) eachSite) ++ (mapAttrsToList (hostName: cfg: { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; message = ''services.wordpress.sites."${hostName}".database.passwordFile cannot be specified if services.wordpress.sites."${hostName}".database.createLocally is set to true.''; }) eachSite);
services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) { enable = true; package = mkDefault pkgs.mariadb; ensureDatabases = mapAttrsToList (hostName: cfg: cfg.database.name) eachSite; ensureUsers = mapAttrsToList (hostName: cfg: { name = cfg.database.user; ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; }) eachSite; };
services.phpfpm.pools = mapAttrs' ( hostName: cfg: (nameValuePair "wordpress-${hostName}" { inherit user; group = webserver.group; settings = { "listen.owner" = webserver.user; "listen.group" = webserver.group; } // cfg.poolConfig; }) ) eachSite;
}
(mkIf (cfg.webserver == "httpd") { services.httpd = { enable = true; extraModules = [ "proxy_fcgi" ]; virtualHosts = mapAttrs ( hostName: cfg: mkMerge [ cfg.virtualHost { documentRoot = mkForce "${pkg hostName cfg}/share/wordpress"; extraConfig = '' <Directory "${pkg hostName cfg}/share/wordpress"> <FilesMatch "\.php$"> <If "-f %{REQUEST_FILENAME}"> SetHandler "proxy:unix:${ config.services.phpfpm.pools."wordpress-${hostName}".socket }|fcgi://localhost/" </If> </FilesMatch>
# standard wordpress .htaccess contents <IfModule mod_rewrite.c> RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule>
DirectoryIndex index.php Require all granted Options +FollowSymLinks -Indexes </Directory>
# https://wordpress.org/support/article/hardening-wordpress/#securing-wp-config-php <Files wp-config.php> Require all denied </Files> ''; } ] ) eachSite; }; })
{ systemd.tmpfiles.rules = flatten ( mapAttrsToList (hostName: cfg: [ "d '${stateDir hostName}' 0750 ${user} ${webserver.group} - -" "d '${cfg.uploadsDir}' 0750 ${user} ${webserver.group} - -" "Z '${cfg.uploadsDir}' 0750 ${user} ${webserver.group} - -" "d '${cfg.fontsDir}' 0750 ${user} ${webserver.group} - -" "Z '${cfg.fontsDir}' 0750 ${user} ${webserver.group} - -" ]) eachSite );
systemd.services = mkMerge [ (mapAttrs' ( hostName: cfg: (nameValuePair "wordpress-init-${hostName}" { wantedBy = [ "multi-user.target" ]; before = [ "phpfpm-wordpress-${hostName}.service" ]; after = optional cfg.database.createLocally "mysql.service"; script = secretsScript (stateDir hostName);
serviceConfig = { Type = "oneshot"; User = user; Group = webserver.group; }; }) ) eachSite)
(optionalAttrs (any (v: v.database.createLocally) (attrValues eachSite)) { httpd.after = [ "mysql.service" ]; }) ];
users.users.${user} = { group = webserver.group; isSystemUser = true; }; }
(mkIf (cfg.webserver == "nginx") { services.nginx = { enable = true; virtualHosts = mapAttrs (hostName: cfg: { serverName = mkDefault hostName; root = "${pkg hostName cfg}/share/wordpress"; extraConfig = '' index index.php; ''; locations = { "/" = { priority = 200; extraConfig = '' try_files $uri $uri/ /index.php$is_args$args; ''; }; "~ \\.php$" = { priority = 500; extraConfig = '' fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:${config.services.phpfpm.pools."wordpress-${hostName}".socket}; fastcgi_index index.php; include "${config.services.nginx.package}/conf/fastcgi.conf"; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; # Mitigate https://httpoxy.org/ vulnerabilities fastcgi_param HTTP_PROXY ""; fastcgi_intercept_errors off; fastcgi_buffer_size 16k; fastcgi_buffers 4 16k; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; ''; }; "~ /\\." = { priority = 800; extraConfig = "deny all;"; }; "~* /(?:uploads|files)/.*\\.php$" = { priority = 900; extraConfig = "deny all;"; }; "~* \\.(js|css|png|jpg|jpeg|gif|ico)$" = { priority = 1000; extraConfig = '' expires max; log_not_found off; ''; }; }; }) eachSite; }; })
(mkIf (cfg.webserver == "caddy") { services.caddy = { enable = true; virtualHosts = mapAttrs' ( hostName: cfg: (nameValuePair hostName { extraConfig = '' root * /${pkg hostName cfg}/share/wordpress file_server
php_fastcgi unix/${config.services.phpfpm.pools."wordpress-${hostName}".socket}
@uploads { path_regexp path /uploads\/(.*)\.php } rewrite @uploads /
@wp-admin { path not ^\/wp-admin/* } rewrite @wp-admin {path}/index.php?{query} ''; }) ) eachSite; }; })
]);}