Make the image usable in single user, and store in /data

This commit is contained in:
Daniel Berteaud 2023-11-15 12:26:36 +01:00
parent e8566b245a
commit ee3cb24a67
5 changed files with 38 additions and 25 deletions

View File

@ -31,13 +31,15 @@ job [[ .acme.instance | toJSON ]] {
]
}
task "vault-acme-cert" {
task "acme-to-vault" {
driver = [[ $c.nomad.driver | toJSON ]]
user = 8787
config {
image = [[ .acme.image | toJSON ]]
readonly_rootfs = true
pids_limit = 20
[[ template "common/tmpfs.tpl" dict "target" "/data" "size" "10000000" ]]
}
vault {

View File

@ -12,16 +12,25 @@ ENV ACME_HTTP_PORT=8787 \
RUN set -eu &&\
apk --no-cache upgrade &&\
apk --no-cache add curl openssl ca-certificates tini lego vault jq supercronic libcap-utils &&\
mkdir -p /secrets &&\
addgroup -g 8787 acme &&\
adduser --system --ingroup acme --disabled-password --uid 8787 --home /secrets --shell /sbin/nologin acme &&\
chown acme:acme /secrets &&\
apk --no-cache add \
curl \
openssl \
ca-certificates \
tini \
lego \
vault \
jq \
supercronic \
libcap-utils \
&&\
mkdir -p /data &&\
# Use very open permissions so we can easily mount a tmpfs over /data \
# A /data/acme subdir will be created with restricted permissions anyway \
chmod 777 /data &&\
setcap -r /usr/sbin/vault
COPY root/ /
USER acme
EXPOSE ${ACME_HTTP_PORT}
ENTRYPOINT ["tini", "--", "/entrypoint.sh"]
CMD ["acme-to-vault.sh"]

View File

@ -2,25 +2,27 @@
set -u
mkdir -p /data/acme
chmod 700 /data/acme
for IDX in $(printenv | grep -E '^ACME_\d+_EMAIL=' | sed -E 's/ACME_(\d+)_EMAIL.*/\1/'); do
for CA in $(vault kv list -format json ${ACME_KV_ACCOUNT_ROOT} | jq -r ".[]"); do
CA_FQDN=$(echo -n ${CA} | sed -E 's|^(https?://)?([^/]+).*|\2|')
for EMAIL in $(vault kv list -format json ${ACME_KV_ACCOUNT_ROOT}/${CA_FQDN} | jq -r ".[]"); do
echo "Restoring ACME account ${EMAIL} for ${CA_FQDN}"
mkdir -p /secrets/acme/accounts/${CA_FQDN}/${EMAIL}/keys
vault kv get -field metadata ${ACME_KV_ACCOUNT_ROOT}/${CA_FQDN}/${EMAIL} > /secrets/acme/accounts/${CA_FQDN}/${EMAIL}/account.json
vault kv get -field key ${ACME_KV_ACCOUNT_ROOT}/${CA_FQDN}/${EMAIL} > /secrets/acme/accounts/${CA_FQDN}/${EMAIL}/keys/${EMAIL}.key
mkdir -p /data/acme/accounts/${CA_FQDN}/${EMAIL}/keys
vault kv get -field metadata ${ACME_KV_ACCOUNT_ROOT}/${CA_FQDN}/${EMAIL} > /data/acme/accounts/${CA_FQDN}/${EMAIL}/account.json
vault kv get -field key ${ACME_KV_ACCOUNT_ROOT}/${CA_FQDN}/${EMAIL} > /data/acme/accounts/${CA_FQDN}/${EMAIL}/keys/${EMAIL}.key
done
done
KV_CERT_ROOT=$(printenv ACME_${IDX}_KV_CERT_ROOT)
mkdir -p /secrets/acme/certificates
mkdir -p /data/acme/certificates
for CERT in $(vault kv list -format json ${KV_CERT_ROOT} | jq -r ".[]"); do
echo "Restoring cert ${CERT} to /secrets/acme/certificates/"
vault kv get -field cert ${KV_CERT_ROOT}/${CERT} > /secrets/acme/certificates/${CERT}.crt
vault kv get -field key ${KV_CERT_ROOT}/${CERT} > /secrets/acme/certificates/${CERT}.key
vault kv get -field metadata ${KV_CERT_ROOT}/${CERT} > /secrets/acme/certificates/${CERT}.json
vault kv get -field issuer ${KV_CERT_ROOT}/${CERT} > /secrets/acme/certificates/${CERT}.issuer.crt
echo "Restoring cert ${CERT} to /data/acme/certificates/"
vault kv get -field cert ${KV_CERT_ROOT}/${CERT} > /data/acme/certificates/${CERT}.crt
vault kv get -field key ${KV_CERT_ROOT}/${CERT} > /data/acme/certificates/${CERT}.key
vault kv get -field metadata ${KV_CERT_ROOT}/${CERT} > /data/acme/certificates/${CERT}.json
vault kv get -field issuer ${KV_CERT_ROOT}/${CERT} > /data/acme/certificates/${CERT}.issuer.crt
done
done

View File

@ -9,14 +9,14 @@ log_and_run(){
}
main(){
mkdir -p /secrets/acme
mkdir -p /data/acme
for IDX in $(printenv | grep -E '^ACME_\d+_EMAIL=' | sed -E 's/ACME_(\d+)_EMAIL.*/\1/'); do
export ACME_IDX=${IDX}
export ACME_CA=$(printenv ACME_${IDX}_CA)
EMAIL=$(printenv ACME_${IDX}_EMAIL)
CHALLENGE=$(printenv ACME_${IDX}_CHALLENGE)
LEGO_OPTS="--path /secrets/acme --server ${ACME_CA} --email ${EMAIL} --accept-tos"
LEGO_OPTS="--path /data/acme --server ${ACME_CA} --email ${EMAIL} --accept-tos"
if [ "${CHALLENGE}" = "http-01" ]; then
LEGO_OPTS="${LEGO_OPTS} --http --http.port 127.0.0.1:${ACME_HTTP_PORT}"
@ -36,9 +36,9 @@ main(){
# Use the first hostname as CN
CN=$(printenv ${CERT} | sed -E 's/([^,]+).*/\1/')
ACTION=run
if [ -e /secrets/acme/certificates/${CN}.crt -a -e /secrets/acme/certificates/${CN}.key ]; then
if [ -e /data/acme/certificates/${CN}.crt -a -e /data/acme/certificates/${CN}.key ]; then
local CONF_DOMAIN=$(printenv ${CERT} | tr "," "\n" | sort | tr "\n" "," | sed -E 's/,$/\n/')
local CUR_DOMAIN=$(openssl x509 -in /secrets/acme/certificates/${CN}.crt -noout -ext subjectAltName | tail -1 | sed -E 's/\s+DNS://g' | tr "," "\n" | sort | tr "\n" "," | sed -E 's/,$/\n/')
local CUR_DOMAIN=$(openssl x509 -in /data/acme/certificates/${CN}.crt -noout -ext subjectAltName | tail -1 | sed -E 's/\s+DNS://g' | tr "," "\n" | sort | tr "\n" "," | sed -E 's/,$/\n/')
if [ "${CUR_DOMAIN}" = "${CONF_DOMAIN}" ]; then
echo "Certificate for ${CN} already exists, trying to renew (if needed)"
ACTION=renew
@ -54,8 +54,8 @@ main(){
echo "Saving ACME account ${EMAIL} for ${ACME_CA} on vault"
CA=$(echo ${ACME_CA} | sed -E 's|^https?://([^/:]+).*|\1|')
vault kv put ${ACME_KV_ACCOUNT_ROOT}/${CA}/${EMAIL} \
metadata=@/secrets/acme/accounts/${CA}/${EMAIL}/account.json \
key=@/secrets/acme/accounts/${CA}/${EMAIL}/keys/${EMAIL}.key
metadata=@/data/acme/accounts/${CA}/${EMAIL}/account.json \
key=@/data/acme/accounts/${CA}/${EMAIL}/keys/${EMAIL}.key
done
}
@ -66,9 +66,9 @@ main
# If a cron expression is defined, run a cron daemon
if [ -n "${ACME_CRON}" -a -z "${ACME_CRON_RUNNING:-}" ]; then
echo "Running using cron with expression ${ACME_CRON}"
cat <<_EOF > /dev/shm/crontab
cat <<_EOF > /data/crontab
${ACME_CRON} /usr/local/bin/acme-to-vault.sh
_EOF
export ACME_CRON_RUNNING=true
supercronic /dev/shm/crontab
supercronic /data/crontab
fi

View File

@ -5,7 +5,7 @@ acme:
instance: acme-to-vault
# The Docker image to use
image: danielberteaud/acme-to-vault:23.10-1
image: danielberteaud/acme-to-vault:23.11-2
# If a cron expression is defined, the service will stay running and renew certs.
# If an empty string, the container will exit after running once