From e8632459107692c355d8511eb79adfd29dbc2d25 Mon Sep 17 00:00:00 2001 From: Nicky van Meurs <1681034+nvanmeurs@users.noreply.github.com> Date: Mon, 2 May 2022 23:31:16 +0200 Subject: [PATCH] Add templates and logic to set up manifests for Github webhook receiver (#308) Co-authored-by: Devin Buhl --- .config.sample.env | 5 +++ README.md | 31 +++++++++++++ configure.sh | 28 ++++++++++++ tmpl/cluster/flux-system/kustomization.yaml | 5 +++ .../flux-system/webhooks/github/ingress.yaml | 28 ++++++++++++ .../webhooks/github/kustomization.yaml | 7 +++ .../flux-system/webhooks/github/receiver.yaml | 43 +++++++++++++++++++ .../webhooks/github/secret.sops.yaml | 8 ++++ .../flux-system/webhooks/kustomization.yaml | 5 +++ 9 files changed, 160 insertions(+) create mode 100644 tmpl/cluster/flux-system/kustomization.yaml create mode 100644 tmpl/cluster/flux-system/webhooks/github/ingress.yaml create mode 100644 tmpl/cluster/flux-system/webhooks/github/kustomization.yaml create mode 100644 tmpl/cluster/flux-system/webhooks/github/receiver.yaml create mode 100644 tmpl/cluster/flux-system/webhooks/github/secret.sops.yaml create mode 100644 tmpl/cluster/flux-system/webhooks/kustomization.yaml diff --git a/.config.sample.env b/.config.sample.env index 78844a1a..e1418646 100644 --- a/.config.sample.env +++ b/.config.sample.env @@ -5,6 +5,11 @@ # The repo you created from this template # e.g. https://github.com/k8s-at-home/home-cluster export BOOTSTRAP_GIT_REPOSITORY="" +# To enable Flux to update your cluster on `git push` set the following to one of: +# `ignored` - this feature will be disabled +# `generated` - this will generate a token and print it in the logs +# Set this to any other string and it will be used for the secret +export BOOTSTRAP_FLUX_GITHUB_WEBHOOK_SECRET="ignored" # Choose one of your cloudflare domains # e.g. k8s-at-home.com export BOOTSTRAP_CLOUDFLARE_DOMAIN="" diff --git a/README.md b/README.md index 269f32ab..9d520124 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ For provisioning the following tools will be used: | [go-task](https://github.com/go-task/task) | A task runner / simpler Make alternative written in Go | | [ipcalc](http://jodies.de/ipcalc) | Used to verify settings in the configure script | | [jq](https://stedolan.github.io/jq/) | Used to verify settings in the configure script | +| [yq](https://github.com/mikefarah/yq) | Used to verify settings in the configure script | | [kubectl](https://kubernetes.io/docs/tasks/tools/) | Allows you to run commands against Kubernetes clusters | | [sops](https://github.com/mozilla/sops) | Encrypts k8s secrets with Age | | [terraform](https://www.terraform.io) | Prepare a Cloudflare domain to be used with the cluster | @@ -337,6 +338,36 @@ To access services from the outside world port forwarded `80` and `443` in your Now if nothing is working, that is expected. This is DNS after all! +### 🪝 Github Webhook + +Flux is pull-based by design meaning it will periodically check your git repository for changes, using a webhook you can enable Flux to update your cluster on `git push`. In order to configure Github to send `push` events from your repository to the Flux webhook receiver you will need two things: + +1. Webhook URL + Your webhook receiver will be deployed on `https://flux-receiver.${BOOTSTRAP_CLOUDFLARE_DOMAIN}/hook/:hookId`. In order to find out your hook id you can run the following command: + + ```sh + kubectl -n flux-system get receiver/github-receiver --kubeconfig=./provision/kubeconfig + # NAME AGE READY STATUS + # github-receiver 6h8m True Receiver initialized with URL: /hook/12ebd1e363c641dc3c2e430ecf3cee2b3c7a5ac9e1234506f6f5f3ce1230e123 + ``` + + So if my domain was `k8s-at-home.com` the full url would look like this: + + ```text + https://flux-receiver.k8s-at-home.com/hook/12ebd1e363c641dc3c2e430ecf3cee2b3c7a5ac9e1234506f6f5f3ce1230e123 + ``` + +2. Webhook secret + Your webhook secret can be found by decrypting the `secret.sops.yaml` using the following command: + + ```sh + sops -d ./cluster/apps/flux-system/webhooks/github/secret.sops.yaml | yq .stringData.token + ``` + + **Note:** Don't forget to update the `BOOTSTRAP_FLUX_GITHUB_WEBHOOK_SECRET` variable in your `.config.env` file so it matches the generated secret if applicable + +Now that you have the webhook url and secret, it's time to set everything up on the Github repository side. Navigate to the settings of your repository on Github, under "Settings/Webhooks" press the "Add webhook" button. Fill in the webhook url and your secret. + ### 👉 Troubleshooting Our [wiki](https://github.com/k8s-at-home/template-cluster-k3s/wiki) (WIP, contributions welcome) is a good place to start troubleshooting issues. If that doesn't cover your issue, come join and say Hi in our [Discord](https://discord.gg/k8s-at-home) server by starting a new thread in the #kubernetes support channel. diff --git a/configure.sh b/configure.sh index e492166b..ec2b1ff2 100755 --- a/configure.sh +++ b/configure.sh @@ -71,6 +71,7 @@ main() { # generate ansible hosts file and secrets generate_ansible_hosts generate_ansible_host_secrets + setup_github_webhook fi } @@ -165,6 +166,7 @@ verify_binaries() { _has_binary "helm" _has_binary "ipcalc" _has_binary "jq" + _has_binary "yq" _has_binary "sops" _has_binary "ssh" _has_binary "task" @@ -206,6 +208,32 @@ verify_git_repository() { export GIT_TERMINAL_PROMPT=1 } +setup_github_webhook() { + WEBHOOK_SECRET="${BOOTSTRAP_FLUX_GITHUB_WEBHOOK_SECRET:-ignored}" + + if [[ "${WEBHOOK_SECRET}" != "ignored" ]]; then + if [[ "${WEBHOOK_SECRET}" == "generated" ]]; then + WEBHOOK_SECRET="$(openssl rand -base64 30)" + fi + + export BOOTSTRAP_FLUX_GITHUB_WEBHOOK_SECRET="${WEBHOOK_SECRET}" + _log "INFO" "Using GitHub Token '${WEBHOOK_SECRET}' for Flux" + + cp -rf "${PROJECT_DIR}/tmpl/cluster/flux-system" "${PROJECT_DIR}/cluster/apps/" + + WEBHOOK_DIR="${PROJECT_DIR}/cluster/apps/flux-system/webhooks/github/" + + envsubst < "${WEBHOOK_DIR}/receiver.yaml" | tee "${WEBHOOK_DIR}/receiver.yaml" > /dev/null + envsubst < "${WEBHOOK_DIR}/secret.sops.yaml" | tee "${WEBHOOK_DIR}/secret.sops.yaml" > /dev/null + + sops --encrypt --in-place "${WEBHOOK_DIR}/secret.sops.yaml" + + if [[ $(yq eval --no-doc 'contains({"resources": ["flux-system"]})' "${PROJECT_DIR}/cluster/apps/kustomization.yaml") == false ]]; then + yq -i '.resources += [ "flux-system" ]' "${PROJECT_DIR}/cluster/apps/kustomization.yaml" + fi + fi +} + verify_cloudflare() { local account_zone= local errors= diff --git a/tmpl/cluster/flux-system/kustomization.yaml b/tmpl/cluster/flux-system/kustomization.yaml new file mode 100644 index 00000000..56dd3721 --- /dev/null +++ b/tmpl/cluster/flux-system/kustomization.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - webhooks diff --git a/tmpl/cluster/flux-system/webhooks/github/ingress.yaml b/tmpl/cluster/flux-system/webhooks/github/ingress.yaml new file mode 100644 index 00000000..19fd5f93 --- /dev/null +++ b/tmpl/cluster/flux-system/webhooks/github/ingress.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: webhook-receiver + namespace: flux-system + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-production" + external-dns/is-public: "true" + external-dns.alpha.kubernetes.io/target: "ipv4.${SECRET_DOMAIN}" + external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" +spec: + ingressClassName: traefik + rules: + - host: "flux-receiver.${SECRET_DOMAIN}" + http: + paths: + - path: /hook/ + pathType: Prefix + backend: + service: + name: webhook-receiver + port: + number: 80 + tls: + - hosts: + - "flux-receiver.${SECRET_DOMAIN}" + secretName: flux-receiver-tls diff --git a/tmpl/cluster/flux-system/webhooks/github/kustomization.yaml b/tmpl/cluster/flux-system/webhooks/github/kustomization.yaml new file mode 100644 index 00000000..b3578f6a --- /dev/null +++ b/tmpl/cluster/flux-system/webhooks/github/kustomization.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ingress.yaml + - receiver.yaml + - secret.sops.yaml diff --git a/tmpl/cluster/flux-system/webhooks/github/receiver.yaml b/tmpl/cluster/flux-system/webhooks/github/receiver.yaml new file mode 100644 index 00000000..5472c091 --- /dev/null +++ b/tmpl/cluster/flux-system/webhooks/github/receiver.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: notification.toolkit.fluxcd.io/v1beta1 +kind: Receiver +metadata: + name: github-receiver + namespace: flux-system +spec: + type: github + events: + - ping + - push + secretRef: + name: github-webhook-secret + resources: + - apiVersion: source.toolkit.fluxcd.io/v1beta2 + kind: GitRepository + name: flux-system + namespace: flux-system + + - apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 + kind: Kustomization + name: apps + namespace: flux-system + + - apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 + kind: Kustomization + name: core + namespace: flux-system + + - apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 + kind: Kustomization + name: crds + namespace: flux-system + + - apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 + kind: Kustomization + name: flux-system + namespace: flux-system + + - apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 + kind: Kustomization + name: traefik-crds + namespace: flux-system diff --git a/tmpl/cluster/flux-system/webhooks/github/secret.sops.yaml b/tmpl/cluster/flux-system/webhooks/github/secret.sops.yaml new file mode 100644 index 00000000..b20dfee4 --- /dev/null +++ b/tmpl/cluster/flux-system/webhooks/github/secret.sops.yaml @@ -0,0 +1,8 @@ +# yamllint disable +apiVersion: v1 +kind: Secret +metadata: + name: github-webhook-secret + namespace: flux-system +stringData: + token: ${BOOTSTRAP_FLUX_GITHUB_WEBHOOK_SECRET} diff --git a/tmpl/cluster/flux-system/webhooks/kustomization.yaml b/tmpl/cluster/flux-system/webhooks/kustomization.yaml new file mode 100644 index 00000000..2c23d3de --- /dev/null +++ b/tmpl/cluster/flux-system/webhooks/kustomization.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - github