cluster

Infrastructure files for Nordgedanken and Midnightthoughts.
git clone git://archive.git.mtrnord.blog/MTRNord/cluster.git
Log | Files | Refs | README

configmap.yaml (13280B)


      1 apiVersion: v1
      2 kind: ConfigMap
      3 metadata:
      4   name: mediawiki-config
      5   namespace: mediawiki
      6 data:
      7   robots.txt: |
      8     User-agent: *
      9     Allow: /wiki/
     10     Allow: /load.php
     11     Disallow: /
     12   LocalSettings.php: |
     13     <?php
     14     # This file is mounted from a ConfigMap.
     15     # Sensitive values are read from environment variables (set via the mediawiki-cred Secret).
     16 
     17     $wgSitename = "MTRNord's Wiki";
     18     $wgMetaNamespace = "MTRNord_Wiki";
     19     $wgServer = "https://wiki.mtrnord.blog";
     20     $wgScriptPath = "";
     21     $wgArticlePath = "/wiki/$1";
     22 
     23     # Site content language
     24     $wgLanguageCode = 'en';
     25 
     26     # Database
     27     $wgDBtype     = "mysql";
     28     $wgDBserver   = "mediawiki-maxscale.mediawiki.svc.cluster.local";
     29     $wgDBname     = getenv('MW_DB_NAME')     ?: 'mediawiki';
     30     $wgDBuser     = getenv('MW_DB_USER')     ?: 'mediawiki';
     31     $wgDBpassword = getenv('MW_DB_PASSWORD') ?: '';
     32 
     33     # Secrets
     34     $wgSecretKey  = getenv('MW_SECRET_KEY')  ?: '';
     35     $wgUpgradeKey = getenv('MW_UPGRADE_KEY') ?: '';
     36 
     37     # File uploads
     38     $wgEnableUploads  = true;
     39     $wgUseImageMagick = true;
     40     $wgUploadPath     = "/images";
     41     $wgUploadDirectory = "/var/www/html/images";
     42     $wgMaxUploadSize  = 50 * 1024 * 1024; # 50 MB
     43     $wgUseInstantCommons = true;
     44     wfLoadExtension( 'MediaUploader' );
     45 
     46     $wgUploadWizardConfig = array(
     47       'altUploadForm' => 'Special:Upload',
     48     );
     49 
     50     // Needed to make UploadWizard work in IE, see https://phabricator.wikimedia.org/T41877
     51     $wgApiFrameOptions = 'SAMEORIGIN';
     52 
     53     $wgUploadNavigationUrl = '/wiki/Special:UploadWizard';
     54 
     55 
     56     # Logo / favicon (update after initial setup)
     57     $wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";
     58 
     59     # Email
     60     $wgEnableEmail         = true;
     61     $wgEnableUserEmail     = true;
     62     $wgEnotifUserTalk      = true;
     63     $wgEnotifWatchlist     = true;
     64     $wgEmailAuthentication = true;
     65     $wgEmergencyContact    = 'support@midnightthoughts.space';
     66     $wgPasswordSender      = 'support@midnightthoughts.space';
     67 
     68     $wgSMTP = [
     69         'host'     => 'stalwart-mail.stalwart.svc.cluster.local',
     70         'IDHost'   => 'wiki.mtrnord.blog',
     71         'port'     => 2587,
     72         'auth'     => true,
     73         'username' => 'support@midnightthoughts.space',
     74         'password' => getenv('MW_SMTP_PASSWORD') ?: '',
     75         // port 2587 is an internal-only listener with no PROXY protocol requirement;
     76         // cert is for mail.midnightthoughts.space so peer verification is disabled
     77         'socket_options' => [
     78             'ssl' => [
     79                 'verify_peer'      => false,
     80                 'verify_peer_name' => false,
     81             ],
     82         ],
     83     ];
     84 
     85     # Caching — Valkey for shared object/session/parser cache, APCu for localisation
     86     $wgObjectCaches['redis'] = [
     87         'class'      => 'RedisBagOStuff',
     88         'servers'    => ['mediawiki-valkey.mediawiki.svc.cluster.local:6379'],
     89         'persistent' => true,
     90     ];
     91     $wgMainCacheType    = 'redis';
     92     $wgSessionCacheType = 'redis';
     93     $wgParserCacheType  = 'redis';
     94 
     95     # Run jobs via CronJob, not on page requests
     96     # Localisation cache — 'array' stores PHP static arrays in $wgCacheDirectory,
     97     # picked up by OPcache on subsequent requests (fastest option)
     98     $wgCacheDirectory = '/tmp/mediawiki-cache';
     99     $wgLocalisationCacheConf = [
    100         'class'          => LocalisationCache::class,
    101         'store'          => 'array',
    102         'storeClass'     => false,
    103         'storeDirectory' => false,
    104         'manualRecache'  => false,
    105     ];
    106 
    107     $wgJobRunRate = 0;
    108     $wgUseLocalMessageCache = true;
    109     $wgEnableSidebarCache = true;
    110 
    111     $wgShowExceptionDetails = true;
    112 
    113     # === Import timeouts for large Wikipedia imports ===
    114     # Increase PHP execution time for imports
    115     set_time_limit(900); # 15 minutes
    116     ini_set('max_execution_time', 900);
    117 
    118     # Increase socket timeout for fetching remote content
    119     ini_set('default_socket_timeout', 300);
    120 
    121     # Allow imports from Wikipedia and other major sources
    122     $wgImportSources = [
    123         'wikipedia'    => 'https://en.wikipedia.org',
    124         'wikipedia_de' => 'https://de.wikipedia.org',
    125     ];
    126 
    127     # Allow local file imports for bulk operations
    128     $wgImportSources[] = 'interwiki';
    129     $wgImportAllowAll = true;
    130 
    131     # === IP Forwarding from Envoy Gateway ===
    132     # Trust X-Forwarded-For from Envoy Gateway (pod CIDR and private network)
    133     $wgUsePrivateIPs = true;
    134     $wgTrustedProxies = ['10.0.128.0/17', '10.0.96.0/19', 'fd00:10:244::/56', 'fd00:10:96::/112'];
    135 
    136     # Tell MediaWiki to read client IP from X-Forwarded-For header
    137     # (Envoy Gateway adds this header with the real client IP)
    138     $wgUseXForwardedFor = true;
    139 
    140     # Also read protocol from header (HTTP vs HTTPS)
    141     $wgHttp['withHttps'] = true;
    142 
    143     # === Spam prevention ===
    144     # Disable public account creation — admins create accounts manually
    145     $wgGroupPermissions['*']['createaccount'] = false;
    146     $wgGroupPermissions['sysop']['createaccount'] = true;
    147 
    148     # QuestyCaptcha on account creation and repeated bad login
    149     wfLoadExtensions(['ConfirmEdit', 'ConfirmEdit/QuestyCaptcha']);
    150     $wgCaptchaClass = 'QuestyCaptcha';
    151     $wgCaptchaTriggers['createaccount'] = true;
    152     $wgCaptchaTriggers['badlogin']      = true;
    153     $wgCaptchaQuestions = [
    154         "What is the owner's username on this wiki?" => 'MTRNord',
    155         'What does the "wiki" in MediaWiki stand for?' => 'wiki',
    156     ];
    157 
    158     wfLoadExtension('SpamBlacklist');
    159     wfLoadExtension('AbuseFilter');
    160     wfLoadExtension('CheckUser');
    161 
    162     # === Editing extensions ===
    163     wfLoadExtension('TemplateData');
    164     wfLoadExtension(
    165       'Parsoid',
    166       "$IP/vendor/wikimedia/parsoid/extension.json"
    167     );
    168     $wgParsoidSettings = [
    169         'useSelser' => true,
    170         'linting' => true
    171     ];
    172     $wgVisualEditorParsoidAutoConfig = false; // to make linting work
    173     wfLoadExtension('Linter');
    174     wfLoadExtension('VisualEditor');
    175     $wgVirtualRestConfig['modules']['parsoid'] = [
    176         'url' => 'https://wiki.mtrnord.blog/rest.php',
    177         'domain' => 'wiki.mtrnord.blog',
    178         'forwardCookies' => true,
    179         'restbaseCompat' => false,
    180         'timeout' => 30,
    181     ];
    182     $wgVisualEditorEnableWikitext = true;
    183     $wgDefaultUserOptions['visualeditor-newwikitext'] = 1;
    184     wfLoadExtension('DiscussionTools');
    185 
    186     wfLoadExtension('WikiEditor');
    187     wfLoadExtension('CodeMirror');
    188     wfLoadExtension('ParserFunctions');
    189     wfLoadExtension('Cite');
    190     wfLoadExtension('CiteThisPage');
    191     wfLoadExtension('SyntaxHighlight_GeSHi');
    192     wfLoadExtension('TemplateStyles');
    193 
    194     # === User / account extensions ===
    195     wfLoadExtension('Babel');
    196     wfLoadExtension('BetaFeatures');
    197     wfLoadExtension('Echo');
    198     wfLoadExtension('GPGMail');
    199     wfLoadExtension('GuidedTour');
    200     wfLoadExtension('LoginNotify');
    201 
    202     # === Language / Translation ===
    203 
    204     wfLoadExtension('CLDR');
    205 
    206     $wgDefaultUserOptions['usenewrc'] = 1;
    207 
    208     # === Parser & Template Extensions ===
    209     wfLoadExtension('ParserFunctions');
    210     wfLoadExtension('Scribunto');
    211     $wgScribuntoUseGeSHi = true;
    212     $wgScribuntoUseCodeEditor = true;
    213     $wgScribuntoSlowFunctionThreshold = 0.99;
    214 
    215     $wgScribuntoDefaultEngine = 'luasandbox';
    216     $wgScribuntoEngineConf['luasandbox'] = [
    217         'memoryLimit' => 52428800,  // 50 MB (default is 50MB)
    218         'cpuLimit' => 30,            // 30 seconds (increased from default 7s)
    219     ];
    220     wfLoadExtension('JsonConfig');
    221     $wgJsonConfigEnableLuaSupport = true;
    222     $wgJsonConfigModels['Tabular.JsonConfig'] = 'JsonConfig\JCTabularContent';
    223     $wgJsonConfigs['Tabular.JsonConfig'] = [
    224             'namespace' => 486,
    225             'nsName' => 'Data',
    226             // page name must end in .tab, and contain at least one symbol
    227             'pattern' => '/.\.tab$/',
    228             'license' => 'CC0-1.0',
    229             'isLocal' => false,
    230     ];
    231 
    232     $wgJsonConfigModels['Map.JsonConfig'] = 'JsonConfig\JCMapDataContent';
    233     $wgJsonConfigs['Map.JsonConfig'] = [
    234             'namespace' => 486,
    235             'nsName' => 'Data',
    236             // page name must end in .map, and contain at least one symbol
    237             'pattern' => '/.\.map$/',
    238             'license' => 'CC0-1.0',
    239             'isLocal' => false,
    240     ];
    241     $wgJsonConfigInterwikiPrefix = "commons";
    242 
    243     $wgJsonConfigs['Tabular.JsonConfig']['remote'] = [
    244             'url' => 'https://commons.wikimedia.org/w/api.php'
    245     ];
    246     $wgJsonConfigs['Map.JsonConfig']['remote'] = [
    247             'url' => 'https://commons.wikimedia.org/w/api.php'
    248     ];
    249 
    250     # Load Translate extension for structured translation workflows
    251     wfLoadExtension('Translate');
    252     $wgGroupPermissions['user']['translate'] = true;
    253     $wgGroupPermissions['user']['translate-messagereview'] = true;
    254     $wgGroupPermissions['user']['translate-groupreview'] = true;
    255     $wgGroupPermissions['user']['translate-import'] = true;
    256     $wgGroupPermissions['sysop']['pagetranslation'] = true;
    257     $wgGroupPermissions['sysop']['translate-manage'] = true;
    258     $wgEnablePageTranslation = true;
    259     $wgTranslateDocumentationLanguageCode = 'qqq';
    260     $wgExtraLanguageNames['qqq'] = 'Message documentation'; # No linguistic content. Used for documenting messages
    261 
    262     # Load UniversalLanguageSelector for language selection UI
    263     wfLoadExtension('UniversalLanguageSelector');
    264     $wgULSEnable = true;
    265     $wgULSPosition = 'personal';
    266     $wgULSGeoService = false;  // Disable GeoIP-based language suggestions
    267     $wgULSAnonCanChangeLanguage = true;  // Allow anonymous users to change UI language
    268 
    269     # === Content / navigation extensions ===
    270     wfLoadExtension('ArticleCreationWorkflow');
    271     wfLoadExtension('Disambiguator');
    272     wfLoadExtension('CreditsSource');
    273     wfLoadExtension('NearbyPages');
    274     wfLoadExtension('RSS');
    275     wfLoadExtension('MultimediaViewer');
    276     wfLoadExtension('RevisionSlider');
    277     wfLoadExtension('ReadingLists');
    278 
    279     wfLoadExtension('AutoCreateCategoryPages');
    280     wfLoadExtension('HitCounters');
    281 
    282     # RelatedArticles — use TextExtracts for descriptions; no CirrusSearch on this wiki
    283     wfLoadExtension('RelatedArticles');
    284     $wgRelatedArticlesUseCirrusSearch = false;
    285     $wgRelatedArticlesDescriptionSource = 'textextracts';
    286     $wgRelatedArticlesFooterAllowedSkins = ['vector', 'vector-2022'];
    287     wfLoadExtension('TextExtracts');
    288     wfLoadExtension('Thanks');
    289     wfLoadExtension('TorBlock');
    290     wfLoadExtension('SecureLinkFixer');
    291 
    292     # === ContactPage ===
    293     wfLoadExtension('ContactPage');
    294     $wgContactConfig['default'] = [
    295         'RecipientUser'  => 'MTRNord',
    296         'SenderName'     => 'Wiki Contact Form',
    297         'SenderEmail'    => 'support@midnightthoughts.space',
    298         'RequireDetails' => true,
    299         'IncludeIP'      => false,
    300     ];
    301 
    302     # === PdfHandler ===
    303     wfLoadExtension('PdfHandler');
    304     $wgFileExtensions[] = 'pdf';
    305 
    306     # === Skins ===
    307     wfLoadSkin('Vector');
    308     wfLoadSkin('MinervaNeue');
    309     $wgDefaultSkin = 'vector-2022';
    310     wfLoadExtension('MobileFrontend');
    311     $wgMFDefaultSkinClass = 'SkinMinerva';
    312 
    313     # === Misc ===
    314     $wgRightsPage    = "";
    315     $wgRightsUrl     = "";
    316     $wgRightsText    = "";
    317     $wgRightsIcon    = "";
    318     $wgDiff3         = "/usr/bin/diff3";
    319     $wgShellLocale   = "en_US.utf8";
    320 
    321     # Allow read for anonymous users but not edit
    322     $wgGroupPermissions['*']['read']   = true;
    323     $wgGroupPermissions['*']['edit']   = false;
    324     $wgGroupPermissions['user']['edit'] = true;
    325 
    326     wfLoadExtension('TitleBlacklist');
    327     $wgTitleBlacklistSources = [
    328       [
    329         'type' => 'localpage',
    330         'src'  => 'MediaWiki:Titleblacklist'
    331       ],
    332       [
    333         'type' => 'url',
    334         'src'  => 'https://en.wikipedia.org/w/index.php?title=MediaWiki:Titleblacklist&action=raw'
    335       ],
    336       [
    337         'type' => 'url',
    338         'src'  => 'https://meta.wikimedia.org/w/index.php?title=Title_blacklist&action=raw'
    339       ],
    340     ];
    341 
    342 
    343     # === Extensions pending MW 1.46 upgrade ===
    344     # Uncomment each block and add the clone to the Dockerfile when upgrading.
    345     #
    346     # EasyTimeline — timeline diagrams via Ploticus
    347     #   Dockerfile: git clone --branch REL1_46 https://gerrit.wikimedia.org/r/mediawiki/extensions/timeline extensions/EasyTimeline
    348     #   System packages: ploticus perl poppler-utils
    349     #   wfLoadExtension('EasyTimeline');
    350     #
    351     # EmailAuth — require email verification token on each login
    352     #   Dockerfile: git clone --branch REL1_46 https://gerrit.wikimedia.org/r/mediawiki/extensions/EmailAuth extensions/EmailAuth
    353     #   wfLoadExtension('EmailAuth');
    354     #
    355     # PropertySuggester — Wikibase property suggestions (only useful if Wikibase is deployed)
    356     #   Dockerfile: git clone --branch REL1_46 https://gerrit.wikimedia.org/r/mediawiki/extensions/PropertySuggester extensions/PropertySuggester
    357     #   wfLoadExtension('PropertySuggester');
    358     #
    359     # RealMe — links user pages to verified real-world identity
    360     #   Dockerfile: git clone --branch REL1_46 https://gerrit.wikimedia.org/r/mediawiki/extensions/RealMe extensions/RealMe
    361     #   wfLoadExtension('RealMe');
    362     #
    363     # TocTree — floating/expandable table of contents (replaces built-in TOC)
    364     #   Dockerfile: git clone --branch REL1_46 https://gerrit.wikimedia.org/r/mediawiki/extensions/TocTree extensions/TocTree
    365     #   wfLoadExtension('TocTree');