├── .editorconfig ├── .gitattributes ├── .github ├── labeler.yaml ├── labels.yaml ├── release.yaml ├── tests │ ├── nodes.yaml │ ├── private.yaml │ └── public.yaml └── workflows │ ├── e2e.yaml │ ├── flux-local.yaml │ ├── label-sync.yaml │ ├── labeler.yaml │ └── release.yaml ├── .gitignore ├── .mise.toml ├── .renovaterc.json5 ├── .shellcheckrc ├── .taskfiles ├── bootstrap │ └── Taskfile.yaml ├── talos │ └── Taskfile.yaml └── template │ ├── Taskfile.yaml │ └── resources │ ├── cluster.schema.cue │ ├── kubeconform.sh │ └── nodes.schema.cue ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── Taskfile.yaml ├── cluster.sample.yaml ├── makejinja.toml ├── nodes.sample.yaml ├── scripts ├── bootstrap-apps.sh └── lib │ └── common.sh └── templates ├── config ├── .sops.yaml.j2 ├── bootstrap │ ├── github-deploy-key.sops.yaml.j2 │ └── helmfile.yaml.j2 ├── kubernetes │ ├── apps │ │ ├── cert-manager │ │ │ ├── cert-manager │ │ │ │ ├── app │ │ │ │ │ ├── clusterissuer.yaml.j2 │ │ │ │ │ ├── helm │ │ │ │ │ │ ├── kustomizeconfig.yaml.j2 │ │ │ │ │ │ └── values.yaml.j2 │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ ├── kustomization.yaml.j2 │ │ │ │ │ └── secret.sops.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ └── kustomization.yaml.j2 │ │ ├── default │ │ │ ├── echo │ │ │ │ ├── app │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ └── kustomization.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ └── kustomization.yaml.j2 │ │ ├── flux-system │ │ │ ├── flux-instance │ │ │ │ ├── app │ │ │ │ │ ├── helm │ │ │ │ │ │ ├── kustomizeconfig.yaml.j2 │ │ │ │ │ │ └── values.yaml.j2 │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ ├── httproute.yaml.j2 │ │ │ │ │ ├── kustomization.yaml.j2 │ │ │ │ │ ├── receiver.yaml.j2 │ │ │ │ │ └── secret.sops.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ ├── flux-operator │ │ │ │ ├── app │ │ │ │ │ ├── helm │ │ │ │ │ │ ├── kustomizeconfig.yaml.j2 │ │ │ │ │ │ └── values.yaml.j2 │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ └── kustomization.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ └── kustomization.yaml.j2 │ │ ├── kube-system │ │ │ ├── cilium │ │ │ │ ├── app │ │ │ │ │ ├── helm │ │ │ │ │ │ ├── kustomizeconfig.yaml.j2 │ │ │ │ │ │ └── values.yaml.j2 │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ ├── kustomization.yaml.j2 │ │ │ │ │ └── networks.yaml.j2 │ │ │ │ ├── gateway │ │ │ │ │ ├── certificate.yaml.j2 │ │ │ │ │ ├── external.yaml.j2 │ │ │ │ │ ├── internal.yaml.j2 │ │ │ │ │ └── kustomization.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ ├── coredns │ │ │ │ ├── app │ │ │ │ │ ├── helm │ │ │ │ │ │ ├── kustomizeconfig.yaml.j2 │ │ │ │ │ │ └── values.yaml.j2 │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ └── kustomization.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ ├── kustomization.yaml.j2 │ │ │ ├── metrics-server │ │ │ │ ├── app │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ └── kustomization.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ ├── reloader │ │ │ │ ├── app │ │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ │ └── kustomization.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ │ └── spegel │ │ │ │ ├── app │ │ │ │ ├── helm │ │ │ │ │ ├── kustomizeconfig.yaml.j2 │ │ │ │ │ └── values.yaml.j2 │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ └── kustomization.yaml.j2 │ │ │ │ └── ks.yaml.j2 │ │ └── network │ │ │ ├── cloudflare-dns │ │ │ ├── app │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ ├── kustomization.yaml.j2 │ │ │ │ └── secret.sops.yaml.j2 │ │ │ └── ks.yaml.j2 │ │ │ ├── cloudflare-tunnel │ │ │ ├── app │ │ │ │ ├── dnsendpoint.yaml.j2 │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ ├── kustomization.yaml.j2 │ │ │ │ ├── resources │ │ │ │ │ └── config.yaml.j2 │ │ │ │ └── secret.sops.yaml.j2 │ │ │ └── ks.yaml.j2 │ │ │ ├── k8s-gateway │ │ │ ├── app │ │ │ │ ├── helmrelease.yaml.j2 │ │ │ │ └── kustomization.yaml.j2 │ │ │ └── ks.yaml.j2 │ │ │ └── kustomization.yaml.j2 │ ├── components │ │ └── common │ │ │ ├── kustomization.yaml.j2 │ │ │ ├── namespace.yaml.j2 │ │ │ ├── repos │ │ │ ├── app-template │ │ │ │ ├── kustomization.yaml.j2 │ │ │ │ └── ocirepository.yaml.j2 │ │ │ └── kustomization.yaml │ │ │ └── sops │ │ │ ├── cluster-secrets.sops.yaml.j2 │ │ │ ├── kustomization.yaml.j2 │ │ │ └── sops-age.sops.yaml.j2 │ └── flux │ │ ├── cluster │ │ └── ks.yaml.j2 │ │ └── meta │ │ ├── kustomization.yaml.j2 │ │ └── repos │ │ ├── external-dns.yaml.j2 │ │ └── kustomization.yaml.j2 └── talos │ ├── patches │ ├── README.md.j2 │ ├── controller │ │ ├── admission-controller-patch.yaml.j2 │ │ └── cluster.yaml.j2 │ └── global │ │ ├── machine-files.yaml.j2 │ │ ├── machine-kubelet.yaml.j2 │ │ ├── machine-network.yaml.j2 │ │ ├── machine-sysctls.yaml.j2 │ │ └── machine-time.yaml.j2 │ ├── talconfig.yaml.j2 │ └── talenv.yaml.j2 ├── overrides └── readme.partial.yaml.j2 └── scripts └── plugin.py /.editorconfig: -------------------------------------------------------------------------------- 1 | ; https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.cue] 14 | indent_style = tab 15 | indent_size = 4 16 | 17 | [*.md] 18 | indent_size = 4 19 | trim_trailing_whitespace = false 20 | 21 | [*.sh] 22 | indent_size = 4 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.env linguist-detectable linguist-language=SHELL 3 | *.json linguist-detectable linguist-language=JSON 4 | *.json5 linguist-detectable linguist-language=JSON5 5 | *.md linguist-detectable linguist-language=MARKDOWN 6 | *.sh linguist-detectable linguist-language=SHELL 7 | *.toml linguist-detectable linguist-language=TOML 8 | *.yml linguist-detectable linguist-language=YAML 9 | *.yaml linguist-detectable linguist-language=YAML 10 | *.yaml.j2 linguist-detectable linguist-language=YAML 11 | -------------------------------------------------------------------------------- /.github/labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | area/bootstrap: 3 | - changed-files: 4 | - any-glob-to-any-file: 5 | - bootstrap/**/* 6 | area/docs: 7 | - changed-files: 8 | - any-glob-to-any-file: 9 | - README.md 10 | area/github: 11 | - changed-files: 12 | - any-glob-to-any-file: 13 | - .github/**/* 14 | area/kubernetes: 15 | - changed-files: 16 | - any-glob-to-any-file: 17 | - kubernetes/**/* 18 | area/mise: 19 | - changed-files: 20 | - any-glob-to-any-file: 21 | - .mise.toml 22 | area/renovate: 23 | - changed-files: 24 | - any-glob-to-any-file: 25 | - .renovate/**/* 26 | - .renovaterc.json5 27 | area/scripts: 28 | - changed-files: 29 | - any-glob-to-any-file: 30 | - scripts/**/* 31 | area/talos: 32 | - changed-files: 33 | - any-glob-to-any-file: 34 | - talos/**/* 35 | area/taskfile: 36 | - changed-files: 37 | - any-glob-to-any-file: 38 | - .taskfiles/**/* 39 | - Taskfile.yaml 40 | area/templates: 41 | - changed-files: 42 | - any-glob-to-any-file: 43 | - templates/**/* 44 | -------------------------------------------------------------------------------- /.github/labels.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Areas 3 | - name: area/bootstrap 4 | color: "0e8a16" 5 | - name: area/docs 6 | color: "0e8a16" 7 | - name: area/github 8 | color: "0e8a16" 9 | - name: area/kubernetes 10 | color: "0e8a16" 11 | - name: area/mise 12 | color: "0e8a16" 13 | - name: area/renovate 14 | color: "0e8a16" 15 | - name: area/scripts 16 | color: "0e8a16" 17 | - name: area/talos 18 | color: "0e8a16" 19 | - name: area/templates 20 | color: "0e8a16" 21 | - name: area/taskfile 22 | color: "0e8a16" 23 | # Renovate Types 24 | - name: renovate/container 25 | color: "027fa0" 26 | - name: renovate/github-action 27 | color: "027fa0" 28 | - name: renovate/grafana-dashboard 29 | color: "027fa0" 30 | - name: renovate/github-release 31 | color: "027fa0" 32 | - name: renovate/helm 33 | color: "027fa0" 34 | # Semantic Types 35 | - name: type/digest 36 | color: "ffeC19" 37 | - name: type/patch 38 | color: "ffeC19" 39 | - name: type/minor 40 | color: "ff9800" 41 | - name: type/major 42 | color: "f6412d" 43 | # Uncategorized 44 | - name: community 45 | color: "370fb2" 46 | - name: hold 47 | color: "ee0701" 48 | -------------------------------------------------------------------------------- /.github/release.yaml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | authors: 4 | - github-actions 5 | - renovate 6 | -------------------------------------------------------------------------------- /.github/tests/nodes.yaml: -------------------------------------------------------------------------------- 1 | nodes: 2 | - name: k8s-0 3 | address: 10.10.10.100 4 | controller: true 5 | disk: /dev/sdfake 6 | mac_addr: 00:00:00:00:00:00 7 | schematic_id: "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba" 8 | - name: k8s-1 9 | address: 10.10.10.101 10 | controller: false 11 | disk: /dev/sdfake 12 | mac_addr: 00:00:00:00:00:01 13 | schematic_id: "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba" 14 | mtu: 1500 15 | secureboot: true 16 | encrypt_disk: true 17 | -------------------------------------------------------------------------------- /.github/tests/private.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | node_cidr: "10.10.10.0/24" 3 | # node_default_gateway: "" 4 | # node_vlan_tag: 5 | # cluster_pod_cidr: "" 6 | # cluster_svc_cidr: "" 7 | # node_dns_servers: [] 8 | # node_ntp_servers: [] 9 | cluster_api_addr: "10.10.10.254" 10 | # cluster_api_tls_sans: [] 11 | cluster_gateway_addr: "10.10.10.252" 12 | cluster_dns_gateway_addr: "10.10.10.253" 13 | repository_name: "onedr0p/cluster-template" 14 | # repository_branch: "" 15 | repository_visibility: "private" 16 | cloudflare_domain: "example.com" 17 | cloudflare_token: "fake" 18 | cloudflare_gateway_addr: "10.10.10.251" 19 | # cilium_bgp_router_addr: "" 20 | # cilium_bgp_router_asn: "" 21 | # cilium_bgp_node_asn: "" 22 | # cilium_loadbalancer_mode: "" 23 | -------------------------------------------------------------------------------- /.github/tests/public.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | node_cidr: "10.10.10.0/24" 3 | node_default_gateway: "10.10.10.1" 4 | node_vlan_tag: "100" 5 | cluster_pod_cidr: "10.42.0.0/16" 6 | cluster_svc_cidr: "10.43.0.0/16" 7 | node_dns_servers: ["1.1.1.1"] 8 | node_ntp_servers: ["162.159.200.123"] 9 | cluster_api_addr: "10.10.10.254" 10 | cluster_api_tls_sans: ["example.com"] 11 | cluster_gateway_addr: "10.10.10.252" 12 | cluster_dns_gateway_addr: "10.10.10.253" 13 | repository_name: "onedr0p/cluster-template" 14 | repository_branch: "main" 15 | repository_visibility: "public" 16 | cloudflare_domain: "example.com" 17 | cloudflare_token: "fake" 18 | cloudflare_gateway_addr: "10.10.10.251" 19 | cilium_loadbalancer_mode: "dsr" 20 | cilium_bgp_router_addr: "10.10.1.1" 21 | cilium_bgp_router_asn: "64513" 22 | cilium_bgp_node_asn: "64514" 23 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: "e2e" 4 | 5 | on: 6 | workflow_dispatch: 7 | pull_request: 8 | branches: ["main"] 9 | paths-ignore: 10 | - kubernetes/** 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.event.number || github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | configure: 18 | if: ${{ github.repository == 'onedr0p/cluster-template' }} 19 | name: configure 20 | runs-on: ubuntu-latest 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config-files: 25 | - public 26 | - private 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 30 | 31 | - name: Setup mise 32 | uses: jdx/mise-action@13abe502c30c1559a5c37dff303831bab82c9402 # v2.2.3 33 | env: 34 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 35 | with: 36 | cache: false 37 | 38 | - name: Run init task 39 | run: task init 40 | 41 | - name: Prepare files 42 | run: | 43 | cp ./.github/tests/${{ matrix.config-files }}.yaml cluster.yaml 44 | cp ./.github/tests/nodes.yaml nodes.yaml 45 | echo '{"AccountTag":"fake","TunnelSecret":"fake","TunnelID":"fake"}' > cloudflare-tunnel.json 46 | touch kubeconfig 47 | 48 | - name: Run configure task 49 | run: task configure --yes 50 | 51 | - name: Run generate talconfig task 52 | run: | 53 | FILENAME=talos/talsecret.sops.yaml 54 | talhelper gensecret | sops --filename-override $FILENAME --encrypt /dev/stdin > $FILENAME 55 | task talos:generate-config 56 | 57 | - name: Run flux-local test 58 | uses: docker://ghcr.io/allenporter/flux-local:v7.5.4@sha256:72dbdeabca1eb4d1a053c78dfa0d1d8e2a4c4aee2c8d3938db1a382d22b0a6f9 59 | with: 60 | args: test --enable-helm --all-namespaces --path /github/workspace/kubernetes/flux/cluster -v 61 | 62 | - name: Dry run bootstrap talos task 63 | run: task bootstrap:talos --dry 64 | 65 | - name: Dry run bootstrap apps task 66 | run: task bootstrap:apps --dry 67 | 68 | - name: Run reset task 69 | run: task template:reset --yes 70 | 71 | - name: Run cleanup task 72 | run: task template:tidy --yes 73 | -------------------------------------------------------------------------------- /.github/workflows/flux-local.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: "Flux Local" 4 | 5 | on: 6 | pull_request: 7 | branches: ["main"] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | pre-job: 15 | name: Flux Local Pre-Job 16 | runs-on: ubuntu-latest 17 | outputs: 18 | any_changed: ${{ steps.changed-files.outputs.any_changed }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | 23 | - name: Get Changed Files 24 | id: changed-files 25 | uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 26 | with: 27 | files: kubernetes/** 28 | 29 | test: 30 | name: Flux Local Test 31 | needs: pre-job 32 | runs-on: ubuntu-latest 33 | if: ${{ needs.pre-job.outputs.any_changed == 'true' }} 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 37 | 38 | - name: Run flux-local test 39 | uses: docker://ghcr.io/allenporter/flux-local:v7.5.4 40 | with: 41 | args: test --enable-helm --all-namespaces --path /github/workspace/kubernetes/flux/cluster -v 42 | 43 | diff: 44 | name: Flux Local Diff 45 | needs: pre-job 46 | runs-on: ubuntu-latest 47 | permissions: 48 | contents: read 49 | pull-requests: write 50 | strategy: 51 | matrix: 52 | resources: ["helmrelease", "kustomization"] 53 | max-parallel: 4 54 | fail-fast: false 55 | if: ${{ needs.pre-job.outputs.any_changed == 'true' }} 56 | steps: 57 | - name: Checkout Pull Request Branch 58 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 59 | with: 60 | path: pull 61 | 62 | - name: Checkout Default Branch 63 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 64 | with: 65 | ref: "${{ github.event.repository.default_branch }}" 66 | path: default 67 | 68 | - name: Run flux-local diff 69 | uses: docker://ghcr.io/allenporter/flux-local:v7.5.4 70 | with: 71 | args: >- 72 | diff ${{ matrix.resources }} 73 | --unified 6 74 | --path /github/workspace/pull/kubernetes/flux/cluster 75 | --path-orig /github/workspace/default/kubernetes/flux/cluster 76 | --strip-attrs "helm.sh/chart,checksum/config,app.kubernetes.io/version,chart" 77 | --limit-bytes 10000 78 | --all-namespaces 79 | --sources "flux-system" 80 | --output-file diff.patch 81 | 82 | - name: Generate Diff 83 | id: diff 84 | run: | 85 | cat diff.patch; 86 | { 87 | echo 'diff<> "$GITHUB_OUTPUT"; 91 | { 92 | echo "### Diff" 93 | echo '```diff' 94 | cat diff.patch 95 | echo '```' 96 | } >> "$GITHUB_STEP_SUMMARY" 97 | 98 | - name: Add Comment 99 | if: ${{ steps.diff.outputs.diff != '' }} 100 | continue-on-error: true 101 | uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 102 | with: 103 | message-id: "${{ github.event.pull_request.number }}/kubernetes/${{ matrix.resources }}" 104 | message-failure: Diff was not successful 105 | message: | 106 | ```diff 107 | ${{ steps.diff.outputs.diff }} 108 | ``` 109 | 110 | flux-local-status: 111 | name: Flux Local Success 112 | needs: ["test", "diff"] 113 | runs-on: ubuntu-latest 114 | if: ${{ always() }} 115 | steps: 116 | - name: Any jobs failed? 117 | if: ${{ contains(needs.*.result, 'failure') }} 118 | run: exit 1 119 | 120 | - name: All jobs passed or skipped? 121 | if: ${{ !(contains(needs.*.result, 'failure')) }} 122 | run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}" 123 | -------------------------------------------------------------------------------- /.github/workflows/label-sync.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: "Label Sync" 4 | 5 | on: 6 | workflow_dispatch: 7 | push: 8 | branches: ["main"] 9 | paths: [".github/labels.yaml"] 10 | 11 | jobs: 12 | label-sync: 13 | name: Label Sync 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | 19 | - name: Sync Labels 20 | uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3 21 | with: 22 | config-file: .github/labels.yaml 23 | delete-other-labels: true 24 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: "Labeler" 4 | 5 | on: 6 | workflow_dispatch: 7 | pull_request_target: 8 | branches: ["main"] 9 | 10 | jobs: 11 | labeler: 12 | name: Labeler 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | pull-requests: write 17 | steps: 18 | - name: Labeler 19 | uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 20 | with: 21 | configuration-path: .github/labeler.yaml 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 3 | name: "Release" 4 | 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 0 1 * *" # 1st of every month at midnight 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Get Previous Release Tag and Determine Next Tag 16 | id: determine-next-tag 17 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 18 | with: 19 | github-token: "${{ secrets.GITHUB_TOKEN }}" 20 | result-encoding: string 21 | script: | 22 | const { data: releases } = await github.rest.repos.listReleases({ 23 | owner: context.repo.owner, 24 | repo: context.repo.repo, 25 | per_page: 1, 26 | }); 27 | 28 | let previousTag = "0.0.0"; // Default if no previous release exists 29 | if (releases.length > 0) { 30 | previousTag = releases[0].tag_name; 31 | } 32 | 33 | const [previousMajor, previousMinor, previousPatch] = previousTag.split('.').map(Number); 34 | const currentYear = new Date().getFullYear(); 35 | const currentMonth = new Date().getMonth() + 1; // Months are 0-indexed in JavaScript 36 | 37 | const nextMajorMinor = `${currentYear}.${currentMonth}`; 38 | let nextPatch; 39 | 40 | if (`${previousMajor}.${previousMinor}` === nextMajorMinor) { 41 | console.log("Month release already exists for the year. Incrementing patch number by 1."); 42 | nextPatch = previousPatch + 1; 43 | } else { 44 | console.log("Month release does not exist for the year. Starting with patch number 0."); 45 | nextPatch = 0; 46 | } 47 | 48 | return `${nextMajorMinor}.${nextPatch}`; 49 | 50 | - name: Create Release 51 | uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 52 | with: 53 | generateReleaseNotes: true 54 | tag: "${{ steps.determine-next-tag.outputs.result }}" 55 | token: "${{ secrets.GITHUB_TOKEN }}" 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Secrets 2 | *.pub 3 | *.key 4 | *.decrypted~*.yaml 5 | /age.key 6 | /cloudflare-tunnel.json 7 | /github-deploy.key 8 | /github-deploy.key.pub 9 | /github-push-token.txt 10 | # Template config files 11 | /cluster.yaml 12 | /nodes.yaml 13 | # Kubernetes 14 | kubeconfig 15 | talosconfig 16 | # Misc. 17 | .private/ 18 | .task/ 19 | .venv/ 20 | .DS_Store 21 | Thumbs.db 22 | -------------------------------------------------------------------------------- /.mise.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | _.python.venv = { path = "{{config_root}}/.venv", create = true } 3 | KUBECONFIG = "{{config_root}}/kubeconfig" 4 | SOPS_AGE_KEY_FILE = "{{config_root}}/age.key" 5 | TALOSCONFIG = "{{config_root}}/talos/clusterconfig/talosconfig" 6 | 7 | [tools] 8 | "python" = "3.13" 9 | "pipx:makejinja" = "2.7.2" 10 | "aqua:budimanjojo/talhelper" = "3.0.28" 11 | "aqua:cilium/cilium-cli" = "0.18.3" 12 | "aqua:cli/cli" = "2.74.0" 13 | "aqua:cloudflare/cloudflared" = "2025.5.0" 14 | "aqua:cue-lang/cue" = "0.13.0" 15 | "aqua:FiloSottile/age" = "1.2.1" 16 | "aqua:fluxcd/flux2" = "2.6.0" 17 | "aqua:getsops/sops" = "3.10.2" 18 | "aqua:go-task/task" = "3.43.3" 19 | "aqua:helm/helm" = "3.18.1" 20 | "aqua:helmfile/helmfile" = "1.1.1" 21 | "aqua:jqlang/jq" = "1.7.1" 22 | "aqua:kubernetes-sigs/kustomize" = "5.6.0" 23 | "aqua:kubernetes/kubectl" = "1.33.1" 24 | "aqua:mikefarah/yq" = "4.45.4" 25 | "aqua:siderolabs/talos" = "1.10.3" 26 | "aqua:yannh/kubeconform" = "0.7.0" 27 | -------------------------------------------------------------------------------- /.renovaterc.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: "https://docs.renovatebot.com/renovate-schema.json", 3 | extends: [ 4 | "config:recommended", 5 | "docker:enableMajor", 6 | "helpers:pinGitHubActionDigests", 7 | ":automergeBranch", 8 | ":dependencyDashboard", 9 | ":disableRateLimiting", 10 | ":semanticCommits", 11 | ], 12 | dependencyDashboard: true, 13 | dependencyDashboardTitle: "Renovate Dashboard 🤖", 14 | schedule: ["every weekend"], 15 | ignorePaths: ["**/*.sops.*"], 16 | flux: { 17 | managerFilePatterns: ["/(^|/)kubernetes/.+\\.ya?ml(?:\\.j2)?$/"], 18 | }, 19 | "helm-values": { 20 | managerFilePatterns: ["/(^|/)kubernetes/.+\\.ya?ml(?:\\.j2)?$/"], 21 | }, 22 | helmfile: { 23 | managerFilePatterns: ["/(^|/)helmfile\\.ya?ml(?:\\.j2)?$/"], 24 | }, 25 | kubernetes: { 26 | managerFilePatterns: ["/(^|/)kubernetes/.+\\.ya?ml(?:\\.j2)?$/"], 27 | }, 28 | kustomize: { 29 | managerFilePatterns: ["/^kustomization\\.ya?ml(?:\\.j2)?$/"], 30 | }, 31 | packageRules: [ 32 | { 33 | description: "Cert-Manager Group", 34 | groupName: "Cert-Manager", 35 | matchDatasources: ["docker"], 36 | matchPackageNames: ["/cert-manager/"], 37 | group: { 38 | commitMessageTopic: "{{{groupName}}} group", 39 | }, 40 | }, 41 | { 42 | description: "CoreDNS Group", 43 | groupName: "CoreDNS", 44 | matchDatasources: ["docker"], 45 | matchPackageNames: ["/coredns/"], 46 | group: { 47 | commitMessageTopic: "{{{groupName}}} group", 48 | }, 49 | }, 50 | { 51 | description: "Flux Operator Group", 52 | groupName: "Flux Operator", 53 | matchPackageNames: ["/flux-operator/", "/flux-instance/"], 54 | matchDatasources: ["docker"], 55 | group: { 56 | commitMessageTopic: "{{{groupName}}} group", 57 | }, 58 | }, 59 | { 60 | description: "Spegel Group", 61 | groupName: "Spegel", 62 | matchDatasources: ["docker"], 63 | matchPackageNames: ["/spegel/"], 64 | group: { 65 | commitMessageTopic: "{{{groupName}}} group", 66 | }, 67 | }, 68 | { 69 | description: "Auto-merge GitHub Actions", 70 | matchManagers: ["github-actions"], 71 | automerge: true, 72 | automergeType: "branch", 73 | matchUpdateTypes: ["minor", "patch", "digest"], 74 | minimumReleaseAge: "3 days", 75 | ignoreTests: true, 76 | }, 77 | { 78 | description: "Auto-merge Mise Tools", 79 | matchManagers: ["mise"], 80 | automerge: true, 81 | automergeType: "branch", 82 | matchUpdateTypes: ["minor", "patch"], 83 | ignoreTests: true, 84 | }, 85 | { 86 | matchUpdateTypes: ["major"], 87 | semanticCommitType: "feat", 88 | commitMessagePrefix: "{{semanticCommitType}}({{semanticCommitScope}})!:", 89 | commitMessageExtra: "( {{currentVersion}} → {{newVersion}} )", 90 | }, 91 | { 92 | matchUpdateTypes: ["minor"], 93 | semanticCommitType: "feat", 94 | commitMessageExtra: "( {{currentVersion}} → {{newVersion}} )", 95 | }, 96 | { 97 | matchUpdateTypes: ["patch"], 98 | semanticCommitType: "fix", 99 | commitMessageExtra: "( {{currentVersion}} → {{newVersion}} )", 100 | }, 101 | { 102 | matchUpdateTypes: ["digest"], 103 | semanticCommitType: "chore", 104 | commitMessageExtra: "( {{currentDigestShort}} → {{newDigestShort}} )", 105 | }, 106 | { 107 | matchDatasources: ["docker"], 108 | semanticCommitScope: "container", 109 | commitMessageTopic: "image {{depName}}", 110 | }, 111 | { 112 | matchDatasources: ["helm"], 113 | semanticCommitScope: "helm", 114 | commitMessageTopic: "chart {{depName}}", 115 | }, 116 | { 117 | matchManagers: ["github-actions"], 118 | semanticCommitType: "ci", 119 | semanticCommitScope: "github-action", 120 | commitMessageTopic: "action {{depName}}", 121 | }, 122 | { 123 | matchDatasources: ["github-releases"], 124 | semanticCommitScope: "github-release", 125 | commitMessageTopic: "release {{depName}}", 126 | }, 127 | { 128 | matchManagers: ["mise"], 129 | semanticCommitScope: "mise", 130 | commitMessageTopic: "tool {{depName}}", 131 | }, 132 | { 133 | matchUpdateTypes: ["major"], 134 | labels: ["type/major"], 135 | }, 136 | { 137 | matchUpdateTypes: ["minor"], 138 | labels: ["type/minor"], 139 | }, 140 | { 141 | matchUpdateTypes: ["patch"], 142 | labels: ["type/patch"], 143 | }, 144 | { 145 | matchDatasources: ["docker"], 146 | addLabels: ["renovate/container"], 147 | }, 148 | { 149 | matchDatasources: ["helm"], 150 | addLabels: ["renovate/helm"], 151 | }, 152 | { 153 | matchManagers: ["github-actions"], 154 | addLabels: ["renovate/github-action"], 155 | }, 156 | { 157 | matchDatasources: ["github-releases"], 158 | addLabels: ["renovate/github-release"], 159 | }, 160 | ], 161 | customManagers: [ 162 | { 163 | description: "Process annotated dependencies", 164 | customType: "regex", 165 | managerFilePatterns: [ 166 | "/(^|/).+\\.env(?:\\.j2)?$/", 167 | "/(^|/).+\\.sh(?:\\.j2)?$/", 168 | "/(^|/).+\\.ya?ml(?:\\.j2)?$/", 169 | ], 170 | matchStrings: [ 171 | // # renovate: datasource=github-releases depName=k3s-io/k3s 172 | // k3s_release_version: &version v1.29.0+k3s1 173 | // # renovate: datasource=helm depName=cilium repository=https://helm.cilium.io 174 | // version: 1.15.1 175 | // # renovate: datasource=docker depName=ghcr.io/siderolabs/kubelet 176 | // KUBERNETES_VERSION=v1.31.1 177 | "datasource=(?\\S+) depName=(?\\S+)( repository=(?\\S+))?\\n.+(:\\s|=)(&\\S+\\s)?(?\\S+)", 178 | // # renovate: datasource=docker depName=ghcr.io/prometheus-operator/prometheus-operator 179 | // https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v0.80.0/example/prometheus-operator-crd/monitoring.coreos.com_alertmanagerconfigs.yaml 180 | "datasource=(?\\S+) depName=(?\\S+)\\n.+/(?(v|\\d)[^/]+)", 181 | ], 182 | datasourceTemplate: "{{#if datasource}}{{{datasource}}}{{else}}github-releases{{/if}}", 183 | }, 184 | ], 185 | } 186 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | disable=SC1091 2 | disable=SC2155 3 | -------------------------------------------------------------------------------- /.taskfiles/bootstrap/Taskfile.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 3 | version: '3' 4 | 5 | tasks: 6 | 7 | talos: 8 | desc: Bootstrap the Talos cluster 9 | dir: '{{.TALOS_DIR}}' 10 | cmds: 11 | - '[ -f talsecret.sops.yaml ] || talhelper gensecret | sops --filename-override talos/talsecret.sops.yaml --encrypt /dev/stdin > talsecret.sops.yaml' 12 | - talhelper genconfig 13 | - talhelper gencommand apply --extra-flags="--insecure" | bash 14 | - until talhelper gencommand bootstrap | bash; do sleep 10; done 15 | - until talhelper gencommand kubeconfig --extra-flags="{{.ROOT_DIR}} --force" | bash; do sleep 10; done 16 | preconditions: 17 | - test -f {{.ROOT_DIR}}/.sops.yaml 18 | - test -f {{.SOPS_AGE_KEY_FILE}} 19 | - test -f {{.TALOS_DIR}}/talconfig.yaml 20 | - which talhelper talosctl sops 21 | 22 | apps: 23 | desc: Bootstrap apps into the Talos cluster 24 | cmd: bash {{.SCRIPTS_DIR}}/bootstrap-apps.sh 25 | preconditions: 26 | - msg: Unsupported bash version, run `brew install bash` to upgrade 27 | sh: '{{if eq OS "darwin"}}test -f /opt/homebrew/bin/bash || test -f /usr/local/bin/bash{{end}}' 28 | - test -f {{.KUBECONFIG}} 29 | - test -f {{.ROOT_DIR}}/.sops.yaml 30 | - test -f {{.SCRIPTS_DIR}}/bootstrap-apps.sh 31 | - test -f {{.SOPS_AGE_KEY_FILE}} 32 | -------------------------------------------------------------------------------- /.taskfiles/talos/Taskfile.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 3 | version: '3' 4 | 5 | tasks: 6 | 7 | generate-config: 8 | desc: Generate Talos configuration 9 | dir: '{{.TALOS_DIR}}' 10 | cmd: talhelper genconfig 11 | preconditions: 12 | - test -f {{.TALOS_DIR}}/talconfig.yaml 13 | - test -f {{.ROOT_DIR}}/.sops.yaml 14 | - test -f {{.SOPS_AGE_KEY_FILE}} 15 | - which talhelper 16 | 17 | apply-node: 18 | desc: Apply Talos config to a node [IP=required] 19 | dir: '{{.TALOS_DIR}}' 20 | cmd: talhelper gencommand apply --node {{.IP}} --extra-flags '--mode={{.MODE}}' | bash 21 | vars: 22 | MODE: '{{.MODE | default "auto"}}' 23 | requires: 24 | vars: [IP] 25 | preconditions: 26 | - talosctl --nodes {{.IP}} get machineconfig 27 | - talosctl config info 28 | - test -f {{.TALOSCONFIG}} 29 | - which talhelper talosctl yq 30 | 31 | upgrade-node: 32 | desc: Upgrade Talos on a single node [IP=required] 33 | dir: '{{.TALOS_DIR}}' 34 | cmd: talhelper gencommand upgrade --node {{.IP}} --extra-flags "--image='{{.TALOS_IMAGE}}:{{.TALOS_VERSION}}' --timeout=10m" | bash 35 | vars: 36 | TALOS_IMAGE: 37 | sh: yq '.nodes[] | select(.ipAddress == "{{.IP}}") | .talosImageURL' {{.TALOS_DIR}}/talconfig.yaml 38 | TALOS_VERSION: 39 | sh: yq '.talosVersion' {{.TALOS_DIR}}/talenv.yaml 40 | requires: 41 | vars: [IP] 42 | preconditions: 43 | - talosctl --nodes {{.IP}} get machineconfig 44 | - talosctl config info 45 | - test -f {{.TALOSCONFIG}} 46 | - which kubectl talhelper talosctl yq 47 | 48 | upgrade-k8s: 49 | desc: Upgrade Kubernetes 50 | dir: '{{.TALOS_DIR}}' 51 | cmd: talhelper gencommand upgrade-k8s --extra-flags "--to '{{.KUBERNETES_VERSION}}'" | bash 52 | vars: 53 | KUBERNETES_VERSION: 54 | sh: yq '.kubernetesVersion' {{.TALOS_DIR}}/talenv.yaml 55 | preconditions: 56 | - talosctl config info 57 | - test -f {{.TALOSCONFIG}} 58 | - which talhelper talosctl yq 59 | 60 | reset: 61 | desc: Resets nodes back to maintenance mode 62 | dir: '{{.TALOS_DIR}}' 63 | prompt: This will destroy your cluster and reset the nodes back to maintenance mode... continue? 64 | cmd: talhelper gencommand reset --extra-flags="--reboot {{- if eq .CLI_FORCE false }} --system-labels-to-wipe STATE --system-labels-to-wipe EPHEMERAL{{ end }} --graceful=false --wait=false" | bash 65 | preconditions: 66 | - which talhelper 67 | -------------------------------------------------------------------------------- /.taskfiles/template/Taskfile.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 3 | version: '3' 4 | 5 | vars: 6 | MAKEJINJA_CONFIG_FILE: '{{.ROOT_DIR}}/makejinja.toml' 7 | TEMPLATE_DIR: '{{.ROOT_DIR}}/templates' 8 | TEMPLATE_RESOURCES_DIR: '{{.ROOT_DIR}}/.taskfiles/template/resources' 9 | TEMPLATE_CONFIG_FILE: '{{.ROOT_DIR}}/cluster.yaml' 10 | TEMPLATE_NODE_CONFIG_FILE: '{{.ROOT_DIR}}/nodes.yaml' 11 | 12 | tasks: 13 | 14 | :init: 15 | desc: Initialize configuration files 16 | cmds: 17 | - task: generate-template-config 18 | - task: generate-age-key 19 | - task: generate-deploy-key 20 | - task: generate-push-token 21 | 22 | generate-template-config: 23 | internal: true 24 | cmds: 25 | - mv {{.TEMPLATE_CONFIG_FILE | replace ".yaml" ".sample.yaml"}} {{.TEMPLATE_CONFIG_FILE}} 26 | - mv {{.TEMPLATE_NODE_CONFIG_FILE | replace ".yaml" ".sample.yaml"}} {{.TEMPLATE_NODE_CONFIG_FILE}} 27 | status: 28 | - test -f {{.TEMPLATE_CONFIG_FILE}} 29 | - test -f {{.TEMPLATE_NODE_CONFIG_FILE}} 30 | 31 | generate-age-key: 32 | internal: true 33 | cmd: age-keygen --output {{.SOPS_AGE_KEY_FILE}} 34 | status: 35 | - test -f {{.SOPS_AGE_KEY_FILE}} 36 | preconditions: 37 | - which age-keygen 38 | 39 | generate-deploy-key: 40 | internal: true 41 | cmd: ssh-keygen -t ecdsa -b 521 -C "deploy-key" -f {{.ROOT_DIR}}/github-deploy.key -q -P "" 42 | status: 43 | - test -f {{.ROOT_DIR}}/github-deploy.key 44 | preconditions: 45 | - which ssh-keygen 46 | 47 | generate-push-token: 48 | internal: true 49 | cmd: python -c "import secrets; print(secrets.token_hex(16))" > {{.ROOT_DIR}}/github-push-token.txt 50 | status: 51 | - test -f {{.ROOT_DIR}}/github-push-token.txt 52 | 53 | :configure: 54 | desc: Render and validate configuration files 55 | prompt: Any conflicting files in the kubernetes directory will be overwritten... continue? 56 | cmds: 57 | - task: validate-schemas 58 | - task: render-configs 59 | - task: encrypt-secrets 60 | - task: validate-kubernetes-config 61 | - task: validate-talos-config 62 | preconditions: 63 | - msg: An existing Age key interferes with the age key in this repository, rename or delete ~/.config/sops/age/keys.txt 64 | sh: '! test -f ~/.config/sops/age/keys.txt' 65 | - msg: File cluster.yaml not found, did you run `task init`? 66 | sh: test -f {{.TEMPLATE_CONFIG_FILE}} 67 | - msg: File nodes.yaml not found, did you run `task init`? 68 | sh: test -f {{.TEMPLATE_NODE_CONFIG_FILE}} 69 | - msg: File cloudflare-tunnel.json not found, see the README for information on creating it. 70 | sh: test -f {{.ROOT_DIR}}/cloudflare-tunnel.json 71 | 72 | validate-schemas: 73 | internal: true 74 | cmds: 75 | - cue vet {{.TEMPLATE_CONFIG_FILE}} {{.TEMPLATE_RESOURCES_DIR}}/cluster.schema.cue 76 | - cue vet {{.TEMPLATE_NODE_CONFIG_FILE}} {{.TEMPLATE_RESOURCES_DIR}}/nodes.schema.cue 77 | preconditions: 78 | - test -f {{.TEMPLATE_RESOURCES_DIR}}/cluster.schema.cue 79 | - test -f {{.TEMPLATE_RESOURCES_DIR}}/nodes.schema.cue 80 | - which cue 81 | 82 | render-configs: 83 | internal: true 84 | cmd: makejinja 85 | env: 86 | PYTHONDONTWRITEBYTECODE: '1' 87 | preconditions: 88 | - test -f {{.TEMPLATE_DIR}}/scripts/plugin.py 89 | - test -f {{.MAKEJINJA_CONFIG_FILE}} 90 | - which makejinja 91 | 92 | encrypt-secrets: 93 | internal: true 94 | cmds: 95 | - for: { var: SECRET_FILES } 96 | cmd: | 97 | if sops filestatus "{{.ITEM}}" | jq --exit-status ".encrypted == false" &>/dev/null; then 98 | sops --encrypt --in-place "{{.ITEM}}" 99 | fi 100 | vars: 101 | SECRET_FILES: 102 | sh: find "{{.BOOTSTRAP_DIR}}" "{{.KUBERNETES_DIR}}" "{{.TALOS_DIR}}" -type f -name "*.sops.*" -print 103 | preconditions: 104 | - test -f {{.SOPS_AGE_KEY_FILE}} 105 | - test -f {{.ROOT_DIR}}/.sops.yaml 106 | - which jq sops 107 | 108 | validate-kubernetes-config: 109 | internal: true 110 | cmd: bash {{.TEMPLATE_RESOURCES_DIR}}/kubeconform.sh {{.KUBERNETES_DIR}} 111 | preconditions: 112 | - test -f {{.TEMPLATE_RESOURCES_DIR}}/kubeconform.sh 113 | - which kubeconform 114 | 115 | validate-talos-config: 116 | internal: true 117 | dir: '{{.TALOS_DIR}}' 118 | cmd: talhelper validate talconfig {{.TALOS_DIR}}/talconfig.yaml 119 | preconditions: 120 | - test -f {{.TALOS_DIR}}/talconfig.yaml 121 | - which talhelper 122 | 123 | debug: 124 | desc: Gather common resources in your cluster 125 | cmds: 126 | - for: 127 | matrix: 128 | RESOURCE: [certificates, certificaterequests, gitrepositories, helmrepositories, helmreleases, httproutes, kustomizations, nodes, pods] 129 | cmd: kubectl get --all-namespaces {{.ITEM.RESOURCE}} 130 | preconditions: 131 | - test -f {{.KUBECONFIG}} 132 | - which kubectl 133 | 134 | tidy: 135 | desc: Archive template related files and directories 136 | prompt: All files and directories related to the templating process will be archived... continue? 137 | cmds: 138 | - mkdir -p {{.TIDY_FOLDER}} 139 | - rm -rf {{.ROOT_DIR}}/.github/tests 140 | - rm -rf {{.ROOT_DIR}}/.github/workflows/e2e.yaml 141 | - rm -rf {{.ROOT_DIR}}/.github/workflows/mise.yaml 142 | - rm -rf {{.ROOT_DIR}}/.github/workflows/release.yaml 143 | - | 144 | {{.SED}} -i 's/(..\.j2)\?//g' {{.ROOT_DIR}}/.renovaterc.json5 145 | - mv {{.TEMPLATE_DIR}} {{.TIDY_FOLDER}}/templates 146 | - mv {{.MAKEJINJA_CONFIG_FILE}} {{.TIDY_FOLDER}}/makejinja.toml 147 | - mv {{.TEMPLATE_CONFIG_FILE}} {{.TIDY_FOLDER}}/cluster.yaml 148 | - mv {{.TEMPLATE_NODE_CONFIG_FILE}} {{.TIDY_FOLDER}}/nodes.yaml 149 | - | 150 | {{.SED}} -i '/template:/d' {{.ROOT_DIR}}/Taskfile.yaml 151 | - mv {{.ROOT_DIR}}/.taskfiles/template {{.TIDY_FOLDER}}/.taskfiles/ 152 | vars: 153 | TIDY_FOLDER: '{{.PRIVATE_DIR}}/{{now | unixEpoch}}' 154 | SED: 155 | sh: which gsed || which sed 156 | preconditions: 157 | - msg: Unsupported sed version, run `brew install gsed` to upgrade 158 | sh: '{{if eq OS "darwin"}}test -f /opt/homebrew/bin/gsed || test -f /usr/local/bin/gsed{{end}}' 159 | - test -d {{.ROOT_DIR}}/.taskfiles/template 160 | - test -d {{.TEMPLATE_DIR}} 161 | - test -f {{.MAKEJINJA_CONFIG_FILE}} 162 | - test -f {{.ROOT_DIR}}/.renovaterc.json5 163 | 164 | reset: 165 | desc: Remove templated files and directories 166 | prompt: Remove all templated files and directories... continue? 167 | cmds: 168 | - rm -rf {{.BOOTSTRAP_DIR}} 169 | - rm -rf {{.KUBERNETES_DIR}} 170 | - rm -rf {{.TALOS_DIR}} 171 | - rm -rf {{.ROOT_DIR}}/.sops.yaml 172 | -------------------------------------------------------------------------------- /.taskfiles/template/resources/cluster.schema.cue: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | #Config: { 8 | node_cidr: net.IPCIDR & !=cluster_pod_cidr & !=cluster_svc_cidr 9 | node_dns_servers?: [...net.IPv4] 10 | node_ntp_servers?: [...net.IPv4] 11 | node_default_gateway?: net.IPv4 & !="" 12 | node_vlan_tag?: string & !="" 13 | cluster_pod_cidr: *"10.42.0.0/16" | net.IPCIDR & !=node_cidr & !=cluster_svc_cidr 14 | cluster_svc_cidr: *"10.43.0.0/16" | net.IPCIDR & !=node_cidr & !=cluster_pod_cidr 15 | cluster_api_addr: net.IPv4 16 | cluster_api_tls_sans?: [...net.FQDN] 17 | cluster_gateway_addr: net.IPv4 & !=cluster_api_addr & !=cluster_dns_gateway_addr & !=cloudflare_gateway_addr 18 | cluster_dns_gateway_addr: net.IPv4 & !=cluster_api_addr & !=cluster_gateway_addr & !=cloudflare_gateway_addr 19 | repository_name: string 20 | repository_branch?: string & !="" 21 | repository_visibility?: *"public" | "private" 22 | cloudflare_domain: net.FQDN 23 | cloudflare_token: string 24 | cloudflare_gateway_addr: net.IPv4 & !=cluster_api_addr & !=cluster_gateway_addr & !=cluster_dns_gateway_addr 25 | cilium_bgp_router_addr?: net.IPv4 & !="" 26 | cilium_bgp_router_asn?: string & !="" 27 | cilium_bgp_node_asn?: string & !="" 28 | cilium_loadbalancer_mode?: *"dsr" | "snat" 29 | } 30 | 31 | #Config 32 | -------------------------------------------------------------------------------- /.taskfiles/template/resources/kubeconform.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | KUBERNETES_DIR=$1 6 | 7 | [[ -z "${KUBERNETES_DIR}" ]] && echo "Kubernetes location not specified" && exit 1 8 | 9 | kustomize_args=("--load-restrictor=LoadRestrictionsNone") 10 | kustomize_config="kustomization.yaml" 11 | kubeconform_args=( 12 | "-strict" 13 | "-ignore-missing-schemas" 14 | "-skip" 15 | "Gateway,HTTPRoute,Secret" 16 | "-schema-location" 17 | "default" 18 | "-schema-location" 19 | "https://kubernetes-schemas.pages.dev/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json" 20 | "-verbose" 21 | ) 22 | 23 | echo "=== Validating standalone manifests in ${KUBERNETES_DIR}/flux ===" 24 | find "${KUBERNETES_DIR}/flux" -maxdepth 1 -type f -name '*.yaml' -print0 | while IFS= read -r -d $'\0' file; 25 | do 26 | kubeconform "${kubeconform_args[@]}" "${file}" 27 | if [[ ${PIPESTATUS[0]} != 0 ]]; then 28 | exit 1 29 | fi 30 | done 31 | 32 | echo "=== Validating kustomizations in ${KUBERNETES_DIR}/flux ===" 33 | find "${KUBERNETES_DIR}/flux" -type f -name $kustomize_config -print0 | while IFS= read -r -d $'\0' file; 34 | do 35 | echo "=== Validating kustomizations in ${file/%$kustomize_config} ===" 36 | kustomize build "${file/%$kustomize_config}" "${kustomize_args[@]}" | kubeconform "${kubeconform_args[@]}" 37 | if [[ ${PIPESTATUS[0]} != 0 ]]; then 38 | exit 1 39 | fi 40 | done 41 | 42 | echo "=== Validating kustomizations in ${KUBERNETES_DIR}/apps ===" 43 | find "${KUBERNETES_DIR}/apps" -type f -name $kustomize_config -print0 | while IFS= read -r -d $'\0' file; 44 | do 45 | echo "=== Validating kustomizations in ${file/%$kustomize_config} ===" 46 | kustomize build "${file/%$kustomize_config}" "${kustomize_args[@]}" | kubeconform "${kubeconform_args[@]}" 47 | if [[ ${PIPESTATUS[0]} != 0 ]]; then 48 | exit 1 49 | fi 50 | done 51 | -------------------------------------------------------------------------------- /.taskfiles/template/resources/nodes.schema.cue: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "net" 5 | "list" 6 | ) 7 | 8 | #Config: { 9 | nodes: [...#Node] 10 | _nodes_check: { 11 | name: list.UniqueItems() & [for item in nodes {item.name}] 12 | address: list.UniqueItems() & [for item in nodes {item.address}] 13 | mac_addr: list.UniqueItems() & [for item in nodes {item.mac_addr}] 14 | } 15 | } 16 | 17 | #Node: { 18 | name: =~"^[a-z0-9][a-z0-9\\-]{0,61}[a-z0-9]$|^[a-z0-9]$" & !="global" & !="controller" & !="worker" 19 | address: net.IPv4 20 | controller: bool 21 | disk: string 22 | mac_addr: =~"^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$" 23 | schematic_id: =~"^[a-z0-9]{64}$" 24 | mtu?: >=1450 & <=9000 25 | secureboot?: bool 26 | encrypt_disk?: bool 27 | } 28 | 29 | #Config 30 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "blueglassblock.better-json5", 4 | "irongeek.vscode-env", 5 | "redhat.vscode-yaml", 6 | "signageos.signageos-vscode-sops" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.bracketPairColorization.enabled": true, 3 | "files.associations": { 4 | "**/*.json5": "json5" 5 | }, 6 | "files.trimTrailingWhitespace": true, 7 | "sops.defaults.ageKeyFile": "age.key", 8 | "vs-kubernetes": { 9 | "vs-kubernetes.kubeconfig": "./kubeconfig", 10 | "vs-kubernetes.knownKubeconfigs": [ 11 | "./kubeconfig" 12 | ] 13 | }, 14 | "yaml.schemaStore.enable": true, 15 | "yaml.schemas": { 16 | "kubernetes": "./kubernetes/**/*.yaml" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 onedr0p 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⛵ Cluster Template 2 | 3 | Welcome to my template designed for deploying a single Kubernetes cluster. Whether you're setting up a cluster at home on bare-metal or virtual machines (VMs), this project aims to simplify the process and make Kubernetes more accessible. This template is inspired by my personal [home-ops](https://github.com/onedr0p/home-ops) repository, providing a practical starting point for anyone interested in managing their own Kubernetes environment. 4 | 5 | At its core, this project leverages [makejinja](https://github.com/mirkolenz/makejinja), a powerful tool for rendering templates. By reading configuration files—such as [cluster.yaml](./cluster.sample.yaml) and [nodes.yaml](./nodes.sample.yaml)—Makejinja generates the necessary configurations to deploy a Kubernetes cluster with the following features: 6 | 7 | - Easy configuration through YAML files. 8 | - Compatibility with home setups, whether on physical hardware or VMs. 9 | - A modular and extensible approach to cluster deployment and management. 10 | 11 | With this approach, you'll gain a solid foundation to build and manage your Kubernetes cluster efficiently. 12 | 13 | ## ✨ Features 14 | 15 | A Kubernetes cluster deployed with [Talos Linux](https://github.com/siderolabs/talos) and an opinionated implementation of [Flux](https://github.com/fluxcd/flux2) using [GitHub](https://github.com/) as the Git provider, [sops](https://github.com/getsops/sops) to manage secrets and [cloudflared](https://github.com/cloudflare/cloudflared) to access applications external to your local network. 16 | 17 | - **Required:** Some knowledge of [Containers](https://opencontainers.org/), [YAML](https://noyaml.com/), [Git](https://git-scm.com/), and a **Cloudflare account** with a **domain**. 18 | - **Included components:** [flux](https://github.com/fluxcd/flux2), [cilium](https://github.com/cilium/cilium), [cert-manager](https://github.com/cert-manager/cert-manager), [spegel](https://github.com/spegel-org/spegel), [reloader](https://github.com/stakater/Reloader), [external-dns](https://github.com/kubernetes-sigs/external-dns) and [cloudflared](https://github.com/cloudflare/cloudflared). 19 | 20 | **Other features include:** 21 | 22 | - Dev env managed w/ [mise](https://mise.jdx.dev/) 23 | - Workflow automation w/ [GitHub Actions](https://github.com/features/actions) 24 | - Dependency automation w/ [Renovate](https://www.mend.io/renovate) 25 | - Flux `HelmRelease` and `Kustomization` diffs w/ [flux-local](https://github.com/allenporter/flux-local) 26 | 27 | Does this sound cool to you? If so, continue to read on! 👇 28 | 29 | ## 🚀 Let's Go! 30 | 31 | There are **5 stages** outlined below for completing this project, make sure you follow the stages in order. 32 | 33 | ### Stage 1: Machine Preparation 34 | 35 | > [!IMPORTANT] 36 | > If you have **3 or more nodes** it is recommended to make 3 of them controller nodes for a highly available control plane. This project configures **all nodes** to be able to run workloads. **Worker nodes** are therefore **optional**. 37 | > 38 | > **Minimum system requirements** 39 | > | Role | Cores | Memory | System Disk | 40 | > |---------|----------|---------------|---------------------------| 41 | > | Control/Worker | 4 | 16GB | 256GB SSD/NVMe | 42 | 43 | 1. Head over to the [Talos Linux Image Factory](https://factory.talos.dev) and follow the instructions. Be sure to only choose the **bare-minimum system extensions** as some might require additional configuration and prevent Talos from booting without it. You can always add system extensions after Talos is installed and working. 44 | 45 | 2. This will eventually lead you to download a Talos Linux ISO (or for SBCs a RAW) image. Make sure to note the **schematic ID** you will need this later on. 46 | 47 | 3. Flash the Talos ISO or RAW image to a USB drive and boot from it on your nodes. 48 | 49 | 4. Verify with `nmap` that your nodes are available on the network. (Replace `192.168.1.0/24` with the network your nodes are on.) 50 | 51 | ```sh 52 | nmap -Pn -n -p 50000 192.168.1.0/24 -vv | grep 'Discovered' 53 | ``` 54 | 55 | ### Stage 2: Local Workstation 56 | 57 | > [!TIP] 58 | > It is recommended to set the visibility of your repository to `Public` so you can easily request help if you get stuck. 59 | 60 | 1. Create a new repository by clicking the green `Use this template` button at the top of this page, then clone the new repo you just created and `cd` into it. Alternatively you can us the [GitHub CLI](https://cli.github.com/) ... 61 | 62 | ```sh 63 | export REPONAME="home-ops" 64 | gh repo create $REPONAME --template onedr0p/cluster-template --disable-wiki --public --clone && cd $REPONAME 65 | ``` 66 | 67 | 2. **Install** the [Mise CLI](https://mise.jdx.dev/getting-started.html#installing-mise-cli) on your workstation. 68 | 69 | 3. **Activate** Mise in your shell by following the [activation guide](https://mise.jdx.dev/getting-started.html#activate-mise). 70 | 71 | 4. Use `mise` to install the **required** CLI tools: 72 | 73 | ```sh 74 | mise trust 75 | pip install pipx 76 | mise install 77 | ``` 78 | 79 | 📍 _**Having trouble installing the tools?** Try unsetting the `GITHUB_TOKEN` env var and then run these commands again_ 80 | 81 | 📍 _**Having trouble compiling Python?** Try running `mise settings python.compile=0` and then run these commands again_ 82 | 83 | 5. Logout of GitHub Container Registry (GHCR) as this may cause authorization problems when using the public registry: 84 | 85 | ```sh 86 | docker logout ghcr.io 87 | helm registry logout ghcr.io 88 | ``` 89 | 90 | ### Stage 3: Cloudflare configuration 91 | 92 | > [!WARNING] 93 | > If any of the commands fail with `command not found` or `unknown command` it means `mise` is either not install or configured incorrectly. 94 | 95 | 1. Create a Cloudflare API token for use with cloudflared and external-dns by reviewing the official [documentation](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) and following the instructions below. 96 | 97 | - Click the blue `Use template` button for the `Edit zone DNS` template. 98 | - Name your token `kubernetes` 99 | - Under `Permissions`, click `+ Add More` and add permissions `Zone - DNS - Edit` and `Account - Cloudflare Tunnel - Read` 100 | - Limit the permissions to a specific account and/or zone resources and then click `Continue to Summary` and then `Create Token`. 101 | - **Save this token somewhere safe**, you will need it later on. 102 | 103 | 2. Create the Cloudflare Tunnel: 104 | 105 | ```sh 106 | cloudflared tunnel login 107 | cloudflared tunnel create --credentials-file cloudflare-tunnel.json kubernetes 108 | ``` 109 | 110 | ### Stage 4: Cluster configuration 111 | 112 | 1. Generate the config files from the sample files: 113 | 114 | ```sh 115 | task init 116 | ``` 117 | 118 | 2. Fill out `cluster.yaml` and `nodes.yaml` configuration files using the comments in those file as a guide. 119 | 120 | 3. Template out the kubernetes and talos configuration files, if any issues come up be sure to read the error and adjust your config files accordingly. 121 | 122 | ```sh 123 | task configure 124 | ``` 125 | 126 | 4. Push your changes to git: 127 | 128 | 📍 _**Verify** all the `./kubernetes/**/*.sops.*` files are **encrypted** with SOPS_ 129 | 130 | ```sh 131 | git add -A 132 | git commit -m "chore: initial commit :rocket:" 133 | git push 134 | ``` 135 | 136 | > [!TIP] 137 | > Using a **private repository**? Make sure to paste the public key from `github-deploy.key.pub` into the deploy keys section of your GitHub repository settings. This will make sure Flux has read/write access to your repository. 138 | 139 | ### Stage 5: Bootstrap Talos, Kubernetes, and Flux 140 | 141 | > [!WARNING] 142 | > It might take a while for the cluster to be setup (10+ minutes is normal). During which time you will see a variety of error messages like: "couldn't get current server API group list," "error: no matching resources found", etc. 'Ready' will remain "False" as no CNI is deployed yet. **This is a normal.** If this step gets interrupted, e.g. by pressing Ctrl + C, you likely will need to [reset the cluster](#-reset) before trying again 143 | 144 | 1. Install Talos: 145 | 146 | ```sh 147 | task bootstrap:talos 148 | ``` 149 | 150 | 2. Push your changes to git: 151 | 152 | ```sh 153 | git add -A 154 | git commit -m "chore: add talhelper encrypted secret :lock:" 155 | git push 156 | ``` 157 | 158 | 3. Install cilium, coredns, spegel, flux and sync the cluster to the repository state: 159 | 160 | ```sh 161 | task bootstrap:apps 162 | ``` 163 | 164 | 4. Watch the rollout of your cluster happen: 165 | 166 | ```sh 167 | kubectl get pods --all-namespaces --watch 168 | ``` 169 | 170 | ## 📣 Post installation 171 | 172 | ### ✅ Verifications 173 | 174 | 1. Check the status of Cilium: 175 | 176 | ```sh 177 | cilium status 178 | ``` 179 | 180 | 2. Check the status of Flux and if the Flux resources are up-to-date and in a ready state: 181 | 182 | 📍 _Run `task reconcile` to force Flux to sync your Git repository state_ 183 | 184 | ```sh 185 | flux check 186 | flux get sources git flux-system 187 | flux get ks -A 188 | flux get hr -A 189 | ``` 190 | 191 | 3. Check TCP connectivity to both the internal and external gateways: 192 | 193 | 📍 _`${cluster_gateway_addr}` and `${cloudflare_gateway_addr}` are only placeholders, replace them with your actual values_ 194 | 195 | ```sh 196 | nmap -Pn -n -p 443 ${cluster_gateway_addr} ${cloudflare_gateway_addr} -vv 197 | ``` 198 | 199 | 4. Check you can resolve DNS for `echo`, this should resolve to `${cluster_gateway_addr}`: 200 | 201 | 📍 _`${cluster_dns_gateway_addr}` and `${cloudflare_domain}` are only placeholders, replace them with your actual values_ 202 | 203 | ```sh 204 | dig @${cluster_dns_gateway_addr} echo.${cloudflare_domain} 205 | ``` 206 | 207 | 5. Check the status of your wildcard `Certificate`: 208 | 209 | ```sh 210 | kubectl -n kube-system describe certificates 211 | ``` 212 | 213 | ### 🌐 Public DNS 214 | 215 | > [!TIP] 216 | > Use the `external` gateway on `HTTPRoutes` to make applications public to the internet. 217 | 218 | The `external-dns` application created in the `network` namespace will handle creating public DNS records. By default, `echo` and the `flux-webhook` are the only subdomains reachable from the public internet. In order to make additional applications public you must **set the correct gateway** like in the HelmRelease for `echo`. 219 | 220 | ### 🏠 Home DNS 221 | 222 | > [!TIP] 223 | > Use the `internal` gateway on `HTTPRoutes` to make applications private to your network. If you're having trouble with internal DNS resolution check out [this](https://github.com/onedr0p/cluster-template/discussions/719) GitHub discussion. 224 | 225 | `k8s_gateway` will provide DNS resolution to external Kubernetes resources (i.e. points of entry to the cluster) from any device that uses your home DNS server. For this to work, your home DNS server must be configured to forward DNS queries for `${cloudflare_domain}` to `${cluster_dns_gateway_addr}` instead of the upstream DNS server(s) it normally uses. This is a form of **split DNS** (aka split-horizon DNS / conditional forwarding). 226 | 227 | _... Nothing working? That is expected, this is DNS after all!_ 228 | 229 | ### 🪝 Github Webhook 230 | 231 | By default Flux will periodically check your git repository for changes. In-order to have Flux reconcile on `git push` you must configure Github to send `push` events to Flux. 232 | 233 | 1. Obtain the webhook path: 234 | 235 | 📍 _Hook id and path should look like `/hook/12ebd1e363c641dc3c2e430ecf3cee2b3c7a5ac9e1234506f6f5f3ce1230e123`_ 236 | 237 | ```sh 238 | kubectl -n flux-system get receiver github-webhook --output=jsonpath='{.status.webhookPath}' 239 | ``` 240 | 241 | 2. Piece together the full URL with the webhook path appended: 242 | 243 | ```text 244 | https://flux-webhook.${cloudflare_domain}/hook/12ebd1e363c641dc3c2e430ecf3cee2b3c7a5ac9e1234506f6f5f3ce1230e123 245 | ``` 246 | 247 | 3. Navigate to the settings of your repository on Github, under "Settings/Webhooks" press the "Add webhook" button. Fill in the webhook URL and your token from `github-push-token.txt`, Content type: `application/json`, Events: Choose Just the push event, and save. 248 | 249 | ## 💥 Reset 250 | 251 | > [!WARNING] 252 | > **Resetting** the cluster **multiple times in a short period of time** could lead to being **rate limited by DockerHub or Let's Encrypt**. 253 | 254 | There might be a situation where you want to destroy your Kubernetes cluster. The following command will reset your nodes back to maintenance mode. 255 | 256 | ```sh 257 | task talos:reset 258 | ``` 259 | 260 | ## 🛠️ Talos and Kubernetes Maintenance 261 | 262 | ### ⚙️ Updating Talos node configuration 263 | 264 | > [!TIP] 265 | > Ensure you have updated `talconfig.yaml` and any patches with your updated configuration. In some cases you **not only need to apply the configuration but also upgrade talos** to apply new configuration. 266 | 267 | ```sh 268 | # (Re)generate the Talos config 269 | task talos:generate-config 270 | # Apply the config to the node 271 | task talos:apply-node IP=? MODE=? 272 | # e.g. task talos:apply-node IP=10.10.10.10 MODE=auto 273 | ``` 274 | 275 | ### ⬆️ Updating Talos and Kubernetes versions 276 | 277 | > [!TIP] 278 | > Ensure the `talosVersion` and `kubernetesVersion` in `talenv.yaml` are up-to-date with the version you wish to upgrade to. 279 | 280 | ```sh 281 | # Upgrade node to a newer Talos version 282 | task talos:upgrade-node IP=? 283 | # e.g. task talos:upgrade-node IP=10.10.10.10 284 | ``` 285 | 286 | ```sh 287 | # Upgrade cluster to a newer Kubernetes version 288 | task talos:upgrade-k8s 289 | # e.g. task talos:upgrade-k8s 290 | ``` 291 | 292 | ## 🤖 Renovate 293 | 294 | [Renovate](https://www.mend.io/renovate) is a tool that automates dependency management. It is designed to scan your repository around the clock and open PRs for out-of-date dependencies it finds. Common dependencies it can discover are Helm charts, container images, GitHub Actions and more! In most cases merging a PR will cause Flux to apply the update to your cluster. 295 | 296 | To enable Renovate, click the 'Configure' button over at their [Github app page](https://github.com/apps/renovate) and select your repository. Renovate creates a "Dependency Dashboard" as an issue in your repository, giving an overview of the status of all updates. The dashboard has interactive checkboxes that let you do things like advance scheduling or reattempt update PRs you closed without merging. 297 | 298 | The base Renovate configuration in your repository can be viewed at [.renovaterc.json5](.renovaterc.json5). By default it is scheduled to be active with PRs every weekend, but you can [change the schedule to anything you want](https://docs.renovatebot.com/presets-schedule), or remove it if you want Renovate to open PRs immediately. 299 | 300 | ## 🐛 Debugging 301 | 302 | Below is a general guide on trying to debug an issue with an resource or application. For example, if a workload/resource is not showing up or a pod has started but in a `CrashLoopBackOff` or `Pending` state. These steps do not include a way to fix the problem as the problem could be one of many different things. 303 | 304 | 1. Check if the Flux resources are up-to-date and in a ready state: 305 | 306 | 📍 _Run `task reconcile` to force Flux to sync your Git repository state_ 307 | 308 | ```sh 309 | flux get sources git -A 310 | flux get ks -A 311 | flux get hr -A 312 | ``` 313 | 314 | 2. Do you see the pod of the workload you are debugging: 315 | 316 | ```sh 317 | kubectl -n get pods -o wide 318 | ``` 319 | 320 | 3. Check the logs of the pod if its there: 321 | 322 | ```sh 323 | kubectl -n logs -f 324 | ``` 325 | 326 | 4. If a resource exists try to describe it to see what problems it might have: 327 | 328 | ```sh 329 | kubectl -n describe 330 | ``` 331 | 332 | 5. Check the namespace events: 333 | 334 | ```sh 335 | kubectl -n get events --sort-by='.metadata.creationTimestamp' 336 | ``` 337 | 338 | Resolving problems that you have could take some tweaking of your YAML manifests in order to get things working, other times it could be a external factor like permissions on a NFS server. If you are unable to figure out your problem see the support sections below. 339 | 340 | ## 🧹 Tidy up 341 | 342 | Once your cluster is fully configured and you no longer need to run `task configure`, it's a good idea to clean up the repository by removing the [templates](./templates) directory and any files related to the templating process. This will help eliminate unnecessary clutter from the upstream template repository and resolve any "duplicate registry" warnings from Renovate. 343 | 344 | 1. Tidy up your repository: 345 | 346 | ```sh 347 | task template:tidy 348 | ``` 349 | 350 | 2. Push your changes to git: 351 | 352 | ```sh 353 | git add -A 354 | git commit -m "chore: tidy up :broom:" 355 | git push 356 | ``` 357 | 358 | ## ❔ What's next 359 | 360 | There's a lot to absorb here, especially if you're new to these tools. Take some time to familiarize yourself with the tooling and understand how all the components interconnect. Dive into the documentation of the various tools included — they are a valuable resource. This shouldn't be a production environment yet, so embrace the freedom to experiment. Move fast, break things intentionally, and challenge yourself to fix them. 361 | 362 | Below are some optional considerations you may want to explore. 363 | 364 | ### DNS 365 | 366 | The template uses [k8s_gateway](https://github.com/ori-edge/k8s_gateway) to provide DNS for your applications, consider exploring [external-dns](https://github.com/kubernetes-sigs/external-dns) as an alternative. 367 | 368 | External-DNS offers broad support for various DNS providers, including but not limited to: 369 | 370 | - [Pi-hole](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/pihole.md) 371 | - [UniFi](https://github.com/kashalls/external-dns-unifi-webhook) 372 | - [Adguard Home](https://github.com/muhlba91/external-dns-provider-adguard) 373 | - [Bind](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/rfc2136.md) 374 | 375 | This flexibility allows you to integrate seamlessly with a range of DNS solutions to suit your environment and offload DNS from your cluster to your router, or external device. 376 | 377 | ### Secrets 378 | 379 | SOPs is an excellent tool for managing secrets in a GitOps workflow. However, it can become cumbersome when rotating secrets or maintaining a single source of truth for secret items. 380 | 381 | For a more streamlined approach to those issues, consider [External Secrets](https://external-secrets.io/latest/). This tool allows you to move away from SOPs and leverage an external provider for managing your secrets. External Secrets supports a wide range of providers, from cloud-based solutions to self-hosted options. 382 | 383 | ### Storage 384 | 385 | If your workloads require persistent storage with features like replication or connectivity to NFS, SMB, or iSCSI servers, there are several projects worth exploring: 386 | 387 | - [rook-ceph](https://github.com/rook/rook) 388 | - [longhorn](https://github.com/longhorn/longhorn) 389 | - [openebs](https://github.com/openebs/openebs) 390 | - [democratic-csi](https://github.com/democratic-csi/democratic-csi) 391 | - [csi-driver-nfs](https://github.com/kubernetes-csi/csi-driver-nfs) 392 | - [csi-driver-smb](https://github.com/kubernetes-csi/csi-driver-smb) 393 | - [synology-csi](https://github.com/SynologyOpenSource/synology-csi) 394 | 395 | These tools offer a variety of solutions to meet your persistent storage needs, whether you’re using cloud-native or self-hosted infrastructures. 396 | 397 | ### Community Repositories 398 | 399 | Community member [@whazor](https://github.com/whazor) created [Kubesearch](https://kubesearch.dev) to allow searching Flux HelmReleases across Github and Gitlab repositories with the `kubesearch` topic. 400 | 401 | ## 🙋 Support 402 | 403 | ### Community 404 | 405 | - Make a post in this repository's Github [Discussions](https://github.com/onedr0p/cluster-template/discussions). 406 | - Start a thread in the `#support` or `#cluster-template` channels in the [Home Operations](https://discord.gg/home-operations) Discord server. 407 | 408 | ### GitHub Sponsors 409 | 410 | If you're having difficulty with this project, can't find the answers you need through the community support options above, or simply want to show your appreciation while gaining deeper insights, I’m offering one-on-one paid support through GitHub Sponsors for a limited time. Payment and scheduling will be coordinated through [GitHub Sponsors](https://github.com/sponsors/onedr0p). 411 | 412 |
413 | 414 | Click to expand the details 415 | 416 |
417 | 418 | - **Rate**: $50/hour (no longer than 2 hours / day). 419 | - **What’s Included**: Assistance with deployment, debugging, or answering questions related to this project. 420 | - **What to Expect**: 421 | 1. Sessions will focus on specific questions or issues you are facing. 422 | 2. I will provide guidance, explanations, and actionable steps to help resolve your concerns. 423 | 3. Support is limited to this project and does not extend to unrelated tools or custom feature development. 424 | 425 |
426 | 427 | ## 🙌 Related Projects 428 | 429 | If this repo is too hot to handle or too cold to hold check out these following projects. 430 | 431 | - [ajaykumar4/cluster-template](https://github.com/ajaykumar4/cluster-template) - _A template for deploying a Talos Kubernetes cluster including Argo for GitOps_ 432 | - [khuedoan/homelab](https://github.com/khuedoan/homelab) - _Fully automated homelab from empty disk to running services with a single command._ 433 | - [mitchross/k3s-argocd-starter](https://github.com/mitchross/k3s-argocd-starter) - starter kit for k3s, argocd 434 | - [ricsanfre/pi-cluster](https://github.com/ricsanfre/pi-cluster) - _Pi Kubernetes Cluster. Homelab kubernetes cluster automated with Ansible and FluxCD_ 435 | - [techno-tim/k3s-ansible](https://github.com/techno-tim/k3s-ansible) - _The easiest way to bootstrap a self-hosted High Availability Kubernetes cluster. A fully automated HA k3s etcd install with kube-vip, MetalLB, and more. Build. Destroy. Repeat._ 436 | 437 | ## ⭐ Stargazers 438 | 439 | 450 | 451 | ## 🤝 Thanks 452 | 453 | Big shout out to all the contributors, sponsors and everyone else who has helped on this project. 454 | -------------------------------------------------------------------------------- /Taskfile.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 3 | version: '3' 4 | 5 | set: [pipefail] 6 | shopt: [globstar] 7 | 8 | vars: 9 | BOOTSTRAP_DIR: '{{.ROOT_DIR}}/bootstrap' 10 | KUBERNETES_DIR: '{{.ROOT_DIR}}/kubernetes' 11 | SCRIPTS_DIR: '{{.ROOT_DIR}}/scripts' 12 | TALOS_DIR: '{{.ROOT_DIR}}/talos' 13 | PRIVATE_DIR: '{{.ROOT_DIR}}/.private' 14 | TALOSCONFIG: '{{.ROOT_DIR}}/talos/clusterconfig/talosconfig' 15 | 16 | env: 17 | KUBECONFIG: '{{.ROOT_DIR}}/kubeconfig' 18 | SOPS_AGE_KEY_FILE: '{{.ROOT_DIR}}/age.key' 19 | TALOSCONFIG: '{{.TALOSCONFIG}}' 20 | 21 | includes: 22 | bootstrap: .taskfiles/bootstrap 23 | talos: .taskfiles/talos 24 | template: .taskfiles/template 25 | 26 | tasks: 27 | 28 | default: task --list 29 | 30 | reconcile: 31 | desc: Force Flux to pull in changes from your Git repository 32 | cmd: flux --namespace flux-system reconcile kustomization flux-system --with-source 33 | preconditions: 34 | - test -f {{.KUBECONFIG}} 35 | - which flux 36 | -------------------------------------------------------------------------------- /cluster.sample.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # -- The network CIDR for the nodes. 3 | # (REQUIRED) / (e.g. 192.168.1.0/24) 4 | node_cidr: "" 5 | 6 | # -- DNS servers to use for the cluster. 7 | # (OPTIONAL) / (DEFAULT: ["1.1.1.1", "1.0.0.1"]) / (Cloudflare DNS) 8 | # node_dns_servers: [] 9 | 10 | # -- NTP servers to use for the cluster. 11 | # (OPTIONAL) / (DEFAULT: ["162.159.200.1", "162.159.200.123"]) / (Cloudflare NTP) 12 | # node_ntp_servers: [] 13 | 14 | # -- The default gateway for the nodes. 15 | # (OPTIONAL) / (DEFAULT: the first IP in the node_cidr) 16 | # node_default_gateway: "" 17 | 18 | # -- Attach a vlan tag to the Talos nodes. Not needed if ports on your switch are tagged. 19 | # (OPTIONAL) / (REF: https://www.talos.dev/latest/advanced/advanced-networking/#vlans) 20 | # node_vlan_tag: "" 21 | 22 | # -- The IP address of the Kube API. 23 | # (REQUIRED) / (NOTE: Choose an unused IP in node_cidr) 24 | cluster_api_addr: "" 25 | 26 | # -- Additional SANs to add to the Kube API cert. This is useful if you want to call the Kube API by hostname rather than IP 27 | # (OPTIONAL) / (e.g. ["mycluster.example.com"]) 28 | # cluster_api_tls_sans: [] 29 | 30 | # -- The pod CIDR for the cluster, this must NOT overlap with any existing networks and should be a /16 (64K IPs). 31 | # (OPTIONAL) / (DEFAULT: "10.42.0.0/16") 32 | # cluster_pod_cidr: "" 33 | 34 | # -- The service CIDR for the cluster, this must NOT overlap with any existing networks and should be a /16 (64K IPs). 35 | # (OPTIONAL) / (DEFAULT: "10.43.0.0/16") 36 | # cluster_svc_cidr: "" 37 | 38 | # -- The Load balancer IP for k8s_gateway, this provides DNS to all your gateways when split DNS is configured on your internal DNS server (Dnsmasq, Pi-hole, etc) 39 | # (REQUIRED) / (NOTE: Choose an unused IP in node_cidr) 40 | cluster_dns_gateway_addr: "" 41 | 42 | # -- The Load balancer IP for the internal gateway 43 | # (REQUIRED) / (NOTE: Choose an unused IP in node_cidr) 44 | cluster_gateway_addr: "" 45 | 46 | # -- GitHub repository 47 | # (REQUIRED) / (e.g. "onedr0p/cluster-template") 48 | repository_name: "" 49 | 50 | # -- GitHub repository branch 51 | # (OPTIONAL) / (DEFAULT: "main") 52 | # repository_branch: "" 53 | 54 | # -- Repository visibility (public or private) 55 | # (OPTIONAL) / (DEFAULT: "public") / (NOTE: See the README for information when set private) 56 | # repository_visibility: "" 57 | 58 | # -- Domain you wish to use from your Cloudflare account 59 | # (REQUIRED) / (e.g. "example.com") 60 | cloudflare_domain: "" 61 | 62 | # -- API Token for Cloudflare with the 'Zone:DNS:Edit' and 'Account:Cloudflare Tunnel:Read' permissions 63 | # (REQUIRED) (NOTE: See the README for information on creating this) 64 | cloudflare_token: "" 65 | 66 | # -- The Load balancer IP for the external gateway 67 | # (REQUIRED) / (NOTE: Choose an unused IP in node_cidr) 68 | cloudflare_gateway_addr: "" 69 | 70 | # -- The load balancer mode for cilium. 71 | # (OPTIONAL) / (DEFAULT: "dsr") / (NOTE: accepted values are 'dsr' or 'snat') / (REF: https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/) 72 | # cilium_loadbalancer_mode: "" 73 | 74 | # -- The IP address of the BGP router, to keep things simple, node network will be used for BGP peering. 75 | # (OPTIONAL) / (e.g. "192.168.1.1") / (REF: https://docs.cilium.io/en/latest/network/bgp-control-plane/bgp-control-plane/) 76 | # cilium_bgp_router_addr: "" 77 | 78 | # -- The BGP router ASN 79 | # (OPTIONAL) / (e.g. "64513") 80 | # cilium_bgp_router_asn: "" 81 | 82 | # -- The BGP node ASN 83 | # (OPTIONAL) / (e.g. "64514") 84 | # cilium_bgp_node_asn: "" 85 | -------------------------------------------------------------------------------- /makejinja.toml: -------------------------------------------------------------------------------- 1 | [makejinja] 2 | inputs = ["./templates/overrides","./templates/config"] 3 | output = "./" 4 | exclude_patterns = ["*.partial.yaml.j2"] 5 | data = ["./cluster.yaml", "./nodes.yaml"] 6 | import_paths = ["./templates/scripts"] 7 | loaders = ["plugin:Plugin"] 8 | jinja_suffix = ".j2" 9 | copy_metadata = true 10 | force = true 11 | undefined = "chainable" 12 | 13 | [makejinja.delimiter] 14 | block_start = "#%" 15 | block_end = "%#" 16 | comment_start = "#|" 17 | comment_end = "#|" 18 | variable_start = "#{" 19 | variable_end = "}#" 20 | -------------------------------------------------------------------------------- /nodes.sample.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | nodes: [] 3 | # - name: "" # (REQUIRED) Name of the node (must match [a-z0-9-\]+) 4 | # address: "" # (REQUIRED) IP address of the node (must be in the node_cidr) 5 | # controller: true # (REQUIRED) Set to true if this is a controller node 6 | # disk: "" # (REQUIRED) Device path or serial number of the disk for this node (talosctl get disks -n --insecure) 7 | # mac_addr: "" # (REQUIRED) MAC address of the NIC for this node (talosctl get links -n --insecure) 8 | # schematic_id: "" # (REQUIRED) Schematic ID from https://factory.talos.dev/ 9 | # mtu: 1500 # (ADVANCED/OPTIONAL) MTU for the NIC. DEFAULT: 1500 10 | # secureboot: false # (ADVANCED/OPTIONAL) SecureBoot mode on UEFI platforms. Ref: https://www.talos.dev/latest/talos-guides/install/bare-metal-platforms/secureboot 11 | # encrypt_disk: false # (ADVANCED/OPTIONAL) TPM-based disk encryption. Ref: https://www.talos.dev/latest/talos-guides/install/bare-metal-platforms/secureboot 12 | # ... 13 | -------------------------------------------------------------------------------- /scripts/bootstrap-apps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | source "$(dirname "${0}")/lib/common.sh" 5 | 6 | export LOG_LEVEL="debug" 7 | export ROOT_DIR="$(git rev-parse --show-toplevel)" 8 | 9 | # Talos requires the nodes to be 'Ready=False' before applying resources 10 | function wait_for_nodes() { 11 | log debug "Waiting for nodes to be available" 12 | 13 | # Skip waiting if all nodes are 'Ready=True' 14 | if kubectl wait nodes --for=condition=Ready=True --all --timeout=10s &>/dev/null; then 15 | log info "Nodes are available and ready, skipping wait for nodes" 16 | return 17 | fi 18 | 19 | # Wait for all nodes to be 'Ready=False' 20 | until kubectl wait nodes --for=condition=Ready=False --all --timeout=10s &>/dev/null; do 21 | log info "Nodes are not available, waiting for nodes to be available. Retrying in 10 seconds..." 22 | sleep 10 23 | done 24 | } 25 | 26 | # Namespaces to be applied before the SOPS secrets are installed 27 | function apply_namespaces() { 28 | log debug "Applying namespaces" 29 | 30 | local -r apps_dir="${ROOT_DIR}/kubernetes/apps" 31 | 32 | if [[ ! -d "${apps_dir}" ]]; then 33 | log error "Directory does not exist" "directory=${apps_dir}" 34 | fi 35 | 36 | for app in "${apps_dir}"/*/; do 37 | namespace=$(basename "${app}") 38 | 39 | # Check if the namespace resources are up-to-date 40 | if kubectl get namespace "${namespace}" &>/dev/null; then 41 | log info "Namespace resource is up-to-date" "resource=${namespace}" 42 | continue 43 | fi 44 | 45 | # Apply the namespace resources 46 | if kubectl create namespace "${namespace}" --dry-run=client --output=yaml \ 47 | | kubectl apply --server-side --filename - &>/dev/null; 48 | then 49 | log info "Namespace resource applied" "resource=${namespace}" 50 | else 51 | log error "Failed to apply namespace resource" "resource=${namespace}" 52 | fi 53 | done 54 | } 55 | 56 | # SOPS secrets to be applied before the helmfile charts are installed 57 | function apply_sops_secrets() { 58 | log debug "Applying secrets" 59 | 60 | local -r secrets=( 61 | "${ROOT_DIR}/bootstrap/github-deploy-key.sops.yaml" 62 | "${ROOT_DIR}/kubernetes/components/common/sops/cluster-secrets.sops.yaml" 63 | "${ROOT_DIR}/kubernetes/components/common/sops/sops-age.sops.yaml" 64 | ) 65 | 66 | for secret in "${secrets[@]}"; do 67 | if [ ! -f "${secret}" ]; then 68 | log warn "File does not exist" "file=${secret}" 69 | continue 70 | fi 71 | 72 | # Check if the secret resources are up-to-date 73 | if sops exec-file "${secret}" "kubectl --namespace flux-system diff --filename {}" &>/dev/null; then 74 | log info "Secret resource is up-to-date" "resource=$(basename "${secret}" ".sops.yaml")" 75 | continue 76 | fi 77 | 78 | # Apply secret resources 79 | if sops exec-file "${secret}" "kubectl --namespace flux-system apply --server-side --filename {}" &>/dev/null; then 80 | log info "Secret resource applied successfully" "resource=$(basename "${secret}" ".sops.yaml")" 81 | else 82 | log error "Failed to apply secret resource" "resource=$(basename "${secret}" ".sops.yaml")" 83 | fi 84 | done 85 | } 86 | 87 | # CRDs to be applied before the helmfile charts are installed 88 | function apply_crds() { 89 | log debug "Applying CRDs" 90 | 91 | local -r crds=( 92 | # renovate: datasource=github-releases depName=kubernetes-sigs/external-dns 93 | https://raw.githubusercontent.com/kubernetes-sigs/external-dns/refs/tags/v0.17.0/config/crd/standard/dnsendpoint.yaml 94 | # renovate: datasource=github-releases depName=kubernetes-sigs/gateway-api 95 | https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/experimental-install.yaml 96 | # renovate: datasource=github-releases depName=prometheus-operator/prometheus-operator 97 | https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.83.0/stripped-down-crds.yaml 98 | ) 99 | 100 | for crd in "${crds[@]}"; do 101 | if kubectl diff --filename "${crd}" &>/dev/null; then 102 | log info "CRDs are up-to-date" "crd=${crd}" 103 | continue 104 | fi 105 | if kubectl apply --server-side --filename "${crd}" &>/dev/null; then 106 | log info "CRDs applied" "crd=${crd}" 107 | else 108 | log error "Failed to apply CRDs" "crd=${crd}" 109 | fi 110 | done 111 | } 112 | 113 | # Sync Helm releases 114 | function sync_helm_releases() { 115 | log debug "Syncing Helm releases" 116 | 117 | local -r helmfile_file="${ROOT_DIR}/bootstrap/helmfile.yaml" 118 | 119 | if [[ ! -f "${helmfile_file}" ]]; then 120 | log error "File does not exist" "file=${helmfile_file}" 121 | fi 122 | 123 | if ! helmfile --file "${helmfile_file}" sync --hide-notes; then 124 | log error "Failed to sync Helm releases" 125 | fi 126 | 127 | log info "Helm releases synced successfully" 128 | } 129 | 130 | function main() { 131 | check_env KUBECONFIG TALOSCONFIG 132 | check_cli helmfile kubectl kustomize sops talhelper yq 133 | 134 | # Apply resources and Helm releases 135 | wait_for_nodes 136 | apply_namespaces 137 | apply_sops_secrets 138 | apply_crds 139 | sync_helm_releases 140 | 141 | log info "Congrats! The cluster is bootstrapped and Flux is syncing the Git repository" 142 | } 143 | 144 | main "$@" 145 | -------------------------------------------------------------------------------- /scripts/lib/common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -Eeuo pipefail 3 | 4 | # Log messages with different levels 5 | function log() { 6 | local level="${1:-info}" 7 | shift 8 | 9 | # Define log levels with their priorities 10 | local -A level_priority=( 11 | [debug]=1 12 | [info]=2 13 | [warn]=3 14 | [error]=4 15 | ) 16 | 17 | # Get the current log level's priority 18 | local current_priority=${level_priority[$level]:-2} # Default to "info" priority 19 | 20 | # Get the configured log level from the environment, default to "info" 21 | local configured_level=${LOG_LEVEL:-info} 22 | local configured_priority=${level_priority[$configured_level]:-2} 23 | 24 | # Skip log messages below the configured log level 25 | if ((current_priority < configured_priority)); then 26 | return 27 | fi 28 | 29 | # Define log colors 30 | local -A colors=( 31 | [debug]="\033[1m\033[38;5;63m" # Blue 32 | [info]="\033[1m\033[38;5;87m" # Cyan 33 | [warn]="\033[1m\033[38;5;192m" # Yellow 34 | [error]="\033[1m\033[38;5;198m" # Red 35 | ) 36 | 37 | # Fallback to "info" if the color for the given level is not defined 38 | local color="${colors[$level]:-${colors[info]}}" 39 | local msg="$1" 40 | shift 41 | 42 | # Prepare additional data 43 | local data= 44 | if [[ $# -gt 0 ]]; then 45 | for item in "$@"; do 46 | if [[ "${item}" == *=* ]]; then 47 | data+="\033[1m\033[38;5;236m${item%%=*}=\033[0m\"${item#*=}\" " 48 | else 49 | data+="${item} " 50 | fi 51 | done 52 | fi 53 | 54 | # Determine output stream based on log level 55 | local output_stream="/dev/stdout" 56 | if [[ "$level" == "error" ]]; then 57 | output_stream="/dev/stderr" 58 | fi 59 | 60 | # Print the log message 61 | printf "%s %b%s%b %s %b\n" "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ 62 | "${color}" "${level^^}" "\033[0m" "${msg}" "${data}" >"${output_stream}" 63 | 64 | # Exit if the log level is error 65 | if [[ "$level" == "error" ]]; then 66 | exit 1 67 | fi 68 | } 69 | 70 | # Check if required environment variables are set 71 | function check_env() { 72 | local envs=("${@}") 73 | local missing=() 74 | local values=() 75 | 76 | for env in "${envs[@]}"; do 77 | if [[ -z "${!env-}" ]]; then 78 | missing+=("${env}") 79 | else 80 | values+=("${env}=${!env}") 81 | fi 82 | done 83 | 84 | if [ ${#missing[@]} -ne 0 ]; then 85 | log error "Missing required env variables" "envs=${missing[*]}" 86 | fi 87 | 88 | log debug "Env variables are set" "envs=${values[*]}" 89 | } 90 | 91 | # Check if required CLI tools are installed 92 | function check_cli() { 93 | local deps=("${@}") 94 | local missing=() 95 | 96 | for dep in "${deps[@]}"; do 97 | if ! command -v "${dep}" &>/dev/null; then 98 | missing+=("${dep}") 99 | fi 100 | done 101 | 102 | if [ ${#missing[@]} -ne 0 ]; then 103 | log error "Missing required deps" "deps=${missing[*]}" 104 | fi 105 | 106 | log debug "Deps are installed" "deps=${deps[*]}" 107 | } 108 | -------------------------------------------------------------------------------- /templates/config/.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | creation_rules: 3 | - path_regex: talos/.*\.sops\.ya?ml 4 | mac_only_encrypted: true 5 | age: "#{ age_key('public') }#" 6 | - path_regex: (bootstrap|kubernetes)/.*\.sops\.ya?ml 7 | encrypted_regex: "^(data|stringData)$" 8 | mac_only_encrypted: true 9 | age: "#{ age_key('public') }#" 10 | stores: 11 | yaml: 12 | indent: 2 13 | -------------------------------------------------------------------------------- /templates/config/bootstrap/github-deploy-key.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | #% if repository_visibility == 'private' %# 2 | --- 3 | # yaml-language-server: $schema=https://kubernetesjsonschema.dev/v1.18.1-standalone-strict/secret-v1.json 4 | apiVersion: v1 5 | kind: Secret 6 | metadata: 7 | name: github-deploy-key 8 | namespace: flux-system 9 | stringData: 10 | identity: | 11 | #% filter indent(width=4, first=False) %# 12 | #{ github_deploy_key() }# 13 | #% endfilter %# 14 | known_hosts: | 15 | github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl 16 | github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= 17 | github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= 18 | #% endif %# 19 | -------------------------------------------------------------------------------- /templates/config/bootstrap/helmfile.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/helmfile 3 | 4 | helmDefaults: 5 | cleanupOnFail: true 6 | wait: true 7 | waitForJobs: true 8 | 9 | repositories: 10 | - name: cilium 11 | url: https://helm.cilium.io 12 | 13 | releases: 14 | - name: cilium 15 | namespace: kube-system 16 | atomic: true 17 | chart: cilium/cilium 18 | version: 1.17.4 19 | values: ['../kubernetes/apps/kube-system/cilium/app/helm/values.yaml'] 20 | 21 | - name: coredns 22 | namespace: kube-system 23 | atomic: true 24 | chart: oci://ghcr.io/coredns/charts/coredns 25 | version: 1.42.2 26 | values: ['../kubernetes/apps/kube-system/coredns/app/helm/values.yaml'] 27 | needs: ['kube-system/cilium'] 28 | 29 | #% if spegel_enabled %# 30 | - name: spegel 31 | namespace: kube-system 32 | atomic: true 33 | chart: oci://ghcr.io/spegel-org/helm-charts/spegel 34 | version: 0.2.0 35 | values: ['../kubernetes/apps/kube-system/spegel/app/helm/values.yaml'] 36 | needs: ['kube-system/coredns'] 37 | #% endif %# 38 | 39 | - name: cert-manager 40 | namespace: cert-manager 41 | atomic: true 42 | chart: oci://quay.io/jetstack/charts/cert-manager 43 | version: v1.17.2 44 | values: ['../kubernetes/apps/cert-manager/cert-manager/app/helm/values.yaml'] 45 | #% if spegel_enabled %# 46 | needs: ['kube-system/spegel'] 47 | #% else %# 48 | needs: ['kube-system/coredns'] 49 | #% endif %# 50 | 51 | - name: flux-operator 52 | namespace: flux-system 53 | atomic: true 54 | chart: oci://ghcr.io/controlplaneio-fluxcd/charts/flux-operator 55 | version: 0.21.0 56 | values: ['../kubernetes/apps/flux-system/flux-operator/app/helm/values.yaml'] 57 | needs: ['cert-manager/cert-manager'] 58 | 59 | - name: flux-instance 60 | namespace: flux-system 61 | atomic: true 62 | chart: oci://ghcr.io/controlplaneio-fluxcd/charts/flux-instance 63 | version: 0.21.0 64 | values: ['../kubernetes/apps/flux-system/flux-instance/app/helm/values.yaml'] 65 | needs: ['flux-system/flux-operator'] 66 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/cert-manager/app/clusterissuer.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/cert-manager.io/clusterissuer_v1.json 3 | apiVersion: cert-manager.io/v1 4 | kind: ClusterIssuer 5 | metadata: 6 | name: letsencrypt-production 7 | spec: 8 | acme: 9 | server: https://acme-v02.api.letsencrypt.org/directory 10 | privateKeySecretRef: 11 | name: letsencrypt-production 12 | solvers: 13 | - dns01: 14 | cloudflare: 15 | apiTokenSecretRef: 16 | name: cert-manager-secret 17 | key: api-token 18 | selector: 19 | dnsZones: ["${SECRET_DOMAIN}"] 20 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/cert-manager/app/helm/kustomizeconfig.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | nameReference: 3 | - kind: ConfigMap 4 | version: v1 5 | fieldSpecs: 6 | - path: spec/valuesFrom/name 7 | kind: HelmRelease 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/cert-manager/app/helm/values.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | crds: 3 | enabled: true 4 | replicaCount: 1 5 | dns01RecursiveNameservers: https://1.1.1.1:443/dns-query,https://1.0.0.1:443/dns-query 6 | dns01RecursiveNameserversOnly: true 7 | prometheus: 8 | enabled: true 9 | servicemonitor: 10 | enabled: true 11 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/cert-manager/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: OCIRepository 5 | metadata: 6 | name: cert-manager 7 | spec: 8 | interval: 5m 9 | layerSelector: 10 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 11 | operation: copy 12 | ref: 13 | tag: v1.17.2 14 | url: oci://quay.io/jetstack/charts/cert-manager 15 | --- 16 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 17 | apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | metadata: 20 | name: cert-manager 21 | spec: 22 | interval: 1h 23 | chartRef: 24 | kind: OCIRepository 25 | name: cert-manager 26 | install: 27 | remediation: 28 | retries: -1 29 | upgrade: 30 | cleanupOnFail: true 31 | remediation: 32 | retries: 3 33 | valuesFrom: 34 | - kind: ConfigMap 35 | name: cert-manager-values 36 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/cert-manager/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./clusterissuer.yaml 7 | - ./helmrelease.yaml 8 | - ./secret.sops.yaml 9 | configMapGenerator: 10 | - name: cert-manager-values 11 | files: 12 | - values.yaml=./helm/values.yaml 13 | configurations: 14 | - ./helm/kustomizeconfig.yaml 15 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/cert-manager/app/secret.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://kubernetesjsonschema.dev/v1.18.1-standalone-strict/secret-v1.json 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: cert-manager-secret 7 | stringData: 8 | api-token: "#{ cloudflare_token }#" 9 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/cert-manager/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app cert-manager 7 | namespace: &namespace cert-manager 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | healthChecks: 17 | - apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | name: *app 20 | namespace: *namespace 21 | - apiVersion: cert-manager.io/v1 22 | kind: ClusterIssuer 23 | name: letsencrypt-production 24 | healthCheckExprs: 25 | - apiVersion: cert-manager.io/v1 26 | kind: ClusterIssuer 27 | failed: status.conditions.filter(e, e.type == 'Ready').all(e, e.status == 'False') 28 | current: status.conditions.filter(e, e.type == 'Ready').all(e, e.status == 'True') 29 | interval: 1h 30 | path: ./kubernetes/apps/cert-manager/cert-manager/app 31 | postBuild: 32 | substituteFrom: 33 | - name: cluster-secrets 34 | kind: Secret 35 | prune: true 36 | retryInterval: 2m 37 | sourceRef: 38 | kind: GitRepository 39 | name: flux-system 40 | namespace: flux-system 41 | targetNamespace: *namespace 42 | timeout: 5m 43 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/cert-manager/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | namespace: cert-manager 6 | components: 7 | - ../../components/common 8 | resources: 9 | - ./cert-manager/ks.yaml 10 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/default/echo/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 3 | apiVersion: helm.toolkit.fluxcd.io/v2 4 | kind: HelmRelease 5 | metadata: 6 | name: echo 7 | spec: 8 | interval: 1h 9 | chartRef: 10 | kind: OCIRepository 11 | name: app-template 12 | install: 13 | remediation: 14 | retries: -1 15 | upgrade: 16 | cleanupOnFail: true 17 | remediation: 18 | retries: 3 19 | dependsOn: 20 | - name: cloudflare-tunnel 21 | namespace: network 22 | values: 23 | controllers: 24 | echo: 25 | strategy: RollingUpdate 26 | containers: 27 | app: 28 | image: 29 | repository: ghcr.io/mendhak/http-https-echo 30 | tag: 37 31 | env: 32 | HTTP_PORT: &port 80 33 | LOG_WITHOUT_NEWLINE: true 34 | LOG_IGNORE_PATH: /healthz 35 | PROMETHEUS_ENABLED: true 36 | probes: 37 | liveness: &probes 38 | enabled: true 39 | custom: true 40 | spec: 41 | httpGet: 42 | path: /healthz 43 | port: *port 44 | initialDelaySeconds: 0 45 | periodSeconds: 10 46 | timeoutSeconds: 1 47 | failureThreshold: 3 48 | readiness: *probes 49 | securityContext: 50 | allowPrivilegeEscalation: false 51 | readOnlyRootFilesystem: true 52 | capabilities: { drop: ["ALL"] } 53 | resources: 54 | requests: 55 | cpu: 10m 56 | limits: 57 | memory: 64Mi 58 | defaultPodOptions: 59 | securityContext: 60 | runAsNonRoot: true 61 | runAsUser: 65534 62 | runAsGroup: 65534 63 | service: 64 | app: 65 | controller: echo 66 | ports: 67 | http: 68 | port: *port 69 | serviceMonitor: 70 | app: 71 | serviceName: echo 72 | endpoints: 73 | - port: http 74 | route: 75 | app: 76 | hostnames: ["{{ .Release.Name }}.${SECRET_DOMAIN}"] 77 | parentRefs: 78 | - name: external 79 | namespace: kube-system 80 | sectionName: https 81 | rules: 82 | - backendRefs: 83 | - identifier: app 84 | port: *port 85 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/default/echo/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/default/echo/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app echo 7 | namespace: &namespace default 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/default/echo/app 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: false 31 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/default/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | namespace: default 6 | components: 7 | - ../../components/common 8 | resources: 9 | - ./echo/ks.yaml 10 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/app/helm/kustomizeconfig.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | nameReference: 3 | - kind: ConfigMap 4 | version: v1 5 | fieldSpecs: 6 | - path: spec/valuesFrom/name 7 | kind: HelmRelease 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/app/helm/values.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | instance: 3 | distribution: 4 | # renovate: datasource=github-releases depName=controlplaneio-fluxcd/distribution 5 | version: 2.6.0 6 | cluster: 7 | networkPolicy: false 8 | components: 9 | - source-controller 10 | - kustomize-controller 11 | - helm-controller 12 | - notification-controller 13 | sync: 14 | kind: GitRepository 15 | #% if repository_visibility == 'private' %# 16 | url: "ssh://git@github.com/#{ repository_name }#.git" 17 | pullSecret: github-deploy-key 18 | #% else %# 19 | url: "https://github.com/#{ repository_name }#.git" 20 | #% endif %# 21 | ref: "refs/heads/#{ repository_branch }#" 22 | path: kubernetes/flux/cluster 23 | commonMetadata: 24 | labels: 25 | app.kubernetes.io/name: flux 26 | kustomize: 27 | patches: 28 | - # Increase the number of workers 29 | patch: | 30 | - op: add 31 | path: /spec/template/spec/containers/0/args/- 32 | value: --concurrent=10 33 | - op: add 34 | path: /spec/template/spec/containers/0/args/- 35 | value: --requeue-dependency=5s 36 | target: 37 | kind: Deployment 38 | name: (kustomize-controller|helm-controller|source-controller) 39 | - # Increase the memory limits 40 | patch: | 41 | apiVersion: apps/v1 42 | kind: Deployment 43 | metadata: 44 | name: all 45 | spec: 46 | template: 47 | spec: 48 | containers: 49 | - name: manager 50 | resources: 51 | limits: 52 | memory: 1Gi 53 | target: 54 | kind: Deployment 55 | name: (kustomize-controller|helm-controller|source-controller) 56 | - # Enable in-memory kustomize builds 57 | patch: | 58 | - op: add 59 | path: /spec/template/spec/containers/0/args/- 60 | value: --concurrent=20 61 | - op: replace 62 | path: /spec/template/spec/volumes/0 63 | value: 64 | name: temp 65 | emptyDir: 66 | medium: Memory 67 | target: 68 | kind: Deployment 69 | name: kustomize-controller 70 | - # Enable Helm repositories caching 71 | patch: | 72 | - op: add 73 | path: /spec/template/spec/containers/0/args/- 74 | value: --helm-cache-max-size=10 75 | - op: add 76 | path: /spec/template/spec/containers/0/args/- 77 | value: --helm-cache-ttl=60m 78 | - op: add 79 | path: /spec/template/spec/containers/0/args/- 80 | value: --helm-cache-purge-interval=5m 81 | target: 82 | kind: Deployment 83 | name: source-controller 84 | - # Flux near OOM detection for Helm 85 | patch: | 86 | - op: add 87 | path: /spec/template/spec/containers/0/args/- 88 | value: --feature-gates=OOMWatch=true 89 | - op: add 90 | path: /spec/template/spec/containers/0/args/- 91 | value: --oom-watch-memory-threshold=95 92 | - op: add 93 | path: /spec/template/spec/containers/0/args/- 94 | value: --oom-watch-interval=500ms 95 | target: 96 | kind: Deployment 97 | name: helm-controller 98 | - # Disable chart digest tracking 99 | patch: | 100 | - op: add 101 | path: /spec/template/spec/containers/0/args/- 102 | value: --feature-gates=DisableChartDigestTracking=true 103 | target: 104 | kind: Deployment 105 | name: helm-controller 106 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: OCIRepository 5 | metadata: 6 | name: flux-instance 7 | spec: 8 | interval: 5m 9 | layerSelector: 10 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 11 | operation: copy 12 | ref: 13 | tag: 0.21.0 14 | url: oci://ghcr.io/controlplaneio-fluxcd/charts/flux-instance 15 | --- 16 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 17 | apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | metadata: 20 | name: flux-instance 21 | spec: 22 | interval: 1h 23 | chartRef: 24 | kind: OCIRepository 25 | name: flux-instance 26 | install: 27 | remediation: 28 | retries: -1 29 | upgrade: 30 | cleanupOnFail: true 31 | remediation: 32 | strategy: rollback 33 | retries: 3 34 | dependsOn: 35 | - name: flux-operator 36 | namespace: flux-system 37 | valuesFrom: 38 | - kind: ConfigMap 39 | name: flux-instance-values 40 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/app/httproute.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://github.com/datreeio/CRDs-catalog/raw/refs/heads/main/gateway.networking.k8s.io/httproute_v1.json 3 | apiVersion: gateway.networking.k8s.io/v1 4 | kind: HTTPRoute 5 | metadata: 6 | name: github-webhook 7 | spec: 8 | hostnames: ["flux-webhook.${SECRET_DOMAIN}"] 9 | parentRefs: 10 | - name: external 11 | namespace: kube-system 12 | sectionName: https 13 | rules: 14 | - backendRefs: 15 | - name: webhook-receiver 16 | namespace: flux-system 17 | port: 80 18 | matches: 19 | - path: 20 | type: PathPrefix 21 | value: /hook/ 22 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | - ./secret.sops.yaml 8 | - ./httproute.yaml 9 | - ./receiver.yaml 10 | configMapGenerator: 11 | - name: flux-instance-values 12 | files: 13 | - values.yaml=./helm/values.yaml 14 | configurations: 15 | - ./helm/kustomizeconfig.yaml 16 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/app/receiver.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/receiver-notification-v1.json 3 | apiVersion: notification.toolkit.fluxcd.io/v1 4 | kind: Receiver 5 | metadata: 6 | name: github-webhook 7 | spec: 8 | type: github 9 | events: ["ping", "push"] 10 | secretRef: 11 | name: github-webhook-token-secret 12 | resources: 13 | - apiVersion: source.toolkit.fluxcd.io/v1 14 | kind: GitRepository 15 | name: flux-system 16 | namespace: flux-system 17 | - apiVersion: kustomize.toolkit.fluxcd.io/v1 18 | kind: Kustomization 19 | name: flux-system 20 | namespace: flux-system 21 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/app/secret.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://kubernetesjsonschema.dev/v1.18.1-standalone-strict/secret-v1.json 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: github-webhook-token-secret 7 | stringData: 8 | token: "#{ github_push_token() }#" 9 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-instance/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app flux-instance 7 | namespace: &namespace flux-system 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | dependsOn: 17 | - name: flux-operator 18 | namespace: *namespace 19 | interval: 1h 20 | path: ./kubernetes/apps/flux-system/flux-instance/app 21 | postBuild: 22 | substituteFrom: 23 | - name: cluster-secrets 24 | kind: Secret 25 | prune: true 26 | retryInterval: 2m 27 | sourceRef: 28 | kind: GitRepository 29 | name: flux-system 30 | namespace: flux-system 31 | targetNamespace: *namespace 32 | timeout: 5m 33 | wait: false 34 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-operator/app/helm/kustomizeconfig.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | nameReference: 3 | - kind: ConfigMap 4 | version: v1 5 | fieldSpecs: 6 | - path: spec/valuesFrom/name 7 | kind: HelmRelease 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-operator/app/helm/values.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | serviceMonitor: 3 | create: true 4 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-operator/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: OCIRepository 5 | metadata: 6 | name: flux-operator 7 | spec: 8 | interval: 5m 9 | layerSelector: 10 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 11 | operation: copy 12 | ref: 13 | tag: 0.21.0 14 | url: oci://ghcr.io/controlplaneio-fluxcd/charts/flux-operator 15 | --- 16 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 17 | apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | metadata: 20 | name: flux-operator 21 | spec: 22 | interval: 1h 23 | chartRef: 24 | kind: OCIRepository 25 | name: flux-operator 26 | install: 27 | remediation: 28 | retries: -1 29 | upgrade: 30 | cleanupOnFail: true 31 | remediation: 32 | strategy: rollback 33 | retries: 3 34 | valuesFrom: 35 | - kind: ConfigMap 36 | name: flux-operator-values 37 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-operator/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | configMapGenerator: 8 | - name: flux-operator-values 9 | files: 10 | - values.yaml=./helm/values.yaml 11 | configurations: 12 | - ./helm/kustomizeconfig.yaml 13 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/flux-operator/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app flux-operator 7 | namespace: &namespace flux-system 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | healthChecks: 17 | - apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | name: *app 20 | namespace: *namespace 21 | interval: 1h 22 | path: ./kubernetes/apps/flux-system/flux-operator/app 23 | postBuild: 24 | substituteFrom: 25 | - name: cluster-secrets 26 | kind: Secret 27 | prune: true 28 | retryInterval: 2m 29 | sourceRef: 30 | kind: GitRepository 31 | name: flux-system 32 | namespace: flux-system 33 | targetNamespace: *namespace 34 | timeout: 5m 35 | wait: false 36 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/flux-system/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | namespace: flux-system 6 | components: 7 | - ../../components/common 8 | resources: 9 | - ./flux-instance/ks.yaml 10 | - ./flux-operator/ks.yaml 11 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/app/helm/kustomizeconfig.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | nameReference: 3 | - kind: ConfigMap 4 | version: v1 5 | fieldSpecs: 6 | - path: spec/valuesFrom/name 7 | kind: HelmRelease 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/app/helm/values.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | autoDirectNodeRoutes: true 3 | bpf: 4 | masquerade: true 5 | # Ref: https://github.com/siderolabs/talos/issues/10002 6 | hostLegacyRouting: true 7 | #% if cilium_bgp_enabled %# 8 | bgpControlPlane: 9 | enabled: true 10 | #% endif %# 11 | cni: 12 | # Required for pairing with Multus CNI 13 | exclusive: false 14 | cgroup: 15 | automount: 16 | enabled: false 17 | hostRoot: /sys/fs/cgroup 18 | # NOTE: devices might need to be set if you have more than one active NIC on your hosts 19 | # devices: eno+ eth+ 20 | dashboards: 21 | enabled: true 22 | endpointRoutes: 23 | enabled: true 24 | envoy: 25 | rollOutPods: true 26 | prometheus: 27 | serviceMonitor: 28 | enabled: true 29 | gatewayAPI: 30 | enabled: true 31 | hubble: 32 | enabled: false 33 | ipam: 34 | mode: kubernetes 35 | ipv4NativeRoutingCIDR: "#{ cluster_pod_cidr }#" 36 | k8sServiceHost: 127.0.0.1 37 | k8sServicePort: 7445 38 | kubeProxyReplacement: true 39 | kubeProxyReplacementHealthzBindAddr: 0.0.0.0:10256 40 | l2announcements: 41 | enabled: true 42 | loadBalancer: 43 | algorithm: maglev 44 | mode: "#{ cilium_loadbalancer_mode }#" 45 | localRedirectPolicy: true 46 | operator: 47 | dashboards: 48 | enabled: true 49 | prometheus: 50 | enabled: true 51 | serviceMonitor: 52 | enabled: true 53 | replicas: 1 54 | rollOutPods: true 55 | prometheus: 56 | enabled: true 57 | serviceMonitor: 58 | enabled: true 59 | trustCRDsExist: true 60 | rollOutCiliumPods: true 61 | routingMode: native 62 | securityContext: 63 | capabilities: 64 | ciliumAgent: 65 | - CHOWN 66 | - KILL 67 | - NET_ADMIN 68 | - NET_RAW 69 | - IPC_LOCK 70 | - SYS_ADMIN 71 | - SYS_RESOURCE 72 | - PERFMON 73 | - BPF 74 | - DAC_OVERRIDE 75 | - FOWNER 76 | - SETGID 77 | - SETUID 78 | cleanCiliumState: 79 | - NET_ADMIN 80 | - SYS_ADMIN 81 | - SYS_RESOURCE 82 | socketLB: 83 | hostNamespaceOnly: true 84 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: HelmRepository 5 | metadata: 6 | name: cilium 7 | namespace: kube-system # Required for Renovate lookups 8 | spec: 9 | interval: 1h 10 | url: https://helm.cilium.io 11 | --- 12 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 13 | apiVersion: helm.toolkit.fluxcd.io/v2 14 | kind: HelmRelease 15 | metadata: 16 | name: cilium 17 | spec: 18 | interval: 1h 19 | chart: 20 | spec: 21 | chart: cilium 22 | version: 1.17.4 23 | sourceRef: 24 | kind: HelmRepository 25 | name: cilium 26 | namespace: kube-system 27 | install: 28 | remediation: 29 | retries: -1 30 | upgrade: 31 | cleanupOnFail: true 32 | remediation: 33 | retries: 3 34 | valuesFrom: 35 | - kind: ConfigMap 36 | name: cilium-values 37 | values: 38 | operator: 39 | tolerations: [] 40 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | - ./networks.yaml 8 | configMapGenerator: 9 | - name: cilium-values 10 | files: 11 | - values.yaml=./helm/values.yaml 12 | configurations: 13 | - ./helm/kustomizeconfig.yaml 14 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/app/networks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/cilium.io/ciliumloadbalancerippool_v2alpha1.json 3 | apiVersion: cilium.io/v2alpha1 4 | kind: CiliumLoadBalancerIPPool 5 | metadata: 6 | name: pool 7 | spec: 8 | allowFirstLastIPs: "No" 9 | blocks: 10 | - cidr: "#{ node_cidr }#" 11 | --- 12 | # yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/cilium.io/ciliuml2announcementpolicy_v2alpha1.json 13 | apiVersion: cilium.io/v2alpha1 14 | kind: CiliumL2AnnouncementPolicy 15 | metadata: 16 | name: l2-policy 17 | spec: 18 | loadBalancerIPs: true 19 | # NOTE: interfaces might need to be set if you have more than one active NIC on your hosts 20 | # interfaces: 21 | # - ^eno[0-9]+ 22 | # - ^eth[0-9]+ 23 | nodeSelector: 24 | matchLabels: 25 | kubernetes.io/os: linux 26 | #% if cilium_bgp_enabled %# 27 | --- 28 | # yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/cilium.io/ciliumbgpadvertisement_v2alpha1.json 29 | apiVersion: cilium.io/v2alpha1 30 | kind: CiliumBGPAdvertisement 31 | metadata: 32 | name: bgp-advertisement-config 33 | labels: 34 | advertise: bgp 35 | spec: 36 | advertisements: 37 | - advertisementType: Service 38 | service: 39 | addresses: 40 | - LoadBalancerIP 41 | selector: 42 | matchExpressions: 43 | - { key: somekey, operator: NotIn, values: ["never-used-value"] } 44 | --- 45 | # yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/cilium.io/ciliumbgppeerconfig_v2alpha1.json 46 | apiVersion: cilium.io/v2alpha1 47 | kind: CiliumBGPPeerConfig 48 | metadata: 49 | name: bgp-peer-config-v4 50 | spec: 51 | families: 52 | - afi: ipv4 53 | safi: unicast 54 | advertisements: 55 | matchLabels: 56 | advertise: bgp 57 | --- 58 | # yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/cilium.io/ciliumbgpclusterconfig_v2alpha1.json 59 | apiVersion: cilium.io/v2alpha1 60 | kind: CiliumBGPClusterConfig 61 | metadata: 62 | name: bgp-cluster-config 63 | spec: 64 | nodeSelector: 65 | matchLabels: 66 | kubernetes.io/os: linux 67 | bgpInstances: 68 | - name: instance-#{ cilium_bgp_node_asn }# 69 | localASN: #{ cilium_bgp_node_asn }# 70 | peers: 71 | - name: peer-#{ cilium_bgp_router_asn }#-v4 72 | peerASN: #{ cilium_bgp_router_asn }# 73 | peerAddress: #{ cilium_bgp_router_addr }# 74 | peerConfigRef: 75 | name: bgp-peer-config-v4 76 | #% endif %# 77 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/gateway/certificate.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/cert-manager.io/certificate_v1.json 3 | apiVersion: cert-manager.io/v1 4 | kind: Certificate 5 | metadata: 6 | name: "${SECRET_DOMAIN/./-}-production" 7 | spec: 8 | secretName: "${SECRET_DOMAIN/./-}-production-tls" 9 | issuerRef: 10 | name: letsencrypt-production 11 | kind: ClusterIssuer 12 | commonName: "${SECRET_DOMAIN}" 13 | dnsNames: ["${SECRET_DOMAIN}", "*.${SECRET_DOMAIN}"] 14 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/gateway/external.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://github.com/datreeio/CRDs-catalog/raw/refs/heads/main/gateway.networking.k8s.io/gateway_v1.json 3 | apiVersion: gateway.networking.k8s.io/v1 4 | kind: Gateway 5 | metadata: 6 | name: external 7 | annotations: 8 | external-dns.alpha.kubernetes.io/target: "external.${SECRET_DOMAIN}" 9 | spec: 10 | gatewayClassName: cilium 11 | addresses: 12 | - type: IPAddress 13 | value: "#{ cloudflare_gateway_addr }#" 14 | infrastructure: 15 | annotations: 16 | external-dns.alpha.kubernetes.io/hostname: "external.${SECRET_DOMAIN}" 17 | listeners: 18 | - name: http 19 | protocol: HTTP 20 | port: 80 21 | hostname: "*.${SECRET_DOMAIN}" 22 | allowedRoutes: 23 | namespaces: 24 | from: Same 25 | - name: https 26 | protocol: HTTPS 27 | port: 443 28 | hostname: "*.${SECRET_DOMAIN}" 29 | allowedRoutes: 30 | namespaces: 31 | from: All 32 | tls: 33 | certificateRefs: 34 | - kind: Secret 35 | name: ${SECRET_DOMAIN/./-}-production-tls 36 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/gateway/internal.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://github.com/datreeio/CRDs-catalog/raw/refs/heads/main/gateway.networking.k8s.io/gateway_v1.json 3 | apiVersion: gateway.networking.k8s.io/v1 4 | kind: Gateway 5 | metadata: 6 | name: internal 7 | annotations: 8 | external-dns.alpha.kubernetes.io/target: "internal.${SECRET_DOMAIN}" 9 | spec: 10 | gatewayClassName: cilium 11 | addresses: 12 | - type: IPAddress 13 | value: "#{ cluster_gateway_addr }#" 14 | infrastructure: 15 | annotations: 16 | external-dns.alpha.kubernetes.io/hostname: "internal.${SECRET_DOMAIN}" 17 | listeners: 18 | - name: http 19 | protocol: HTTP 20 | port: 80 21 | hostname: "*.${SECRET_DOMAIN}" 22 | allowedRoutes: 23 | namespaces: 24 | from: Same 25 | - name: https 26 | protocol: HTTPS 27 | port: 443 28 | hostname: "*.${SECRET_DOMAIN}" 29 | allowedRoutes: 30 | namespaces: 31 | from: All 32 | tls: 33 | certificateRefs: 34 | - kind: Secret 35 | name: ${SECRET_DOMAIN/./-}-production-tls 36 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/gateway/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./certificate.yaml 7 | - ./external.yaml 8 | - ./internal.yaml 9 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/cilium/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app cilium 7 | namespace: &namespace kube-system 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/kube-system/cilium/app 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: false 31 | --- 32 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 33 | apiVersion: kustomize.toolkit.fluxcd.io/v1 34 | kind: Kustomization 35 | metadata: 36 | name: &app cilium-gateway 37 | namespace: &namespace kube-system 38 | spec: 39 | commonMetadata: 40 | labels: 41 | app.kubernetes.io/name: *app 42 | decryption: 43 | provider: sops 44 | secretRef: 45 | name: sops-age 46 | dependsOn: 47 | - name: cert-manager 48 | namespace: cert-manager 49 | interval: 1h 50 | path: ./kubernetes/apps/kube-system/cilium/gateway 51 | postBuild: 52 | substituteFrom: 53 | - name: cluster-secrets 54 | kind: Secret 55 | prune: true 56 | sourceRef: 57 | kind: GitRepository 58 | name: flux-system 59 | namespace: flux-system 60 | targetNamespace: *namespace 61 | timeout: 15m 62 | wait: true 63 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/coredns/app/helm/kustomizeconfig.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | nameReference: 3 | - kind: ConfigMap 4 | version: v1 5 | fieldSpecs: 6 | - path: spec/valuesFrom/name 7 | kind: HelmRelease 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/coredns/app/helm/values.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | fullnameOverride: coredns 3 | image: 4 | repository: mirror.gcr.io/coredns/coredns 5 | k8sAppLabelOverride: kube-dns 6 | serviceAccount: 7 | create: true 8 | service: 9 | name: kube-dns 10 | clusterIP: "#{ cluster_svc_cidr | nthhost(10) }#" 11 | replicaCount: 2 12 | servers: 13 | - zones: 14 | - zone: . 15 | scheme: dns:// 16 | use_tcp: true 17 | port: 53 18 | plugins: 19 | - name: errors 20 | - name: health 21 | configBlock: |- 22 | lameduck 5s 23 | - name: ready 24 | - name: kubernetes 25 | parameters: cluster.local in-addr.arpa ip6.arpa 26 | configBlock: |- 27 | pods verified 28 | fallthrough in-addr.arpa ip6.arpa 29 | - name: autopath 30 | parameters: "@kubernetes" 31 | - name: forward 32 | parameters: . /etc/resolv.conf 33 | - name: cache 34 | configBlock: |- 35 | prefetch 20 36 | serve_stale 37 | - name: loop 38 | - name: reload 39 | - name: loadbalance 40 | - name: prometheus 41 | parameters: 0.0.0.0:9153 42 | - name: log 43 | configBlock: |- 44 | class error 45 | affinity: 46 | nodeAffinity: 47 | requiredDuringSchedulingIgnoredDuringExecution: 48 | nodeSelectorTerms: 49 | - matchExpressions: 50 | - key: node-role.kubernetes.io/control-plane 51 | operator: Exists 52 | tolerations: 53 | - key: CriticalAddonsOnly 54 | operator: Exists 55 | - key: node-role.kubernetes.io/control-plane 56 | operator: Exists 57 | effect: NoSchedule 58 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/coredns/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: OCIRepository 5 | metadata: 6 | name: coredns 7 | spec: 8 | interval: 5m 9 | layerSelector: 10 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 11 | operation: copy 12 | url: oci://ghcr.io/coredns/charts/coredns 13 | ref: 14 | tag: 1.42.2 15 | --- 16 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 17 | apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | metadata: 20 | name: coredns 21 | spec: 22 | interval: 1h 23 | chartRef: 24 | kind: OCIRepository 25 | name: coredns 26 | install: 27 | remediation: 28 | retries: -1 29 | upgrade: 30 | cleanupOnFail: true 31 | remediation: 32 | strategy: rollback 33 | retries: 3 34 | valuesFrom: 35 | - kind: ConfigMap 36 | name: coredns-values 37 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/coredns/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | configMapGenerator: 8 | - name: coredns-values 9 | files: 10 | - values.yaml=./helm/values.yaml 11 | configurations: 12 | - ./helm/kustomizeconfig.yaml 13 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/coredns/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app coredns 7 | namespace: &namespace kube-system 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/kube-system/coredns/app 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: false 31 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | namespace: kube-system 6 | components: 7 | - ../../components/common 8 | resources: 9 | - ./cilium/ks.yaml 10 | - ./coredns/ks.yaml 11 | - ./metrics-server/ks.yaml 12 | - ./reloader/ks.yaml 13 | #% if spegel_enabled %# 14 | - ./spegel/ks.yaml 15 | #% endif %# 16 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/metrics-server/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: HelmRepository 5 | metadata: 6 | name: metrics-server 7 | namespace: kube-system # Required for Renovate lookups 8 | spec: 9 | interval: 1h 10 | url: https://kubernetes-sigs.github.io/metrics-server 11 | --- 12 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 13 | apiVersion: helm.toolkit.fluxcd.io/v2 14 | kind: HelmRelease 15 | metadata: 16 | name: metrics-server 17 | spec: 18 | interval: 1h 19 | chart: 20 | spec: 21 | chart: metrics-server 22 | version: 3.12.2 23 | sourceRef: 24 | kind: HelmRepository 25 | name: metrics-server 26 | namespace: kube-system 27 | install: 28 | remediation: 29 | retries: -1 30 | upgrade: 31 | cleanupOnFail: true 32 | remediation: 33 | retries: 3 34 | values: 35 | args: 36 | - --kubelet-insecure-tls 37 | - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname 38 | - --kubelet-use-node-status-port 39 | - --metric-resolution=10s 40 | - --kubelet-request-timeout=2s 41 | metrics: 42 | enabled: true 43 | serviceMonitor: 44 | enabled: true 45 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/metrics-server/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/metrics-server/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app metrics-server 7 | namespace: &namespace kube-system 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/kube-system/metrics-server/app 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: false 31 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/reloader/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: OCIRepository 5 | metadata: 6 | name: reloader 7 | spec: 8 | interval: 5m 9 | layerSelector: 10 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 11 | operation: copy 12 | ref: 13 | tag: 2.1.3 14 | url: oci://ghcr.io/stakater/charts/reloader 15 | --- 16 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 17 | apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | metadata: 20 | name: reloader 21 | spec: 22 | interval: 1h 23 | chartRef: 24 | kind: OCIRepository 25 | name: reloader 26 | install: 27 | remediation: 28 | retries: -1 29 | upgrade: 30 | cleanupOnFail: true 31 | remediation: 32 | retries: 3 33 | values: 34 | fullnameOverride: reloader 35 | reloader: 36 | readOnlyRootFileSystem: true 37 | podMonitor: 38 | enabled: true 39 | namespace: "{{ .Release.Namespace }}" 40 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/reloader/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/reloader/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app reloader 7 | namespace: &namespace kube-system 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/kube-system/reloader/app 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: false 31 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/spegel/app/helm/kustomizeconfig.yaml.j2: -------------------------------------------------------------------------------- 1 | #% if spegel_enabled %# 2 | --- 3 | nameReference: 4 | - kind: ConfigMap 5 | version: v1 6 | fieldSpecs: 7 | - path: spec/valuesFrom/name 8 | kind: HelmRelease 9 | #% endif %# 10 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/spegel/app/helm/values.yaml.j2: -------------------------------------------------------------------------------- 1 | #% if spegel_enabled %# 2 | --- 3 | spegel: 4 | containerdSock: /run/containerd/containerd.sock 5 | containerdRegistryConfigPath: /etc/cri/conf.d/hosts 6 | service: 7 | registry: 8 | hostPort: 29999 9 | serviceMonitor: 10 | enabled: true 11 | grafanaDashboard: 12 | enabled: true 13 | #% endif %# 14 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/spegel/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | #% if spegel_enabled %# 2 | --- 3 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 4 | apiVersion: source.toolkit.fluxcd.io/v1 5 | kind: OCIRepository 6 | metadata: 7 | name: spegel 8 | spec: 9 | interval: 5m 10 | layerSelector: 11 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 12 | operation: copy 13 | ref: 14 | tag: 0.2.0 15 | url: oci://ghcr.io/spegel-org/helm-charts/spegel 16 | --- 17 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 18 | apiVersion: helm.toolkit.fluxcd.io/v2 19 | kind: HelmRelease 20 | metadata: 21 | name: spegel 22 | spec: 23 | interval: 1h 24 | chartRef: 25 | kind: OCIRepository 26 | name: spegel 27 | install: 28 | remediation: 29 | retries: -1 30 | upgrade: 31 | cleanupOnFail: true 32 | remediation: 33 | retries: 3 34 | valuesFrom: 35 | - kind: ConfigMap 36 | name: spegel-values 37 | #% endif %# 38 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/spegel/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | #% if spegel_enabled %# 2 | --- 3 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 4 | apiVersion: kustomize.config.k8s.io/v1beta1 5 | kind: Kustomization 6 | resources: 7 | - ./helmrelease.yaml 8 | configMapGenerator: 9 | - name: spegel-values 10 | files: 11 | - values.yaml=./helm/values.yaml 12 | configurations: 13 | - ./helm/kustomizeconfig.yaml 14 | #% endif %# 15 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/kube-system/spegel/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | #% if spegel_enabled %# 2 | --- 3 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 4 | apiVersion: kustomize.toolkit.fluxcd.io/v1 5 | kind: Kustomization 6 | metadata: 7 | name: &app spegel 8 | namespace: &namespace kube-system 9 | spec: 10 | commonMetadata: 11 | labels: 12 | app.kubernetes.io/name: *app 13 | decryption: 14 | provider: sops 15 | secretRef: 16 | name: sops-age 17 | interval: 1h 18 | path: ./kubernetes/apps/kube-system/spegel/app 19 | postBuild: 20 | substituteFrom: 21 | - name: cluster-secrets 22 | kind: Secret 23 | prune: true 24 | retryInterval: 2m 25 | sourceRef: 26 | kind: GitRepository 27 | name: flux-system 28 | namespace: flux-system 29 | targetNamespace: *namespace 30 | timeout: 5m 31 | wait: false 32 | #% endif %# 33 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-dns/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 3 | apiVersion: helm.toolkit.fluxcd.io/v2 4 | kind: HelmRelease 5 | metadata: 6 | name: &app cloudflare-dns 7 | spec: 8 | interval: 1h 9 | chart: 10 | spec: 11 | chart: external-dns 12 | version: 1.16.1 13 | sourceRef: 14 | kind: HelmRepository 15 | name: external-dns 16 | namespace: flux-system 17 | install: 18 | remediation: 19 | retries: -1 20 | upgrade: 21 | cleanupOnFail: true 22 | remediation: 23 | strategy: rollback 24 | retries: 3 25 | values: 26 | fullnameOverride: *app 27 | provider: cloudflare 28 | env: 29 | - name: CF_API_TOKEN 30 | valueFrom: 31 | secretKeyRef: 32 | name: &secret cloudflare-dns-secret 33 | key: api-token 34 | extraArgs: 35 | - --cloudflare-dns-records-per-page=1000 36 | - --cloudflare-proxied 37 | - --crd-source-apiversion=externaldns.k8s.io/v1alpha1 38 | - --crd-source-kind=DNSEndpoint 39 | - --gateway-name=external 40 | triggerLoopOnEvent: true 41 | policy: sync 42 | sources: ["crd", "gateway-httproute"] 43 | txtPrefix: k8s. 44 | txtOwnerId: default 45 | domainFilters: ["${SECRET_DOMAIN}"] 46 | serviceMonitor: 47 | enabled: true 48 | podAnnotations: 49 | secret.reloader.stakater.com/reload: *secret 50 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-dns/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./secret.sops.yaml 7 | - ./helmrelease.yaml 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-dns/app/secret.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://kubernetesjsonschema.dev/v1.18.1-standalone-strict/secret-v1.json 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: cloudflare-dns-secret 7 | stringData: 8 | api-token: "#{ cloudflare_token }#" 9 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-dns/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app cloudflare-dns 7 | namespace: &namespace network 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/network/cloudflare-dns 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: true 31 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-tunnel/app/dnsendpoint.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/externaldns.k8s.io/dnsendpoint_v1alpha1.json 3 | apiVersion: externaldns.k8s.io/v1alpha1 4 | kind: DNSEndpoint 5 | metadata: 6 | name: cloudflare-tunnel 7 | spec: 8 | endpoints: 9 | - dnsName: "external.${SECRET_DOMAIN}" 10 | recordType: CNAME 11 | targets: ["#{ cloudflare_tunnel_id() }#.cfargotunnel.com"] 12 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-tunnel/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 3 | apiVersion: helm.toolkit.fluxcd.io/v2 4 | kind: HelmRelease 5 | metadata: 6 | name: cloudflare-tunnel 7 | spec: 8 | interval: 1h 9 | chartRef: 10 | kind: OCIRepository 11 | name: app-template 12 | install: 13 | remediation: 14 | retries: -1 15 | upgrade: 16 | cleanupOnFail: true 17 | remediation: 18 | retries: 3 19 | values: 20 | controllers: 21 | cloudflare-tunnel: 22 | strategy: RollingUpdate 23 | annotations: 24 | reloader.stakater.com/auto: "true" 25 | containers: 26 | app: 27 | image: 28 | repository: docker.io/cloudflare/cloudflared 29 | tag: 2025.5.0 30 | env: 31 | NO_AUTOUPDATE: true 32 | TUNNEL_METRICS: 0.0.0.0:8080 33 | TUNNEL_ORIGIN_ENABLE_HTTP2: true 34 | TUNNEL_POST_QUANTUM: true 35 | TUNNEL_TRANSPORT_PROTOCOL: quic 36 | envFrom: 37 | - secretRef: 38 | name: cloudflare-tunnel-secret 39 | args: ["tunnel", "run"] 40 | probes: 41 | liveness: &probes 42 | enabled: true 43 | custom: true 44 | spec: 45 | httpGet: 46 | path: /ready 47 | port: &port 8080 48 | initialDelaySeconds: 0 49 | periodSeconds: 10 50 | timeoutSeconds: 1 51 | failureThreshold: 3 52 | readiness: *probes 53 | securityContext: 54 | allowPrivilegeEscalation: false 55 | readOnlyRootFilesystem: true 56 | capabilities: { drop: ["ALL"] } 57 | resources: 58 | requests: 59 | cpu: 10m 60 | limits: 61 | memory: 256Mi 62 | defaultPodOptions: 63 | securityContext: 64 | runAsNonRoot: true 65 | runAsUser: 65534 66 | runAsGroup: 65534 67 | service: 68 | app: 69 | controller: cloudflare-tunnel 70 | ports: 71 | http: 72 | port: *port 73 | serviceMonitor: 74 | app: 75 | serviceName: cloudflare-tunnel 76 | endpoints: 77 | - port: http 78 | persistence: 79 | config-file: 80 | type: configMap 81 | name: cloudflare-tunnel-configmap 82 | globalMounts: 83 | - path: /etc/cloudflared/config.yaml 84 | subPath: config.yaml 85 | readOnly: true 86 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-tunnel/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./dnsendpoint.yaml 7 | - ./secret.sops.yaml 8 | - ./helmrelease.yaml 9 | configMapGenerator: 10 | - name: cloudflare-tunnel-configmap 11 | files: 12 | - config.yaml=./resources/config.yaml 13 | generatorOptions: 14 | disableNameSuffixHash: true 15 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-tunnel/app/resources/config.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | originRequest: 3 | originServerName: "external.${SECRET_DOMAIN}" 4 | 5 | ingress: 6 | - hostname: "${SECRET_DOMAIN}" 7 | service: &svc https://cilium-gateway-external.kube-system.svc.cluster.local 8 | - hostname: "*.${SECRET_DOMAIN}" 9 | service: *svc 10 | - service: http_status:404 11 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-tunnel/app/secret.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://kubernetesjsonschema.dev/v1.18.1-standalone-strict/secret-v1.json 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: cloudflare-tunnel-secret 7 | stringData: 8 | TUNNEL_TOKEN: "#{ cloudflare_tunnel_secret() }#" 9 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/cloudflare-tunnel/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app cloudflare-tunnel 7 | namespace: &namespace network 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/network/cloudflare-tunnel 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: false 31 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/k8s-gateway/app/helmrelease.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: OCIRepository 5 | metadata: 6 | name: k8s-gateway 7 | spec: 8 | interval: 1h 9 | layerSelector: 10 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 11 | operation: copy 12 | ref: 13 | tag: 3.2.0 14 | url: oci://ghcr.io/k8s-gateway/charts/k8s-gateway 15 | --- 16 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2.json 17 | apiVersion: helm.toolkit.fluxcd.io/v2 18 | kind: HelmRelease 19 | metadata: 20 | name: k8s-gateway 21 | spec: 22 | interval: 1h 23 | chartRef: 24 | kind: OCIRepository 25 | name: k8s-gateway 26 | install: 27 | remediation: 28 | retries: -1 29 | upgrade: 30 | cleanupOnFail: true 31 | remediation: 32 | retries: 3 33 | values: 34 | fullnameOverride: k8s-gateway 35 | domain: "${SECRET_DOMAIN}" 36 | ttl: 1 37 | service: 38 | type: LoadBalancer 39 | port: 53 40 | annotations: 41 | lbipam.cilium.io/ips: "#{ cluster_dns_gateway_addr }#" 42 | externalTrafficPolicy: Cluster 43 | watchedResources: ["HTTPRoute", "Service"] 44 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/k8s-gateway/app/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./helmrelease.yaml 7 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/k8s-gateway/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: &app k8s-gateway 7 | namespace: &namespace network 8 | spec: 9 | commonMetadata: 10 | labels: 11 | app.kubernetes.io/name: *app 12 | decryption: 13 | provider: sops 14 | secretRef: 15 | name: sops-age 16 | interval: 1h 17 | path: ./kubernetes/apps/network/k8s-gateway 18 | postBuild: 19 | substituteFrom: 20 | - name: cluster-secrets 21 | kind: Secret 22 | prune: true 23 | retryInterval: 2m 24 | sourceRef: 25 | kind: GitRepository 26 | name: flux-system 27 | namespace: flux-system 28 | targetNamespace: *namespace 29 | timeout: 5m 30 | wait: false 31 | -------------------------------------------------------------------------------- /templates/config/kubernetes/apps/network/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | namespace: network 6 | components: 7 | - ../../components/common 8 | resources: 9 | - ./cloudflare-dns/ks.yaml 10 | - ./cloudflare-tunnel/ks.yaml 11 | - ./k8s-gateway/ks.yaml 12 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1alpha1 4 | kind: Component 5 | resources: 6 | - ./namespace.yaml 7 | - ./repos 8 | - ./sops 9 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/namespace.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: not-used 6 | annotations: 7 | kustomize.toolkit.fluxcd.io/prune: disabled 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/repos/app-template/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./ocirepository.yaml 7 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/repos/app-template/ocirepository.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/ocirepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: OCIRepository 5 | metadata: 6 | name: app-template 7 | spec: 8 | interval: 5m 9 | layerSelector: 10 | mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip 11 | operation: copy 12 | ref: 13 | tag: 4.0.1 14 | url: oci://ghcr.io/bjw-s-labs/helm/app-template 15 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/repos/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./app-template 7 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/sops/cluster-secrets.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: cluster-secrets 6 | stringData: 7 | SECRET_DOMAIN: "#{ cloudflare_domain }#" 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/sops/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./cluster-secrets.sops.yaml 7 | - ./sops-age.sops.yaml 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/components/common/sops/sops-age.sops.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: sops-age 6 | stringData: 7 | age.agekey: "#{ age_key('private') }#" 8 | -------------------------------------------------------------------------------- /templates/config/kubernetes/flux/cluster/ks.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 3 | apiVersion: kustomize.toolkit.fluxcd.io/v1 4 | kind: Kustomization 5 | metadata: 6 | name: cluster-meta 7 | namespace: &namespace flux-system 8 | spec: 9 | decryption: 10 | provider: sops 11 | secretRef: 12 | name: sops-age 13 | interval: 1h 14 | path: ./kubernetes/flux/meta 15 | prune: true 16 | retryInterval: 2m 17 | sourceRef: 18 | kind: GitRepository 19 | name: flux-system 20 | namespace: flux-system 21 | # Flux repositories under this need flux-system hardcoded as namespace for Renovate lookups 22 | targetNamespace: *namespace 23 | wait: true 24 | --- 25 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/kustomization-kustomize-v1.json 26 | apiVersion: kustomize.toolkit.fluxcd.io/v1 27 | kind: Kustomization 28 | metadata: 29 | name: cluster-apps 30 | namespace: flux-system 31 | spec: 32 | decryption: 33 | provider: sops 34 | secretRef: 35 | name: sops-age 36 | dependsOn: 37 | - name: cluster-meta 38 | namespace: flux-system 39 | interval: 1h 40 | path: ./kubernetes/apps 41 | prune: true 42 | retryInterval: 2m 43 | sourceRef: 44 | kind: GitRepository 45 | name: flux-system 46 | namespace: flux-system 47 | timeout: 5m 48 | wait: false 49 | -------------------------------------------------------------------------------- /templates/config/kubernetes/flux/meta/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./repos 7 | -------------------------------------------------------------------------------- /templates/config/kubernetes/flux/meta/repos/external-dns.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrepository-source-v1.json 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: HelmRepository 5 | metadata: 6 | name: external-dns 7 | namespace: flux-system 8 | spec: 9 | interval: 1h 10 | url: https://kubernetes-sigs.github.io/external-dns 11 | -------------------------------------------------------------------------------- /templates/config/kubernetes/flux/meta/repos/kustomization.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # yaml-language-server: $schema=https://json.schemastore.org/kustomization 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | resources: 6 | - ./external-dns.yaml 7 | -------------------------------------------------------------------------------- /templates/config/talos/patches/README.md.j2: -------------------------------------------------------------------------------- 1 | # Talos Patching 2 | 3 | This directory contains Kustomization patches that are added to the talhelper configuration file. 4 | 5 | 6 | 7 | ## Patch Directories 8 | 9 | Under this `patches` directory, there are several sub-directories that can contain patches that are added to the talhelper configuration file. 10 | Each directory is optional and therefore might not created by default. 11 | 12 | - `global/`: patches that are applied to both the controller and worker configurations 13 | - `controller/`: patches that are applied to the controller configurations 14 | - `worker/`: patches that are applied to the worker configurations 15 | - `${node-hostname}/`: patches that are applied to the node with the specified name 16 | -------------------------------------------------------------------------------- /templates/config/talos/patches/controller/admission-controller-patch.yaml.j2: -------------------------------------------------------------------------------- 1 | - op: remove 2 | path: /cluster/apiServer/admissionControl 3 | -------------------------------------------------------------------------------- /templates/config/talos/patches/controller/cluster.yaml.j2: -------------------------------------------------------------------------------- 1 | cluster: 2 | allowSchedulingOnControlPlanes: true 3 | apiServer: 4 | extraArgs: 5 | # https://kubernetes.io/docs/tasks/extend-kubernetes/configure-aggregation-layer/ 6 | enable-aggregator-routing: true 7 | controllerManager: 8 | extraArgs: 9 | bind-address: 0.0.0.0 10 | coreDNS: 11 | disabled: true 12 | etcd: 13 | extraArgs: 14 | listen-metrics-urls: http://0.0.0.0:2381 15 | advertisedSubnets: 16 | - #{ node_cidr }# 17 | proxy: 18 | disabled: true 19 | scheduler: 20 | extraArgs: 21 | bind-address: 0.0.0.0 22 | -------------------------------------------------------------------------------- /templates/config/talos/patches/global/machine-files.yaml.j2: -------------------------------------------------------------------------------- 1 | machine: 2 | files: 3 | - op: create 4 | path: /etc/cri/conf.d/20-customization.part 5 | permissions: 0o644 6 | content: |- 7 | [plugins."io.containerd.cri.v1.images"] 8 | discard_unpacked_layers = false 9 | -------------------------------------------------------------------------------- /templates/config/talos/patches/global/machine-kubelet.yaml.j2: -------------------------------------------------------------------------------- 1 | machine: 2 | kubelet: 3 | extraConfig: 4 | serializeImagePulls: false 5 | nodeIP: 6 | validSubnets: 7 | - #{ node_cidr }# 8 | -------------------------------------------------------------------------------- /templates/config/talos/patches/global/machine-network.yaml.j2: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | disableSearchDomain: true 4 | nameservers: 5 | #% for item in node_dns_servers %# 6 | - #{ item }# 7 | #% endfor %# 8 | -------------------------------------------------------------------------------- /templates/config/talos/patches/global/machine-sysctls.yaml.j2: -------------------------------------------------------------------------------- 1 | machine: 2 | sysctls: 3 | fs.inotify.max_user_watches: "1048576" # Watchdog 4 | fs.inotify.max_user_instances: "8192" # Watchdog 5 | net.core.rmem_max: "7500000" # Cloudflared | QUIC 6 | net.core.wmem_max: "7500000" # Cloudflared | QUIC 7 | -------------------------------------------------------------------------------- /templates/config/talos/patches/global/machine-time.yaml.j2: -------------------------------------------------------------------------------- 1 | machine: 2 | time: 3 | disabled: false 4 | servers: 5 | #% for item in node_ntp_servers %# 6 | - #{ item }# 7 | #% endfor %# 8 | -------------------------------------------------------------------------------- /templates/config/talos/talconfig.yaml.j2: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/budimanjojo/talhelper/master/pkg/config/schemas/talconfig.json 2 | --- 3 | clusterName: kubernetes 4 | 5 | talosVersion: "${talosVersion}" 6 | kubernetesVersion: "${kubernetesVersion}" 7 | 8 | endpoint: https://#{ cluster_api_addr }#:6443 9 | additionalApiServerCertSans: &sans 10 | - "127.0.0.1" 11 | - "#{ cluster_api_addr }#" 12 | #% for item in cluster_api_tls_sans %# 13 | - "#{ item }#" 14 | #% endfor %# 15 | additionalMachineCertSans: *sans 16 | 17 | clusterPodNets: ["#{ cluster_pod_cidr }#"] 18 | clusterSvcNets: ["#{ cluster_svc_cidr }#"] 19 | 20 | # Disable built-in CNI to use Cilium 21 | cniConfig: 22 | name: none 23 | 24 | nodes: 25 | #% for item in nodes %# 26 | - hostname: "#{ item.name }#" 27 | ipAddress: "#{ item.address }#" 28 | #% if item.disk.startswith('/') %# 29 | installDisk: "#{ item.disk }#" 30 | #% else %# 31 | installDiskSelector: 32 | serial: "#{ item.disk }#" 33 | #% endif %# 34 | machineSpec: 35 | secureboot: #{ (true if item.secureboot else false) | string | lower }# 36 | talosImageURL: factory.talos.dev/installer#{ "-secureboot" if item.secureboot | default(false, true) }#/#{ item.schematic_id }# 37 | controlPlane: #{ (item.controller) | string | lower }# 38 | networkInterfaces: 39 | - deviceSelector: 40 | hardwareAddr: "#{ item.mac_addr | lower }#" 41 | #% if node_vlan_tag %# 42 | vlans: 43 | - vlanId: #{ node_vlan_tag }# 44 | addresses: 45 | - "#{ item.address }#/#{ node_cidr.split('/') | last }#" 46 | mtu: #{ item.mtu | default(1500, true) }# 47 | routes: 48 | - network: "0.0.0.0/0" 49 | gateway: "#{ node_default_gateway }#" 50 | #% if item.controller %# 51 | vip: 52 | ip: "#{ cluster_api_addr }#" 53 | #% endif %# 54 | #% else %# 55 | dhcp: false 56 | addresses: 57 | - "#{ item.address }#/#{ node_cidr.split('/') | last }#" 58 | routes: 59 | - network: "0.0.0.0/0" 60 | gateway: "#{ node_default_gateway }#" 61 | mtu: #{ item.mtu | default(1500, true) }# 62 | #% if item.controller %# 63 | vip: 64 | ip: "#{ cluster_api_addr }#" 65 | #% endif %# 66 | #% endif %# 67 | #% if talos_patches('%s' % (item.name)) | length == 0 %# 68 | #% if item.encrypt_disk | default(false, true) %# 69 | patches: 70 | - # Encrypt system disk with TPM 71 | |- 72 | machine: 73 | systemDiskEncryption: 74 | state: 75 | provider: luks2 76 | keys: 77 | - slot: 0 78 | tpm: {} 79 | ephemeral: 80 | provider: luks2 81 | keys: 82 | - slot: 0 83 | tpm: {} 84 | #% endif %# 85 | #% else %# 86 | #% for file in talos_patches('%s' % (item.name)) %# 87 | #% if loop.index == 1 %# 88 | patches: 89 | #% if item.encrypt_disk | default(false, true) %# 90 | - |- 91 | machine: 92 | systemDiskEncryption: 93 | state: 94 | provider: luks2 95 | keys: 96 | - slot: 0 97 | tpm: {} 98 | ephemeral: 99 | provider: luks2 100 | keys: 101 | - slot: 0 102 | tpm: {} 103 | #% endif %# 104 | #% endif %# 105 | - "@./patches/#{ item.name }#/#{ file | basename }#" 106 | #% endfor %# 107 | #% endif %# 108 | #% endfor %# 109 | 110 | #% for file in talos_patches('global') %# 111 | #% if loop.index == 1 %# 112 | # Global patches 113 | patches: 114 | #% endif %# 115 | - "@./patches/global/#{ file | basename }#" 116 | #% endfor %# 117 | 118 | #% for file in talos_patches('controller') %# 119 | #% if loop.index == 1 %# 120 | # Controller patches 121 | controlPlane: 122 | patches: 123 | #% endif %# 124 | - "@./patches/controller/#{ file | basename }#" 125 | #% endfor %# 126 | 127 | #% if (nodes | selectattr('controller', 'equalto', False) | list | length) and (talos_patches('worker') | length) %# 128 | #% for file in talos_patches('worker') %# 129 | #% if loop.index == 1 %# 130 | # Worker patches 131 | worker: 132 | patches: 133 | #% endif %# 134 | - "@./patches/worker/#{ file | basename }#" 135 | #% endfor %# 136 | #% endif %# 137 | -------------------------------------------------------------------------------- /templates/config/talos/talenv.yaml.j2: -------------------------------------------------------------------------------- 1 | # renovate: datasource=docker depName=ghcr.io/siderolabs/installer 2 | talosVersion: v1.10.3 3 | # renovate: datasource=docker depName=ghcr.io/siderolabs/kubelet 4 | kubernetesVersion: v1.33.1 5 | -------------------------------------------------------------------------------- /templates/overrides/readme.partial.yaml.j2: -------------------------------------------------------------------------------- 1 | #| Place user jinja template overrides in this file's directory |# 2 | #| Docs: https://mirkolenz.github.io/makejinja/makejinja.html |# 3 | #| Example: https://github.com/mirkolenz/makejinja/blob/main/tests/data/makejinja.toml |# 4 | #| Example: https://github.com/mirkolenz/makejinja/blob/main/tests/data/input1/not-empty.yaml.jinja |# 5 | #| Example: https://github.com/mirkolenz/makejinja/blob/main/tests/data/input2/not-empty.yaml.jinja |# 6 | -------------------------------------------------------------------------------- /templates/scripts/plugin.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Any 3 | 4 | import base64 5 | import ipaddress 6 | import makejinja 7 | import re 8 | import json 9 | 10 | 11 | # Return the filename of a path without the j2 extension 12 | def basename(value: str) -> str: 13 | return Path(value).stem 14 | 15 | 16 | # Return the nth host in a CIDR range 17 | def nthhost(value: str, query: int) -> str: 18 | try: 19 | network = ipaddress.ip_network(value, strict=False) 20 | if 0 <= query < network.num_addresses: 21 | return str(network[query]) 22 | except ValueError: 23 | pass 24 | return False 25 | 26 | 27 | # Return the age public or private key from age.key 28 | def age_key(key_type: str, file_path: str = 'age.key') -> str: 29 | try: 30 | with open(file_path, 'r') as file: 31 | file_content = file.read().strip() 32 | if key_type == 'public': 33 | key_match = re.search(r"# public key: (age1[\w]+)", file_content) 34 | if not key_match: 35 | raise ValueError("Could not find public key in the age key file.") 36 | return key_match.group(1) 37 | elif key_type == 'private': 38 | key_match = re.search(r"(AGE-SECRET-KEY-[\w]+)", file_content) 39 | if not key_match: 40 | raise ValueError("Could not find private key in the age key file.") 41 | return key_match.group(1) 42 | else: 43 | raise ValueError("Invalid key type. Use 'public' or 'private'.") 44 | except FileNotFoundError: 45 | raise FileNotFoundError(f"File not found: {file_path}") 46 | except Exception as e: 47 | raise RuntimeError(f"Unexpected error while processing {file_path}: {e}") 48 | 49 | 50 | # Return cloudflare tunnel fields from cloudflare-tunnel.json 51 | def cloudflare_tunnel_id(file_path: str = 'cloudflare-tunnel.json') -> str: 52 | try: 53 | with open(file_path, 'r') as file: 54 | data = json.load(file) 55 | tunnel_id = data.get("TunnelID") 56 | if tunnel_id is None: 57 | raise KeyError(f"Missing 'TunnelID' key in {file_path}") 58 | return tunnel_id 59 | 60 | except FileNotFoundError: 61 | raise FileNotFoundError(f"File not found: {file_path}") 62 | except json.JSONDecodeError: 63 | raise ValueError(f"Could not decode JSON file: {file_path}") 64 | except KeyError as e: 65 | raise KeyError(f"Error in JSON structure: {e}") 66 | except Exception as e: 67 | raise RuntimeError(f"Unexpected error while processing {file_path}: {e}") 68 | 69 | 70 | # Return cloudflare tunnel fields from cloudflare-tunnel.json in TUNNEL_TOKEN format 71 | def cloudflare_tunnel_secret(file_path: str = 'cloudflare-tunnel.json') -> str: 72 | try: 73 | with open(file_path, 'r') as file: 74 | data = json.load(file) 75 | transformed_data = { 76 | "a": data["AccountTag"], 77 | "t": data["TunnelID"], 78 | "s": data["TunnelSecret"] 79 | } 80 | json_string = json.dumps(transformed_data, separators=(',', ':')) 81 | return base64.b64encode(json_string.encode('utf-8')).decode('utf-8') 82 | 83 | except FileNotFoundError: 84 | raise FileNotFoundError(f"File not found: {file_path}") 85 | except json.JSONDecodeError: 86 | raise ValueError(f"Could not decode JSON file: {file_path}") 87 | except KeyError as e: 88 | raise KeyError(f"Missing key in JSON file {file_path}: {e}") 89 | except Exception as e: 90 | raise RuntimeError(f"Unexpected error while processing {file_path}: {e}") 91 | 92 | 93 | # Return the GitHub deploy key from github-deploy.key 94 | def github_deploy_key(file_path: str = 'github-deploy.key') -> str: 95 | try: 96 | with open(file_path, 'r') as file: 97 | return file.read().strip() 98 | except FileNotFoundError: 99 | raise FileNotFoundError(f"File not found: {file_path}") 100 | except Exception as e: 101 | raise RuntimeError(f"Unexpected error while reading {file_path}: {e}") 102 | 103 | 104 | # Return the Flux / GitHub push token from github-push-token.txt 105 | def github_push_token(file_path: str = 'github-push-token.txt') -> str: 106 | try: 107 | with open(file_path, 'r') as file: 108 | return file.read().strip() 109 | except FileNotFoundError: 110 | raise FileNotFoundError(f"File not found: {file_path}") 111 | except Exception as e: 112 | raise RuntimeError(f"Unexpected error while reading {file_path}: {e}") 113 | 114 | 115 | # Return a list of files in the talos patches directory 116 | def talos_patches(value: str) -> list[str]: 117 | path = Path(f'templates/config/talos/patches/{value}') 118 | if not path.is_dir(): 119 | return [] 120 | return [str(f) for f in sorted(path.glob('*.yaml.j2')) if f.is_file()] 121 | 122 | 123 | class Plugin(makejinja.plugin.Plugin): 124 | def __init__(self, data: dict[str, Any]): 125 | self._data = data 126 | 127 | 128 | def data(self) -> makejinja.plugin.Data: 129 | data = self._data 130 | 131 | # Set default values for optional fields 132 | data.setdefault('node_default_gateway', nthhost(data.get('node_cidr'), 1)) 133 | data.setdefault('node_dns_servers', ['1.1.1.1', '1.0.0.1']) 134 | data.setdefault('node_ntp_servers', ['162.159.200.1', '162.159.200.123']) 135 | data.setdefault('cluster_pod_cidr', '10.42.0.0/16') 136 | data.setdefault('cluster_svc_cidr', '10.43.0.0/16') 137 | data.setdefault('repository_branch', 'main') 138 | data.setdefault('repository_visibility', 'public') 139 | data.setdefault('cilium_loadbalancer_mode', 'dsr') 140 | 141 | # If all BGP keys are set, enable BGP 142 | bgp_keys = ['cilium_bgp_router_addr', 'cilium_bgp_router_asn', 'cilium_bgp_node_asn'] 143 | bgp_enabled = all(data.get(key) for key in bgp_keys) 144 | data.setdefault('cilium_bgp_enabled', bgp_enabled) 145 | 146 | # If there is more than one node, enable spegel 147 | spegel_enabled = len(data.get('nodes')) > 1 148 | data.setdefault('spegel_enabled', spegel_enabled) 149 | 150 | return data 151 | 152 | 153 | def filters(self) -> makejinja.plugin.Filters: 154 | return [ 155 | basename, 156 | nthhost 157 | ] 158 | 159 | 160 | def functions(self) -> makejinja.plugin.Functions: 161 | return [ 162 | age_key, 163 | cloudflare_tunnel_id, 164 | cloudflare_tunnel_secret, 165 | github_deploy_key, 166 | github_push_token, 167 | talos_patches 168 | ] 169 | --------------------------------------------------------------------------------