<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Forge on ln --help</title>
    <link>https://blog.mei-home.net/tags/forge/</link>
    <description>Recent content in Forge on ln --help</description>
    <generator>Hugo -- 0.147.2</generator>
    <language>en</language>
    <lastBuildDate>Fri, 07 Feb 2025 22:50:37 +0100</lastBuildDate>
    <atom:link href="https://blog.mei-home.net/tags/forge/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Nomad to k8s, Part 16: Migrating Gitea</title>
      <link>https://blog.mei-home.net/posts/k8s-migration-16-gitea/</link>
      <pubDate>Fri, 07 Feb 2025 22:50:37 +0100</pubDate>
      <guid>https://blog.mei-home.net/posts/k8s-migration-16-gitea/</guid>
      <description>Migrating my Gitea instance from Nomad to Kubernetes</description>
      <content:encoded><![CDATA[<p>Wherein I migrate my Gitea instance from Nomad to k8s.</p>
<p>This is part 17 of my <a href="https://blog.mei-home.net/tags/k8s-migration/">k8s migration series</a>.</p>
<p>I&rsquo;ve been using <a href="https://about.gitea.com/">Gitea</a> as my Git forge for a while
now. What&rsquo;s now the Gitea instance started life is a <a href="https://github.com/gogs/gogs">Gogs</a>
instance in 2016, when I had to downsize my Homelab to a Raspberry Pi 3B that
couldn&rsquo;t handle all the things I wanted to run on it. I decided to get rid of
my Gitlab instance and exchange it for Gogs. The switch back to Gitea then
happened because Gitlab started eating 12% of my new home server&rsquo;s CPU even
when idle.</p>
<p>This is what the front page looks like when logged in:
<figure>
    <img loading="lazy" src="gitea_frontpage.png"
         alt="A screenshot of the Gitea home page for a logged in user. At the top is a heat map, similar to the one on GitHub&#39;s user profile page. It shows a brighter color for days with a lot of activity, and a lighter color for days with less activity. It shows a full year&#39;s worth of activity, showing one colored box per day, with columns for weeks and rows for days of the week. My activity shows almost all weekend days with activity, while the winter months also show lots of activity on workdays. Below the heat map is an activity feed, showing activities from the last couple of days, like pushes to different repositories. Most of them are to the adm/homelab and mmeier/blog repository. On the right side is a list of repositories, showing ones like &#39;adm/homenet-docs&#39;, &#39;mmeier/smokes.cli&#39; or &#39;learning/learning-go&#39;. Next to some of them is a green check mark or a red cross, indicating the state of the last CI pipeline."/> <figcaption>
            <p>Screenshot of Gitea&rsquo;s home page for my user.</p>
        </figcaption>
</figure>
</p>
<p>I&rsquo;m quite liking it and, in contrast to Gitlab, I never had any problems with it.
It&rsquo;s pretty snappy (again, especially in contrast to Gitlab) and relatively light
on resources. Most of the time I can&rsquo;t even tell whether it got assigned one
of my beefier x86 nodes or a Raspberry Pi.</p>
<p>I&rsquo;ve got 82 repositories stored in it, from relatively small dead projects which
never got much farther than a README to extremely large repos containing 3D
models and such for a <a href="https://www.sinsofasolarempire1.com/">Sins of a Solar Empire</a>
mod I was once involved in. Most repos don&rsquo;t see a lot of activity and I&rsquo;m the
only user at the moment. The instance is not publicly accessible, but I might
change that when the <a href="https://forgefed.org/">ForgeFed</a> project matures.</p>
<p>My way of working depends on the repository. For my Homelab, this blog and my
Homelab docs for example I&rsquo;m just pushing to the master branch. (Which again
reminds me to finally get around to the <code>main</code> branch migration.)
In my development projects though I&rsquo;m mostly working with Pull Requests. I find
Gitea&rsquo;s interface pretty convenient, and like seeing all the information and
CI runs for a specific feature in one place.</p>
<p>So I&rsquo;m not using too many actual features of Gitea, it&rsquo;s mainly a convenient UI
for my Git repos. But I must admit: I&rsquo;m rather fond of that activity heat map.
The only Gitlab feature I&rsquo;m genuinely missing are the repository stats. If any
of you know a good web app, either dynamic or statically generated, that can
show stats on a Git repo, I&rsquo;d be very interested.</p>
<h2 id="database-setup-and-migration">Database setup and migration</h2>
<p>I promise, this is the last time one of my migration articles will have a
long-winded section on databases. &#x1f609;
But in this case, it&rsquo;s warranted, because this is the first time I&rsquo;m actually
migrating a database, instead of setting up a new one.</p>
<p>I&rsquo;m using <a href="https://cloudnative-pg.io/">CloudNativePG</a> to manage the Postgres
databases in my k8s cluster. More details can be found <a href="https://blog.mei-home.net/posts/k8s-migration-8-cloud-native-pg/">here</a>.
CNPG has a number of methods for seeding a new DB cluster with data. Generally,
those approaches are split into two. The first way involves another online cluster
and full replication or restoration of a backup.
The second method is more suited to what I needed, namely a one-time import
from another cluster using <code>initdb</code> to bootstrap the CNPG cluster.
This method uses <code>pg_dump</code>/<code>pg_restore</code> from another running cluster. This
method suited me somewhat well, because my Nomad Postgres setup is still up and
running. The docs for this method can be found <a href="https://cloudnative-pg.io/documentation/1.25/database_import/">here</a>.</p>
<p>There was just one problem: In Nomad, I&rsquo;m using <a href="https://developer.hashicorp.com/consul/docs/connect">Consul Connect Service Mesh</a>
to connect services and only allow access between specific services instead of
having open ports everywhere. This has been working pretty nicely in the past
several years. Remember, I&rsquo;m switching away from HashiCorp&rsquo;s stuff not because
their software is bad, but rather for ideological reasons.</p>
<p>But in this instance, I was stumped. For using <code>pg_dump</code>, CNPG needs access
to the other cluster. But of course no k8s service is currently inside the
Consul Mesh, so there&rsquo;s no way to access the Postgres DB. I thought: Well, I
can just open up a node port temporarily. And I failed. As in: I spend an entire
evening trying to figure this out and had to give up. For reference, the
network config for my Postgres Nomad job looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>  <span style="color:#66d9ef">group</span> <span style="color:#e6db74">&#34;postgres&#34;</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">network</span> {
</span></span><span style="display:flex;"><span>      mode <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;bridge&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">service</span> {
</span></span><span style="display:flex;"><span>      name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;postgres&#34;</span>
</span></span><span style="display:flex;"><span>      port <span style="color:#f92672">=</span> <span style="color:#ae81ff">5432</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">connect</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">sidecar_service</span> {}
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">check</span> {
</span></span><span style="display:flex;"><span>        type     <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;script&#34;</span>
</span></span><span style="display:flex;"><span>        command  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;/usr/bin/pg_isready&#34;</span>
</span></span><span style="display:flex;"><span>        args     <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;-U&#34;, &#34;postgres&#34;</span>]
</span></span><span style="display:flex;"><span>        interval <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;30s&#34;</span>
</span></span><span style="display:flex;"><span>        timeout  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;2s&#34;</span>
</span></span><span style="display:flex;"><span>        task     <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;postgres&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>[...]
</span></span></code></pre></div><p>With that config, Consul launches an <a href="https://www.envoyproxy.io/">Envoy</a> container
next to the Postgres container in the network namespace, by default on a random
port. Inside the network namespace, Postgres&rsquo; <code>5432</code> port is connected to Envoy.
Envoy then listens on a public port, but only lets through connections with the
right mTLS cert. Other services can then be allowed to access Postgres via
their own Envoy proxy. As best as I&rsquo;ve been able to figure out, there&rsquo;s no
way for a service outside the mesh to get through the Envoy proxy to the
Postgres port.</p>
<p>But opening another port also did not work. I&rsquo;m reasonably sure that&rsquo;s because
trying to connect Postgres&rsquo; socket to two other sockets (the temporary public one, and Envoy&rsquo;s)
is just not something that can ever work. I still tried though. Pretty hard
even.</p>
<p>But in the end I threw up my hands and had to admit that I was trying something
that&rsquo;s simply not possible. I could either have that port accessible on the node,
or via the Consul Mesh, but not both.</p>
<p>I also couldn&rsquo;t just temporarily switch off the Consul Mesh for Postgres, because
that would have impacted other workloads on my Nomad cluster. Took me quite a
while to come up with the solution: I remembered that, during my initial migration
to Nomad from my Docker Compose setup, I had set up an <a href="https://developer.hashicorp.com/consul/docs/connect/gateways/ingress-gateway">Ingress Gateway</a>
to provide access to the already migrated services from the apps still running
in Docker Compose.
That Ingress Gateway does pretty much what it says on the tin: It allows services
from outside the mesh access to services inside the mesh. It was of course not
as fine-grained as the service mesh itself. If a service could reach the gateway,
it could access all services inside the mesh that the gateway was allowed to
access.</p>
<p>Luckily, by the time I originally set up the Ingress Gateway, I had already
started to put my Homelab under version control, and I was still able to find
the old Ingress Gateway definition. I pared it down to only Postgres, and the
Nomad job ended up looking like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">job</span> <span style="color:#e6db74">&#34;ingress-gateways&#34;</span> {
</span></span><span style="display:flex;"><span>  datacenters <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;homenet&#34;</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">group</span> <span style="color:#e6db74">&#34;internal&#34;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">network</span> {
</span></span><span style="display:flex;"><span>      mode <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;bridge&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">port</span> <span style="color:#e6db74">&#34;postgres&#34;</span> {
</span></span><span style="display:flex;"><span>        static <span style="color:#f92672">=</span> <span style="color:#ae81ff">5577</span>
</span></span><span style="display:flex;"><span>        to <span style="color:#f92672">=</span> <span style="color:#ae81ff">5577</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">service</span> {
</span></span><span style="display:flex;"><span>      name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;ingress-internal&#34;</span>
</span></span><span style="display:flex;"><span>      port <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;8080&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">connect</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">gateway</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">ingress</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">listener</span> {
</span></span><span style="display:flex;"><span>              port <span style="color:#f92672">=</span> <span style="color:#ae81ff">5577</span>
</span></span><span style="display:flex;"><span>              protocol <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;tcp&#34;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#66d9ef">service</span> {
</span></span><span style="display:flex;"><span>                name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;postgres&#34;</span>
</span></span><span style="display:flex;"><span>              }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This definition starts by setting up a bridged network namespace, meaning no
outside access by default. Then it creates a listener for Postgres. With that,
the Envoy proxy of the service would create a socket at port <code>5577</code> in the
namespace, connected to the Postgres service&rsquo;s Envoy proxy. The gateway would
also open a static port on <code>5577</code> on the node it is running on, which would
be connected to port <code>5577</code> inside the network namespace. And with that,
any service connecting to port <code>5577</code> on the host running the Ingress Gateway
would be connected to the Postgres database. Pretty neat and simple setup,
but took me a while to remember.</p>
<p>I ran test connections with this command to confirm that I finally had
external access to the cluster:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>psql -U gogs -h ingress-internal.service.consul -p <span style="color:#ae81ff">5577</span> -d gitea
</span></span></code></pre></div><p>With that, I finally had access to the Postgres cluster from inside my k8s
cluster.</p>
<p>The CNPG Cluster manifest then looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">postgresql.cnpg.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Cluster</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-pg-cluster</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">homelab/part-of</span>: <span style="color:#ae81ff">gitea</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">instances</span>: <span style="color:#ae81ff">2</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">imageName</span>: <span style="color:#e6db74">&#34;ghcr.io/cloudnative-pg/postgresql:17.2&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">bootstrap</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">initdb</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">database</span>: <span style="color:#ae81ff">gitea</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">owner</span>: <span style="color:#ae81ff">gitea</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">import</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">microservice</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">databases</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#ae81ff">gitea</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">source</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">externalCluster</span>: <span style="color:#ae81ff">nomad-pg</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">200M</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">150m</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">postgresql</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">parameters</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">max_connections</span>: <span style="color:#e6db74">&#34;200&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">shared_buffers</span>: <span style="color:#e6db74">&#34;50MB&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">effective_cache_size</span>: <span style="color:#e6db74">&#34;150MB&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">maintenance_work_mem</span>: <span style="color:#e6db74">&#34;12800kB&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">checkpoint_completion_target</span>: <span style="color:#e6db74">&#34;0.9&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">wal_buffers</span>: <span style="color:#e6db74">&#34;1536kB&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">default_statistics_target</span>: <span style="color:#e6db74">&#34;100&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">random_page_cost</span>: <span style="color:#e6db74">&#34;1.1&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">effective_io_concurrency</span>: <span style="color:#e6db74">&#34;300&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">work_mem</span>: <span style="color:#e6db74">&#34;128kB&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">huge_pages</span>: <span style="color:#e6db74">&#34;off&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">max_wal_size</span>: <span style="color:#e6db74">&#34;128MB&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">wal_keep_size</span>: <span style="color:#e6db74">&#34;512MB&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">storage</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">size</span>: <span style="color:#ae81ff">1.</span><span style="color:#ae81ff">5G</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">storageClass</span>: <span style="color:#ae81ff">rbd-fast</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">backup</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">barmanObjectStore</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">endpointURL</span>: <span style="color:#ae81ff">http://rook-ceph-rgw-rgw-bulk.rook-cluster.svc:80</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">destinationPath</span>: <span style="color:#e6db74">&#34;s3://backup-cnpg/&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">s3Credentials</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">accessKeyId</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">rook-ceph-object-user-rgw-bulk-cnpg-backup-gitea</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">AccessKey</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretAccessKey</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">rook-ceph-object-user-rgw-bulk-cnpg-backup-gitea</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">SecretKey</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">retentionPolicy</span>: <span style="color:#e6db74">&#34;30d&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">externalClusters</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nomad-pg</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">connectionParameters</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">host</span>: <span style="color:#ae81ff">ingress-internal.service.consul</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">port</span>: <span style="color:#e6db74">&#34;5577&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">user</span>: <span style="color:#ae81ff">gogs</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">dbname</span>: <span style="color:#ae81ff">gitea</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">password</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">name</span>: <span style="color:#ae81ff">olddb-secret</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">key</span>: <span style="color:#ae81ff">pw</span>
</span></span></code></pre></div><p>I&rsquo;ve omitted some standard things like the backup bucket setup here. The important
parts for the migration are the <code>spec.bootstrap.initdb.import</code> and <code>spec.externalClusters</code>
keys.</p>
<p>Let&rsquo;s start with the <code>externalClusters</code> definition. It&rsquo;s documented <a href="https://cloudnative-pg.io/documentation/1.25/bootstrap/#the-externalclusters-section">here</a> and describes the connection to another cluster. This doesn&rsquo;t need to be
a CNPG cluster. One problem was that seemingly, no documentation exists of the
<code>externalClusters.connectionParameters.port</code> option. I spend quite a while
trying to figure out whether the port was supposed to go on the end of the <code>host</code>
parameter, or whether it was a separate key.
I was finally saved by the fact that CNPG is open source, and so I could look
at the code - specifically a Yaml file from their test setup <a href="https://github.com/cloudnative-pg/cloudnative-pg/blob/62d48282bdd4c640d1af104b9cf637087148075e/tests/e2e/fixtures/replica_mode_cluster/cluster-replica-tls.yaml.template#L22">here</a>.
The password for the connection was coming from a Secret with the old database
credentials. As you can see in the <code>user</code> parameter, the Gitea database was
originally created during the Gogs phase of my Git hosting. &#x1f601;</p>
<p>The second part of the config is in <code>spec.bootstrap.initdb.import</code>, which tells
CNPG what it should import from the external cluster.
The first choice to make here is the <code>type</code> of the import. This describes the
destination cluster, meaning the new CNPG cluster. The choices are <code>microservice</code>,
meaning that the cluster serves only one app with one user, or <code>monolith</code>,
meaning a cluster hosting the databases of multiple services.
Besides that, I just needed to provide the name of the database in the source
cluster and the name of said cluster in the <code>externalClusters</code> list.</p>
<p>This import, as configured above, worked immediately. I was very positively
surprised. All data was imported properly, and CNPG automatically created the
customary Secret with the connection details and credentials for accessing
the cluster.
After the initial import, I was able to remove the <code>spec.bootstrap.initdb.import</code>
and <code>externalClusters</code> keys completely from the manifest without any error.</p>
<h2 id="helm-chart">Helm Chart</h2>
<p>For the Gitea deployment itself, I made use of the <a href="https://gitea.com/gitea/helm-chart">official Helm chart</a>.
It is one of the better ones I&rsquo;ve encountered since starting the migration,
providing the ability to set config options in the <code>values.yaml</code> instead of
having to maintain a separate <code>app.init</code> file. What I value extremely highly
is that they provide the ability to add environment variables via <code>env.ValueFrom</code>,
so I can directly use the automatically created Secrets from CNPG for the DB
and Rook Ceph for the S3 bucket. This safes me the roundabout setup I had to do
for other charts, where I had to use external-secrets to template the auto
Secrets into new Secrets with a different format to conform to the Chart&rsquo;s
expectations.</p>
<p>One downside at the time of writing is that the chart is not on the newest
Gitea version 1.23.1. But I just changed the image tag and chart version 10.6.0
worked without issue.
Going by <a href="https://gitea.com/gitea/helm-chart/issues/783">this GitHub issue</a> the
delay is simply due to some internal refactoring of the chart they want to finish
before the next release.</p>
<p>I will split the exploration of my <code>values.yaml</code> file into two parts, one with
the Gitea config under the <code>gitea</code> key and one for everything else.</p>
<h3 id="everything-besides-the-gitea-config">Everything besides the Gitea config</h3>
<p>Let&rsquo;s start with the &ldquo;everything else&rdquo; part:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">replicaCount</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">image</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">rootsless</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">tag</span>: <span style="color:#e6db74">&#34;1.23.1&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">strategy</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Recreate</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">containerSecurityContext</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">capabilities</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">add</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">SYS_CHROOT</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">service</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ssh</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">type</span>: <span style="color:#ae81ff">LoadBalancer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">port</span>: <span style="color:#ae81ff">2222</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">externalTrafficPolicy</span>: <span style="color:#ae81ff">Local</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">annotations</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">external-dns.alpha.kubernetes.io/hostname</span>: <span style="color:#ae81ff">git.example.com</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">homelab/public-service</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ingress</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">annotations</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">traefik.ingress.kubernetes.io/router.entrypoints</span>: <span style="color:#ae81ff">very-safe-entrypoint</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">hosts</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">host</span>: <span style="color:#ae81ff">gitea.example.com</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">paths</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f92672">path</span>: <span style="color:#ae81ff">/</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">pathType</span>: <span style="color:#ae81ff">Prefix</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">tls</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">hosts</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">gitea.example.com</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">limits</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">1500Mi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">persistence</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">create</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">mount</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">claimName</span>: <span style="color:#ae81ff">gitea-data-volume</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">signing</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">actions</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">redis-cluster</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">postgresql-ha</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">postgresql</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span></code></pre></div><p>As I noted above, I had to hardcode the Gitea tag for now, because the newest
chart version is still on 1.22.3. I also opted for using the rootless image,
which I did not use in the Nomad job. It just is a little bit nicer than
having a root-capable image, although Gitea automatically drops root privileges
at startup and even with the root image, Gitea doesn&rsquo;t actually run as root.</p>
<p>I also had to hardcode the update strategy to <code>Recreate</code>. I&rsquo;m not sure why
it&rsquo;s set to rolling updates by default. This doesn&rsquo;t really work, because
Gitea is launched in a Deployment and has a PVC mounted, so the newly started
instance won&rsquo;t actually be able to start because the RWO volume can&rsquo;t be mounted
in two Pods at the same time.</p>
<p>Then comes an important one, the <code>SYS_CHROOT</code> capability. This is documented as
required when using <a href="https://cri-o.io/">cri-o</a> as the container runtime in the
Helm chart&rsquo;s extensive <a href="https://gitea.com/gitea/helm-chart/src/tag/v10.6.0/README.md">README.md</a>.</p>
<p>External access is split between two different subdomains, one for Gitea&rsquo;s web
frontend going through my Traefik ingress and one for git access with SSH. I
like setting my LoadBalancer services, <a href="https://blog.mei-home.net/posts/k8s-migration-2a-cilium-bgp/">provided by Cilium</a>,
to the Local <code>externalTrafficPolicy</code>. This ensures that the client IP those
services see are the actual client IPs, and not the IPs of the machine which
received the request and forwarded it to the service it was intended for.
The <code>homelab/public-service</code> label is simply a sign for Cilium that it should
handle the service.</p>
<p>The ingress config has one important point, the <code>tls</code> key. I initially did not
set that key, because I&rsquo;ve never set it before - my Traefik automatically
uses HTTPS and I&rsquo;m using a wildcard cert. But Gitea needs to generate publicly
addressable URLs for some things, e.g. when providing clone URLs for HTTPS or
providing callback URLs in webhooks, e.g. for Woodpecker.
The domain itself is fine, but the chart determines the protocol like this:</p>
<pre tabindex="0"><code>{{- define &#34;gitea.public_protocol&#34; -}}
{{- if and .Values.ingress.enabled (gt (len .Values.ingress.tls) 0) -}}
https
{{- else -}}
{{ .Values.gitea.config.server.PROTOCOL }}
{{- end -}}
{{- end -}}
</code></pre><p>In there, <code>https</code> is only set as the protocol if the <code>ingress.tls</code> list has at
least one entry. And setting the <code>server.PROTOCOL</code> config comes with it&rsquo;s own
problems, so I decided to just add the <code>tls.hosts</code> setting, even if it means
that I have to repeat my Gitea domain a number of times in the <code>config.yaml</code>.</p>
<p>For the <code>persistence</code> setting I had to go with a pre-created volume, because
I had to make the migrated Gitea data available. One thing to note here is
that you should delete the <code>app.ini</code> file your previous setup might have left
on the disk. The init container which puts together the <code>app.ini</code> from the
values and env variables and so on doesn&rsquo;t handle it well when there&rsquo;s an
existing <code>app.ini</code> it didn&rsquo;t create itself.</p>
<p>I also disabled a number of features I didn&rsquo;t need, like signing or actions
or the Redis and Postgres instances the Helm chart can deploy, because I&rsquo;ve
already got my own deployments.</p>
<h3 id="the-gitea-config">The Gitea config</h3>
<p>Here&rsquo;s the full <code>gitea:</code> section of the <code>config.yaml</code>, just for reference. I
will post the relevant subsections as I go over them:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">gitea</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">admin</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">existingSecret</span>: <span style="color:#66d9ef">null</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">username</span>: <span style="color:#66d9ef">null</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>: <span style="color:#66d9ef">null</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">metrics</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">enabled</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">oauth</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Keycloak&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">provider</span>: <span style="color:#e6db74">&#34;openidConnect&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">existingSecret</span>: <span style="color:#ae81ff">oidc-credentials</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">autoDiscoverUrl</span>: <span style="color:#e6db74">&#34;https://key.example.com/realms/homelab/.well-known/openid-configuration&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">config</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">APP_NAME</span>: <span style="color:#e6db74">&#34;My Gitea&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">RUN_MODE</span>: <span style="color:#e6db74">&#34;prod&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">server</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SSH_SERVER_HOST_KEYS</span>: <span style="color:#e6db74">&#34;ssh/gitea.ed25519&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">APP_DATA_PATH</span>: <span style="color:#e6db74">&#34;/data/gitea_data&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SSH_DOMAIN</span>: <span style="color:#e6db74">&#34;git.example.com&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SSH_PORT</span>: <span style="color:#ae81ff">2222</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">database</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DB_TYPE</span>: <span style="color:#e6db74">&#34;postgres&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">LOG_SQL</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">oauth2</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">service</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DISABLE_REGISTRATION</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">REQUIRE_SIGNIN_VIEW</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_KEEP_EMAIL_PRIVATE</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_ALLOW_CREATE_ORGANIZATION</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_ORG_VISIBILITY</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_ORG_MEMBER_VISIBLE</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_ENABLE_TIMETRACKING</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SHOW_REGISTRATION_BUTTON</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">repository</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ROOT</span>: <span style="color:#e6db74">&#34;/data/git-repos&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SCRIPT_TYPE</span>: <span style="color:#ae81ff">bash</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_PRIVATE</span>: <span style="color:#ae81ff">private</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_BRANCH</span>: <span style="color:#ae81ff">main</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ui</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_THEME</span>: <span style="color:#ae81ff">gitea-auto</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">queue</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">TYPE</span>: <span style="color:#ae81ff">redis</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">CONN_STR</span>: <span style="color:#e6db74">&#34;addr=redis.redis.svc.cluster.local:6379&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">WORKERS</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">BOOST_WORKERS</span>: <span style="color:#ae81ff">5</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">admin</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_EMAIL_NOTIFICATIONS</span>: <span style="color:#ae81ff">disabled</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">openid</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLE_OPENID_SIGNIN</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">webhook</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ALLOWED_HOST_LIST</span>: <span style="color:#ae81ff">private</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">mailer</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SUBJECT_PREFIX</span>: <span style="color:#e6db74">&#34;[Gitea]&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SMTP_ADDR</span>: <span style="color:#ae81ff">mail.example.com</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SMTP_PORT</span>: <span style="color:#e6db74">&#34;465&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">FROM</span>: <span style="color:#e6db74">&#34;gitea@example.com&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">USER</span>: <span style="color:#e6db74">&#34;gitea@example.com&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cache</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ADAPTER</span>: <span style="color:#e6db74">&#34;redis&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">INTERVAL</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">HOST</span>: <span style="color:#e6db74">&#34;network=tcp,addr=redis.redis.svc.cluster.local:6379,db=0,pool_size=100,idle_timeout=180&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ITEM_TTL</span>: <span style="color:#ae81ff">7d</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">session</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">PROVIDER</span>: <span style="color:#ae81ff">redis</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">PROVIDER_CONFIG</span>: <span style="color:#ae81ff">network=tcp,addr=redis.redis.svc.cluster.local:6379,db=0,pool_size=100,idle_timeout=180</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">time</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">DEFAULT_UI_LOCATION</span>: <span style="color:#e6db74">&#34;Europe/Berlin&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron.archive_cleanup</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SCHEDULE</span>: <span style="color:#e6db74">&#34;@every 24h&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron.update_mirrors</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron.repo_health_check</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SCHEDULE</span>: <span style="color:#e6db74">&#34;0 30 5 * * *&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">TIMEOUT</span>: <span style="color:#e6db74">&#34;5m&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron.check_repo_stats</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SCHEDULE</span>: <span style="color:#e6db74">&#34;0 0 5 * * *&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron.update_migration_poster_id</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SCHEDULE</span>: <span style="color:#e6db74">&#34;@every 24h&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron.sync_external_users</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SCHEDULE</span>: <span style="color:#e6db74">&#34;@every 24h&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">UPDATE_EXISTING</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cron.deleted_branches_cleanup</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">RUN_AT_START</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">SCHEDULE</span>: <span style="color:#e6db74">&#34;@every 24h&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">migrations</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ALLOW_LOCALNETWORKS</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">packages</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ENABLED</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">storage</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">STORAGE_TYPE</span>: <span style="color:#ae81ff">minio</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">MINIO_ENDPOINT</span>: <span style="color:#ae81ff">rook-ceph-rgw-rgw-bulk.rook-cluster.svc:80</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">MINIO_LOCATION</span>: <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">MINIO_USE_SSL</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">additionalConfigFromEnvs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__DATABASE__HOST</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-pg-cluster-app</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">host</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__DATABASE__NAME</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-pg-cluster-app</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">dbname</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__DATABASE__USER</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-pg-cluster-app</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">user</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__DATABASE__PASSWD</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-pg-cluster-app</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">password</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__SECURITY__SECRET_KEY</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">secret-key</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">key</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__OAUTH2__JWT_SECRET</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">jwt-secret</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">jwt</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__MAILER__PASSWD</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mail-pw</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">pw</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__STORAGE__MINIO_BUCKET</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">configMapKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-bucket</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">BUCKET_NAME</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__STORAGE__MINIO_ACCESS_KEY_ID</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-bucket</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">AWS_ACCESS_KEY_ID</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__STORAGE__MINIO_SECRET_ACCESS_KEY</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-bucket</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">AWS_SECRET_ACCESS_KEY</span>
</span></span></code></pre></div><p>Let&rsquo;s start with the <code>gitea.admin</code> config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">gitea</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">admin</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">existingSecret</span>: <span style="color:#66d9ef">null</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">username</span>: <span style="color:#66d9ef">null</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>: <span style="color:#66d9ef">null</span>
</span></span></code></pre></div><p>I&rsquo;ve already got an admin account, so I didn&rsquo;t want the Helm chart to create a
new one. I thought I could do that by just setting <code>admin: {}</code>, but that of course
doesn&rsquo;t work. So the Helm chart created an admin user with the chart&rsquo;s default
<code>gitea.admin.password</code>. But I then figured out that setting all values to <code>null</code>
does work. It&rsquo;s important to note that Gitea doesn&rsquo;t then remove the newly
created admin user again. It needs to be deleted manually via the UI.</p>
<p>The <code>gitea.oauth</code> config is also worth a paragraph. First, it&rsquo;s important to note
that this is the config for Gitea as an Oauth2 <em>client</em>. The config for Gitea
as an identity provider has to be done in another place.
I&rsquo;m using <a href="https://www.keycloak.org/">Keycloak</a> as my identity provider in the
Homelab. For more details, see <a href="https://blog.mei-home.net/posts/sso/">this post</a>.
The issue is that Gitea&rsquo;s OAuth2 client config can only be done in the UI or
via the CLI, not via the config file. And I had already taken my Nomad instance
down at this point. I could get the client ID and secret from Keycloak, but not
the name under which it was saved in the database for example. It was also
pretty unclear what options should be set under the <code>gitea.oauth</code> key. I finally
ended up looking into the <a href="https://gitea.com/gitea/helm-chart/src/tag/v10.6.0/templates/gitea/init.yaml">init container script</a>,
which is a bash script using the Gitea CLI to create the OAuth2 entry:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>    <span style="color:#66d9ef">function</span> configure_oauth<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">{{</span>- <span style="color:#66d9ef">if</span> .Values.gitea.oauth <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">{{</span>- range $idx, $value :<span style="color:#f92672">=</span> .Values.gitea.oauth <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>      local OAUTH_NAME<span style="color:#f92672">={{</span> <span style="color:#f92672">(</span>printf <span style="color:#e6db74">&#34;%s&#34;</span> $value.name<span style="color:#f92672">)</span> | squote <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>      local full_auth_list<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>gitea admin auth list --vertical-bars<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>      local actual_auth_table<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># We might have distorted output due to warning logs, so we have to detect the actual user table by its headline and trim output above that line</span>
</span></span><span style="display:flex;"><span>      local regex<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;(.*)(ID\s+\|Name\s+\|Type\s+\|Enabled.*)&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>full_auth_list<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">=</span>~ $regex <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>        actual_auth_table<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>echo <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>BASH_REMATCH[2]<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> | tail -n+2<span style="color:#66d9ef">)</span> <span style="color:#75715e"># tail&#39;ing to drop the table headline</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">[</span>...<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      local AUTH_ID<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>echo <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>actual_auth_table<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> | grep -E <span style="color:#e6db74">&#34;\|</span><span style="color:#e6db74">${</span>OAUTH_NAME<span style="color:#e6db74">}</span><span style="color:#e6db74">\s+\|&#34;</span> | grep -iE <span style="color:#e6db74">&#39;\|OAuth2\s+\|&#39;</span> | awk -F <span style="color:#e6db74">&#34; &#34;</span>  <span style="color:#e6db74">&#34;{print \$1}&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> -z <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>AUTH_ID<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#34;No oauth configuration found with name &#39;</span><span style="color:#e6db74">${</span>OAUTH_NAME<span style="color:#e6db74">}</span><span style="color:#e6db74">&#39;. Installing it now...&#34;</span>
</span></span><span style="display:flex;"><span>        gitea admin auth add-oauth <span style="color:#f92672">{{</span>- include <span style="color:#e6db74">&#34;gitea.oauth_settings&#34;</span> <span style="color:#f92672">(</span>list $idx $value<span style="color:#f92672">)</span> | indent <span style="color:#ae81ff">1</span> <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#39;...installed.&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#34;Existing oauth configuration with name &#39;</span><span style="color:#e6db74">${</span>OAUTH_NAME<span style="color:#e6db74">}</span><span style="color:#e6db74">&#39;: &#39;</span><span style="color:#e6db74">${</span>AUTH_ID<span style="color:#e6db74">}</span><span style="color:#e6db74">&#39;. Running update to sync settings...&#34;</span>
</span></span><span style="display:flex;"><span>        gitea admin auth update-oauth --id <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>AUTH_ID<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">{{</span>- include <span style="color:#e6db74">&#34;gitea.oauth_settings&#34;</span> <span style="color:#f92672">(</span>list $idx $value<span style="color:#f92672">)</span> | indent <span style="color:#ae81ff">1</span> <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#39;...sync settings done.&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">{{</span>- end <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">{{</span>- <span style="color:#66d9ef">else</span> <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#39;no oauth configuration... skipping.&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">{{</span>- end <span style="color:#f92672">}}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    configure_oauth
</span></span></code></pre></div><p>I&rsquo;ve removed some unimportant bits for the sake of brevity (heh, brevity &#x1f602;).
What we can see here is that the entries in the <code>gitea.oauth</code> section are converted
into CLI flags and their parameters 1:1.
For finding the right options I used for my Keycloak setup, I ended up looking
into the database:</p>
<pre tabindex="0"><code>\c gitea
SELECT * FROM login_source;

id | type |   name   | is_sync_enabled |                                                                                                                                                                                               cfg                                                                                                                                                                                               | created_unix | updated_unix | is_active
----+------+----------+-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+-----------
  1 |    6 | Keycloak | f               | {&#34;Provider&#34;:&#34;openidConnect&#34;,&#34;ClientID&#34;:&#34;bar&#34;,&#34;ClientSecret&#34;:&#34;foo&#34;,&#34;OpenIDConnectAutoDiscoveryURL&#34;:&#34;https://key.example.com/realms/homelab/.well-known/openid-configuration&#34;,&#34;CustomURLMapping&#34;:null,&#34;IconURL&#34;:&#34;&#34;,&#34;Scopes&#34;:null,&#34;RequiredClaimName&#34;:&#34;&#34;,&#34;RequiredClaimValue&#34;:&#34;&#34;,&#34;GroupClaimName&#34;:&#34;&#34;,&#34;AdminGroup&#34;:&#34;&#34;,&#34;RestrictedGroup&#34;:&#34;&#34;,&#34;SkipLocalTwoFA&#34;:true} |   1678573526 |   1678573526 | t
(1 row)
</code></pre><p>But this still left the question of how the <code>gitea.oauth.existingSecret</code> should
be formatted. Which keys was the chart expecting the Secret to have?
I wasn&rsquo;t able to find any info, so I ended up looking first for the place where
the <code>gitea.oauth_settings</code> from the init script above was defined, which lead
me to the chart&rsquo;s <a href="https://gitea.com/gitea/helm-chart/src/tag/v10.6.0/templates/_helpers.tpl">helpers</a>
again:</p>
<pre tabindex="0"><code>{{- define &#34;gitea.oauth_settings&#34; -}}
{{- $idx := index . 0 }}
{{- $values := index . 1 }}

{{- if not (hasKey $values &#34;key&#34;) -}}
{{- $_ := set $values &#34;key&#34; (printf &#34;${GITEA_OAUTH_KEY_%d}&#34; $idx) -}}
{{- end -}}

{{- if not (hasKey $values &#34;secret&#34;) -}}
{{- $_ := set $values &#34;secret&#34; (printf &#34;${GITEA_OAUTH_SECRET_%d}&#34; $idx) -}}
{{- end -}}

{{- range $key, $val := $values -}}
{{- if ne $key &#34;existingSecret&#34; -}}
{{- printf &#34;--%s %s &#34; ($key | kebabcase) ($val | quote) -}}
{{- end -}}
{{- end -}}
{{- end -}}
</code></pre><p>Here, the <code>key</code> and <code>secret</code> values, if not defined in the chart, are set to
the <code>GITEA_OAUH_KEY_$ID</code> and <code>GITEA_OUATH_SECRET_$ID</code> env variables. Looking for those variables then lead me
to the <a href="https://gitea.com/gitea/helm-chart/src/branch/main/templates/gitea/deployment.yaml">Deployment template</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA_OAUTH_KEY_{{ $idx }}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">key</span>:  <span style="color:#ae81ff">key</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: {{ <span style="color:#ae81ff">$value.existingSecret }}</span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA_OAUTH_SECRET_{{ $idx }}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">key</span>:  <span style="color:#ae81ff">secret</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: {{ <span style="color:#ae81ff">$value.existingSecret }}</span>
</span></span></code></pre></div><p>And here I finally had my answer: The Secret should have a key <code>key</code> and a key
<code>secret</code> for the two values. Armed with that info I could finally define the
OAuth2 options:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">gitea</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">oauth</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Keycloak&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">provider</span>: <span style="color:#e6db74">&#34;openidConnect&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">existingSecret</span>: <span style="color:#ae81ff">oidc-credentials</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">autoDiscoverUrl</span>: <span style="color:#e6db74">&#34;https://key.example.com/realms/homelab/.well-known/openid-configuration&#34;</span>
</span></span></code></pre></div><p>One thing which annoyed me is in Gitea&rsquo;s S3 config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">gitea</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">config</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">storage</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">STORAGE_TYPE</span>: <span style="color:#ae81ff">minio</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">MINIO_ENDPOINT</span>: <span style="color:#ae81ff">rook-ceph-rgw-rgw-bulk.rook-cluster.svc:80</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">MINIO_LOCATION</span>: <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">MINIO_USE_SSL</span>: <span style="color:#66d9ef">false</span>
</span></span></code></pre></div><p>The <code>MINIO_ENDPOINT</code> needs to have the host and port in one value. But the
ConfigMap created by Rook for a new bucket contains them only in separate keys,
meaning I had to hardcode the value in the <code>values.yaml</code> instead of taking it
from the ConfigMap.
But at least I could still use the Secret Rook creates to get the S3 credentials:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">gitea</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">additionalConfigFromEnvs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__STORAGE__MINIO_ACCESS_KEY_ID</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-bucket</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">AWS_ACCESS_KEY_ID</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GITEA__STORAGE__MINIO_SECRET_ACCESS_KEY</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">valueFrom</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">secretKeyRef</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">gitea-bucket</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">AWS_SECRET_ACCESS_KEY</span>
</span></span></code></pre></div><p>An option like this is what more Helm charts should have: The ability to use
the <code>valueFrom</code> form of defining env variables. With this, I can easily use
autogenerated Secrets and ConfigMaps without having to jump through hoops.</p>
<p>Next stumbling block was the redis config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">gitea</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">config</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cache</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ADAPTER</span>: <span style="color:#e6db74">&#34;redis&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">INTERVAL</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">HOST</span>: <span style="color:#e6db74">&#34;network=tcp,addr=redis.redis.svc.cluster.local:6379,db=0,pool_size=100,idle_timeout=180&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ITEM_TTL</span>: <span style="color:#ae81ff">7d</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">session</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">PROVIDER</span>: <span style="color:#ae81ff">redis</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">PROVIDER_CONFIG</span>: <span style="color:#ae81ff">network=tcp,addr=redis.redis.svc.cluster.local:6379,db=0,pool_size=100,idle_timeout=180</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">queue</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">TYPE</span>: <span style="color:#ae81ff">redis</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">CONN_STR</span>: <span style="color:#e6db74">&#34;addr=redis.redis.svc.cluster.local:6379&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">WORKERS</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">BOOST_WORKERS</span>: <span style="color:#ae81ff">5</span>
</span></span></code></pre></div><p>Here I wasn&rsquo;t aware that the connection string has to have a certain format
and isn&rsquo;t just host:port. Took me a while to figure out why I wasn&rsquo;t able to make
a connection with Redis.</p>
<p>And finally, another word on YAML: Check your indentation! &#x1f605;
I had to make sure that the entire network could reach the SSH service so I
could actually use it for git operations. So I added <code>fromEntities:\n - world</code> to the
network policy:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#e6db74">&#34;cilium.io/v2&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">CiliumNetworkPolicy</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;gitea-access&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">endpointSelector</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">matchExpressions</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">key</span>: <span style="color:#e6db74">&#34;app.kubernetes.io/name&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">operator</span>: <span style="color:#ae81ff">In</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">values</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#e6db74">&#34;gitea&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ingress</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">fromEndpoints</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">homelab/ingress</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">io.kubernetes.pod.namespace</span>: <span style="color:#ae81ff">traefik-ingress</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">fromEntities</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#ae81ff">world</span>
</span></span></code></pre></div><p>And when I still could not connect, I checked with Cilium&rsquo;s monitoring:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl -n kube-system exec -ti cilium-vh5jj -- cilium monitor --type drop
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span><span style="display:flex;"><span>xx drop <span style="color:#f92672">(</span>Policy denied<span style="color:#f92672">)</span> flow 0x0 to endpoint 896, ifindex 5, file bpf_lxc.c:2067, , identity world-&gt;63410: 300.300.300.1:59774 -&gt; 10.8.5.79:2222 tcp SYN
</span></span></code></pre></div><p>Fast forward through an hour of reading through Cilium&rsquo;s network policy docs,
and I took another look at the policy - and realized that I had screwed up the
indentation. &#x1f926;
It should of course look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#e6db74">&#34;cilium.io/v2&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">CiliumNetworkPolicy</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;gitea-access&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">endpointSelector</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">matchExpressions</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">key</span>: <span style="color:#e6db74">&#34;app.kubernetes.io/name&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">operator</span>: <span style="color:#ae81ff">In</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">values</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#e6db74">&#34;gitea&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ingress</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">fromEndpoints</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">homelab/ingress</span>: <span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">io.kubernetes.pod.namespace</span>: <span style="color:#ae81ff">traefik-ingress</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">fromEntities</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#ae81ff">world</span>
</span></span></code></pre></div><p>With the <code>fromEntities</code> entry in the <code>ingress:</code> list, not the <code>fromEndpoints:</code>
list.
And after that it was all up and running. Woodpecker, my CI, did not need any
additional config to access Gitea, it worked out of the box. Likely because
it uses HTTPS for Git access and goes through the standard Gitea URL. And I
don&rsquo;t think I can change that to have it use the internal service instead of
going through the ingress. That&rsquo;s because it also uses Gitea for auth, and
I don&rsquo;t think it will handle having two different URLs to access Gitea very
well. But that still ended up on the rickety pile of Homelab tasks to look at
at some point.</p>
<p>Overall, it was a good migration and allowed me to figure out my DB migration
strategy with a service which I could do without for a couple of days.
I also have to congratulate the Gitea community on their work on the Helm
chart. It was definitely one of the better ones I&rsquo;ve used.</p>
<p>And that&rsquo;s it for today. I can&rsquo;t say what&rsquo;s going to be next on the migration
list, as I haven&rsquo;t decided yet. I first thought to migrate my IoT services,
Mosquitto, zigbee2mqtt and friends, but I&rsquo;d also like to tackle some of the
bigger items, like Nextcloud. On the other hand, I&rsquo;m really not looking forward
to touching my Nextcloud deployment. It has been working so nicely.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
