{"id":22481755,"url":"https://github.com/riotkit-org/wordpress-hardened","last_synced_at":"2025-06-16T07:40:38.382Z","repository":{"id":38323093,"uuid":"175692383","full_name":"riotkit-org/wordpress-hardened","owner":"riotkit-org","description":"Secure and performant Wordpress installation on your Kubernetes cluster","archived":false,"fork":false,"pushed_at":"2023-01-02T21:41:41.000Z","size":140,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2023-03-03T21:54:27.948Z","etag":null,"topics":["docker","docker-compose","hardened","k8s","kubernetes","multirun","nginx","nonroot-user","openshift","p2cli","rootless-containers","supervisord","waf","web-application-firewall","wordpress","wordpress-automation","wordpress-kubernetes","wordpress-security","wordpress-updater"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/riotkit-org.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-14T20:11:58.000Z","updated_at":"2023-01-19T15:50:53.000Z","dependencies_parsed_at":"2023-02-01T04:17:35.268Z","dependency_job_id":null,"html_url":"https://github.com/riotkit-org/wordpress-hardened","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riotkit-org%2Fwordpress-hardened","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riotkit-org%2Fwordpress-hardened/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riotkit-org%2Fwordpress-hardened/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riotkit-org%2Fwordpress-hardened/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/riotkit-org","download_url":"https://codeload.github.com/riotkit-org/wordpress-hardened/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228491918,"owners_count":17928719,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","docker-compose","hardened","k8s","kubernetes","multirun","nginx","nonroot-user","openshift","p2cli","rootless-containers","supervisord","waf","web-application-firewall","wordpress","wordpress-automation","wordpress-kubernetes","wordpress-security","wordpress-updater"],"created_at":"2024-12-06T16:15:16.148Z","updated_at":"2024-12-06T16:15:16.698Z","avatar_url":"https://github.com/riotkit-org.png","language":"Shell","readme":"WordPress Hardened\n==================\n\nHardened version of official WordPress container, with special support for Kubernetes.\n\n**Features:**\n- Scheduled updates via wp-cli\n- **NGINX instead of Apache**\n- Supports [NGINX-PROXY](https://github.com/nginx-proxy/nginx-proxy) (VIRTUAL_HOST environment variable)\n- Hardened settings for WordPress: limiting access to code execution from wp-content directory, basic auth on wp-login.php\n- Basic Auth enabled by default to protect wp-login against bots (default user: `riotkit`, password: `riotkit`), can be changed using environment variables\n- Non-root container\n- Free from Supervisord, using lightweight [multirun](https://github.com/nicolas-van/multirun) instead\n- Runtime NGINX and PHP configuration to adjust things like `memory_limit`, `error_reporting` or `post_max_size`\n- Pre-configuration of admin account, website name and list of installed plugins\n- Possible to upgrade WordPress together with docker container\n- Built-in primitive rules to block common exploits targeting PHP\n\n**Kubernetes-only features:**\n- Helm installer\n- Integration with [Backup Repository](https://github.com/riotkit-org/backup-repository) (for Kubernetes-native backups)\n- Integration with [Volume Syncing Controller](https://github.com/riotkit-org/volume-syncing-controller) (for WordPress volume synchronization between Pod and cloud filesystem)\n- Web Application Firewall and OWASP CRS support (experimental)\n- Schedule SQL query execution periodically to perform tasks like deletion of old, unapproved comments\n\nRoadmap\n-------\n\n- [x] Use GitHub Actions as CI\n- [x] Replace J2cli with [P2cli](https://github.com/wrouesnel/p2cli)\n- [x] Replace Supervisord with [multirun](https://github.com/nicolas-van/multirun)\n- [x] Non-root container\n- [x] Helm Chart\n- [x] Plugins management - container installs selected plugins right after start or before starting\n- [ ] Support for Network Policy templates\n- [x] Support for Backup Repository template\n- [ ] Support WAF (Web Application Firewall) with [OWASP CRS](https://owasp.org/www-project-modsecurity-core-rule-set/)\n- [x] Real liveness and readiness checks\n- [ ] PHP-FPM chroot (to verify first)\n- [ ] Support for WP Super Cache plugin (https://www.nginx.com/blog/9-tips-for-improving-wordpress-performance-with-nginx/#wp-super-cache)\n\nChanging basic auth password or disabling it at all\n---------------------------------------------------\n\n**Disabling:**\n\n```bash\n-e BASIC_AUTH_ENABLED=false\n```\n\n**Changing password:**\n\n```bash\n-e BASIC_AUTH_USER=some-user -e BASIC_AUTH_PASSWORD=some-password\n```\n\nVersions\n--------\n\nhttps://github.com/riotkit-org/wordpress-hardened/packages\n\nExample: `ghcr.io/riotkit-org/wordpress-hardened:5.9.3-1`\n\nRunning\n-------\n\nWith docker command:\n\n```bash\nsudo docker run -v $(pwd)/your-www-files:/var/www/html -e WORDPRESS_DB_HOST=... -e WORDPRESS_DB_USER=... -e WORDPRESS_DB_PASSWORD=... -e WORDPRESS_DB_NAME=... -p 80:80 ghcr.io/riotkit-org/wordpress-hardened:5.9.3-1\n```\n\nOr with docker-compose:\n\n```yaml\nversion: \"2.3\"\nservices:\n    app_your_app:\n        image: ghcr.io/riotkit-org/wordpress-hardened:5.9.3-1\n        volumes:\n            - ./your-www-files/:/var/www/html\n        environment:\n            WORDPRESS_DB_HOST: \"db\"\n            WORDPRESS_DB_USER: \"your_user\"\n            WORDPRESS_DB_PASSWORD: \"${DB_PASSWORD_THERE}\"\n            WORDPRESS_DB_NAME: \"your_app\"\n            WORDPRESS_TABLE_PREFIX: \"wp_\"\n            AUTO_UPDATE_CRON: \"0 5 * * SAT\"\n            XMLRPC_DISABLED: \"true\"\n            DISABLE_DIRECT_CONTENT_PHP_EXECUTION: \"false\"\n            ENABLED_PLUGINS: \"amazon-s3-and-cloudfront\"\n\n            # basic auth on administrative endpoints\n            BASIC_AUTH_ENABLED: \"true\"\n            BASIC_AUTH_USER: john\n            BASIC_AUTH_PASSWORD: secret\n\n            # main page URL\n            WP_PAGE_URL: \"zsp.net.pl\"\n\n            # multiple domains can be pointing at this container\n            VIRTUAL_HOST: \"zsp.net.pl,www.zsp.net.pl,wroclaw.zsp.net.pl,wwww.wroclaw.zsp.net.pl\"\n\n```\n\nAutomating installation\n-----------------------\n\nYou can skip installation wizard by installing WordPress on container startup.\nThis container uses `wp-cli` to install WordPress and plugins allowing you to prepare a fully automated website.\n\n**Example configuration:**\n```yaml\nWP_PREINSTALL: true\nWP_SITE_URL: example.org\nWP_SITE_ADMIN_LOGIN: admin\nWP_SITE_ADMIN_PASSWORD: riotkit\nWP_SITE_ADMIN_EMAIL: example@example.org\n\n# NOTICE: The plugins will be installed right after WordPress installation is finished, \n#         this means that when `WP_PREINSTALL=false`, then the entrypoint will wait for user \n#         to complete the installation wizard, then the plugins will be installed\nENABLED_PLUGINS: \"amazon-s3-and-cloudfront,classic-editor\"\n\nWP_INSTALLATION_WAIT_INTERVAL: 20   # in seconds, how long to wait until the WordPress is installed to start installing plugins\nWP_PLUGINS_REINSTALL_RETRIES: 30    # 30 retries with 20s interval\n```\n\n**Example log:**\n\n```bash\n \u003e\u003e Checking if autoupdate should be scheduled... [scheduling at '0 5 * * TUE']\n \u003e\u003e Writing to basic auth file - /opt/htpasswd\nAdding password for user riotkit\n \u003e\u003e Rendering configuration files...\n \u003e\u003e Installing Wordpress\n \u003e\u003e UID=65161, GID=65161\nWordPress not found in /var/www/riotkit - copying now...\nsending incremental file list\nindex.php\nliveness.php\nreadiness.php\n...\nwp-includes/widgets/class-wp-widget-text.php\n\nsent 58,545,704 bytes  received 54,312 bytes  39,066,677.33 bytes/sec\ntotal size is 58,341,389  speedup is 1.00\nComplete! WordPress has been successfully copied to /var/www/riotkit\nNo 'wp-config.php' found in /var/www/riotkit, but 'WORDPRESS_...' variables supplied; copying 'wp-config-docker.php' (WORDPRESS_DB_HOST WORDPRESS_DB_NAME WORDPRESS_DB_PASSWORD WORDPRESS_DB_USER)\nSuccess: WordPress installed successfully.\n \u003e\u003e Installing plugin 'amazon-s3-and-cloudfront'\nInstalling WP Offload Media Lite for Amazon S3, DigitalOcean Spaces, and Google Cloud Storage (2.6.2)\nDownloading installation package from https://downloads.wordpress.org/plugin/amazon-s3-and-cloudfront.2.6.2.zip...\nUnpacking the package...\nInstalling the plugin...\nPlugin installed successfully.\nSuccess: Installed 1 of 1 plugins.\n \u003e\u003e Installing plugin 'classic-editor'\nInstalling Classic Editor (1.6.2)\nDownloading installation package from https://downloads.wordpress.org/plugin/classic-editor.1.6.2.zip...\nUnpacking the package...\nInstalling the plugin...\nPlugin installed successfully.\nSuccess: Installed 1 of 1 plugins.\n```\n\n### How it works?\n\n- Plugins will be installed AFTER WordPress will be installed. Use `WP_PREINSTALL: true` to install WordPress immediately, else the plugins will be installed after user will finish installation process\n- There will be `WP_PLUGINS_REINSTALL_RETRIES` retries of plugins installation\n- If at least one plugin installation will fail, then **after exceeding maximum number of retries the container will exit**\n- Even if some plugins installation will fail, the rest will be installed (installation process does not exit immediately after first fail)\n\n\nAccess log and error log\n------------------------\n\nPoint access and error logs to files, to stdout/stderr or disable logging by using environment variables.\n\n```bash\nACCESS_LOG: /dev/stdout\nERROR_LOG: /dev/stderr\n```\n\n```bash\nACCESS_LOG: /mnt/logs/access.log\nERROR_LOG: /mnt/logs/error.log\n```\n\n```bash\nACCESS_LOG: off\nERROR_LOG: off\n```\n\nUsing proxy to connect to the internet (egress traffic, http proxy)\n-------------------------------------------------------------------\n\nIn restricted environments you may want to deny all egress traffic from your WordPress instance, but let the WordPress\nupdate itself and install/upgrade plugins using a HTTP proxy, where you are in control of allowed internet destinations.\n\n```yaml\nenv:\n    WP_PROXY_HOST: my-proxy.proxy.svc.cluster.local\n    WP_PROXY_PORT: 8080\n    WP_PROXY_USERNAME: user\n    WP_PROXY_PASSWORD: xxxxx\n    WP_PROXY_BYPASS_HOSTS: localhost\n```\n\n## Kubernetes\n\n`wordpress-hardened` provides both container image and **Helm Chart**.\n\nHelm Chart can be installed from an OCI repository [ghcr.io/riotkit-org/charts/wordpress-hardened - check all available versions there](https://ghcr.io/riotkit-org/charts/wordpress-hardened)\n\n```bash\n# change version to non-latest :-)\nhelm pull oci://ghcr.io/riotkit-org/charts/wordpress-hardened --version 0.0-latest-master\nhelm install myrelease oci://ghcr.io/riotkit-org/charts/wordpress-hardened --version 0.0-latest-master\n```\n\n[Check Helm values there](./helm/wordpress-hardened)\n-----------------------\n\nKeeping wp-content and themes in GIT repository (Kubernetes only)\n-----------------------------------------------------------------\n\n[git-clone-controller](https://github.com/riotkit-org/git-clone-controller) is a Kubernetes controller allowing to clone a GIT repository before a Pod is launched, can be used to automatically fetch your website theme within just few seconds before Pod starts.\n\nUse and adjust following Helm values to clone your WordPress theme from a GIT repository before the application will start up.\n\n**values.yaml**\n\n```yaml\n# ----------------------------------------------------------\n# theme\n# ----------------------------------------------------------\npodLabels:\n    riotkit.org/git-clone-controller: \"true\"\npodAnnotations:\n    git-clone-controller/revision: main\n    git-clone-controller/url: \"https://git.example.org/my-example/my-theme.git\"\n    git-clone-controller/path: /var/www/riotkit/wp-content/themes/my-theme\n    git-clone-controller/secretName: my-secret-name\n    git-clone-controller/secretTokenKey: gitToken\n    git-clone-controller/owner: \"65161\"\n    git-clone-controller/group: \"65161\"\n```\n\nBackup with Backup Repository (Kubernetes only)\n-----------------------------------------------\n\nAutomatically taking a snapshot of database + files using a CronJob can be configured within Helm Values. Requires to have a [Backup Repository](https://github.com/riotkit-org/backup-repository) instance installed inside the cluster or outside the cluster.\n\n**values.yaml**\n\n```yaml\nbackups:\n    enabled: true\n    schedule: \"16 1 * * *\"\n    collectionId: \"xxx\"\n```\n\nEnabling WAF protection using waf-proxy (Kubernetes only)\n---------------------------------------------------------\n\n\u003e :warning: This is experimental and may not work yet.\n\nUse following values to enable Web Application Firewall rules to protect your WordPress instance against various forms of attacks.\n\n_Learn more about Web Application Firewall setup - [waf-proxy](https://github.com/riotkit-org/waf-proxy)_\n\n**values.yaml**\n\n```yaml\nwaf:\n    enabled: true\n    env:\n        ENABLE_RULE_WORDPRESS: true\n        ENABLE_CRS: true\n        ENABLE_RATE_LIMITER: true\n        RATE_LIMIT_EVENTS: \"30\"\n        RATE_LIMIT_WINDOW: \"5s\"\n        ENABLE_CORAZA_WAF: true\n        #DEBUG: true\n```\n\nEnabling WAF protecting using ingress-nginx (Kubernetes only)\n-------------------------------------------------------------\n\n\u003e :information_source: Works only if you are using [ingress-nginx](https://artifacthub.io/packages/helm/ingress-nginx/ingress-nginx) as Ingress Controller.\n\n[ingress-nginx](https://artifacthub.io/packages/helm/ingress-nginx/ingress-nginx) has a built-in mod_security v3, that needs to be enabled on global configuration level using those helm values:\n\n```yaml\n# Ingress NGINX config snippet\ncontroller:\n    config:\n        enable-modsecurity: \"true\"\n        enable-owasp-modsecurity-crs: \"true\"\n        modsecurity-snippet: |\n            SecRuleEngine On\n```\n\n\u003e :warning: This will enable mod_security and OWASP Core RuleSet on all ingress resources by default! According to documentation you need to set ingress annotation `nginx.ingress.kubernetes.io/enable-modsecurity: \"false\"` in every ingress, where you want the WAF to be disabled\n\n**WordPress requires additional tweaking, this can be done using Helm values of our Helm Chart as follows:**\n\n```yaml\n# WordPress Hardened config snippet\ningresses:\n    - name: wp-https\n      className: nginx\n      annotations:\n          cert-manager.io/cluster-issuer: letsencrypt-staging\n\n          # WAF provided by Ingress NGINX\n          nginx.ingress.kubernetes.io/enable-modsecurity: \"true\"\n          nginx.ingress.kubernetes.io/enable-owasp-core-rules: \"true\"\n          nginx.ingress.kubernetes.io/modsecurity-transaction-id: \"$request_id\"\n          nginx.ingress.kubernetes.io/modsecurity-snippet: |\n              SecRuleEngine On\n              SecAction \"id:900130,phase:1,nolog,pass,t:none,setvar:tx.crs_exclusions_drupal=0,setvar:tx.crs_exclusions_wordpress=1,setvar:tx.crs_exclusions_nextcloud=0,setvar:tx.crs_exclusions_dokuwiki=0,setvar:tx.crs_exclusions_cpanel=0\"\n      hosts:\n          - host: my-domain.org\n            paths:\n                - path: /\n                  pathType: ImplementationSpecific\n      tls:\n          - hosts: [\"my-domain.org\"]\n            secretName: my-domain-tls\n```\n\nMounting extra volumes (Kubernetes only)\n----------------------------------------\n\nEvery file placed in `/mnt/extra-files` will be copied during startup to `/var/www/riotkit/`, this mechanism ensures that\nno any file will be created with root-permissions inside a `/var/www/riotkit` directory - mounting a volume directly could do so.\n\n```yaml\n# create extra ConfigMaps\nextraConfigMaps:\n    - name: my-configmap-name\n      data: \n          something.php: |\n              \u003c?php\n              echo \"Hello anarchism!\";\n\n# create extra mounts\npv:\n    # Pod-level volumes section\n    extraVolumes:\n        - name: my-config\n          configMap:\n              name: my-configmap-name\n\n    # Container-level volumeMounts section\n    extraVolumeMounts:\n        - name: my-config\n          mountPath: /mnt/extra-files/wp-content/some-file.php\n          subPath: some-file.php\n```\n\nDatabase management Jobs and CronJobs (Kubernetes only)\n-------------------------------------------------------\n\nTo clean up the database out of rubbish there is a possibility to define SQL commands that would be executed right after deployment, or periodically.\n\n```yaml\n# (...)\ndb:\n    # (...)\n    administrativeJobs:\n        # This Job executes right after deployment - install/upgrade\n        test-1:\n            image: bitnami/mariadb:10.6\n            isHelmHook: true\n            # language=mysql\n            sql: |\n                SHOW TABLES;\n\n        # CronJob\n        delete-old-unapproved-comments:\n            image: bitnami/mariadb:10.6\n            isHelmHook: false\n            schedule: \"*/30 * * * *\"\n            # language=mysql\n            sql: |\n                DELETE FROM wp_comments WHERE comment_approved = 0 AND DATEDIFF(NOW(), comment_date) \u003e 7;\n```\n\n\nFrom authors\n------------\n\nProject was started as a part of RiotKit initiative, for the needs of grassroot organizations such as:\n\n- Fighting for better working conditions syndicalist (International Workers Association for example)\n- Tenants rights organizations\n- Political prisoners supporting organizations\n- Various grassroot organizations that are helping people to organize themselves without authority\n\nWe provide tools that organizations can host themselves with trust.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Friotkit-org%2Fwordpress-hardened","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Friotkit-org%2Fwordpress-hardened","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Friotkit-org%2Fwordpress-hardened/lists"}