apiVersion: v1 kind: ConfigMap metadata: name: nginx-conf data: nginx.conf: | user nginx; worker_processes 1; error_log /dev/stderr warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; client_max_body_size 20M; ## # SSL Settings ## ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; set_real_ip_from 0.0.0.0/0; log_format main '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent"'; access_log /dev/stdout main; ## # Gzip Settings ## gzip on; gzip_disable "msie6"; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; include /etc/nginx/conf.d/*.conf; } --- apiVersion: v1 kind: ConfigMap metadata: name: wordpress-conf-tpl data: wordpress.conf.template: | server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /var/www/html; index index.html index.php; client_max_body_size 20M; # Security include /etc/nginx/custom.conf.d/nginx-custom.conf; include /etc/nginx/custom.conf.d/wp-hardening.conf; # Prevent Clickjacking add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; charset utf-8; # Set the custom error pages error_page 404 /index.php; error_page 403 /index.php; # Logs error_log /dev/stderr; access_log /dev/stdout main; location ~* /xmlrpc.php$ { fastcgi_pass ${KUBE_SVC_NAME}:9000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; allow ${SECURE_SUBNET}; allow 127.0.0.1; deny all; } # Remove direct access to the following folders & files location ~* ^/(?:\.|conf|data/(?:files|personal|logs|plugins|tmp|cache)|plugins/editor.zoho/agent/files) { deny all; } location ~* /data/public/.*.(ser|htaccess)$ { deny all; } # Stops the annoying error messages in the logs location ~* ^/(favicon.ico|robots.txt) { log_not_found off; } location = /favicon.ico { expires 1y; log_not_found off; access_log off; } location ~ ^/(wp-admin|wp-login.php) { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass ${KUBE_SVC_NAME}:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_NAME $fastcgi_script_name; allow ${SECURE_SUBNET}; allow 127.0.0.1; deny all; } # Enables PHP location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass ${KUBE_SVC_NAME}:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_NAME $fastcgi_script_name; } # Enables Caching location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 7d; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } # The rewrite magic location / { try_files $uri $uri/ /index.php?$args; } } --- apiVersion: v1 kind: ConfigMap metadata: name: nginx-custom-conf data: nginx-custom.conf: | # Directives to send expires headers and turn off 404 error logging. location ~* ^.+\.(curl|heic|swf|tiff|rss|atom|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ { log_not_found off; expires max; } # Web fonts send expires headers location ~* \.(?:eot|otf|ttf|woff|woff2)$ { expires max; add_header Cache-Control "public"; } # SVGs & MP4 WEBM send expires headers - this rule is set specific to ns site location ~* \.(?:svg|svgz|mp4|webm)$ { expires max; add_header Cache-Control "public"; } # Media: images, icons, video, audio send expires headers. location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|aac|m4a|mp3|ogg|ogv|webp)$ { expires 1M; add_header Cache-Control "public"; } # Cache css & js files location ~* \.(?:css(\.map)?|js(\.map)?)$ { add_header "Access-Control-Allow-Origin" "*"; log_not_found off; expires 30d; } # CSS and Javascript send expires headers. location ~* \.(?:css|js)$ { expires 1y; add_header Cache-Control "public"; } # HTML send expires headers. location ~* \.(html)$ { expires 7d; add_header Cache-Control "public"; } # Security settings for better privacy # Deny hidden files # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac). location ~ /\. { deny all; } # Return 403 forbidden for readme.(txt|html) or license.(txt|html) or example.(txt|html) or other common git repository files location ~* "/(^$|readme|license|example|README|LEGALNOTICE|INSTALLATION|CHANGELOG)\.(txt|html|md)" { deny all; } # Deny backup extensions & log files and return 403 forbidden location ~* "\.(old|orig|original|php#|php~|php_bak|save|swo|aspx?|tpl|sh|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rdf)$" { deny all; } # common nginx configuration to block sql injection and other attacks location ~* "(eval\()" { deny all; } location ~* "(127\.0\.0\.1)" { deny all; } location ~* "([a-z0-9]{2000})" { deny all; } location ~* "(javascript\:)(.*)(\;)" { deny all; } location ~* "(base64_encode)(.*)(\()" { deny all; } location ~* "(GLOBALS|REQUEST)(=|\[|%)" { deny all; } location ~* "(<|%3C).*script.*(>|%3)" { deny all; } location ~ "(\\|\.\.\.|\.\./|~|`|<|>|\|)" { deny all; } location ~* "(boot\.ini|etc/passwd|self/environ)" { deny all; } location ~* "(thumbs?(_editor|open)?|tim(thumb)?)\.php" { deny all; } location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" { deny all; } location ~* "(https?|ftp|php):/" { deny all; } location ~* "(=\\\'|=\\%27|/\\\'/?)\." { deny all; } location ~ "(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")" { deny all; } location ~ "(~|`|<|>|:|;|%|\\|\s|\{|\}|\[|\]|\|)" { deny all; } location ~* "/(=|\$&|_mm|(wp-)?config\.|cgi-|etc/passwd|muieblack)" { deny all; } location ~* "(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)" { deny all; } location ~* "/(^$|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell|config|settings|configuration)\.php" { deny all; } --- apiVersion: v1 kind: ConfigMap metadata: name: wp-hardening data: wp-hardening.conf: | # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac). location ~ /\. { deny all; } # Deny access to any files with a .php extension in the uploads directory location ~* /uploads/.*\.php$ { deny all; } # Deny access to any files with a .php extension in the uploads directory for multisite location ~* /files/.*\.php$ { deny all; } # Since version 2.5.7, Akismet introduced a new .htaccess file to block direct access to php files # Ref: http://wordpress.org/extend/plugins/akismet/changelog/ location ~* /akismet/.*\.php$ { allow 127.0.0.1; deny all; } # Restrict direct access to cached content location /wp-content/cache/ { deny all; } # hide any backup or SQL dump files location ~ ^.+\.(sql|bak|php~|php#|php.save|php.swp|php.swo)$ { deny all; } #Deny access to wp-content folders for suspicious files location ~* ^/(wp-content)/(.*?)\.(zip|gz|tar|bzip2|7z)\$ { deny all; } location ~ ^/wp-content/uploads/sucuri { deny all; } location ~ ^/wp-content/updraft { deny all; } #Disable execution of scripts other than PHP from your document root location ~* .(pl|cgi|py|sh|lua|asp)$ { return 444; } #Disable access to your configuration files and other files that you don’t want to users are able to see location ~* /(wp-config.php|readme.html|license.txt|nginx.conf) { deny all; } # Disable wp-config.txt location = /wp-config.txt { deny all; } # nginx block wpscann on plugins folder location ~* ^/wp-content/plugins/.+\.(txt|log|md)$ { deny all; error_page 403 =404 / ; } # Deny access to any files with a .php extension in the uploads directory # Works in sub-directory installs and also in multisite network # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) location ~* /(?:uploads|files)/.*\.php$ { deny all; } # Stop scann for the follow files on plugins folder location ~* ^/wp-content/plugins/.+\.(txt|log|md)$ { deny all; error_page 403 =404 / ; } # Stop scann for the follow files on themes folder location ~* ^/wp-content/themes/.+\.(txt|log|md)$ { deny all; error_page 403 =404 / ; } #This module will allow us to pattern match certain key files and inject random text in the files that # is non-destructive / non-invasive and will most importantly alter the md5sum calculated on such files. All transparent to WPScan. location ~* ^/(license.txt|wp-includes/(.*)/.+\.(js|css)|wp-admin/(.*)/.+\.(js|css))$ { sub_filter_types text/css text/javascript text/plain; sub_filter_once on; sub_filter ';' '; /* $msec */ '; } #Direct PHP File Access #If somehow, a hacker successfully sneaks in a PHP file onto your site, #they’ll be able to run this file by loading file which effectively becomes a backdoor to infiltrate your site. #We should disable direct access to any PHP files by adding the following rules: location ~* /(?:uploads|files|wp-content|wp-includes|akismet)/.*.php$ { deny all; } #Dotfiles #Similar to PHP file, a dotfile like .htaccess, .user.ini, and .git may contain sensitive information. #To be on the safer side, it’s better to disable direct access to these files. location ~ /\.(svn|git)/* { deny all; } location ~ /\.ht { deny all; } location ~ /\.user.ini { deny all; } #WordFence location ~ \.user\.ini$ { deny all; } # WordPress: deny wp-content, wp-includes php files location ~* ^/(?:wp-content|wp-includes)/.*\.php$ { deny all; } # WordPress: deny wp-content/uploads nasty stuff location ~* ^/wp-content/uploads/.*\.(?:s?html?|php|js|swf)$ { deny all; } --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: nginx name: nginx spec: replicas: 1 selector: matchLabels: app: nginx tier: frontend strategy: {} template: metadata: labels: app: nginx tier: frontend spec: containers: - image: nginx:latest name: nginx env: - name: SECURE_SUBNET value: 8.8.8.8/32 # change-me - name: KUBE_SVC_NAME value: wordpress-svc volumeMounts: - name: "wordpress-persistent-storage" mountPath: "/var/www/html" - name: nginx-custom-conf mountPath: /etc/nginx/custom.conf.d/nginx-custom.conf subPath: nginx-custom.conf readOnly: true - name: wp-hardening mountPath: /etc/nginx/custom.conf.d/wp-hardening.conf subPath: wp-hardening.conf readOnly: true - name: nginx-conf mountPath: /etc/nginx/nginx.conf subPath: nginx.conf readOnly: true - name: wordpress-conf-tpl mountPath: /etc/nginx/templates/wordpress.conf.template subPath: wordpress.conf.template readOnly: true volumes: - name: nginx-custom-conf configMap: name: nginx-custom-conf items: - key: nginx-custom.conf path: nginx-custom.conf - name: wp-hardening configMap: name: wp-hardening items: - key: wp-hardening.conf path: wp-hardening.conf - name: nginx-conf configMap: name: nginx-conf items: - key: nginx.conf path: nginx.conf - name: wordpress-conf-tpl configMap: name: wordpress-conf-tpl items: - key: wordpress.conf.template path: wordpress.conf.template - name: wordpress-persistent-storage persistentVolumeClaim: claimName: wordpress-pvc --- apiVersion: v1 kind: Service metadata: labels: app: nginx tier: frontend name: nginx-svc spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx tier: frontend type: ClusterIP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-wp-ingress spec: ingressClassName: nginx rules: - http: paths: - path: / pathType: Prefix backend: service: name: nginx-svc port: number: 80