# 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}"