Skip to content
-
Subscribe to our newsletter & never miss our best posts. Subscribe Now!
Joe's Lab Joe's Lab Joe's Lab

Its running in the Lab

Joe's Lab Joe's Lab Joe's Lab

Its running in the Lab

  • Home
  • Home
Close

Search

  • https://www.facebook.com/
  • https://twitter.com/
  • https://t.me/
  • https://www.instagram.com/
  • https://youtube.com/
Subscribe
Building The LabThat didn't go so well.

The Account That Owned Every Page

By Joseph Werle
October 12, 2026 7 Min Read
0

Adding XWiki to the homelab cluster, with single sign-on, and the one setting I should never have touched

The lab needed somewhere to write things down. Not a README in a repo, which only helps if you already know which repo to open, but a wiki: searchable, linkable, and running on the same cluster it would end up documenting. The brief was one sentence: add XWiki to the Kubernetes cluster, tie it to my Authentik server for single sign-on, and deploy it the same way as everything else already running there.

I built this one with Claude Code. It wrote the Helm chart, ran the commands and read the logs; I made the decisions and approved every step before it happened. That arrangement matters for one section below, where the worst mistake of the project was the AI’s suggestion, and I said yes to it.


The plan: five steps, one at a time

Every app on this cluster follows the same pattern, so the plan mostly wrote itself:

  1. A Helm chart in the lab’s chart repository, published to the private registry by tagging it.
  2. A deployment repository for XWiki: values, the chart version pin, scripts that create the Secrets, and docs.
  3. Create the namespace and Secrets, run a preflight check, and install by hand once.
  4. Hand the release to Flux, so a git push becomes the deploy, and confirm the takeover changes nothing.
  5. Turn on single sign-on through Authentik.

The rule that shapes all of it: charts never create Secrets. A script generates them into the cluster and the password vault keeps the copies, so nothing credential-bearing ever lands in git or the registry.

The chart: XWiki forgets its own configuration

The official XWiki image has a quirk that decided the whole chart design. On every new container, its entrypoint rebuilds xwiki.cfg and xwiki.properties from the files baked into the image. Edit them inside a running pod and the edit lasts exactly until the next restart.

The image does have one supported hook: if a copy of either file exists in the permanent data directory, it is copied over the image’s version before Tomcat starts. So the chart adds an init container that takes the image’s own default files, strips out every setting the chart manages, appends the chart’s values, and writes the result onto the volume. Configuration lives in git, an image upgrade still brings its new defaults with it, and a hand edit cannot quietly survive.

The rest is the same shape as every other app here: XWiki on Tomcat, its own PostgreSQL, a 10 GB volume for attachments and extensions, an Ingress with a certificate from the internal CA, and a nightly backup job.

Failure one: a directory that did not exist yet

The first install failed in the init container:

/usr/local/xwiki/data/xwiki.cfg.new: No such file or directory

On a brand-new volume, XWiki’s data directory does not exist until XWiki itself creates it on first start, and the init container runs before XWiki ever has. The fix was one line, creating the directory first. The detail worth keeping is the permission: the script runs under umask 077, so a bare mkdir would have made it readable by its owner only, and the backup job would have been the next thing to break. It is created 0755 explicitly.

Failure two: the backup that could not read the attachments

XWiki keeps attachments on the filesystem, not in the database, so the nightly backup dumps PostgreSQL and tars the attachment store. The first run did the database and then stopped:

tar: store: Permission denied

Two unrelated defaults combine here. Tomcat starts XWiki under umask 0027, so every file it writes is readable by owner and group only. And the cluster’s storage is an NFS export with root_squash, so everything XWiki writes is owned by nobody. The backup ran as the PostgreSQL user, which is neither.

The fix was to run the backup as root with every Linux capability dropped. That sounds wrong until you remember root_squash: on the NFS server, that root becomes nobody too, which is the owner of the files. It can read them without loosening XWiki’s own file modes.

Failure three: every chart release restarted the wiki

The Deployment carries a checksum of its configuration as an annotation, so a config change rolls the pod. The checksum hashed the whole rendered ConfigMap, and the ConfigMap carries a label with the chart’s version. So every chart release, even one that changed nothing about XWiki, restarted it.

That is usually harmless. It was not harmless at that moment: I was halfway through the first-run wizard, which installs about 1,600 pages of the standard flavor, and a restart mid-install leaves it half done. The fixed chart hashes only the ConfigMap’s data, and that deploy waited until the wizard had finished.

Handing it to Flux

Step four was to point Flux at the new deployment repo and check that taking over the hand-installed release changed nothing. It made one new Helm revision rather than none, which is expected: Flux adds its own labels to the resources it owns. Nothing about the pod changed, so nothing restarted. From then on, git push is the deploy.

Single sign-on, and a 500 that blamed JSON

XWiki talks OpenID Connect through an extension, so the order matters: install the extension from XWiki’s extension manager first, create the provider and application in Authentik, store the client secret in a Kubernetes Secret, and only then switch XWiki’s authentication class over. Switch the class before the extension exists and nobody can log in at all.

Sign-in redirected to Authentik, I approved it, and XWiki answered with a 500:

java.lang.IllegalArgumentException: The JSON object must not be null

Nothing to do with JSON. The Authentik provider had an encryption key selected, an optional field, so Authentik was encrypting the ID token. XWiki’s extension cannot decrypt one, so it found nothing readable where the token’s claims should be. Authentik’s discovery document gave it away: it was advertising ID-token encryption. Clearing that one field fixed it; the signing key stays.

A second account with my name

The next sign-in worked, but as <my username>-0. The admin account I had made in the setup wizard already used my username, so XWiki created a new account beside it with a suffix.

The obvious fix is to rename one of them. XWiki has no rename button on user profiles, and that turned out to be lucky, because of what happened next.

The setting I should never have touched

The plan to tidy up the accounts, which Claude proposed and I approved, went like this: create a fresh local admin account for emergencies, make it the wiki’s owner, then delete the two old accounts and sign in again cleanly. The moment the owner changed, every page filled with errors:

Failed to execute the [velocity] macro. Cause: [The execution of the [velocity]
script macro is not allowed in [xwiki:XWiki.Lightbox.WebHome]. Check the rights
of its last author ...]

Here is what was going on. XWiki runs a page’s scripts only if the page’s last author has script rights. The setup wizard had installed the standard flavor as my wizard account, so that account was the last author of all 1,582 flavor pages: the menus, the editor, the panels, everything. And that account did not have script rights through any group. It had them because it was the wiki’s owner, and the owner can do anything. Move the ownership and 1,582 pages lose the right to run their own code in one save.

Setting the owner back fixed it. Then two smaller lessons followed:

  • Logged-out visitors still saw errors in two side panels, because XWiki had cached them while the rights were wrong. A restart cleared it.
  • The step after that, deleting the wizard account, would have made the damage permanent. That account can never be deleted or renamed. It is now documented as such in the deployment docs and the wiki.

The fix for the duplicate account needed no renaming at all. XWiki’s OIDC extension recognises a returning user by a small record on their profile page, holding the identity provider’s issuer and the user’s ID there, not by the page’s name. Copying that record from the -0 account onto the original and deleting the -0 account made the next Authentik sign-in land on the right account, with admin rights coming from an Authentik group, while the wizard account stays the owner and the local break-glass login.

The first thing written in it

With the wiki working, the first thing that went in was the cluster’s own documentation, sixteen pages on what the cluster is, how it is built, how to rebuild it from nothing, every app on it, and how it is monitored. It is written from the repositories and checked against the live cluster.

Writing it down turned up two problems that reading never had:

  • Alerts that did not exist. Nine repositories’ docs promised alerts for failed and forgotten Flux deployments. The failed-deploy alerts exist under other names; the forgotten-suspend one never existed at all, because the metric it needs went away in the Flux version this cluster runs. The docs now say so plainly.
  • A certificate authority that was not backed up. Every internal certificate comes from a cluster-local CA. Rebuild the cluster and it would mint a new one, and every browser and hypervisor that trusts the old one would have to be told again. The CA’s key pair is now in the vault, with tested restore steps.

What I would tell myself before starting

  • Read the image before writing the chart. XWiki rewriting its own config on every start decided the whole design. Finding that out from a failed deploy would have cost far more than reading the entrypoint did.
  • On NFS with root_squash, know who owns what. Both storage failures were the same question: which uid does the server think this is?
  • Find out where an admin’s power actually comes from before moving it. “The admin account” turned out to be “the owner, who is also the author of everything”. Those are very different things to change.
  • Approving every step only works if you understand each step. I approved the ownership change without asking what depended on it. The approvals caught plenty of other things; they cannot catch a question nobody asks.
  • Documenting a system is a good way to audit it. Two silent problems surfaced only because writing them down meant checking them.
Author

Joseph Werle

Follow Me
Other Articles
Previous

Four Failures That Lied About Their Cause

Next

GitLab Warns of a Max-Severity Code Execution Flaw in Its CI/CD Regex Compiler

No Comment! Be the first one.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • PHP2Shell Is Now Exploited in the Wild: What to Do Today
  • Your Downloads Folder Can Now Give Orders to Your AI Coding Agent
  • ConnectWise Application Identities Compromised: What MSPs and Partners Should Check in Microsoft 365
  • Vghost: A Nine-Year-Old Linux vhost Bug Gives Local Users Root and Lets Guests Hit the Host
  • SlugFest: How a Repo Rename Could Hand Your npm Package to an Attacker

Recent Comments

No comments to show.

Archives

  • October 2026
  • September 2026

Categories

  • Building The Lab
  • Joes Lab Services
  • Security News
  • That didn't go so well.
  • October 2026
  • September 2026
Copyright 2026 — Joe's Lab. All rights reserved. Blogsy WordPress Theme