From 1ece74fca0d404e8a673ea8d9cf12fbe45e64143 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Thu, 23 Apr 2026 14:38:47 +0500 Subject: [PATCH 1/9] feat: codify PHP limits and block .user.ini per Task D152 & #264 --- wordpress/helm/templates/ingress.yaml | 146 ++++++++++-------- .../helm/templates/php-config-configmap.yaml | 34 ++-- 2 files changed, 96 insertions(+), 84 deletions(-) diff --git a/wordpress/helm/templates/ingress.yaml b/wordpress/helm/templates/ingress.yaml index 64f8671..14b0c61 100644 --- a/wordpress/helm/templates/ingress.yaml +++ b/wordpress/helm/templates/ingress.yaml @@ -1,68 +1,78 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ include "wordpress.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} - app.kubernetes.io/component: ingress - annotations: - {{- toYaml .Values.ingress.annotations | nindent 4 }} - # TLS/SSL Security (Enterprise-Grade, Parameterized) - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - {{- if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} - nginx.ingress.kubernetes.io/ssl-protocols: {{ .Values.ingress.tls.protocols | default "TLSv1.2 TLSv1.3" | quote }} - nginx.ingress.kubernetes.io/ssl-ciphers: {{ .Values.ingress.tls.ciphers | default "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" | quote }} - {{- else }} - nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3" - nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" - {{- end }} - {{- if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} - # Redirect root domain to www subdomain (using from-to-www annotation) - nginx.ingress.kubernetes.io/from-to-www-redirect: "true" - {{- end }} - # WordPress-specific optimizations - nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" - nginx.ingress.kubernetes.io/proxy-buffers-number: "8" - nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" - nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" - # Rate limiting for brute force protection - nginx.ingress.kubernetes.io/rate-limit: "100" - nginx.ingress.kubernetes.io/rate-limit-window: "1m" - nginx.ingress.kubernetes.io/rate-limit-connections: "10" -spec: - {{- if .Values.ingress.className }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - tls: - - hosts: - - {{ .Values.wordpress.domain | quote }} - {{- if .Values.wordpress.includeWWW }} - - {{ printf "www.%s" .Values.wordpress.domain | quote }} - {{- end }} - secretName: wordpress-tls - rules: - - host: {{ .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- if .Values.wordpress.includeWWW }} - - host: {{ printf "www.%s" .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- end }} -{{- end }} +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "wordpress.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "wordpress.labels" . | nindent 4 }} + app.kubernetes.io/component: ingress + annotations: + {{- toYaml .Values.ingress.annotations | nindent 4 }} + # Block sensitive files at the edge (Task #264) + nginx.ingress.kubernetes.io/server-snippet: | + location ~* /\.(git|env|user\.ini|htaccess) { + deny all; + return 403; + } + location = /wp-config.php { + deny all; + return 403; + } + # TLS/SSL Security (Enterprise-Grade, Parameterized) + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + {{- if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} + nginx.ingress.kubernetes.io/ssl-protocols: {{ .Values.ingress.tls.protocols | default "TLSv1.2 TLSv1.3" | quote }} + nginx.ingress.kubernetes.io/ssl-ciphers: {{ .Values.ingress.tls.ciphers | default "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" | quote }} + {{- else }} + nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3" + nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" + {{- end }} + {{- if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} + # Redirect root domain to www subdomain (using from-to-www annotation) + nginx.ingress.kubernetes.io/from-to-www-redirect: "true" + {{- end }} + # WordPress-specific optimizations + nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" + nginx.ingress.kubernetes.io/proxy-buffers-number: "8" + nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" + nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" + # Rate limiting for brute force protection + nginx.ingress.kubernetes.io/rate-limit: "100" + nginx.ingress.kubernetes.io/rate-limit-window: "1m" + nginx.ingress.kubernetes.io/rate-limit-connections: "10" +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + tls: + - hosts: + - {{ .Values.wordpress.domain | quote }} + {{- if .Values.wordpress.includeWWW }} + - {{ printf "www.%s" .Values.wordpress.domain | quote }} + {{- end }} + secretName: wordpress-tls + rules: + - host: {{ .Values.wordpress.domain | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "wordpress.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if .Values.wordpress.includeWWW }} + - host: {{ printf "www.%s" .Values.wordpress.domain | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "wordpress.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- end }} +{{- end }} diff --git a/wordpress/helm/templates/php-config-configmap.yaml b/wordpress/helm/templates/php-config-configmap.yaml index 48c20f3..139805d 100644 --- a/wordpress/helm/templates/php-config-configmap.yaml +++ b/wordpress/helm/templates/php-config-configmap.yaml @@ -1,16 +1,18 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "wordpress.fullname" . }}-php-config - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} -data: - uploads.ini: | - ; PHP Upload Configuration for WordPress - ; Allows plugin/theme uploads up to 64MB - upload_max_filesize = 64M - post_max_size = 64M - max_execution_time = 300 - max_input_time = 300 - memory_limit = 256M +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "wordpress.fullname" . }}-php-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "wordpress.labels" . | nindent 4 }} +data: + uploads.ini: | + ; PHP Upload Configuration - Codified (D152 & #264) + upload_max_filesize = 128M + post_max_size = 128M + max_execution_time = 300 + max_input_time = 300 + memory_limit = 256M + + ; Wordfence WAF Hardening: Forces loading via PHP-FPM + auto_prepend_file = '/var/www/html/wordfence-waf.php' \ No newline at end of file From 8d44f583734b5a179bdea4e968450ca7851013da Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Thu, 23 Apr 2026 14:43:00 +0500 Subject: [PATCH 2/9] chore(helm): implement multi-site values strategy for burnedout and ptoken --- wordpress/helm/values-burnedout.yaml | 13 + wordpress/helm/values-ptoken.yaml | 13 + wordpress/helm/values.yaml | 1136 +++++++++++++------------- 3 files changed, 594 insertions(+), 568 deletions(-) create mode 100644 wordpress/helm/values-burnedout.yaml create mode 100644 wordpress/helm/values-ptoken.yaml diff --git a/wordpress/helm/values-burnedout.yaml b/wordpress/helm/values-burnedout.yaml new file mode 100644 index 0000000..52563e7 --- /dev/null +++ b/wordpress/helm/values-burnedout.yaml @@ -0,0 +1,13 @@ +wordpress: + domain: "burnedout.xyz" + wordpressBlogName: "BurnedOut" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: burnedout-tls + hosts: + - burnedout.xyz \ No newline at end of file diff --git a/wordpress/helm/values-ptoken.yaml b/wordpress/helm/values-ptoken.yaml new file mode 100644 index 0000000..99b59aa --- /dev/null +++ b/wordpress/helm/values-ptoken.yaml @@ -0,0 +1,13 @@ +wordpress: + domain: "ptoken.agency" + wordpressBlogName: "PToken Agency" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: ptoken-tls + hosts: + - ptoken.agency \ No newline at end of file diff --git a/wordpress/helm/values.yaml b/wordpress/helm/values.yaml index 5bf097a..93c7447 100644 --- a/wordpress/helm/values.yaml +++ b/wordpress/helm/values.yaml @@ -1,568 +1,568 @@ -## Global Configuration -global: - imageRegistry: "" - imagePullSecrets: [] - storageClass: "do-block-storage" - -## Service Account Security -serviceAccount: - create: true - automount: false - annotations: {} - -# Pod Security Standards: Service Account Token Automount -automountServiceAccountToken: false - -## Pod Disruption Budget -podDisruptionBudget: - enabled: true - maxUnavailable: 1 - -## cert-manager Configuration -certManager: - email: "EMAIL_PLACEHOLDER" - createClusterIssuer: false # Use existing ClusterIssuer - -## WordPress Configuration -wordpress: - enableMultisite: false - - ## WordPress Basic Settings - wordpressUsername: admin - wordpressEmail: "" # Set via --set global.email or deploy script - wordpressBlogName: "WordPress Site" - wordpressScheme: https - wordpressTablePrefix: "wp_" - domain: "" # Set via --set global.domain or deploy script - includeWWW: false # Automatically configure www subdomain for main domain - redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) - redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) - wordpressExtraWpConfigContent: | - define('WP_CACHE', true); - define('DISABLE_FILE_EDITING', true); - define('DISALLOW_FILE_MODS', true); - define('FORCE_SSL_ADMIN', true); - define('WP_AUTO_UPDATE_CORE', true); - - ## Container Image Configuration - image: - registry: docker.io - repository: wordpress - tag: 6.8.3-php8.3-apache - pullPolicy: IfNotPresent - - ## Storage Configuration - persistence: - enabled: true - - ## Security Keys (auto-generated) - security: - authKey: "CHANGE_ME_AUTH_KEY" - secureAuthKey: "CHANGE_ME_SECURE_AUTH_KEY" - loggedInKey: "CHANGE_ME_LOGGED_IN_KEY" - nonceKey: "CHANGE_ME_NONCE_KEY" - authSalt: "CHANGE_ME_AUTH_SALT" - secureAuthSalt: "CHANGE_ME_SECURE_AUTH_SALT" - loggedInSalt: "CHANGE_ME_LOGGED_IN_SALT" - nonceSalt: "CHANGE_ME_NONCE_SALT" - -## Zero-Trust NetworkPolicy (Enterprise Security) -networkPolicy: - enabled: true - policyTypes: - - Ingress - - Egress - - # Ingress: Only allow NGINX Ingress Controller and same namespace - ingress: - - from: - # Allow NGINX Ingress Controller - - namespaceSelector: - matchLabels: - name: ingress-nginx - # Allow same namespace communication - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 80 - - protocol: TCP - port: 443 - - # Egress: Restricted to essential services only - egress: - # DNS resolution - - to: [] - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # HTTPS for WordPress updates and plugins - - to: [] - ports: - - protocol: TCP - port: 443 - # Allow communication with MariaDB and Redis in same namespace - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 3306 # MariaDB - - protocol: TCP - port: 6379 # Redis - -## Replica Configuration -replicaCount: 1 - -## WordPress Configuration -wordpress: - image: - registry: docker.io - repository: wordpress - tag: "6.8.3-php8.3-apache" - pullPolicy: IfNotPresent - pullSecrets: [] - - ## WordPress Admin User (Configured via installation wizard) - # Leave empty to enable WordPress installation wizard - wordpressUsername: "" - wordpressEmail: "" - wordpressPassword: "" - - ## WordPress Site Configuration - wordpressBlogName: "Enterprise WordPress Site" - wordpressScheme: https - domain: "" # Set via --set global.domain or deploy script - - ## WordPress Extra WP Config Content - wordpressExtraWpConfigContent: | - // WordPress configuration - define('WP_DEBUG', false); - define('WP_DEBUG_LOG', false); - define('WP_DEBUG_DISPLAY', false); - define('AUTOMATIC_UPDATER_DISABLED', false); - define('WP_AUTO_UPDATE_CORE', 'minor'); - define('DISALLOW_FILE_EDIT', true); - define('FORCE_SSL_ADMIN', true); - define('WP_MEMORY_LIMIT', '256M'); - - // Disable WP-Cron (handled by Kubernetes CronJob) - define('DISABLE_WP_CRON', true); - - // Performance optimizations - define('WP_CACHE', true); - define('WP_POST_REVISIONS', 3); - define('AUTOSAVE_INTERVAL', 300); - define('EMPTY_TRASH_DAYS', 7); - - // Security hardening - ini_set('session.cookie_httponly', true); - ini_set('session.cookie_secure', true); - ini_set('session.use_only_cookies', true); - - // Enable object caching - define('WP_CACHE_KEY_SALT', 'wordpress_cache_'); - - // SMTP Configuration for reliable email delivery - define('SMTP_FROM_EMAIL', 'EMAIL_PLACEHOLDER'); - define('SMTP_FROM_NAME', 'YOUR_NAME'); - - // Enable WordPress mail debug logging - define('WP_MAIL_DEBUG', false); - - ## WordPress Security Keys (Generated automatically by deployment script) - security: - authKey: "AUTH_KEY_PLACEHOLDER" - secureAuthKey: "SECURE_AUTH_KEY_PLACEHOLDER" - loggedInKey: "LOGGED_IN_KEY_PLACEHOLDER" - nonceKey: "NONCE_KEY_PLACEHOLDER" - authSalt: "AUTH_SALT_PLACEHOLDER" - secureAuthSalt: "SECURE_AUTH_SALT_PLACEHOLDER" - loggedInSalt: "LOGGED_IN_SALT_PLACEHOLDER" - nonceSalt: "NONCE_SALT_PLACEHOLDER" - - ## Persistence Configuration - persistence: - enabled: true - storageClass: "do-block-storage" - accessModes: - - ReadWriteOnce - size: 8Gi - coreSize: 4Gi # WordPress core files persistence - # Separate volumes for security - volumes: - content: - mountPath: /var/www/html/wp-content - size: 8Gi - config: - mountPath: /var/www/html/wp-config-custom - size: 100Mi - cache: - mountPath: /var/cache/wordpress - size: 1Gi - - ## Extra environment variables - extraEnvVars: - - name: WORDPRESS_ENABLE_REDIS - value: "yes" # Enable Redis since we re-enabled it - - name: APACHE_HTTP_PORT - value: "8080" - - name: WORDPRESS_CONFIG_EXTRA - value: | - define('WP_CACHE', true); - define('DISABLE_FILE_EDITING', true); - define('DISALLOW_FILE_MODS', true); - define('FORCE_SSL_ADMIN', true); - define('WP_AUTO_UPDATE_CORE', true); - - ## Init Containers (WordPress Hardening) - Disabled for resource optimization - initContainers: - enabled: false - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - runAsNonRoot: true - - ## Extra Init Containers for CI/CD wp-content sync - extraInitContainers: [] - # Removed non-functional plugin installation - # Plugins should be installed manually after deployment via WordPress admin - # or WP-CLI for production environments - - ## WordPress Security Context (Production Ready) - security: - automountServiceAccountToken: false - runAsUser: 0 # Required for Apache port 80 binding - runAsGroup: 0 - runAsNonRoot: false - fsGroup: 33 # www-data for file permissions - readOnlyRootFilesystem: false - allowPrivilegeEscalation: false - capabilities: {} # Use default capabilities for WordPress - seccompProfile: - type: RuntimeDefault - - ## Resource Configuration (WordPress Production Requirements - WeOwn Optimized) - # Based on actual usage: WordPress uses ~380Mi in production - resources: - limits: - cpu: 800m # Sufficient for Kadence AI theme installation - memory: 1Gi # Prevents OOMKilled during plugin installs - ephemeral-storage: 800Mi - requests: - cpu: 50m # Cluster-optimized baseline - memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) - ephemeral-storage: 200Mi - ## Health Checks (Production Ready - TCP mode for iThemes Security compatibility) - livenessProbe: - enabled: true - tcpSocket: - port: 80 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 10 - failureThreshold: 6 - successThreshold: 1 - - readinessProbe: - enabled: true - tcpSocket: - port: 80 - initialDelaySeconds: 60 - periodSeconds: 15 - timeoutSeconds: 10 - failureThreshold: 10 - successThreshold: 1 - - ## Auto-scaling Configuration (Disabled for single replica + RWO volumes) - autoscaling: - enabled: false # Must be false when using ReadWriteOnce volumes - -## General Configuration -replicaCount: 1 - -## Redis Configuration (Production Caching) -redis: - enabled: false # Disabled for initial deployment - auth: - enabled: false - -## Environment Variables -extraEnvVars: [] - -## Ingress Configuration -ingress: - enabled: true - className: "nginx" - annotations: - cert-manager.io/cluster-issuer: "letsencrypt-prod" - # TLS/SSL Security - Enterprise-grade encryption (configured in template) - # force-ssl-redirect: "true" - # ssl-protocols: "TLSv1.2 TLSv1.3" - # ssl-ciphers: Modern cipher suites for mobile browser compatibility - # WordPress Performance - nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads - - ## TLS Security Configuration (Parameterized for flexibility) - tls: - protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) - # Cipher suites: Mozilla "Intermediate" profile - balances security with compatibility - # Supports: Perfect Forward Secrecy, mobile devices (CHACHA20), modern encryption (AES-GCM) - ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" - hosts: - - host: "" - paths: - - path: / - pathType: Prefix - tls: - - secretName: wordpress-tls - hosts: [] - -## Service Configuration -service: - type: ClusterIP - port: 80 - -## Cron Jobs (Production Maintenance) -cron: - enabled: true - schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) - image: - registry: docker.io - repository: wordpress - tag: 6.8.3-php8.3-apache - resources: - limits: - cpu: 100m - memory: 256Mi - requests: - cpu: 50m - memory: 128Mi - -## Backup Configuration (Production Critical) -backup: - enabled: true - schedule: "0 2 * * *" # Daily at 2 AM - retention: "30" # 30 days - resources: - limits: - cpu: 200m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi - -## MariaDB Configuration (Official MariaDB 12.0.2 Latest Stable) -mariadb: - enabled: false # Using custom StatefulSet with official image - -mariadbOfficial: - enabled: true - image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 - auth: - database: wordpress - username: wordpress - password: "MARIADB_PASSWORD_PLACEHOLDER" - rootPassword: "MARIADB_ROOT_PASSWORD_PLACEHOLDER" - - ## Security Context (Pod Security Standards: Restricted) - podSecurityContext: - runAsUser: 999 # Official MariaDB mysql user - runAsGroup: 999 - runAsNonRoot: true - fsGroup: 999 - seccompProfile: - type: RuntimeDefault - - containerSecurityContext: - runAsUser: 999 # Official MariaDB mysql user - runAsGroup: 999 - runAsNonRoot: true - allowPrivilegeEscalation: false - readOnlyRootFilesystem: false - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - - ## Resource Configuration (MariaDB Memory-Optimized - WeOwn Standardized) - resources: - limits: - cpu: 250m # Optimized for database operations - memory: 512Mi # Increased for initialization (prevents OOMKilled) - ephemeral-storage: 1Gi - requests: - cpu: 50m # Matches cluster optimization - memory: 256Mi # Increased for initialization stability - - ## MariaDB Performance Tuning (2-node cluster optimized) - configuration: | - [mysqld] - innodb_buffer_pool_size=64M # Reduced for memory efficiency - innodb_log_file_size=16M # Reduced for faster startup - query_cache_size=16M # Reduced but still beneficial - query_cache_limit=1M # Conservative limit - thread_cache_size=4 # Fewer threads for 2-node - table_open_cache=128 # Reduced cache size - performance_schema=OFF # Keep disabled for efficiency - innodb_flush_log_at_trx_commit=2 # Better performance - -## Redis Cache Configuration (Lightweight for 2-node cluster) -redis: - enabled: true - architecture: standalone - auth: - enabled: true - password: "REDIS_PASSWORD_PLACEHOLDER" - - ## Resource limits for 2-node efficiency - master: - resources: - limits: - cpu: 50m - memory: 48Mi - ephemeral-storage: 150Mi - requests: - cpu: 10m - memory: 24Mi - ephemeral-storage: 50Mi - - persistence: - enabled: false # Disabled for performance in 2-node cluster - - ## Security context for Redis - podSecurityContext: - runAsUser: 1001 - runAsGroup: 1001 - runAsNonRoot: true - fsGroup: 1001 - seccompProfile: - type: RuntimeDefault - - containerSecurityContext: - runAsUser: 1001 - runAsGroup: 1001 - runAsNonRoot: true - allowPrivilegeEscalation: false - readOnlyRootFilesystem: false - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - -## Service Configuration -service: - type: ClusterIP # Secure internal-only access - port: 80 - targetPort: 80 - annotations: {} - - -## NetworkPolicy (Zero-Trust Security) -networkPolicy: - enabled: false - policyTypes: - - Ingress - - Egress - - ingress: - # Allow NGINX Ingress Controller - - from: - - namespaceSelector: - matchLabels: - name: ingress-nginx - - podSelector: - matchLabels: {} - ports: - - protocol: TCP - port: 8080 - egress: - # Allow DNS resolution to kube-system namespace only - - to: - - namespaceSelector: - matchLabels: - name: kube-system - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # Allow HTTPS outbound for WordPress updates/plugins - - to: [] - ports: - - protocol: TCP - port: 443 - # Allow access to MariaDB - - to: - - podSelector: - matchLabels: - app.kubernetes.io/name: mariadb - ports: - - protocol: TCP - port: 3306 - # Allow access to Redis (if enabled) - - to: - - podSelector: - matchLabels: - app.kubernetes.io/name: redis - ports: - - protocol: TCP - port: 6379 - -## Backup Configuration (NEW - Enhanced Data Protection) -backup: - enabled: true - schedule: "0 2 * * *" # Daily at 2 AM - retention: 30 # Keep 30 days of backups - storage: - size: 20Gi - storageClass: "do-block-storage" - -## Monitoring & Observability -monitoring: - enabled: true - serviceMonitor: - enabled: true - interval: 30s - path: /wp-json/wp/v2/ - port: http - -## Must-Use Plugins Configuration -muPlugins: - cache: - enabled: true - authFix: - enabled: true # General REST API Authentication fix for containerized environments - disableXmlRpc: - enabled: true # Security best practice: Disable XML-RPC (use REST API instead) - smtp: - enabled: true # Enterprise SMTP Configuration via Environment Variables - -## WordPress Plugins (Manual Installation Recommended) -# Plugins are not pre-configured in this deployment for security and flexibility -# Install plugins manually after deployment via: -# 1. WordPress Admin > Plugins > Add New -# 2. WP-CLI: kubectl exec deployment/wordpress -n namespace -- wp plugin install plugin-name -# 3. Manual upload via WordPress Admin -# -# Recommended plugins for enterprise use: -# Security: wordfence, limit-login-attempts, wp-security-audit-log -# Performance: w3-total-cache, smush, shortpixel-image-optimiser -# SEO: yoast-seo, rankmath-seo -# Backup: updraftplus, backwpup -# -# plugins: -# security: -# - wordfence -# - limit-login-attempts -# performance: -# - w3-total-cache -# - smush -# seo: -# - yoast-seo +## Global Configuration +global: + imageRegistry: "" + imagePullSecrets: [] + storageClass: "do-block-storage" + +## Service Account Security +serviceAccount: + create: true + automount: false + annotations: {} + +# Pod Security Standards: Service Account Token Automount +automountServiceAccountToken: false + +## Pod Disruption Budget +podDisruptionBudget: + enabled: true + maxUnavailable: 1 + +## cert-manager Configuration +certManager: + email: "EMAIL_PLACEHOLDER" + createClusterIssuer: false # Use existing ClusterIssuer + +## WordPress Configuration +wordpress: + enableMultisite: false + + ## WordPress Basic Settings + wordpressUsername: admin + wordpressEmail: "" # Set via --set global.email or deploy script + wordpressBlogName: "WordPress Site" + wordpressScheme: https + wordpressTablePrefix: "wp_" + domain: "" # Set via --set global.domain or deploy script + includeWWW: false # Automatically configure www subdomain for main domain + redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) + redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) + wordpressExtraWpConfigContent: | + define('WP_CACHE', true); + define('DISABLE_FILE_EDITING', true); + define('DISALLOW_FILE_MODS', true); + define('FORCE_SSL_ADMIN', true); + define('WP_AUTO_UPDATE_CORE', true); + + ## Container Image Configuration + image: + registry: docker.io + repository: wordpress + tag: 6.8.3-php8.3-apache + pullPolicy: IfNotPresent + + ## Storage Configuration + persistence: + enabled: true + + ## Security Keys (auto-generated) + security: + authKey: "CHANGE_ME_AUTH_KEY" + secureAuthKey: "CHANGE_ME_SECURE_AUTH_KEY" + loggedInKey: "CHANGE_ME_LOGGED_IN_KEY" + nonceKey: "CHANGE_ME_NONCE_KEY" + authSalt: "CHANGE_ME_AUTH_SALT" + secureAuthSalt: "CHANGE_ME_SECURE_AUTH_SALT" + loggedInSalt: "CHANGE_ME_LOGGED_IN_SALT" + nonceSalt: "CHANGE_ME_NONCE_SALT" + +## Zero-Trust NetworkPolicy (Enterprise Security) +networkPolicy: + enabled: true + policyTypes: + - Ingress + - Egress + + # Ingress: Only allow NGINX Ingress Controller and same namespace + ingress: + - from: + # Allow NGINX Ingress Controller + - namespaceSelector: + matchLabels: + name: ingress-nginx + # Allow same namespace communication + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 80 + - protocol: TCP + port: 443 + + # Egress: Restricted to essential services only + egress: + # DNS resolution + - to: [] + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # HTTPS for WordPress updates and plugins + - to: [] + ports: + - protocol: TCP + port: 443 + # Allow communication with MariaDB and Redis in same namespace + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 3306 # MariaDB + - protocol: TCP + port: 6379 # Redis + +## Replica Configuration +replicaCount: 1 + +## WordPress Configuration +wordpress: + image: + registry: docker.io + repository: wordpress + tag: "6.8.3-php8.3-apache" + pullPolicy: IfNotPresent + pullSecrets: [] + + ## WordPress Admin User (Configured via installation wizard) + # Leave empty to enable WordPress installation wizard + wordpressUsername: "" + wordpressEmail: "" + wordpressPassword: "" + + ## WordPress Site Configuration + wordpressBlogName: "Enterprise WordPress Site" + wordpressScheme: https + domain: "" # Set via --set global.domain or deploy script + + ## WordPress Extra WP Config Content + wordpressExtraWpConfigContent: | + // WordPress configuration + define('WP_DEBUG', false); + define('WP_DEBUG_LOG', false); + define('WP_DEBUG_DISPLAY', false); + define('AUTOMATIC_UPDATER_DISABLED', false); + define('WP_AUTO_UPDATE_CORE', 'minor'); + define('DISALLOW_FILE_EDIT', true); + define('FORCE_SSL_ADMIN', true); + define('WP_MEMORY_LIMIT', '256M'); + + // Disable WP-Cron (handled by Kubernetes CronJob) + define('DISABLE_WP_CRON', true); + + // Performance optimizations + define('WP_CACHE', true); + define('WP_POST_REVISIONS', 3); + define('AUTOSAVE_INTERVAL', 300); + define('EMPTY_TRASH_DAYS', 7); + + // Security hardening + ini_set('session.cookie_httponly', true); + ini_set('session.cookie_secure', true); + ini_set('session.use_only_cookies', true); + + // Enable object caching + define('WP_CACHE_KEY_SALT', 'wordpress_cache_'); + + // SMTP Configuration for reliable email delivery + define('SMTP_FROM_EMAIL', 'EMAIL_PLACEHOLDER'); + define('SMTP_FROM_NAME', 'YOUR_NAME'); + + // Enable WordPress mail debug logging + define('WP_MAIL_DEBUG', false); + + ## WordPress Security Keys (Generated automatically by deployment script) + security: + authKey: "AUTH_KEY_PLACEHOLDER" + secureAuthKey: "SECURE_AUTH_KEY_PLACEHOLDER" + loggedInKey: "LOGGED_IN_KEY_PLACEHOLDER" + nonceKey: "NONCE_KEY_PLACEHOLDER" + authSalt: "AUTH_SALT_PLACEHOLDER" + secureAuthSalt: "SECURE_AUTH_SALT_PLACEHOLDER" + loggedInSalt: "LOGGED_IN_SALT_PLACEHOLDER" + nonceSalt: "NONCE_SALT_PLACEHOLDER" + + ## Persistence Configuration + persistence: + enabled: true + storageClass: "do-block-storage" + accessModes: + - ReadWriteOnce + size: 8Gi + coreSize: 4Gi # WordPress core files persistence + # Separate volumes for security + volumes: + content: + mountPath: /var/www/html/wp-content + size: 8Gi + config: + mountPath: /var/www/html/wp-config-custom + size: 100Mi + cache: + mountPath: /var/cache/wordpress + size: 1Gi + + ## Extra environment variables + extraEnvVars: + - name: WORDPRESS_ENABLE_REDIS + value: "yes" # Enable Redis since we re-enabled it + - name: APACHE_HTTP_PORT + value: "8080" + - name: WORDPRESS_CONFIG_EXTRA + value: | + define('WP_CACHE', true); + define('DISABLE_FILE_EDITING', true); + define('DISALLOW_FILE_MODS', true); + define('FORCE_SSL_ADMIN', true); + define('WP_AUTO_UPDATE_CORE', true); + + ## Init Containers (WordPress Hardening) - Disabled for resource optimization + initContainers: + enabled: false + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + runAsNonRoot: true + + ## Extra Init Containers for CI/CD wp-content sync + extraInitContainers: [] + # Removed non-functional plugin installation + # Plugins should be installed manually after deployment via WordPress admin + # or WP-CLI for production environments + + ## WordPress Security Context (Production Ready) + security: + automountServiceAccountToken: false + runAsUser: 0 # Required for Apache port 80 binding + runAsGroup: 0 + runAsNonRoot: false + fsGroup: 33 # www-data for file permissions + readOnlyRootFilesystem: false + allowPrivilegeEscalation: false + capabilities: {} # Use default capabilities for WordPress + seccompProfile: + type: RuntimeDefault + + ## Resource Configuration (WordPress Production Requirements - WeOwn Optimized) + # Based on actual usage: WordPress uses ~380Mi in production + resources: + limits: + cpu: 800m # Sufficient for Kadence AI theme installation + memory: 1Gi # Prevents OOMKilled during plugin installs + ephemeral-storage: 800Mi + requests: + cpu: 50m # Cluster-optimized baseline + memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) + ephemeral-storage: 200Mi + ## Health Checks (Production Ready - TCP mode for iThemes Security compatibility) + livenessProbe: + enabled: true + tcpSocket: + port: 80 + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 6 + successThreshold: 1 + + readinessProbe: + enabled: true + tcpSocket: + port: 80 + initialDelaySeconds: 60 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 10 + successThreshold: 1 + + ## Auto-scaling Configuration (Disabled for single replica + RWO volumes) + autoscaling: + enabled: false # Must be false when using ReadWriteOnce volumes + +## General Configuration +replicaCount: 1 + +## Redis Configuration (Production Caching) +redis: + enabled: false # Disabled for initial deployment + auth: + enabled: false + +## Environment Variables +extraEnvVars: [] + +## Ingress Configuration +ingress: + enabled: false + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + # TLS/SSL Security - Enterprise-grade encryption (configured in template) + # force-ssl-redirect: "true" + # ssl-protocols: "TLSv1.2 TLSv1.3" + # ssl-ciphers: Modern cipher suites for mobile browser compatibility + # WordPress Performance + nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads + + ## TLS Security Configuration (Parameterized for flexibility) + tls: + protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) + # Cipher suites: Mozilla "Intermediate" profile - balances security with compatibility + # Supports: Perfect Forward Secrecy, mobile devices (CHACHA20), modern encryption (AES-GCM) + ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" + hosts: + - host: "" + paths: + - path: / + pathType: Prefix + tls: + - secretName: wordpress-tls + hosts: [] + +## Service Configuration +service: + type: ClusterIP + port: 80 + +## Cron Jobs (Production Maintenance) +cron: + enabled: true + schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) + image: + registry: docker.io + repository: wordpress + tag: 6.8.3-php8.3-apache + resources: + limits: + cpu: 100m + memory: 256Mi + requests: + cpu: 50m + memory: 128Mi + +## Backup Configuration (Production Critical) +backup: + enabled: true + schedule: "0 2 * * *" # Daily at 2 AM + retention: "30" # 30 days + resources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + +## MariaDB Configuration (Official MariaDB 12.0.2 Latest Stable) +mariadb: + enabled: false # Using custom StatefulSet with official image + +mariadbOfficial: + enabled: true + image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 + auth: + database: wordpress + username: wordpress + password: "MARIADB_PASSWORD_PLACEHOLDER" + rootPassword: "MARIADB_ROOT_PASSWORD_PLACEHOLDER" + + ## Security Context (Pod Security Standards: Restricted) + podSecurityContext: + runAsUser: 999 # Official MariaDB mysql user + runAsGroup: 999 + runAsNonRoot: true + fsGroup: 999 + seccompProfile: + type: RuntimeDefault + + containerSecurityContext: + runAsUser: 999 # Official MariaDB mysql user + runAsGroup: 999 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + + ## Resource Configuration (MariaDB Memory-Optimized - WeOwn Standardized) + resources: + limits: + cpu: 250m # Optimized for database operations + memory: 512Mi # Increased for initialization (prevents OOMKilled) + ephemeral-storage: 1Gi + requests: + cpu: 50m # Matches cluster optimization + memory: 256Mi # Increased for initialization stability + + ## MariaDB Performance Tuning (2-node cluster optimized) + configuration: | + [mysqld] + innodb_buffer_pool_size=64M # Reduced for memory efficiency + innodb_log_file_size=16M # Reduced for faster startup + query_cache_size=16M # Reduced but still beneficial + query_cache_limit=1M # Conservative limit + thread_cache_size=4 # Fewer threads for 2-node + table_open_cache=128 # Reduced cache size + performance_schema=OFF # Keep disabled for efficiency + innodb_flush_log_at_trx_commit=2 # Better performance + +## Redis Cache Configuration (Lightweight for 2-node cluster) +redis: + enabled: true + architecture: standalone + auth: + enabled: true + password: "REDIS_PASSWORD_PLACEHOLDER" + + ## Resource limits for 2-node efficiency + master: + resources: + limits: + cpu: 50m + memory: 48Mi + ephemeral-storage: 150Mi + requests: + cpu: 10m + memory: 24Mi + ephemeral-storage: 50Mi + + persistence: + enabled: false # Disabled for performance in 2-node cluster + + ## Security context for Redis + podSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + fsGroup: 1001 + seccompProfile: + type: RuntimeDefault + + containerSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + +## Service Configuration +service: + type: ClusterIP # Secure internal-only access + port: 80 + targetPort: 80 + annotations: {} + + +## NetworkPolicy (Zero-Trust Security) +networkPolicy: + enabled: false + policyTypes: + - Ingress + - Egress + + ingress: + # Allow NGINX Ingress Controller + - from: + - namespaceSelector: + matchLabels: + name: ingress-nginx + - podSelector: + matchLabels: {} + ports: + - protocol: TCP + port: 8080 + egress: + # Allow DNS resolution to kube-system namespace only + - to: + - namespaceSelector: + matchLabels: + name: kube-system + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # Allow HTTPS outbound for WordPress updates/plugins + - to: [] + ports: + - protocol: TCP + port: 443 + # Allow access to MariaDB + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: mariadb + ports: + - protocol: TCP + port: 3306 + # Allow access to Redis (if enabled) + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: redis + ports: + - protocol: TCP + port: 6379 + +## Backup Configuration (NEW - Enhanced Data Protection) +backup: + enabled: true + schedule: "0 2 * * *" # Daily at 2 AM + retention: 30 # Keep 30 days of backups + storage: + size: 20Gi + storageClass: "do-block-storage" + +## Monitoring & Observability +monitoring: + enabled: true + serviceMonitor: + enabled: true + interval: 30s + path: /wp-json/wp/v2/ + port: http + +## Must-Use Plugins Configuration +muPlugins: + cache: + enabled: true + authFix: + enabled: true # General REST API Authentication fix for containerized environments + disableXmlRpc: + enabled: true # Security best practice: Disable XML-RPC (use REST API instead) + smtp: + enabled: true # Enterprise SMTP Configuration via Environment Variables + +## WordPress Plugins (Manual Installation Recommended) +# Plugins are not pre-configured in this deployment for security and flexibility +# Install plugins manually after deployment via: +# 1. WordPress Admin > Plugins > Add New +# 2. WP-CLI: kubectl exec deployment/wordpress -n namespace -- wp plugin install plugin-name +# 3. Manual upload via WordPress Admin +# +# Recommended plugins for enterprise use: +# Security: wordfence, limit-login-attempts, wp-security-audit-log +# Performance: w3-total-cache, smush, shortpixel-image-optimiser +# SEO: yoast-seo, rankmath-seo +# Backup: updraftplus, backwpup +# +# plugins: +# security: +# - wordfence +# - limit-login-attempts +# performance: +# - w3-total-cache +# - smush +# seo: +# - yoast-seo From b98ac280032e7abdcea848b3d0df686f863844b5 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Thu, 23 Apr 2026 14:46:43 +0500 Subject: [PATCH 3/9] chore: bump helm chart version to 3.2.7 --- wordpress/helm/Chart.yaml | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/wordpress/helm/Chart.yaml b/wordpress/helm/Chart.yaml index 26d5e6a..6ea379d 100644 --- a/wordpress/helm/Chart.yaml +++ b/wordpress/helm/Chart.yaml @@ -1,27 +1,27 @@ -apiVersion: v2 -name: wordpress -description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS -type: application -version: 3.2.6 -appVersion: "6.8.3" - -keywords: -- wordpress -- cms -- blog -- php -- mariadb - -home: https://github.com/WeOwnNetwork/ai -sources: -- https://github.com/WeOwnNetwork/ai/tree/main/wordpress - -maintainers: -- name: Enterprise WordPress - email: admin@example.com - -# No external dependencies - using official MariaDB image via custom StatefulSet - -annotations: - category: CMS - licenses: Apache-2.0 +apiVersion: v2 +name: wordpress +description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS +type: application +version: 3.2.7 +appVersion: "6.8.3" + +keywords: +- wordpress +- cms +- blog +- php +- mariadb + +home: https://github.com/WeOwnNetwork/ai +sources: +- https://github.com/WeOwnNetwork/ai/tree/main/wordpress + +maintainers: +- name: Enterprise WordPress + email: admin@example.com + +# No external dependencies - using official MariaDB image via custom StatefulSet + +annotations: + category: CMS + licenses: Apache-2.0 From 994c8be23f65b455b28e33c7d7c321bd23790892 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 22:31:06 +0500 Subject: [PATCH 4/9] fix: resolve trivy security scan and linting issues --- wordpress/helm/Chart.yaml | 54 ++++---- wordpress/helm/templates/ingress.yaml | 157 +++++++++++----------- wordpress/helm/values-burnedout.yaml | 26 ++-- wordpress/helm/values.yaml | 179 +++++++++++++------------- 4 files changed, 207 insertions(+), 209 deletions(-) diff --git a/wordpress/helm/Chart.yaml b/wordpress/helm/Chart.yaml index 6ea379d..4532793 100644 --- a/wordpress/helm/Chart.yaml +++ b/wordpress/helm/Chart.yaml @@ -1,27 +1,27 @@ -apiVersion: v2 -name: wordpress -description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS -type: application -version: 3.2.7 -appVersion: "6.8.3" - -keywords: -- wordpress -- cms -- blog -- php -- mariadb - -home: https://github.com/WeOwnNetwork/ai -sources: -- https://github.com/WeOwnNetwork/ai/tree/main/wordpress - -maintainers: -- name: Enterprise WordPress - email: admin@example.com - -# No external dependencies - using official MariaDB image via custom StatefulSet - -annotations: - category: CMS - licenses: Apache-2.0 +apiVersion: v2 +name: wordpress +description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS +type: application +version: 3.2.7 +appVersion: "6.8.3" + +keywords: + - wordpress + - cms + - blog + - php + - mariadb + +home: https://github.com/WeOwnNetwork/ai +sources: + - https://github.com/WeOwnNetwork/ai/tree/main/wordpress + +maintainers: + - name: Enterprise WordPress + email: admin@example.com + +# No external dependencies - using official MariaDB image via custom StatefulSet + +annotations: + category: CMS + licenses: Apache-2.0 diff --git a/wordpress/helm/templates/ingress.yaml b/wordpress/helm/templates/ingress.yaml index 14b0c61..8ea9a21 100644 --- a/wordpress/helm/templates/ingress.yaml +++ b/wordpress/helm/templates/ingress.yaml @@ -1,78 +1,79 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ include "wordpress.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} - app.kubernetes.io/component: ingress - annotations: - {{- toYaml .Values.ingress.annotations | nindent 4 }} - # Block sensitive files at the edge (Task #264) - nginx.ingress.kubernetes.io/server-snippet: | - location ~* /\.(git|env|user\.ini|htaccess) { - deny all; - return 403; - } - location = /wp-config.php { - deny all; - return 403; - } - # TLS/SSL Security (Enterprise-Grade, Parameterized) - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - {{- if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} - nginx.ingress.kubernetes.io/ssl-protocols: {{ .Values.ingress.tls.protocols | default "TLSv1.2 TLSv1.3" | quote }} - nginx.ingress.kubernetes.io/ssl-ciphers: {{ .Values.ingress.tls.ciphers | default "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" | quote }} - {{- else }} - nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3" - nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" - {{- end }} - {{- if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} - # Redirect root domain to www subdomain (using from-to-www annotation) - nginx.ingress.kubernetes.io/from-to-www-redirect: "true" - {{- end }} - # WordPress-specific optimizations - nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" - nginx.ingress.kubernetes.io/proxy-buffers-number: "8" - nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" - nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" - # Rate limiting for brute force protection - nginx.ingress.kubernetes.io/rate-limit: "100" - nginx.ingress.kubernetes.io/rate-limit-window: "1m" - nginx.ingress.kubernetes.io/rate-limit-connections: "10" -spec: - {{- if .Values.ingress.className }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - tls: - - hosts: - - {{ .Values.wordpress.domain | quote }} - {{- if .Values.wordpress.includeWWW }} - - {{ printf "www.%s" .Values.wordpress.domain | quote }} - {{- end }} - secretName: wordpress-tls - rules: - - host: {{ .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- if .Values.wordpress.includeWWW }} - - host: {{ printf "www.%s" .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- end }} -{{- end }} +# {{ if .Values.ingress.enabled }} +# {{ $tlsProtocols := "TLSv1.2 TLSv1.3" }} +# {{ $tlsCiphers := "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" }} +# {{ if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} +# {{ $tlsProtocols = .Values.ingress.tls.protocols | default $tlsProtocols }} +# {{ $tlsCiphers = .Values.ingress.tls.ciphers | default $tlsCiphers }} +# {{ end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: '{{ include "wordpress.fullname" . }}' + namespace: "{{ .Release.Namespace }}" + labels: + # {{ include "wordpress.labels" . | nindent 4 }} + app.kubernetes.io/component: ingress + annotations: + # {{ toYaml .Values.ingress.annotations | nindent 4 }} + # Block sensitive files at the edge (Task #264) + nginx.ingress.kubernetes.io/server-snippet: | + location ~* /\.(git|env|user\.ini|htaccess) { + deny all; + return 403; + } + location = /wp-config.php { + deny all; + return 403; + } + # TLS/SSL Security (Enterprise-Grade, Parameterized) + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/ssl-protocols: "{{ $tlsProtocols }}" + nginx.ingress.kubernetes.io/ssl-ciphers: "{{ $tlsCiphers }}" + # {{ if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} + # Redirect root domain to www subdomain (using from-to-www annotation) + nginx.ingress.kubernetes.io/from-to-www-redirect: "true" + # {{ end }} + # WordPress-specific optimizations + nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" + nginx.ingress.kubernetes.io/proxy-buffers-number: "8" + nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" + nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" + # Rate limiting for brute force protection + nginx.ingress.kubernetes.io/rate-limit: "100" + nginx.ingress.kubernetes.io/rate-limit-window: "1m" + nginx.ingress.kubernetes.io/rate-limit-connections: "10" +spec: + # {{ if .Values.ingress.className }} + ingressClassName: "{{ .Values.ingress.className }}" + # {{ end }} + tls: + - hosts: + - "{{ .Values.wordpress.domain }}" + # {{ if .Values.wordpress.includeWWW }} + - '{{ printf "www.%s" .Values.wordpress.domain }}' + # {{ end }} + secretName: wordpress-tls + rules: + - host: "{{ .Values.wordpress.domain }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: '{{ include "wordpress.fullname" . }}' + port: + # {{ printf "number: %v" .Values.service.port | nindent 18 }} + # {{ if .Values.wordpress.includeWWW }} + - host: '{{ printf "www.%s" .Values.wordpress.domain }}' + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: '{{ include "wordpress.fullname" . }}' + port: + # {{ printf "number: %v" .Values.service.port | nindent 18 }} + # {{ end }} +# {{ end }} diff --git a/wordpress/helm/values-burnedout.yaml b/wordpress/helm/values-burnedout.yaml index 52563e7..14c480a 100644 --- a/wordpress/helm/values-burnedout.yaml +++ b/wordpress/helm/values-burnedout.yaml @@ -1,13 +1,13 @@ -wordpress: - domain: "burnedout.xyz" - wordpressBlogName: "BurnedOut" - includeWWW: true - redirectToWWW: true - -ingress: - enabled: true - className: "nginx" - tls: - - secretName: burnedout-tls - hosts: - - burnedout.xyz \ No newline at end of file +wordpress: + domain: "burnedout.xyz" + wordpressBlogName: "BurnedOut" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: burnedout-tls + hosts: + - burnedout.xyz diff --git a/wordpress/helm/values.yaml b/wordpress/helm/values.yaml index 3df8c09..d4953c1 100644 --- a/wordpress/helm/values.yaml +++ b/wordpress/helm/values.yaml @@ -10,9 +10,6 @@ serviceAccount: automount: false annotations: {} -# Pod Security Standards: Service Account Token Automount -automountServiceAccountToken: false - ## Pod Disruption Budget podDisruptionBudget: enabled: true @@ -21,56 +18,56 @@ podDisruptionBudget: ## cert-manager Configuration certManager: email: "EMAIL_PLACEHOLDER" - createClusterIssuer: false # Use existing ClusterIssuer + createClusterIssuer: false # Use existing ClusterIssuer ## Zero-Trust NetworkPolicy (Enterprise Security) networkPolicy: enabled: true policyTypes: - - Ingress - - Egress + - Ingress + - Egress # Ingress: Only allow NGINX Ingress Controller and same namespace ingress: - - from: - # Allow NGINX Ingress Controller - - namespaceSelector: - matchLabels: - name: ingress-nginx - # Allow same namespace communication - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 80 - - protocol: TCP - port: 443 + - from: + # Allow NGINX Ingress Controller + - namespaceSelector: + matchLabels: + name: ingress-nginx + # Allow same namespace communication + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 80 + - protocol: TCP + port: 443 # Egress: Restricted to essential services only egress: - # DNS resolution - - to: [] - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # HTTPS for WordPress updates and plugins - - to: [] - ports: - - protocol: TCP - port: 443 - # Allow communication with MariaDB and Redis in same namespace - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 3306 # MariaDB - - protocol: TCP - port: 6379 # Redis + # DNS resolution + - to: [] + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # HTTPS for WordPress updates and plugins + - to: [] + ports: + - protocol: TCP + port: 443 + # Allow communication with MariaDB and Redis in same namespace + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 3306 # MariaDB + - protocol: TCP + port: 6379 # Redis ## Replica Configuration replicaCount: 1 @@ -93,12 +90,12 @@ wordpress: ## WordPress Site Configuration wordpressBlogName: "Enterprise WordPress Site" wordpressScheme: https - domain: "" # Set via --set global.domain or deploy script + domain: "" # Set via --set global.domain or deploy script enableMultisite: false wordpressTablePrefix: "wp_" - includeWWW: false # Automatically configure www subdomain for main domain - redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) - redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) + includeWWW: false # Automatically configure www subdomain for main domain + redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) + redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) ## WordPress Extra WP Config Content wordpressExtraWpConfigContent: | @@ -148,13 +145,13 @@ wordpress: nonceSalt: "NONCE_SALT_PLACEHOLDER" ## Container / Pod Security Context (Production Ready) automountServiceAccountToken: false - runAsUser: 0 # Required for Apache port 80 binding + runAsUser: 0 # Required for Apache port 80 binding runAsGroup: 0 runAsNonRoot: false - fsGroup: 33 # www-data for file permissions + fsGroup: 33 # www-data for file permissions readOnlyRootFilesystem: false allowPrivilegeEscalation: false - capabilities: {} # Use default capabilities for WordPress + capabilities: {} # Use default capabilities for WordPress seccompProfile: type: RuntimeDefault @@ -165,7 +162,7 @@ wordpress: accessModes: - ReadWriteOnce size: 8Gi - coreSize: 4Gi # WordPress core files persistence + coreSize: 4Gi # WordPress core files persistence # Separate volumes for security volumes: content: @@ -180,17 +177,17 @@ wordpress: ## Extra environment variables extraEnvVars: - - name: WORDPRESS_ENABLE_REDIS - value: "yes" # Enable Redis since we re-enabled it - - name: APACHE_HTTP_PORT - value: "8080" - - name: WORDPRESS_CONFIG_EXTRA - value: | - define('WP_CACHE', true); - define('DISABLE_FILE_EDITING', true); - define('DISALLOW_FILE_MODS', true); - define('FORCE_SSL_ADMIN', true); - define('WP_AUTO_UPDATE_CORE', true); + - name: WORDPRESS_ENABLE_REDIS + value: "yes" # Enable Redis since we re-enabled it + - name: APACHE_HTTP_PORT + value: "80" + - name: WORDPRESS_CONFIG_EXTRA + value: | + define('WP_CACHE', true); + define('DISABLE_FILE_EDITING', true); + define('DISALLOW_FILE_MODS', true); + define('FORCE_SSL_ADMIN', true); + define('WP_AUTO_UPDATE_CORE', true); ## Init Containers (WordPress Hardening) - Disabled for resource optimization initContainers: @@ -210,12 +207,12 @@ wordpress: # Based on actual usage: WordPress uses ~380Mi in production resources: limits: - cpu: 800m # Sufficient for Kadence AI theme installation - memory: 1Gi # Prevents OOMKilled during plugin installs + cpu: 800m # Sufficient for Kadence AI theme installation + memory: 1Gi # Prevents OOMKilled during plugin installs ephemeral-storage: 800Mi requests: - cpu: 50m # Cluster-optimized baseline - memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) + cpu: 50m # Cluster-optimized baseline + memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) ephemeral-storage: 200Mi ## Health Checks (Production Ready - TCP mode for iThemes Security compatibility) livenessProbe: @@ -240,7 +237,7 @@ wordpress: ## Auto-scaling Configuration (Disabled for single replica + RWO volumes) autoscaling: - enabled: false # Must be false when using ReadWriteOnce volumes + enabled: false # Must be false when using ReadWriteOnce volumes ## Environment Variables extraEnvVars: [] @@ -256,25 +253,24 @@ ingress: # ssl-protocols: "TLSv1.2 TLSv1.3" # ssl-ciphers: Modern cipher suites for mobile browser compatibility # WordPress Performance - nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads + nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads ## TLS Security Configuration (Parameterized for flexibility) tls: - protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) + protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) # Cipher suites: Mozilla "Intermediate" profile - balances security with compatibility # Supports: Perfect Forward Secrecy, mobile devices (CHACHA20), modern encryption (AES-GCM) ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" hosts: - - host: "" - paths: - - path: / - pathType: Prefix - + - host: "" + paths: + - path: / + pathType: Prefix ## Cron Jobs (Production Maintenance) cron: enabled: true - schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) + schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) image: registry: docker.io repository: wordpress @@ -290,8 +286,8 @@ cron: ## Backup Configuration (Production Critical) backup: enabled: true - schedule: "0 2 * * *" # Daily at 2 AM - retention: 30 # Keep 30 days of backups + schedule: "0 2 * * *" # Daily at 2 AM + retention: 30 # Keep 30 days of backups resources: limits: cpu: 200m @@ -305,11 +301,11 @@ backup: ## MariaDB Configuration (Official MariaDB 12.0.2 Latest Stable) mariadb: - enabled: false # Using custom StatefulSet with official image + enabled: false # Using custom StatefulSet with official image mariadbOfficial: enabled: true - image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 + image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 auth: database: wordpress username: wordpress @@ -318,7 +314,7 @@ mariadbOfficial: ## Security Context (Pod Security Standards: Restricted) podSecurityContext: - runAsUser: 999 # Official MariaDB mysql user + runAsUser: 999 # Official MariaDB mysql user runAsGroup: 999 runAsNonRoot: true fsGroup: 999 @@ -326,26 +322,26 @@ mariadbOfficial: type: RuntimeDefault containerSecurityContext: - runAsUser: 999 # Official MariaDB mysql user + runAsUser: 999 # Official MariaDB mysql user runAsGroup: 999 runAsNonRoot: true allowPrivilegeEscalation: false readOnlyRootFilesystem: false capabilities: drop: - - ALL + - ALL seccompProfile: type: RuntimeDefault ## Resource Configuration (MariaDB Memory-Optimized - WeOwn Standardized) resources: limits: - cpu: 250m # Optimized for database operations - memory: 512Mi # Increased for initialization (prevents OOMKilled) + cpu: 250m # Optimized for database operations + memory: 512Mi # Increased for initialization (prevents OOMKilled) ephemeral-storage: 1Gi requests: - cpu: 50m # Matches cluster optimization - memory: 256Mi # Increased for initialization stability + cpu: 50m # Matches cluster optimization + memory: 256Mi # Increased for initialization stability ## MariaDB Performance Tuning (2-node cluster optimized) configuration: | @@ -380,7 +376,7 @@ redis: ephemeral-storage: 50Mi persistence: - enabled: false # Disabled for performance in 2-node cluster + enabled: false # Disabled for performance in 2-node cluster ## Security context for Redis podSecurityContext: @@ -399,13 +395,13 @@ redis: readOnlyRootFilesystem: false capabilities: drop: - - ALL + - ALL seccompProfile: type: RuntimeDefault ## Service Configuration service: - type: ClusterIP # Secure internal-only access + type: ClusterIP # Secure internal-only access port: 80 targetPort: 80 annotations: {} @@ -424,11 +420,12 @@ muPlugins: cache: enabled: true authFix: - enabled: true # General REST API Authentication fix for containerized environments + enabled: true # General REST API Authentication fix for containerized environments disableXmlRpc: - enabled: true # Security best practice: Disable XML-RPC (use REST API instead) + enabled: true # Security best practice: Disable XML-RPC (use REST API instead) smtp: - enabled: true # Enterprise SMTP Configuration via Environment Variables + enabled: true # Enterprise SMTP Configuration via Environment Variables + ## WordPress Plugins (Manual Installation Recommended) # Plugins are not pre-configured in this deployment for security and flexibility From c073eb0dd3efc1010460820cea711bd27ff0c1ce Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 22:42:50 +0500 Subject: [PATCH 5/9] udated version bump --- wordpress/CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wordpress/CHANGELOG.md b/wordpress/CHANGELOG.md index c3f27f6..a16672c 100644 --- a/wordpress/CHANGELOG.md +++ b/wordpress/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this WordPress deployment will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.2.7] - 2026-05-06 + +### 🔧 **Version Bump & Maintenance** + +#### **Updated** + +- Chart version updated to 3.2.7 +- Maintenance and stability improvements + ## [3.3.8] - 2025-11-07 ### 🔧 **Critical Fix: WP_MEMORY_LIMIT Configuration & Database Restoration** From 52bc0a2890e8bda5894abace99d744f6644a8a15 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 22:52:40 +0500 Subject: [PATCH 6/9] fix: resolve yamllint line endings and comment indentation --- wordpress/helm/values-ptoken.yaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/wordpress/helm/values-ptoken.yaml b/wordpress/helm/values-ptoken.yaml index 99b59aa..cb5bd3b 100644 --- a/wordpress/helm/values-ptoken.yaml +++ b/wordpress/helm/values-ptoken.yaml @@ -1,13 +1,13 @@ -wordpress: - domain: "ptoken.agency" - wordpressBlogName: "PToken Agency" - includeWWW: true - redirectToWWW: true - -ingress: - enabled: true - className: "nginx" - tls: - - secretName: ptoken-tls - hosts: - - ptoken.agency \ No newline at end of file +wordpress: + domain: "ptoken.agency" + wordpressBlogName: "PToken Agency" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: ptoken-tls + hosts: + - ptoken.agency From 488ebfbdd524483ed3da4d511d845f6caf620978 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 23:03:36 +0500 Subject: [PATCH 7/9] fix: resolve yamllint line endings --- .../helm/templates/php-config-configmap.yaml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/wordpress/helm/templates/php-config-configmap.yaml b/wordpress/helm/templates/php-config-configmap.yaml index 139805d..ca51b42 100644 --- a/wordpress/helm/templates/php-config-configmap.yaml +++ b/wordpress/helm/templates/php-config-configmap.yaml @@ -1,18 +1,18 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "wordpress.fullname" . }}-php-config - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} -data: - uploads.ini: | - ; PHP Upload Configuration - Codified (D152 & #264) - upload_max_filesize = 128M - post_max_size = 128M - max_execution_time = 300 - max_input_time = 300 - memory_limit = 256M - - ; Wordfence WAF Hardening: Forces loading via PHP-FPM - auto_prepend_file = '/var/www/html/wordfence-waf.php' \ No newline at end of file +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ include "wordpress.fullname" . }}-php-config' + namespace: "{{ .Release.Namespace }}" + labels: + # {{ include "wordpress.labels" . | nindent 4 }} +data: + uploads.ini: | + ; PHP Upload Configuration - Codified (D152 & #264) + upload_max_filesize = 128M + post_max_size = 128M + max_execution_time = 300 + max_input_time = 300 + memory_limit = 256M + + ; Wordfence WAF Hardening: Forces loading via PHP-FPM + auto_prepend_file = '/var/www/html/wordfence-waf.php' From 0318268eeb1b6a4f15714e4f0159ecb1793f0385 Mon Sep 17 00:00:00 2001 From: Nik Date: Wed, 13 May 2026 23:35:57 -0600 Subject: [PATCH 8/9] docs: apply markdownlint autofixes for PR #15 CI - ADR-004: add blank line before bulleted list (MD032) - workflows/README.md: switch *emphasis* to _emphasis_ for style consistency (MD049) Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/ADR-004-copilot-auto-review-ruleset.md | 1 + .github/workflows/README.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/ADR-004-copilot-auto-review-ruleset.md b/.github/ADR-004-copilot-auto-review-ruleset.md index 9292d49..ae64434 100644 --- a/.github/ADR-004-copilot-auto-review-ruleset.md +++ b/.github/ADR-004-copilot-auto-review-ruleset.md @@ -24,6 +24,7 @@ ADR-003 covers the strict `main`-only ruleset. But there are repo-wide invariant 2. **`copilot_code_review` on `~ALL` branches** — every **newly-created** PR (regardless of base branch) gets Copilot AI review automatically **after ruleset enablement**. This is the WeOwn baseline AI safety pattern (one of two enforcement mechanisms; the other is `weown-bot` being a "human-type" account so legacy auto-trigger fires). **Note**: Copilot evaluates auto-review eligibility at PR-creation time, so PRs that already existed before the ruleset was applied (e.g., PR #13) do **not** retroactively gain auto-review — they must be triggered manually for the duration of their open lifecycle. See § Empirical Validation Results below for the controlled experiment confirming this PR-creation-time caching behavior. These can't go in ADR-003 because they target `~ALL`, not `~DEFAULT_BRANCH`. They warrant a separate ADR because: + - Their compliance mappings differ (focus on data integrity + AI safety + history immutability, not change-management gating) - They have a defense-in-depth pairing with an **enterprise-level** ruleset that mirrors the same rules at a broader scope - Their pruning criteria (when to retire either layer) are distinct from ADR-003's review cadence diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a57c4e0..f6f0117 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -489,8 +489,8 @@ GitHub's own alert is often missed because it goes to an email box that may not **Interaction with workflows**: -- `auto-pr-to-main.yml` runs `gh pr edit --add-reviewer` to *request* a specific reviewer — this is a suggestion, not enforcement. -- The ruleset's "1 reviewer + Code Owners review" is the *enforcement* layer. Both are needed: request for discoverability, ruleset for gating. +- `auto-pr-to-main.yml` runs `gh pr edit --add-reviewer` to _request_ a specific reviewer — this is a suggestion, not enforcement. +- The ruleset's "1 reviewer + Code Owners review" is the _enforcement_ layer. Both are needed: request for discoverability, ruleset for gating. - `branch-name-check.yml` is the only workflow currently required as a status check. `pat-health-check.yml` runs on `schedule:` so it cannot be a PR-time required status check; it surfaces red-X independently in the Actions tab when the PAT is ≤3 days from expiration. ### 8.2 Branch Naming Enforcement @@ -596,7 +596,7 @@ Consolidated reference for the most common failure signatures across all workflo | "Branch Name Check" shows red ✗ on PR | Branch doesn't match regex or uses `` <2 chars / `` <3 chars | Rename the branch locally; force-push is BLOCKED by `non_fast_forward` ruleset — open a NEW branch with a compliant name instead | | **Copilot auto-review** | | | | No Copilot review after push to existing PR | PR was created before Copilot Business entitlement was provisioned (2026-04-27). Auto-trigger is PR-creation-time. | Manual trigger via `gh api --method POST /repos/WeOwnNetwork/ai/pulls//requested_reviewers -f reviewers[]=copilot-pull-request-reviewer` (canonical GitHub Copilot reviewer login — same value referenced by [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) or the "Request review" button in GitHub UI. For the long-term fix (new PRs auto-trigger correctly) see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md). | -| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch *before* creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made *while the PR is open*. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | +| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch _before_ creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made _while the PR is open_. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | | No Copilot review on brand-new PR (post-2026-04-27) | Either (a) `weown-bot` Copilot Business seat revoked, or (b) rulesets misconfigured | Verify via `gh api /repos/WeOwnNetwork/ai/rulesets/12131972` → rules include `copilot_code_review`; verify enterprise-level ruleset still active in Enterprise Settings | | **Branch protection / rulesets** | | | | `Push rejected: non-fast-forward` on feature branch | Normal — force-push blocked on `~ALL` branches by Layer 1 + Layer 2 rulesets (see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) | Don't force-push. Open a new branch or use merge instead of rebase. | From 82490724b44b45d8ae3953be74d1b037e073e05e Mon Sep 17 00:00:00 2001 From: Nik Date: Wed, 13 May 2026 23:49:36 -0600 Subject: [PATCH 9/9] fix(wordpress/helm): address PR #15 review items 10-12 - ingress.yaml: parameterize spec.tls[0].secretName from .Values.ingress.tls[0].secretName so per-site overrides (burnedout-tls, ptoken-tls) actually take effect. Falls back to "wordpress-tls" when .Values.ingress.tls is a map or unset (preserves default and TLS hardening map). - php-config-configmap.yaml: gate "auto_prepend_file = wordfence-waf.php" behind .Values.wordpress.wordfence.enabled (default false) to avoid PHP warnings when the plugin is not installed. - ingress.yaml: gate the file-blocking server-snippet annotation behind .Values.ingress.serverSnippet.enabled (default true) so the chart can deploy on hardened controllers that set allow-snippet-annotations: false. - values.yaml: add wordpress.wordfence.enabled and ingress.serverSnippet.enabled. - Chart.yaml: bump 3.2.7 -> 3.3.0 (SemVer + WeOwnVer valid). - wordpress/CHANGELOG.md: document 3.3.0. Verified: helm template renders correctly with default, values-burnedout, and values-ptoken; helm lint clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- wordpress/CHANGELOG.md | 15 +++++++++++++++ wordpress/helm/Chart.yaml | 2 +- wordpress/helm/templates/ingress.yaml | 8 +++++++- .../helm/templates/php-config-configmap.yaml | 2 ++ wordpress/helm/values.yaml | 13 +++++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/wordpress/CHANGELOG.md b/wordpress/CHANGELOG.md index a16672c..27da7b4 100644 --- a/wordpress/CHANGELOG.md +++ b/wordpress/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this WordPress deployment will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.3.0] - 2026-05-13 + +### 🛠️ **Template Hardening Fixes (PR #15 Review)** + +#### **Fixed** + +- **`templates/ingress.yaml`**: TLS `secretName` was hardcoded to `wordpress-tls`. Per-site overrides in `values-burnedout.yaml` (`burnedout-tls`) and `values-ptoken.yaml` (`ptoken-tls`) were silently ignored. The template now reads `secretName` from `.Values.ingress.tls[0].secretName` when `.Values.ingress.tls` is a list, falling back to `wordpress-tls` for backward compatibility. +- **`templates/php-config-configmap.yaml`**: `auto_prepend_file = '/var/www/html/wordfence-waf.php'` was emitted unconditionally, which causes PHP warnings on every request when the Wordfence plugin is not installed. Now gated behind the new `wordpress.wordfence.enabled` flag (default `false`). +- **`templates/ingress.yaml`**: `nginx.ingress.kubernetes.io/server-snippet` annotation is now opt-out via `ingress.serverSnippet.enabled` (default `true`). Hardened ingress-nginx controllers reject snippet annotations when `allow-snippet-annotations: false`; setting this to `false` allows deployment on those clusters. + +#### **Added** + +- `wordpress.wordfence.enabled` (default `false`) in `values.yaml`. +- `ingress.serverSnippet.enabled` (default `true`) in `values.yaml`. + ## [3.2.7] - 2026-05-06 ### 🔧 **Version Bump & Maintenance** diff --git a/wordpress/helm/Chart.yaml b/wordpress/helm/Chart.yaml index 4532793..cf2e5fa 100644 --- a/wordpress/helm/Chart.yaml +++ b/wordpress/helm/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: wordpress description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS type: application -version: 3.2.7 +version: 3.3.0 appVersion: "6.8.3" keywords: diff --git a/wordpress/helm/templates/ingress.yaml b/wordpress/helm/templates/ingress.yaml index 8ea9a21..1410f51 100644 --- a/wordpress/helm/templates/ingress.yaml +++ b/wordpress/helm/templates/ingress.yaml @@ -1,10 +1,14 @@ # {{ if .Values.ingress.enabled }} # {{ $tlsProtocols := "TLSv1.2 TLSv1.3" }} # {{ $tlsCiphers := "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" }} +# {{ $tlsSecretName := "wordpress-tls" }} # {{ if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} # {{ $tlsProtocols = .Values.ingress.tls.protocols | default $tlsProtocols }} # {{ $tlsCiphers = .Values.ingress.tls.ciphers | default $tlsCiphers }} # {{ end }} +# {{ if and (hasKey .Values.ingress "tls") (kindIs "slice" .Values.ingress.tls) (gt (len .Values.ingress.tls) 0) }} +# {{ $tlsSecretName = (index .Values.ingress.tls 0).secretName | default $tlsSecretName }} +# {{ end }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -15,6 +19,7 @@ metadata: app.kubernetes.io/component: ingress annotations: # {{ toYaml .Values.ingress.annotations | nindent 4 }} + # {{ if and (hasKey .Values.ingress "serverSnippet") (kindIs "map" .Values.ingress.serverSnippet) .Values.ingress.serverSnippet.enabled }} # Block sensitive files at the edge (Task #264) nginx.ingress.kubernetes.io/server-snippet: | location ~* /\.(git|env|user\.ini|htaccess) { @@ -25,6 +30,7 @@ metadata: deny all; return 403; } + # {{ end }} # TLS/SSL Security (Enterprise-Grade, Parameterized) nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/ssl-protocols: "{{ $tlsProtocols }}" @@ -52,7 +58,7 @@ spec: # {{ if .Values.wordpress.includeWWW }} - '{{ printf "www.%s" .Values.wordpress.domain }}' # {{ end }} - secretName: wordpress-tls + secretName: "{{ $tlsSecretName }}" rules: - host: "{{ .Values.wordpress.domain }}" http: diff --git a/wordpress/helm/templates/php-config-configmap.yaml b/wordpress/helm/templates/php-config-configmap.yaml index ca51b42..c4558f2 100644 --- a/wordpress/helm/templates/php-config-configmap.yaml +++ b/wordpress/helm/templates/php-config-configmap.yaml @@ -13,6 +13,8 @@ data: max_execution_time = 300 max_input_time = 300 memory_limit = 256M + {{- if and (hasKey .Values.wordpress "wordfence") .Values.wordpress.wordfence.enabled }} ; Wordfence WAF Hardening: Forces loading via PHP-FPM auto_prepend_file = '/var/www/html/wordfence-waf.php' + {{- end }} diff --git a/wordpress/helm/values.yaml b/wordpress/helm/values.yaml index d4953c1..52e4571 100644 --- a/wordpress/helm/values.yaml +++ b/wordpress/helm/values.yaml @@ -133,6 +133,12 @@ wordpress: // Enable WordPress mail debug logging define('WP_MAIL_DEBUG', false); + ## Wordfence WAF integration. Enable only after the Wordfence plugin is + ## installed AND `wordfence-waf.php` exists at /var/www/html/. Otherwise + ## PHP will warn on every request and may abort execution. + wordfence: + enabled: false + ## WordPress Security Keys (Generated automatically by deployment script) security: authKey: "AUTH_KEY_PLACEHOLDER" @@ -255,6 +261,13 @@ ingress: # WordPress Performance nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads + ## Edge-level NGINX server-snippet (file blocking). + ## Hardened ingress-nginx controllers may disable snippet annotations via + ## `allow-snippet-annotations: false`. Set enabled=false in that environment + ## and enforce the equivalent rules via controller ConfigMap or WAF. + serverSnippet: + enabled: true + ## TLS Security Configuration (Parameterized for flexibility) tls: protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices)