1
0
Fork 0
Commit Graph

489 Commits (58d2f9484242b3983c75fd20af4c9603e687aa00)

Author SHA1 Message Date
Dustin a235fbd5ac firefly-iii: Use a single Data Importer instance
Tabitha has decided not to use Firefly to manage her finances.  We've
mostly consolidated our expenses and income now, which I manage in my
Firefly account.  In fact, the Ingress for Firefly III itself always
sets the `Remote-User: dustin` header, so only my account is accessible
anyway.  Thus, there is no longer any reason to have two Data Importer
instances.
2023-12-10 08:55:20 -06:00
Dustin bfe3523285 firefly: importer: Set VANITY_URL
The Firefly III Data Importer uses the value of `FIREFLY_III_URL` to
constuct links to transactions in email notifications.  Since this URL
points to the internal Kubernetes service rather than the canonical URL
used by clients, these links are invalid.  Fortunately, there is another
setting, `VANITY_URL`, that the Data Importer will use only when
constructing public-facing links.
2023-12-10 08:39:13 -06:00
Dustin c1d9ee0495 firefly: importer: Allow autoimport via HTTP
The Firefly III Data Importer does not allow transaction imports by
unattended HTTP requests by default, but this can be enabled with the
`CAN_POST_FILES` environment variable.  Additionally, an
`AUTO_IMPORT_SECRET` environment variable must be set containing a
shared "secret" value which must be provided in the querystring of
autoimport requests.

Since we have the Data Importer protected by Authelia, we need to make
some additional changes to the Ingress to allow unattended
authentication.  Authelia supports passing the username and password of
an authorized user in the `Proxy-Authorization` HTTP request header.  If
this header is valid, it will allow the request through.  Unfortunately,
many HTTP clients will not set this header unless they are also
configured to explicitly connect via a forward proxy.  To simplify
usage of such clients, we can configure nginx to copy the value of the
normal `Authorization` header into `Proxy-Authorization`, thus allowing
clients to use simple HTTP Basic authentication, even though the Data
Importer doesn't actually support it.
2023-12-10 08:24:59 -06:00
Dustin 087b4988d1 firefly: ingress: Increase max body size
Some receipt images are pretty big.  Increase the maximum request body
size to allow these to be uploaded/attached to transactions.
2023-12-01 22:04:07 +00:00
Dustin 95d2dba039 jenkins: Add credentials to sign, publish RPMs
The *jenkins-repohost* Secret contains an SSH private key Jenkins jobs
can use to publish RPM packages to the Yum repo host on
*files.pyrocufflink.blue*.

The *rpm-gpg-key* and *rpm-gpg-key-passphrase* Secrets contain the GnuPG
private key and its encryption passphrase, respectively, that can be
used to sign RPM packages.  This key is trusted by managed nodes on the
Pyrocufflink network.
2023-11-10 15:31:55 -06:00
Dustin 12d3c32855 jenkins: RBAC for K8s Credentials Provider
The [Kubernetes Credentials Provider][0] plugin for Jenkins allows
Jenkins to expose Kubernetes Secret resources as Jenkins Credentials.
Jobs can use them like normal Jenkins credentials, e.g. using
`withCredentials`, `sshagent`, etc.  The only drawback is that every
credential exposed this way is available to every job, at least until
[PR #40][1] is merged.  Fortunately, jobs managed by this Jenkins
instance are all trusted; no anonymous pull requests are possible, so
the risk is mitigated.

[0]: https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/
[1]: https://github.com/jenkinsci/kubernetes-credentials-provider-plugin/pull/40
2023-11-10 15:27:38 -06:00
Dustin 29150f54bb jenkins: Add files.p.b SSH host key
This will allow jobs that need to publish to *files.pyrocufflink.blue*
to verify its SSH host key.
2023-11-10 15:17:06 -06:00
Dustin c5188d042b jenkins: Add default imagePullSecrets for jobs
Setting the `imagePullSecrets` property on the default service account
for the *jenkins-jobs* namespace allows jobs to run from private
container images automatically, without additional configuration in the
pipeline definitions.
2023-11-10 15:13:19 -06:00
Dustin fe2a84a222 sshca: Deploy SSH CA service
[sshca] is a simple web service I wrote to automatically create signed
SSH certificates for hosts' public keys.  It authenticates hosts by
their machine UUID, which it can find using the libvirt API.

[sshca]: https://git.pyrocufflink.net/dustin/sshca
2023-11-10 14:22:31 -06:00
Dustin 0e7bd36d34 jenkins: Pin to x86_64 nodes
The Raspberry Pi usually has the most free RAM of all the Kubernetes
nodes, so pods tend to get assigned there even when it would not be
appropriate.  Jenkins, for example definitely does not need to run
there, so let's force it to run on the bigger nodes.
2023-10-22 22:07:22 -05:00
Dustin 52153d0e20 jenkins: Update to 2.414.3 2023-10-22 21:58:47 -05:00
Dustin 7797da19f9 jenkins: Add Argo CD pre-sync hook
Argo CD will delete and re-create this Job each time it synchronizes the
*jenkins* application.  The job creates a snapshot of the Jenkins volume
using an HTTP request to the Longhorn UI.
2023-10-22 21:50:25 -05:00
Dustin cf7ec7dd64 postgresql: Fix pod secrets
When migrating the `pod-secrets` Secret to a SealedSecret, I
accidentally created it using the `--from-file` instead of
`--from-env-file` argument to `kubectl secret create generic`.  This had
the effect of creating a single key named `pod.secrets` with the entire
contents of the file as its value.  This broke backups to MinIO, since
the PostgreSQL containers could no longer read the credentials from the
environment.  Regenerating the SealedSecret with the correct arguments
resolves this issue.
2023-10-19 07:12:16 -05:00
Dustin b07e141fa3 authelia: Convert to a stateless service
By default, Authelia uses a local SQLite database for persistent data
(e.g. authenticator keys, TOTP secrets, etc.) and keeps session data in
memory.  Together, these have some undesirable side effects.  First,
since needing access to the filesystem to store the SQLite database
means that the pod has to be managed by a StatefulSet.  Restarting
StatefulSet pods means stopping them all and then starting them back up,
which causes downtime.  Additionally, the SQLite database file needs to
be backed up, which I never got around to setting up.  Further, any time
the service is restarted, all sessions are invalidated, so users have to
sign back in.

All of these issues can be resolved by configuring Authelia to store all
of its state externally.  The persistent data can be stored in a
PostgreSQL database and the session state can be stored in Redis.  Using
a database managed by the existing Postgres Operator infrastructure
automaticaly enables high availability and backups as well.

To migrate the contents of the database, I used [pgloader].  With
Authelia shut down, I ran the migration job.  Authelia's database schema
is pretty simple, so there were no problems with the conversion.
Authelia started back up with the new database configuration without any
issues.

Session state are still stored only in memory of the Redis process.
This is probably fine, since Redis will not need restarted often, except
for updates.  At least restarting Authelia to adjust its configuration
will not log everyone out.

[pgloader]: https://pgloader.readthedocs.io/en/latest/ref/sqlite.html
2023-10-19 07:12:02 -05:00
Dustin 7698e039d1 postgresql: Use a private CA-signed TLS cert
The PostgreSQL server managed by *Postgres Operator* uses a self-signed
certificate by default.  In order to enable full validation of the
server certificate, we need to use a certificate signed by a known CA
that the clients can trust.  To that end, I have added a *cert-manager*
Issuer specifically for PostgreSQL.  The CA certificate is also managed
by *cert-manager*; it is self-signed and needs to be distributed to
clients out-of-band.
2023-10-18 18:47:09 -05:00
Dustin ba070e74a9 home-assistant: Upgrade ZWaveJS UI to 9.1.2 2023-10-14 22:26:09 -05:00
Dustin 29ec26c369 home-assistant: Upgrade Zigbee2MQTT to 1.33.1 2023-10-14 22:25:56 -05:00
Dustin 29e51e3641 home-assistant: Upgrade to 2023.10.3 2023-10-14 22:20:26 -05:00
Dustin 5d0d002efd argocd/apps: kitchen: Initial commit 2023-10-14 22:13:09 -05:00
Dustin 0eb121b833 kitchen: Remove config.yml ConfigMap generator
The `config.yml` document for *kitchen* contains several "secret" values
(e.g. passwords to Nextcloud, MQTT, etc.).  We don't want to commit
these to the Git repository, of course, but as long as Kustomize expects
to find the `config.yml` file, we won't be able to manage the
application with Argo CD.  Ultimately, *kitchen* needs to be modified to
be able to read secrets separately from config, but until then, we will
have to avoid managing `config.yml` with Kustomize.
2023-10-14 22:01:26 -05:00
Dustin 53a057666a kitchen: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 21:45:01 -05:00
Dustin c5c63d2684 kitchen: Migrate to Sealed Secrets 2023-10-14 21:44:47 -05:00
Dustin 28e8ac58db postgresql: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 21:32:29 -05:00
Dustin 3b05ab5b75 argocd/apps: Add PostgreSQL
The *postgresql* application monitors the PostgreSQL operator and its
assocaited resources.
2023-10-14 21:30:49 -05:00
Dustin 6ef8d3256e postgresql/default-cluster: Add Home Assistant DB
I actually created this a long time ago, but forgot to update the
manifest in Git.

The *homeassistant* database is used by Home Assistant for its
*recorder* component, which stores long-term statistics.  The data
stored here are only used for e.g. History and Logbook; current entity
states are still stored on the filesystem.
2023-10-14 21:28:41 -05:00
Dustin c23aa38eff postgresql: Migrate to Sealed Secrets 2023-10-14 21:28:32 -05:00
Dustin ccec71213a photoframesvc: Initial commit
The photo frame service has actually been deployed for a while, I just
forgot to commit it.
2023-10-14 11:25:50 -05:00
Dustin 5611ba5f8c argocd/apps: Add authelia 2023-10-14 11:17:16 -05:00
Dustin 629d1bb93a argocd: Configure SSO for CLI
The `argocd` command needs to have its own OIDC client configuration,
since it works like a "public" client.  To log in, run

```sh
argocd login argocd.pyrocufflink.blue --sso
```
2023-10-14 11:13:06 -05:00
Dustin e6f0261a44 phpipam: Migrate to Sealed Secrets 2023-10-14 10:56:20 -05:00
Dustin 721edd0d59 authelia: Use ConfigMap name suffix hash
Without `disableNameSuffixHash` enabled, Kustomize will create a unique
ConfigMap any time the contents of source file change.  It will also
update any Deployment, StatefulSet, etc resources to point to the new
ConfigMap.  This has the effect of restarting any pods that refer to the
ConfigMap whenever its contents change.

I had avoided using this initially because Kustomize does *not* delete
previous ConfigMap resources whenever it creates a new one.  Now that we
have Argo CD, though, this is not an issue, as it will clean up the old
resources whenever it synchronizes.
2023-10-14 10:47:23 -05:00
Dustin 7bdf1f86b9 authelia: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 10:47:07 -05:00
Dustin 63ff1bf637 authelia: Migrate to Sealed Secrets 2023-10-14 10:35:54 -05:00
Dustin fdbf1d3432 argocd: Deploy Argo CD
[Argo CD] is a Kubernetes-native GitOps/continuous deployment manager.
It monitors the state of Kubnernetes resources, such as Pods,
Deployments, ConfigMaps, Secrets, and Custom Resources, and synchronizes
them with their canonical definitions from a Git repository.

*Argo CD* consists of various components, including a Repository
Service, an Application Controller, a Notification Controller, and an
API server/Web UI.  It also has some optional components, such as a
bundled Dex server for authentication/authorization, and an
ApplicationSet controller, which we will not be using.

[Argo CD]: https://argo-cd.readthedocs.io/
2023-10-14 10:17:04 -05:00
Dustin 8ccac0e845 dynk8s-provisioner: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 07:43:37 -05:00
Dustin c317931f9c firefly-iii: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 07:40:14 -05:00
Dustin 3fb44e7ed6 phpipam: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 07:35:23 -05:00
Dustin 4081a2c8c4 paperless-ngx: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 07:33:27 -05:00
Dustin 1a83e4be89 ntfy: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 07:28:05 -05:00
Dustin 860bfb1e2c jenkins: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 07:24:42 -05:00
Dustin f3fcb92066 home-assistant: Set instance label for Argo CD
Argo CD wants every resource managed by an application to have that
application's name as the value of the `app.kubernetes.io/instance`
label.
2023-10-14 07:19:28 -05:00
Dustin 9f06e48269 firefly-iii: Migrate to sealed secrets 2023-10-13 21:32:58 -05:00
Dustin 84091869f1 home-assistant: Migrate to Sealed Secrets
The only Secret object in the *home-assistant* namespace is the `passwd`
file for Mosquitto.
2023-10-13 19:07:21 -05:00
Dustin 0592f450c4 sealed-secrets: Deploy Bitnami Sealed Secrets
[Sealed Secrets] will allow us to store secret values in the Git
repository, since the actual secrets are encrypted and can only be
decrypted using the private key stored in the Kubernetes cluster.

I have been looking for a better way to deal with secrets for some time
now.  For one thing, having the secret files ignored by Git means they
only exist on my main desktop.  If I need to make changes to an
application from another machine, I have to not only clone the
repository, but also manually copy the secret files.  That sort of
makes my desktop a single point-of-failure.  I tried moving all the
secret files to another (private) repository and adding it as a
submodule, but Kustomize did not like that; it will only load files from
the current working directory, or another Kustomize project.  Having to
create two projects for each application, one for the secrets and one
for everything else, would be tedious and annoying.  I also considered
encrypting all the secret files with e.g. GnuPG and creating Make
recipies for each project to decrypt them before running `kubectl
apply`.  I eventually want to use Argo CD, though, so that prerequisite
step would make that a lot more complex.  Eventually, I discovered
[KSOPS] and *Sealed Secrets*.  KSOPS operates entirely on the client
side, and thus requires a plugin for Kustomize and/or Argo CD in order
to work, so it's not significantly different than the GnuPG/Make idea.
I like that Sealed Secrets does not require anything on the client side,
except when initially creating the manifests for the SealedSecret
objects, so Argo CD will "just work" without any extra tools or
configuration.

[Sealed Secrets]: https://github.com/bitnami-labs/sealed-secrets
[KSOPS]: https://github.com/viaduct-ai/kustomize-sops
2023-10-13 18:34:01 -05:00
Dustin d943c936a7 phpipam: Update PVC storage class name
The other day, when I was dealing with the mess that I accidentally
created by letting the *phpipam* MySQL database automaticall upgrade
itself, I attempted to restore from a Longhorn backup to try to get the
database working again.  This did work, but as a side-effect, it changed
the storage class name of the *phpipam-pvc* persistent volume claim from
`longhorn` to `longhorn-static`.  Now, when attempting to apply the
YAML manifest, `kubectl` complains because this field is immutable.  As
such, the manifest needs to be updated to reflect the value set by
Longhorn when the backup was restored and the PVC was recreated.
2023-10-10 22:42:14 -05:00
Dustin 934c07ceba device-plugins: Add fuse-device-plugin DaemonSet
The *fuse-device-plugin* handles mapping the `/dev/fuse` device into
unprivileged containers, e.g. for `buildah`.

Although *fuse-device-plugin* was recommended by Red Hat in their
blog post [How to use Podman inside of Kubernetes][0], it's probably
not the best choice any more.  It's working for now, giving me the
ability to build container images in Kubernetes without running
`buildah` in a privileged container, but I will probably investigate
replacing it with the [generic-device-plugin][1] eventually.

[0]: https://www.redhat.com/sysadmin/podman-inside-kubernetes
[1]: https://github.com/squat/generic-device-plugin
2023-10-10 22:31:44 -05:00
Dustin f7a8f391ea dch-webhooks: Configure SSH cert signer
The *dch-webhooks* tool now provides an operation for hosts to request a
signed SSH certificate from the SSH CA.  It's primarily useful for
unattended deployments like CoreOS Ignition, where hosts do not have
any credentials to authenticate with the CA directly.
2023-10-10 22:31:44 -05:00
Dustin 92a5aee7b4 authelia: Add OIDC client for step-ca
*step-ca* uses OpenID Connect to authenticate requests for SSH and X.509
certificates.
2023-10-10 22:31:44 -05:00
Dustin 0a9596d8bd step-ca: Deploy Step CA service
[Step CA] is an open-source online X.509 and SSH certificate authority
service.  It supports issuing certificates via various protocols,
including ACME and its own HTTP API via the `step` command-line utility.
Clients can authenticate using a variety of methods, such as JWK, Open
ID Connect, or mTLS.  This makes it very flexible and easy to introduce
to an existing ecosystem.

Although the CA service is mostly stateless, it does have an on-disk
database where stores some information, notably the list of SSH hosts
for which it has signed certificates.  Most other operations, though, do
not require any persistent state; the service does not keep track of
every single certificate it signed, for example.  It can be configured
to store authentication information (referred to as "provisioners") in
the database instead of the configuration file, by enabling the "remote
provisioner management" feature.  This has the advantage of being able
to modify authentication configuration without updating a Kubernetes
ConfigMap and restarting the service.

The official Step CA documentation recommends using the `step ca init`
command initialize a new certificate authority.  This command performs a
few steps:

* Generates an ECDSA key pair and uses it to create a self-signed root
  certificate
* Generates a second ECDSA key pair and signs an intermediate CA
  certificate using the root CA key
* Generates an ECDSA key pair and SSH root certificate
* Creates a `ca.json` configuration file

These steps can be performed separately, and in fact, I created the
intermediate CA certificate and signed it with the (offline) *dch Root
CA* certificate.

When the service starts for the first time, because
`authority/enableAdmin` is `true` and `authority/provisioners` is empty,
a new "Admin JWK" provisioner will be created automatically.  This key
will be encrypted with the same password used to encrypt the
intermediate CA certificate private key, and can be used to create other
provisioners.

[Step CA]: https://smallstep.com/docs/step-ca/
2023-10-10 22:31:44 -05:00
Dustin 6cd7eae0d3 phpipam: Use Authelia for authentication
phpIPAM supports "Apache authentication" which effectively delegates
authentication to the web server and trusts the `PHP_AUTH_USER` server
variable.  This variable is usually set by an Apache authentication
module, but it can be set manually in the config.  Here, we're using
`SetEnvIf` to populate it from the value of the `Remote-User` header
set by Authelia.
2023-09-22 21:24:24 -05:00