Files
fluxer/deploy/helm/infra/templates/geoip-sync-cronjob.yaml
T

134 lines
5.6 KiB
YAML

# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Daily sync of the MaxMind GeoLite2 City and ASN MMDB files into S3.
# Runtime services read these out of the CDN bucket. Adopted into helm
# from a previously hand-applied kubectl manifest.
apiVersion: batch/v1
kind: CronJob
metadata:
name: geoip-sync
namespace: {{ .Values.global.namespace }}
labels:
app.kubernetes.io/name: geoip-sync
{{- include "fluxer.labels" . | nindent 4 }}
spec:
schedule: {{ .Values.geoipSync.schedule | quote }}
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
activeDeadlineSeconds: {{ .Values.geoipSync.activeDeadlineSeconds }}
backoffLimit: {{ .Values.geoipSync.backoffLimit }}
template:
metadata:
labels:
app.kubernetes.io/name: geoip-sync
app.kubernetes.io/part-of: fluxer
spec:
restartPolicy: OnFailure
terminationGracePeriodSeconds: 60
imagePullSecrets:
- name: {{ .Values.global.imagePullSecret }}
containers:
- name: sync
image: {{ .Values.geoipSync.image }}
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
env:
- name: GEOIP_BUCKET
value: {{ .Values.geoipSync.bucket | quote }}
- name: GEOIP_CITY_UPSTREAM_URL
value: {{ .Values.geoipSync.cityUpstreamUrl | quote }}
- name: GEOIP_ASN_UPSTREAM_URL
value: {{ .Values.geoipSync.asnUpstreamUrl | quote }}
envFrom:
- secretRef:
name: {{ .Values.geoipSync.envSecret }}
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi
command:
- sh
- -c
- |
set -eu
: "${GEOIP_BUCKET:?missing GEOIP_BUCKET}"
: "${GEOIP_CITY_UPSTREAM_URL:?missing GEOIP_CITY_UPSTREAM_URL}"
: "${GEOIP_ASN_UPSTREAM_URL:?missing GEOIP_ASN_UPSTREAM_URL}"
: "${FLUXER_S3_ACCESS_KEY_ID:?missing FLUXER_S3_ACCESS_KEY_ID}"
: "${FLUXER_S3_SECRET_ACCESS_KEY:?missing FLUXER_S3_SECRET_ACCESS_KEY}"
: "${FLUXER_S3_ENDPOINT:?missing FLUXER_S3_ENDPOINT}"
: "${FLUXER_S3_REGION:?missing FLUXER_S3_REGION}"
export AWS_ACCESS_KEY_ID="$FLUXER_S3_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$FLUXER_S3_SECRET_ACCESS_KEY"
export AWS_DEFAULT_REGION="$FLUXER_S3_REGION"
WORKDIR=$(mktemp -d)
trap 'rm -rf "$WORKDIR"' EXIT
# MMDB files end with the ASCII string "MaxMind.com" after
# their metadata marker. Verifying this tail before upload
# catches the case where an upstream returns an HTML error
# page or a zero-byte body.
fetch_and_verify() {
local url="$1"
local dest="$2"
echo "-> fetching $url"
curl --fail --location --silent --show-error \
--user-agent 'fluxer-geoip-sync/1.0' \
--max-time 120 \
--output "$dest" \
"$url"
local size
size=$(wc -c < "$dest")
if [ "$size" -lt 1024 ]; then
echo "refusing to upload ${dest}: file is ${size} bytes, too small" >&2
return 1
fi
if ! tail -c 2048 "$dest" | grep -q "MaxMind.com"; then
echo "refusing to upload ${dest}: MaxMind.com marker not found in trailer" >&2
return 1
fi
echo " ok (${size} bytes)"
}
fetch_and_verify "$GEOIP_CITY_UPSTREAM_URL" "$WORKDIR/GeoLite2-City.mmdb"
fetch_and_verify "$GEOIP_ASN_UPSTREAM_URL" "$WORKDIR/GeoLite2-ASN.mmdb"
# Atomic-ish replacement: upload to a versioned side-key
# first, then copy to the canonical key. If the final copy
# fails the previous canonical file is untouched.
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
aws --endpoint-url "$FLUXER_S3_ENDPOINT" s3 cp \
"$WORKDIR/GeoLite2-City.mmdb" \
"s3://${GEOIP_BUCKET}/archive/GeoLite2-City-${STAMP}.mmdb"
aws --endpoint-url "$FLUXER_S3_ENDPOINT" s3 cp \
"$WORKDIR/GeoLite2-ASN.mmdb" \
"s3://${GEOIP_BUCKET}/archive/GeoLite2-ASN-${STAMP}.mmdb"
aws --endpoint-url "$FLUXER_S3_ENDPOINT" s3 cp \
"$WORKDIR/GeoLite2-City.mmdb" \
"s3://${GEOIP_BUCKET}/GeoLite2-City.mmdb"
aws --endpoint-url "$FLUXER_S3_ENDPOINT" s3 cp \
"$WORKDIR/GeoLite2-ASN.mmdb" \
"s3://${GEOIP_BUCKET}/GeoLite2-ASN.mmdb"
echo "geoip-sync complete: city=${STAMP} asn=${STAMP}"