fetchcert: Deploy tool to get cert from k8s Secret

The `fetchcert` tool is a short shell script that fetches an X.509
certificate and corresponding private key from a Kubernetes Secret,
using the Kubernetes API.  I originally wrote it for the Frigate server
so it could fetch the _pyrocufflink.blue_ wildcard certificate, which is
managed by _cert-manager_.  Since then, I have adapted it to be more
generic, so it will be useful to fetch the _loki.pyrocufflink.blue_
certificate for Grafana Loki.

Although the script is rather simple, it does have several required
configuration parameters.  It needs to know the URL of the Kubernetes
API server and have the certificate for the CA that signs the server
certificate, as well as an authorization token.  It also needs to know
the namespace and name of the Secret from which it will fetch the
certificate and private key.  Finally,  needs to know the paths to the
files where the fetched data will be written.

Generally, after certificates are updated, some action needs to be
performed in order to make use of them.  This typically involves
restarting or reloading a daemon.  Since the `fetchcert` tool runs in
a container, it can't directly perform those actions, so it simply
indicates via a special exit code that the certificate has been updated
and some further action may be needed.  The
`/etc/fetchcert/postupdate.sh` script is executed by _systemd_ after
`fetchcert` finishes.  If the `EXIT_STATUS` environment variable (which
is set by _systemd_ to the return code of the main service process)
matches the expected code, the configured post-update actions will be
executed.
master
Dustin 2024-02-17 19:37:12 -06:00
parent b51428c363
commit 29afcae52e
8 changed files with 121 additions and 0 deletions

View File

@ -0,0 +1,12 @@
package fetchcert
#Fetchcert: {
ca_cert: string
kubernetes_url: string
token: string
namespace: string
secret: string
cert: string
key: string
postupdate?: string
}

View File

@ -0,0 +1,39 @@
package fetchcert
import "du5t1n.me/cfg/base/schema/instructions"
templates: [...instructions.#RenderInstruction] & [
{
template: "fetchcert/ca.crt"
dest: "/etc/fetchcert/ca.crt"
},
{
template: "fetchcert/token"
dest: "/etc/fetchcert/token"
},
{
template: "fetchcert/postupdate.sh"
dest: "/etc/fetchcert/postupdate.sh"
mode: "u=rwx,go=rx"
},
{
template: "fetchcert/fetchcert.container"
dest: "/etc/containers/systemd/fetchcert.container"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
]
}
},
{
template: "fetchcert/fetchcert.timer"
dest: "/etc/systemd/system/fetchcert.timer"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
{run: "systemctl enable fetchcert.timer"},
{run: "systemctl start fetchcert.timer"},
]
}
},
]

29
env/prod/fetchcert.cue vendored Normal file
View File

@ -0,0 +1,29 @@
package prod
import f "du5t1n.me/cfg/app/fetchcert"
fetchcert: base: f.#Fetchcert & {
ca_cert: """
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIyMDgwMTAyNTUzM1oXDTMyMDcyOTAyNTUzM1owFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMs6
2PUOzIClsAgPv1Mn9CTwzSFMntAn7OppwK5BQ4E5Vd1yMjz3p0uA1ZINv1ORorG0
mLl95C7y+CWUGPx+stHKQr/40sLGyypbX+AfjoPzHiDbIcbZEff8X5RwKqzmT9V7
Yt29KewADod0z+fqNYa62MJDaUunfwaV8kKFU/WJM8IKv2eJxAtWzvK3iHAFhx0j
Xo4TlyINL9V9UMKLf12w6CA3G41uZIBCN3G7aJEm++eGoMdrPZUXlbCpbSztO85/
hbulVs+0hCIxWiI+mRmB5OoWlRYL4jA45oK/RtpEqSwZ95zlGNAChmH7rb0pTtNf
N0/C2wKAEL4POLx9kscCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFHYActCjEWdtsA+Ju25gxJh/vaLQMBUGA1UdEQQO
MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAAfkYHecXUwyqvMSXmqr
ETqEzDCBini14s89VDhaDHOXBID9TKMVyeePdEYcPAJz3wo8fbx/+TL37K6hEuo+
7bUaamaumznsjg9L0Hth19GvuRKMXJlEpndRmE5K9hnaDLr94MLg9n1qGcEOt6tw
O6X5qqHf9AuuL39vt1+kSw6PeZZFZNMDZ8BdiTssw4btjQ2bsWu0wSiOSz/F8iRf
2vN5An5dheroDsFs4dZ9gnJ69TmqV1YqQxfRWqCxzfNJbgVm6AoBPwhL1JRuAU4N
3nCNoM9n2tLFDojT4un1778UVU91PtcBVdM9Nq+RC2jhXIyLBqsEK0ofOqFYqj3F
0EQ=
-----END CERTIFICATE-----
"""
kubernetes_url: "https://kubernetes.pyrocufflink.blue:6443"
namespace: "dch-ca"
}

View File

@ -0,0 +1 @@
{{ fetchcert.ca_cert }}

View File

@ -0,0 +1,22 @@
[Unit]
Description=Fetch HTTPS certificate from Kubernetes Secret API
Wants=network-online.target
After=network-online.target
[Container]
Image=git.pyrocufflink.net/containerimages/fetchcert
Exec={{ fetchcert.namespace }} {{ fetchcert.secret }} /etc/fetchcert/certs/{{ fetchcert.key }} /etc/fetchcert/certs/{{ fetchcert.cert }}
ReadOnly=yes
ReadOnlyTmpfs=yes
Volume=/etc/fetchcert:/etc/fetchcert:ro
Volume=/etc/fetchcert/certs:/etc/fetchcert/certs:rw,z
Environment=KUBERNETES_URL={{ fetchcert.kubernetes_url }}
AddCapability=CAP_CHOWN
DropCapability=all
NoNewPrivileges=yes
[Service]
Type=oneshot
SuccessExitStatus=20
ExecStartPre=/bin/mkdir -p /etc/fetchcert/certs
ExecStopPost=-/etc/fetchcert/postupdate.sh

View File

@ -0,0 +1,9 @@
[Unit]
Description=Periodically fetch certificate from Kubernetes
[Timer]
OnCalendar=*-*-* 0:0:0
RandomizedDelaySec=8h
[Install]
WantedBy=timers.target

View File

@ -0,0 +1,8 @@
#!/bin/sh
if [ ${EXIT_STATUS:-1} -ne 20 ]; then
exit 0
fi
{% if fetchcert.postupdate %}
{{ fetchcert.postupdate }}
{% endif -%}

View File

@ -0,0 +1 @@
{{ fetchcert.token | decrypt }}