commit 3aa514e62ef9a1c7e4ff426f0daff7c010a53695 parent 70911c5eaa66361c2424a12d34ddccea890ec017 Author: MTRNord <MTRNord@users.noreply.github.com> Date: Fri, 17 Apr 2026 16:57:42 +0200 deploy mediawiki Signed-off-by: MTRNord <MTRNord@users.noreply.github.com> Diffstat:
| M | apps/talos_cluster/image-policies/kustomization.yaml | | | 1 | + |
| A | apps/talos_cluster/image-policies/mediawiki.yaml | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
| M | apps/talos_cluster/kustomization.yaml | | | 1 | + |
| A | apps/talos_cluster/mediawiki/configmap.yaml | | | 101 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | apps/talos_cluster/mediawiki/deployment.yaml | | | 104 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | apps/talos_cluster/mediawiki/kustomization.yaml | | | 12 | ++++++++++++ |
| A | apps/talos_cluster/mediawiki/mariadb.yaml | | | 125 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | apps/talos_cluster/mediawiki/namespace.yaml | | | 4 | ++++ |
| A | apps/talos_cluster/mediawiki/pvc.yaml | | | 12 | ++++++++++++ |
| A | apps/talos_cluster/mediawiki/route.yaml | | | 18 | ++++++++++++++++++ |
| A | apps/talos_cluster/mediawiki/secret.yaml | | | 33 | +++++++++++++++++++++++++++++++++ |
11 files changed, 458 insertions(+), 0 deletions(-)
diff --git a/apps/talos_cluster/image-policies/kustomization.yaml b/apps/talos_cluster/image-policies/kustomization.yaml @@ -7,3 +7,4 @@ resources: - bookwyrm.yaml - continuwuity.yaml - base-images.yaml + - mediawiki.yaml diff --git a/apps/talos_cluster/image-policies/mediawiki.yaml b/apps/talos_cluster/image-policies/mediawiki.yaml @@ -0,0 +1,47 @@ +# Custom MediaWiki image (registry.midnightthoughts.space) +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: mediawiki + namespace: flux-system +spec: + image: registry.midnightthoughts.space/mtrnord/mediawiki + interval: 1h +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: mediawiki + namespace: flux-system +spec: + imageRepositoryRef: + name: mediawiki + filterTags: + pattern: '^\d+\.\d+\.\d+$' + policy: + semver: + range: ">=1.45.0" +--- +# MariaDB upstream image (Docker Hub) +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: docker-mariadb + namespace: flux-system +spec: + image: docker.io/library/mariadb + interval: 6h +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: docker-mariadb-12 + namespace: flux-system +spec: + imageRepositoryRef: + name: docker-mariadb + filterTags: + pattern: '^12\.\d+\.\d+$' + policy: + semver: + range: ">=12.0.0 <13.0.0" diff --git a/apps/talos_cluster/kustomization.yaml b/apps/talos_cluster/kustomization.yaml @@ -46,3 +46,4 @@ resources: #- ./proxmox-ccm - ./jenkins - ./wanderer + - ./mediawiki diff --git a/apps/talos_cluster/mediawiki/configmap.yaml b/apps/talos_cluster/mediawiki/configmap.yaml @@ -0,0 +1,101 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mediawiki-config + namespace: mediawiki +data: + LocalSettings.php: | + <?php + # This file is mounted from a ConfigMap. + # Sensitive values are read from environment variables (set via the mediawiki-cred Secret). + + $wgSitename = "MTRNord's Wiki"; + $wgMetaNamespace = "MTRNord_Wiki"; + $wgServer = "https://wiki.mtrnord.blog"; + $wgScriptPath = ""; + $wgArticlePath = "/wiki/$1"; + + # Database + $wgDBtype = "mysql"; + $wgDBserver = "mediawiki-mariadb.mediawiki.svc.cluster.local"; + $wgDBname = getenv('MW_DB_NAME') ?: 'mediawiki'; + $wgDBuser = getenv('MW_DB_USER') ?: 'mediawiki'; + $wgDBpassword = getenv('MW_DB_PASSWORD') ?: ''; + + # Secrets + $wgSecretKey = getenv('MW_SECRET_KEY') ?: ''; + $wgUpgradeKey = getenv('MW_UPGRADE_KEY') ?: ''; + + # File uploads + $wgEnableUploads = true; + $wgUploadPath = "/images"; + $wgUploadDirectory = "/var/www/html/images"; + $wgMaxUploadSize = 50 * 1024 * 1024; # 50 MB + + # Logo / favicon (update after initial setup) + $wgLogo = "$wgResourceBasePath/resources/assets/wiki.png"; + + # Email (optional — configure SMTP if you want email notifications) + $wgEnableEmail = false; + $wgEnableUserEmail = false; + $wgEnotifUserTalk = false; + $wgEnotifWatchlist = false; + $wgEmailAuthentication = false; + + # Caching + $wgMainCacheType = CACHE_NONE; + $wgSessionCacheType = CACHE_DB; + + # Performance + $wgParserCacheType = CACHE_DB; + $wgJobRunRate = 1; + + # === Spam prevention === + # Disable public account creation — admins create accounts manually + $wgGroupPermissions['*']['createaccount'] = false; + $wgGroupPermissions['sysop']['createaccount'] = true; + + # QuestyCaptcha on account creation and repeated bad login + wfLoadExtensions(['ConfirmEdit', 'ConfirmEdit/QuestyCaptcha']); + $wgCaptchaClass = 'QuestyCaptcha'; + $wgCaptchaTriggers['createaccount'] = true; + $wgCaptchaTriggers['badlogin'] = true; + $wgCaptchaQuestions = [ + "What is the owner's username on this wiki?" => 'MTRNord', + 'What does the "wiki" in MediaWiki stand for?' => 'wiki', + ]; + + wfLoadExtension('SpamBlacklist'); + wfLoadExtension('AbuseFilter'); + wfLoadExtension('CheckUser'); + + # === Editing extensions === + wfLoadExtension('VisualEditor'); + # VisualEditor needs the internal Parsoid endpoint to avoid SSL loopback in K8s + $wgVirtualRestConfig['modules']['parsoid'] = [ + 'url' => 'http://mediawiki.mediawiki.svc.cluster.local/rest.php', + ]; + + wfLoadExtension('WikiEditor'); + wfLoadExtension('CodeMirror'); + wfLoadExtension('ParserFunctions'); + wfLoadExtension('Cite'); + wfLoadExtension('SyntaxHighlight_GeSHi'); + wfLoadExtension('TemplateStyles'); + + # === Skin === + wfLoadSkin('Vector'); + $wgDefaultSkin = 'vector-2022'; + + # === Misc === + $wgRightsPage = ""; + $wgRightsUrl = ""; + $wgRightsText = ""; + $wgRightsIcon = ""; + $wgDiff3 = "/usr/bin/diff3"; + $wgShellLocale = "en_US.utf8"; + + # Allow read for anonymous users but not edit + $wgGroupPermissions['*']['read'] = true; + $wgGroupPermissions['*']['edit'] = false; + $wgGroupPermissions['user']['edit'] = true; diff --git a/apps/talos_cluster/mediawiki/deployment.yaml b/apps/talos_cluster/mediawiki/deployment.yaml @@ -0,0 +1,104 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mediawiki + namespace: mediawiki +spec: + replicas: 1 + selector: + matchLabels: + app: mediawiki + template: + metadata: + labels: + app: mediawiki + spec: + automountServiceAccountToken: false + securityContext: + fsGroup: 33 + containers: + - name: mediawiki + image: registry.midnightthoughts.space/mtrnord/mediawiki:1.45.3 # {"$imagepolicy": "flux-system:mediawiki"} + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + runAsNonRoot: false + seccompProfile: + type: RuntimeDefault + env: + - name: MW_DB_NAME + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: db-name + - name: MW_DB_USER + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: username + - name: MW_DB_PASSWORD + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: password + - name: MW_SECRET_KEY + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: wg-secret-key + - name: MW_UPGRADE_KEY + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: wg-upgrade-key + volumeMounts: + - name: mediawiki-config + mountPath: /var/www/html/LocalSettings.php + subPath: LocalSettings.php + readOnly: true + - name: mediawiki-images + mountPath: /var/www/html/images + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /api.php?action=query&meta=siteinfo&format=json + port: 80 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /api.php?action=query&meta=siteinfo&format=json + port: 80 + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 3 + volumes: + - name: mediawiki-config + configMap: + name: mediawiki-config + - name: mediawiki-images + persistentVolumeClaim: + claimName: mediawiki-images +--- +apiVersion: v1 +kind: Service +metadata: + name: mediawiki + namespace: mediawiki +spec: + type: ClusterIP + selector: + app: mediawiki + ports: + - protocol: TCP + port: 80 + targetPort: 80 diff --git a/apps/talos_cluster/mediawiki/kustomization.yaml b/apps/talos_cluster/mediawiki/kustomization.yaml @@ -0,0 +1,12 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: mediawiki +resources: + - namespace.yaml + - secret.yaml + - configmap.yaml + - pvc.yaml + - mariadb.yaml + - deployment.yaml + # route.yaml is intentionally omitted until after initial setup + # Add it back once the wiki is installed and verified via port-forward diff --git a/apps/talos_cluster/mediawiki/mariadb.yaml b/apps/talos_cluster/mediawiki/mariadb.yaml @@ -0,0 +1,125 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mediawiki-mariadb-config + namespace: mediawiki +data: + performance.cnf: | + [mysqld] + innodb_buffer_pool_size = 256M + innodb_log_file_size = 64M + max_allowed_packet = 64M + max_connections = 50 + table_open_cache = 512 + tmp_table_size = 32M + max_heap_table_size = 32M +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mediawiki-mariadb + namespace: mediawiki +spec: + serviceName: mediawiki-mariadb + replicas: 1 + selector: + matchLabels: + app: mediawiki-mariadb + template: + metadata: + labels: + app: mediawiki-mariadb + spec: + automountServiceAccountToken: false + securityContext: + fsGroup: 999 + containers: + - name: mariadb + image: mariadb:12.3.1 # {"$imagepolicy": "flux-system:docker-mariadb-12"} + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + env: + - name: MARIADB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: admin-password + - name: MARIADB_USER + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: username + - name: MARIADB_PASSWORD + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: password + - name: MARIADB_DATABASE + valueFrom: + secretKeyRef: + name: mediawiki-cred + key: db-name + - name: MYSQL_ROOT_HOST + value: "%" + volumeMounts: + - name: mariadb-data + mountPath: /var/lib/mysql + - name: mariadb-config + mountPath: /etc/mysql/conf.d + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "400m" + livenessProbe: + tcpSocket: + port: 3306 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + tcpSocket: + port: 3306 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + volumes: + - name: mariadb-config + configMap: + name: mediawiki-mariadb-config + volumeClaimTemplates: + - metadata: + name: mariadb-data + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: longhorn + resources: + requests: + storage: 2Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: mediawiki-mariadb + namespace: mediawiki +spec: + type: ClusterIP + selector: + app: mediawiki-mariadb + ports: + - protocol: TCP + port: 3306 + targetPort: 3306 diff --git a/apps/talos_cluster/mediawiki/namespace.yaml b/apps/talos_cluster/mediawiki/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: mediawiki diff --git a/apps/talos_cluster/mediawiki/pvc.yaml b/apps/talos_cluster/mediawiki/pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mediawiki-images + namespace: mediawiki +spec: + accessModes: + - ReadWriteMany + storageClassName: longhorn + resources: + requests: + storage: 2Gi diff --git a/apps/talos_cluster/mediawiki/route.yaml b/apps/talos_cluster/mediawiki/route.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mediawiki + namespace: mediawiki +spec: + parentRefs: + - name: envoy-gateway + namespace: envoy-gateway + hostnames: + - wiki.mtrnord.blog + rules: + - backendRefs: + - name: mediawiki + port: 80 + timeouts: + request: 120s + backendRequest: 0s diff --git a/apps/talos_cluster/mediawiki/secret.yaml b/apps/talos_cluster/mediawiki/secret.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mediawiki-cred + namespace: mediawiki +type: Opaque +stringData: + admin-password: ENC[AES256_GCM,data:jsSsW9NdIiI+kM4ADVfQ7efvSroVIjr2TnzzPA5wp7b7oZxF1wIYVC1KaNSXSKgD0MbGRFXBUoFoGoAGwakO,iv:fizRdIFYigznvaQx2PKrrlkur9xeBVM9c4hHcYaXm40=,tag:f/xaw0oCbwbT24JLbGn2VA==,type:str] + username: ENC[AES256_GCM,data:aa2PhIgWskbN,iv:8G7gXW50kVJLSSZ6xU4UVYfDlFuTgDBQ/igGhtUII98=,tag:cPficJMzybn8D00UIguuRQ==,type:str] + password: ENC[AES256_GCM,data:wEC6WOgM7c8guLNaT8YoWcnhg9Zftsjkb+nQ7Sj4kiCfke00XLtWS0HyUVWsslfqLzQGMI/M0pwS7wrgiGJQ,iv:X+NSDlh6+m8oUbNZUuFB4liDLzIvJWmFE5YIVpqOHO4=,tag:9ZJ45S8v3iwr65YWjEMfJQ==,type:str] + db-name: ENC[AES256_GCM,data:RQmXW7fjWHmv,iv:YVs4cBxiqCnvEbTjeuRln4+f83FW0gAIVyKKAoWZDy8=,tag:ZAL90n0XTOpZb43FpSO4FA==,type:str] + wg-secret-key: ENC[AES256_GCM,data:6G1TYtL+lGkS7upUmXt0kQ8HSJatvURdSXeBv0pGgs30nyof7InMhgRzTVd7Z04pxsK+ToEPhX4/lnC91XwTOQ==,iv:Woo1gQqyYnpsQ7qCS/nSj2z92mLHsxpi5kUMvsTsuzk=,tag:AVfdKROYOgAM0ASqLERaow==,type:str] + wg-upgrade-key: ENC[AES256_GCM,data:mqtfeKlp2F3SCWEh3nwVS+ZSQcsXOIFy0allk+zEUdLMUtMCzXSj+JZbWon+3YQtMJcJY3JfX4aX80c8Na7f4A==,iv:OdWpLVBakHjHtM/zYYN0uCEgiMLH72oY1ycp/iihY1k=,tag:OkTrj1jUHhtl12Foerthcg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1esjyg2qfy49awv0ptkzvpk425adczjr38m37w2mmcahzc4p8n54sll2nzh + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhRnltRUphOEh2WGZXVzZF + L3hkcjVEdTkxK21rR1NQTDAyQ2UrMFVORWdvCkFUNjBYNDkyZ1ZCc2JZbDNSaEht + ZXJGOENmdmRSa293K2lYTmgxemNoVm8KLS0tIDNrUVh1dFVmSFdxYlBudXh3VHRB + SWJwY2djTFVvR0YxdWlaL2UrbzVxOUEKIA610Ni152yvC6Lhcmf4XwtzBPv2z2Mk + ve7xkqt5l4jRNf3Bgwl7vdF/SLGOa5tkKlPHbPcxvn22WN4fDVa7Wg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-04-17T14:57:29Z" + mac: ENC[AES256_GCM,data:v7Okh91u92n866eEasiAUGQV/EQ0a+/Eh9f2aiMkpYWILTx9U6c4W7OoqUqoKQv6OlaeJm5mqm+Pdioa1Ip60PZtChcDWgbFHX/HHQ8DPU1iG1/g48GUy6zFr5PXaT+sJyjtiNfhZple2JzuoXrFe0weTc1mbEpfK4n8+VexQZc=,iv:cr0iS4FIxyTaJPzg8OQbcVcDVPjhafltfyklV4iAZJI=,tag:gjdeUtFypvjJY8ziwND6uQ==,type:str] + pgp: [] + encrypted_regex: ^(apiKey|appUserPassword|otelUserPassword|harborAdminPassword|totpVaultKey|kimaiAppSecret|kimaiAdminPassword|GITHUB_CLIENT_ID|GITHUB_CLIENT_SECRET|GITHUB_PRIVATE_KEY|woosh|root_password|rspamd_password|pgdb_password|matrix_access_token|pgdb_remote_url|hmac_secret_key|adminPassword|adminEmail|jenkinsAdminEmail|securityRealm|gerrit.config|routing_key|DATABASE_URL|SMTP_PASSWORD|SECRET_KEY_BASE|admin_password|extraCommands|key|clickhouseDatabaseURL|databaseURL|client_id|client_secret|secret_key_base|otp_secret|private_key|public_key|primaryKey|deterministicKey|keyDerivationSalt|token|clientId|secretKey|installationId|installationKey|uriOverride|adminToken.value|password.value|sql_password|erlangCookie|AUTHENTICATION_PASSWORD|ROOM_API_SECRET_KEY|adminPassword|configPassword|adminUser|configUser|MAIL_PASSWORD|APP_KEY|api_key|api_secret|keys|livekit_key|livekit_secret|secret_key|admin_pass|admin_email|mariadbPassword|mariadbRootPassword|privateKey|data|stringData|PASSWD|password|pass|postgresPassword|smtp_auth_password|addresses|smtp_auth_username|authorization_credentials|postgresqlPassword|redminePassword|smtpPassword|registration_shared_secret|shared_secret|secret|admin_token|integrationKey|integration_key|rootPassword|adminPassword|adminUser|adminEmail|emailPassword|secretKey|appId|clientSecret|webhookSecret|content)$ + version: 3.9.1