From b5386785caf52283df2a29f67b2fb21e9d456127 Mon Sep 17 00:00:00 2001 From: bg1991 Date: Tue, 12 Dec 2017 18:13:55 +0200 Subject: [PATCH] Upload ingress certificates stored in k8s secret. Currently, when one uses an Ingress resource fronted by Cloudflare CDN, and that Ingress uses a TLS certificate, that certificate has to be uploaded to Cloudflare as well. Otherwise, Cloudflare cannot serve the content under SSL since it does not have the certificate. This commit enables automatically uploading the Ingress's certificate (found under the Ingress's TLS secret) to Cloudflare, via its APIs. Then, Cloudflare can serve that Ingress under SSL. --- Dockerfile | 4 +-- cloudflared.sh | 10 ++++++++ upload_certificate.py | 58 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 upload_certificate.py diff --git a/Dockerfile b/Dockerfile index 3572147..69d4771 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,6 @@ MAINTAINER Arik Kfir RUN apk --no-cache --update add jq tree bash python3 py3-pip && \ pip3 install requests && \ gcloud components install kubectl -COPY cloudflared.sh update_dns_records.py /usr/local/bin/ -RUN chmod a+x /usr/local/bin/cloudflared.sh /usr/local/bin/update_dns_records.py +COPY cloudflared.sh update_dns_records.py upload_certificate.py /usr/local/bin/ +RUN chmod a+x /usr/local/bin/cloudflared.sh /usr/local/bin/update_dns_records.py /usr/local/bin/upload_certificate.py ENTRYPOINT ["/usr/local/bin/cloudflared.sh"] diff --git a/cloudflared.sh b/cloudflared.sh index be1b026..e83e3be 100755 --- a/cloudflared.sh +++ b/cloudflared.sh @@ -62,6 +62,16 @@ while true; do exit 1 fi + # Get a list of secrets containing certificates + kubectl get secrets --all-namespaces --output=json | jq -r ' + .items[] | + select(.type=="kubernetes.io/tls") | + {type: .type, name: .metadata.name, data: .data} + ' | $(dirname $0)/upload_certificate.py "${DOMAIN}" "${AUTH_EMAIL}" "${AUTH_KEY}" + if [[ $? != 0 ]];then + echo "Uploading certificates to Cloudflare failed!" >&2 + exit 1 + fi # rinse & repeat sleep 10 if [[ $? != 0 ]]; then diff --git a/upload_certificate.py b/upload_certificate.py new file mode 100644 index 0000000..6a47367 --- /dev/null +++ b/upload_certificate.py @@ -0,0 +1,58 @@ +import argparse +import json +import sys +import base64 +from typing import Mapping, Sequence, Any + +import requests + + +# build headers +def build_cloudflare_request_headers(auth_email: str, auth_key: str) -> Mapping[str, str]: + return { + "Content-Type": "application/json", + "X-Auth-Key": auth_key, + "X-Auth-Email": auth_email + } + + +# upload certificate to cloudflare +def upload_certificate(zone_id: str, auth_email: str, auth_key: str, key: str, crt: str): + url: str = f"{CF_BASE_URL}/zones/{zone_id}" + certificates_url: str = url + '/custom_certificates' + + certificate: dict = { + 'certificate': crt, + 'private_key': key + } + + requests.post(url=certificates_url, + headers=build_cloudflare_request_headers(auth_email=auth_email, auth_key=auth_key), + json=certificate).raise_for_status() + + +def main(): + argparser = argparse.ArgumentParser(description="Uploads custom certificate to cloudflare") + argparser.add_argument('domain', help='public suffix domain name, eg. \'mydomain.com\'') + argparser.add_argument('auth_email', metavar='EMAIL', help='Email of the account used to connect to Cloudflare') + argparser.add_argument('auth_key', metavar='KEY', help='authentication key of the Cloudflare account') + args = argparser.parse_args() + + zone: dict = requests.get( + url=f"{CF_BASE_URL}/zones", + headers=build_cloudflare_request_headers(auth_email=args.auth_email, auth_key=args.auth_key), + params={'name': args.domain}).json()['result'][0] + + certificate_list: Sequence[Mapping[str, Any]] = json.loads('\n'.join(sys.stdin.readlines())) + for obj in certificate_list: + private_key = base64.b64decode(obj['tls.key']).decode('utf-8').replace('\n', '\\n') + cert = base64.b64decode(obj['tls.crt']).decode('utf-8').replace('\n', '\\n') + upload_certificate(zone_id=zone['id'], + auth_email=args.auth_email, + auth_key=args.auth_key, + key=private_key, + crt=cert) + + +if __name__ == '__main__': + main() \ No newline at end of file