1{ config, lib, pkgs, serverInfo, php, ... }: 2# http://codex.wordpress.org/Hardening_WordPress 3 4with lib; 5 6let 7 8 version = "4.3.1"; 9 fullversion = "${version}"; 10 11 # Our bare-bones wp-config.php file using the above settings 12 wordpressConfig = pkgs.writeText "wp-config.php" '' 13 <?php 14 define('DB_NAME', '${config.dbName}'); 15 define('DB_USER', '${config.dbUser}'); 16 define('DB_PASSWORD', '${config.dbPassword}'); 17 define('DB_HOST', '${config.dbHost}'); 18 define('DB_CHARSET', 'utf8'); 19 $table_prefix = '${config.tablePrefix}'; 20 ${config.extraConfig} 21 if ( !defined('ABSPATH') ) 22 define('ABSPATH', dirname(__FILE__) . '/'); 23 require_once(ABSPATH . 'wp-settings.php'); 24 ''; 25 26 # .htaccess to support pretty URLs 27 htaccess = pkgs.writeText "htaccess" '' 28 <IfModule mod_rewrite.c> 29 RewriteEngine On 30 RewriteBase / 31 RewriteRule ^index\.php$ - [L] 32 33 # add a trailing slash to /wp-admin 34 RewriteRule ^wp-admin$ wp-admin/ [R=301,L] 35 36 RewriteCond %{REQUEST_FILENAME} -f [OR] 37 RewriteCond %{REQUEST_FILENAME} -d 38 RewriteRule ^ - [L] 39 RewriteRule ^(wp-(content|admin|includes).*) $1 [L] 40 RewriteRule ^(.*\.php)$ $1 [L] 41 RewriteRule . index.php [L] 42 </IfModule> 43 44 ${config.extraHtaccess} 45 ''; 46 47 # WP translation can be found here: 48 # https://github.com/nixcloud/wordpress-translations 49 supportedLanguages = { 50 en_GB = { revision="d6c005372a5318fd758b710b77a800c86518be13"; sha256="0qbbsi87k47q4rgczxx541xz4z4f4fr49hw4lnaxkdsf5maz8p9p"; }; 51 de_DE = { revision="3c62955c27baaae98fd99feb35593d46562f4736"; sha256="1shndgd11dk836dakrjlg2arwv08vqx6j4xjh4jshvwmjab6ng6p"; }; 52 zh_ZN = { revision="12b9f811e8cae4b6ee41de343d35deb0a8fdda6d"; sha256="1339ggsxh0g6lab37jmfxicsax4h702rc3fsvv5azs7mcznvwh47"; }; 53 fr_FR = { revision="688c8b1543e3d38d9e8f57e0a6f2a2c3c8b588bd"; sha256="1j41iak0i6k7a4wzyav0yrllkdjjskvs45w53db8vfm8phq1n014"; }; 54 }; 55 56 downloadLanguagePack = language: revision: sha256s: 57 pkgs.stdenv.mkDerivation rec { 58 name = "wp_${language}"; 59 src = pkgs.fetchFromGitHub { 60 owner = "nixcloud"; 61 repo = "wordpress-translations"; 62 rev = revision; 63 sha256 = sha256s; 64 }; 65 installPhase = "mkdir -p $out; cp -R * $out/"; 66 }; 67 68 selectedLanguages = map (lang: downloadLanguagePack lang supportedLanguages.${lang}.revision supportedLanguages.${lang}.sha256) (config.languages); 69 70 # The wordpress package itself 71 wordpressRoot = pkgs.stdenv.mkDerivation rec { 72 name = "wordpress"; 73 src = pkgs.fetchFromGitHub { 74 owner = "WordPress"; 75 repo = "WordPress"; 76 rev = "${fullversion}"; 77 sha256 = "1rk10vcv4z9p04hfzc0wkbilrgx7m9ssyr6c3w6vw3vl1bcgqxza"; 78 }; 79 installPhase = '' 80 mkdir -p $out 81 # copy all the wordpress files we downloaded 82 cp -R * $out/ 83 84 # symlink the wordpress config 85 ln -s ${wordpressConfig} $out/wp-config.php 86 # symlink custom .htaccess 87 ln -s ${htaccess} $out/.htaccess 88 # symlink uploads directory 89 ln -s ${config.wordpressUploads} $out/wp-content/uploads 90 91 # remove bundled plugins(s) coming with wordpress 92 rm -Rf $out/wp-content/plugins/* 93 # remove bundled themes(s) coming with wordpress 94 rm -Rf $out/wp-content/themes/* 95 96 # symlink additional theme(s) 97 ${concatMapStrings (theme: "ln -s ${theme} $out/wp-content/themes/${theme.name}\n") config.themes} 98 # symlink additional plugin(s) 99 ${concatMapStrings (plugin: "ln -s ${plugin} $out/wp-content/plugins/${plugin.name}\n") (config.plugins) } 100 101 # symlink additional translation(s) 102 mkdir -p $out/wp-content/languages 103 ${concatMapStrings (language: "ln -s ${language}/*.mo ${language}/*.po $out/wp-content/languages/\n") (selectedLanguages) } 104 ''; 105 }; 106 107in 108 109{ 110 111 # And some httpd extraConfig to make things work nicely 112 extraConfig = '' 113 <Directory ${wordpressRoot}> 114 DirectoryIndex index.php 115 Allow from * 116 Options FollowSymLinks 117 AllowOverride All 118 </Directory> 119 ''; 120 121 enablePHP = true; 122 123 options = { 124 dbHost = mkOption { 125 default = "localhost"; 126 description = "The location of the database server."; 127 example = "localhost"; 128 }; 129 dbName = mkOption { 130 default = "wordpress"; 131 description = "Name of the database that holds the Wordpress data."; 132 example = "localhost"; 133 }; 134 dbUser = mkOption { 135 default = "wordpress"; 136 description = "The dbUser, read: the username, for the database."; 137 example = "wordpress"; 138 }; 139 dbPassword = mkOption { 140 default = "wordpress"; 141 description = "The mysql password to the respective dbUser."; 142 example = "wordpress"; 143 }; 144 tablePrefix = mkOption { 145 default = "wp_"; 146 description = '' 147 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 <link xlink:href='http://codex.wordpress.org/Editing_wp-config.php#table_prefix'/>. 148 ''; 149 }; 150 wordpressUploads = mkOption { 151 default = "/data/uploads"; 152 description = '' 153 This directory is used for uploads of pictures and must be accessible (read: owned) by the httpd running user. The directory passed here is automatically created and permissions are given to the httpd running user. 154 ''; 155 }; 156 plugins = mkOption { 157 default = []; 158 type = types.listOf types.path; 159 description = 160 '' 161 List of path(s) to respective plugin(s) which are symlinked from the 'plugins' directory. Note: These plugins need to be packaged before use, see example. 162 ''; 163 example = '' 164 # Wordpress plugin 'akismet' installation example 165 akismetPlugin = pkgs.stdenv.mkDerivation { 166 name = "akismet-plugin"; 167 # Download the theme from the wordpress site 168 src = pkgs.fetchurl { 169 url = https://downloads.wordpress.org/plugin/akismet.3.1.zip; 170 sha256 = "1i4k7qyzna08822ncaz5l00wwxkwcdg4j9h3z2g0ay23q640pclg"; 171 }; 172 # We need unzip to build this package 173 buildInputs = [ pkgs.unzip ]; 174 # Installing simply means copying all files to the output directory 175 installPhase = "mkdir -p $out; cp -R * $out/"; 176 }; 177 178 And then pass this theme to the themes list like this: 179 plugins = [ akismetPlugin ]; 180 ''; 181 }; 182 themes = mkOption { 183 default = []; 184 type = types.listOf types.path; 185 description = 186 '' 187 List of path(s) to respective theme(s) which are symlinked from the 'theme' directory. Note: These themes need to be packaged before use, see example. 188 ''; 189 example = '' 190 # For shits and giggles, let's package the responsive theme 191 responsiveTheme = pkgs.stdenv.mkDerivation { 192 name = "responsive-theme"; 193 # Download the theme from the wordpress site 194 src = pkgs.fetchurl { 195 url = http://wordpress.org/themes/download/responsive.1.9.7.6.zip; 196 sha256 = "06i26xlc5kdnx903b1gfvnysx49fb4kh4pixn89qii3a30fgd8r8"; 197 }; 198 # We need unzip to build this package 199 buildInputs = [ pkgs.unzip ]; 200 # Installing simply means copying all files to the output directory 201 installPhase = "mkdir -p $out; cp -R * $out/"; 202 }; 203 204 And then pass this theme to the themes list like this: 205 themes = [ responsiveTheme ]; 206 ''; 207 }; 208 languages = mkOption { 209 default = []; 210 description = "Installs wordpress language packs based on the list, see wordpress.nix for possible translations."; 211 example = "[ \"en_GB\" \"de_DE\" ];"; 212 }; 213 extraConfig = mkOption { 214 default = ""; 215 example = 216 '' 217 define( 'AUTOSAVE_INTERVAL', 60 ); // Seconds 218 ''; 219 description = '' 220 Any additional text to be appended to Wordpress's wp-config.php 221 configuration file. This is a PHP script. For configuration 222 settings, see <link xlink:href='http://codex.wordpress.org/Editing_wp-config.php'/>. 223 ''; 224 }; 225 extraHtaccess = mkOption { 226 default = ""; 227 example = 228 '' 229 php_value upload_max_filesize 20M 230 php_value post_max_size 20M 231 ''; 232 description = '' 233 Any additional text to be appended to Wordpress's .htaccess file. 234 ''; 235 }; 236 }; 237 238 documentRoot = wordpressRoot; 239 240 # FIXME adding the user has to be done manually for the time being 241 startupScript = pkgs.writeScript "init-wordpress.sh" '' 242 #!/bin/sh 243 mkdir -p ${config.wordpressUploads} 244 chown ${serverInfo.serverConfig.user} ${config.wordpressUploads} 245 246 # we should use systemd dependencies here 247 #waitForUnit("network-interfaces.target"); 248 if [ ! -d ${serverInfo.fullConfig.services.mysql.dataDir}/${config.dbName} ]; then 249 echo "Need to create the database '${config.dbName}' and grant permissions to user named '${config.dbUser}'." 250 # Wait until MySQL is up 251 while [ ! -e ${serverInfo.fullConfig.services.mysql.pidDir}/mysqld.pid ]; do 252 sleep 1 253 done 254 ${pkgs.mysql}/bin/mysql -e 'CREATE DATABASE ${config.dbName};' 255 ${pkgs.mysql}/bin/mysql -e 'GRANT ALL ON ${config.dbName}.* TO ${config.dbUser}@localhost IDENTIFIED BY "${config.dbPassword}";' 256 else 257 echo "Good, no need to do anything database related." 258 fi 259 ''; 260}