├── .devcontainer ├── Dockerfile ├── devcontainer.json ├── post-create.sh └── post-start.sh ├── .github ├── dependabot.yml └── workflows │ ├── generate-cluster-claim.yml │ ├── release.yaml │ ├── validate-apps-workflow.yml │ └── validate-cluster-claim.yml ├── .gitignore ├── README.md ├── argocd ├── application │ └── cluster-application.yaml └── appsets │ ├── appset_kyverno.yaml │ └── appset_kyverno_policies.yaml ├── backstage ├── .dockerignore ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── README.md ├── app-config.yaml ├── backstage.json ├── catalog-info.yaml ├── catalog │ ├── components.yaml │ ├── components │ │ ├── argocd.yaml │ │ ├── backstage.yaml │ │ ├── crossplane.yaml │ │ ├── kyverno-repo.yaml │ │ └── kyverno.yaml │ ├── resources.yaml │ ├── resources │ │ └── hub.yaml │ ├── system.yaml │ ├── templates.yaml │ └── templates │ │ ├── application │ │ ├── skeleton │ │ │ └── ${{values.application_name}}.Application.yaml │ │ └── template.yaml │ │ └── cluster │ │ ├── aks │ │ ├── skeleton │ │ │ └── ${{values.cluster_id}}.AKSCluster.yaml │ │ └── template.yaml │ │ └── eks │ │ ├── skeleton │ │ └── ${{values.cluster_id}}.EKSCluster.yaml │ │ └── template.yaml ├── lerna.json ├── package.json ├── packages │ ├── README.md │ ├── app │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── e2e-tests │ │ │ └── app.test.ts │ │ ├── package.json │ │ ├── public │ │ │ ├── android-chrome-192x192.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── robots.txt │ │ │ └── safari-pinned-tab.svg │ │ └── src │ │ │ ├── App.test.tsx │ │ │ ├── App.tsx │ │ │ ├── apis.ts │ │ │ ├── components │ │ │ ├── Root │ │ │ │ ├── LogoFull.tsx │ │ │ │ ├── LogoIcon.tsx │ │ │ │ ├── Root.tsx │ │ │ │ └── index.ts │ │ │ ├── catalog │ │ │ │ └── EntityPage.tsx │ │ │ └── search │ │ │ │ └── SearchPage.tsx │ │ │ ├── index.tsx │ │ │ └── setupTests.ts │ └── backend │ │ ├── .eslintrc.js │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── package.json │ │ └── src │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── plugins │ │ ├── app.ts │ │ ├── auth.ts │ │ ├── catalog.ts │ │ ├── crossplane.ts │ │ ├── kubernetes.ts │ │ ├── proxy.ts │ │ ├── scaffolder.ts │ │ ├── search.ts │ │ ├── techdocs.ts │ │ └── vault.ts │ │ └── types.ts ├── playwright.config.ts ├── plugins │ └── README.md ├── tsconfig.json └── yarn.lock ├── bundle ├── .dockerignore ├── helpers.sh ├── kind.cluster.config ├── manifests │ ├── back-stack.yaml │ ├── ess-plugin-vault.yaml │ ├── ingress-nginx │ │ ├── values-eks.yaml │ │ └── values-kind.yaml │ └── providers │ │ ├── provider-configs.yaml │ │ ├── providers.yaml │ │ └── runtime-config.yaml ├── porter.yaml └── template.Dockerfile ├── catalog-info.yaml ├── crossplane ├── apis │ ├── cluster │ │ ├── aks │ │ │ ├── aks │ │ │ │ ├── composition.yaml │ │ │ │ └── definition.yaml │ │ │ ├── composition.yaml │ │ │ ├── definition.yaml │ │ │ └── network │ │ │ │ ├── basic │ │ │ │ └── composition.yaml │ │ │ │ └── definition.yaml │ │ └── eks │ │ │ ├── composition.yaml │ │ │ ├── definition.yaml │ │ │ ├── eks │ │ │ ├── composition.yaml │ │ │ └── definition.yaml │ │ │ └── network │ │ │ ├── basic │ │ │ └── composition.yaml │ │ │ └── definition.yaml │ └── hub │ │ ├── composition.yaml │ │ └── definition.yaml └── crossplane.yaml └── kyverno └── policies ├── crossplane ├── limit-node-count-policy.yaml ├── restrict-node-size-policy.yaml └── validate-cluster-name-policy.yaml └── pod-security ├── disallow-capabilities.yaml ├── disallow-host-namespaces.yaml ├── disallow-host-path.yaml ├── disallow-privileged-containers.yaml ├── require-run-as-nonroot.yaml ├── require_pod_requests_limits.yaml ├── require_probes.yaml ├── require_ro_rootfs.yaml ├── restrict-privileged-port.yaml ├── restrict-seccomp.yaml └── restrict_image_registries.yaml /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/ubuntu/.devcontainer/base.Dockerfile 2 | FROM mcr.microsoft.com/vscode/devcontainers/base:0-ubuntu-22.04 3 | 4 | # Select desired TAG from https://github.com/argoproj/argo-cd/releases 5 | ENV ARGOCD_VERSION=v2.9.3 6 | RUN curl -sSL -o /usr/local/bin/argocd "https://github.com/argoproj/argo-cd/releases/download/${ARGOCD_VERSION}/argocd-linux-amd64" \ 7 | && chmod +x /usr/local/bin/argocd 8 | 9 | # Same K8s version that kind creates. 10 | ENV KUBECTL_VERSION=v1.27.3 11 | RUN curl -Lo /usr/local/bin/kubectl "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" \ 12 | && chmod +x /usr/local/bin/kubectl 13 | 14 | # https://github.com/helm/helm/releases 15 | ENV HELM_VERSION=v3.13.2 16 | RUN curl -Lo /tmp/helm.tar.gz https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz \ 17 | && tar -xvf /tmp/helm.tar.gz -C /tmp \ 18 | && mv /tmp/linux-amd64/helm /usr/local/bin/helm \ 19 | && chmod +x /usr/local/bin/helm 20 | 21 | # https://releases.crossplane.io/stable/ 22 | ENV CROSSPLANE_VERSION=v1.14.4 23 | RUN curl -Lo /usr/local/bin/crossplane "https://releases.crossplane.io/stable/${CROSSPLANE_VERSION}/bin/linux_amd64/crank" \ 24 | && chmod +x /usr/local/bin/crossplane 25 | 26 | # Used for Backstage development. 27 | ENV NODE_MAJOR=18 28 | RUN apt-get update \ 29 | && apt-get install -y ca-certificates curl gnupg \ 30 | && mkdir -p /etc/apt/keyrings \ 31 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ 32 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ 33 | && apt-get update \ 34 | && apt-get install -y nodejs \ 35 | && npm install --global yarn 36 | 37 | # https://github.com/kubernetes-sigs/kustomize/releases 38 | ENV KUSTOMIZE_VERSION=v5.3.0 39 | RUN curl -Lo /tmp/kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64.tar.gz \ 40 | && tar -xvf /tmp/kustomize.tar.gz -C /tmp \ 41 | && mv /tmp/kustomize /usr/local/bin/kustomize \ 42 | && chmod +x /usr/local/bin/kustomize 43 | 44 | ENV YQ_VERSION=v4.40.5 45 | RUN wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64.tar.gz -O - | tar xz \ 46 | && mv yq_linux_amd64 /usr/bin/yq 47 | 48 | RUN apt-get clean -y && rm -rf /var/lib/apt/lists/* 49 | RUN mkdir /usr/local/etc/bash_completion.d 50 | 51 | USER vscode 52 | 53 | ENV PORTER_VERSION="v1.0.14" 54 | RUN curl -L https://cdn.porter.sh/$PORTER_VERSION/install-linux.sh | bash 55 | RUN echo 'PATH="$HOME/.porter:$PATH"' >> ~/.profile \ 56 | && porter completion bash | sudo tee /usr/local/etc/bash_completion.d/porter 57 | 58 | RUN echo alias k=kubectl >> ~/.bashrc \ 59 | && mkdir ~/.kube 60 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/kubernetes-helm-minikube 3 | { 4 | "name": "back-stack/showcase", 5 | // Use a Dockerfile or Docker Compose file. 6 | // https://containers.dev/guide/dockerfile 7 | "build": { 8 | "dockerfile": "Dockerfile" 9 | }, 10 | 11 | "features": { 12 | "ghcr.io/devcontainers/features/docker-in-docker:2": { 13 | "enableNonRootDocker": "true", 14 | "moby": "true" 15 | } 16 | }, 17 | 18 | "hostRequirements": { 19 | "cpus": 4 20 | }, 21 | 22 | 23 | // Configure tool-specific properties. 24 | "customizations": { 25 | // Configure properties specific to VS Code. 26 | "vscode": { 27 | // Set *default* container specific settings.json values on container create. 28 | "settings": { 29 | "files.insertFinalNewline": true, 30 | }, 31 | // Add the IDs of extensions you want installed when the container is created. 32 | "extensions": [ 33 | "github.vscode-github-actions", 34 | ] 35 | } 36 | }, 37 | 38 | // Use 'postCreateCommand' to run commands everytime the container is created. 39 | "postCreateCommand": "/bin/bash -c .devcontainer/post-create.sh", 40 | 41 | // Use 'postStartCommand' to run commands everytime the container is started. 42 | "postStartCommand": "/bin/bash -c .devcontainer/post-start.sh" 43 | 44 | } 45 | -------------------------------------------------------------------------------- /.devcontainer/post-create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "post-create start" >> ~/status 4 | 5 | # This runs each time the container is created. 6 | 7 | echo "post-create complete" >> ~/status 8 | -------------------------------------------------------------------------------- /.devcontainer/post-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "post-start start" >> ~/status 4 | 5 | # This runs each time the container starts. 6 | 7 | echo "post-start complete" >> ~/status 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "docker" # See documentation for possible values 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "npm" # See documentation for possible values 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | - package-ecosystem: "devcontainers" # See documentation for possible values 17 | directory: "/" 18 | schedule: 19 | interval: "weekly" 20 | - package-ecosystem: "github-actions" 21 | directory: "/" 22 | schedule: 23 | interval: "weekly" 24 | -------------------------------------------------------------------------------- /.github/workflows/generate-cluster-claim.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow that is manually triggered 2 | 3 | name: Generate cluster claim workflow 4 | 5 | # Controls when the action will run. Workflow runs when manually triggered using the UI 6 | # or API. 7 | on: 8 | workflow_dispatch: 9 | # Inputs the workflow accepts. 10 | inputs: 11 | clustername: 12 | # Friendly description to be shown in the UI instead of 'name' 13 | description: "Cluster name" 14 | # Default value if no value is explicitly provided 15 | default: "cluster" 16 | # Input has to be provided for the workflow to run 17 | required: true 18 | # The data type of the input 19 | type: string 20 | numNodes: 21 | # Friendly description to be shown in the UI instead of 'name' 22 | description: "Number of nodes in this cluster" 23 | # Default value if no value is explicitly provided 24 | default: "1" 25 | # Input has to be provided for the workflow to run 26 | required: true 27 | # The data type of the input 28 | type: string 29 | 30 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 31 | jobs: 32 | # This workflow contains a single job to generate the cluster claim yaml 33 | add-cluster-yaml: 34 | # The type of runner that the job will run on 35 | runs-on: ubuntu-latest 36 | 37 | # Steps represent a sequence of tasks that will be executed as part of the job 38 | steps: 39 | - name: Checkout template 40 | uses: actions/checkout@v4 41 | 42 | # Runs a single command using the runners shell 43 | - name: Generate cluster claim 44 | run: | 45 | cp demo/template/cluster-claim-template.yaml ./demo/clusters/${{ inputs.clustername }}-cluster.yaml 46 | sed -i 's/{{cluster-name}}/${{ inputs.clustername }}/g' ./demo/clusters/${{ inputs.clustername }}-cluster.yaml 47 | sed -i 's/{{num-nodes}}/${{ inputs.numNodes }}/g' ./demo/clusters/${{ inputs.clustername }}-cluster.yaml 48 | 49 | - name: Install Kyverno CLI 50 | uses: kyverno/action-install-cli@v0.2.0 51 | 52 | - name: Check Kyverno install 53 | run: kyverno version 54 | 55 | - name: Validate cluster claim 56 | run: | 57 | kyverno apply ./kyverno/policies/crossplane -b main --resource=./demo/clusters/${{ inputs.clustername }}-cluster.yaml 58 | 59 | - name: Setup git config 60 | run: | 61 | # setup the username and email. I tend to use 'GitHub Actions Bot' with no email by default 62 | git config user.name "GitHub Actions Bot" 63 | git config user.email "<>" 64 | - name: Commit file 65 | run: | 66 | git add ./demo/clusters/${{ inputs.clustername }}-cluster.yaml 67 | git commit -m "Created cluster claim" 68 | git push origin 69 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: {} 8 | 9 | env: 10 | BACK_STACK_VERSION: v0.1.0 11 | REGISTRY: ghcr.io 12 | IMAGE_NAME: ${{ github.repository }} 13 | CROSSPLANE_VERSION: v1.14.4 14 | PORTER_VERSION: v1.0.15 15 | PORTER_MIXIN_DOCKER_VERSION: v1.0.2 16 | PORTER_MIXIN_KUBERNETES_VERSION: v1.0.3 17 | PORTER_MIXIN_HELM_VERSION: v1.0.1 18 | PORTER_MIXIN_HELM_URL: https://mchorfa.github.io/porter-helm3/atom.xml 19 | 20 | jobs: 21 | configuration: 22 | runs-on: ubuntu-22.04 23 | 24 | permissions: 25 | contents: read 26 | packages: write 27 | 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | 34 | - name: Fetch Package Tag 35 | run: echo "VERSION_TAG=$(git rev-parse --short=8 HEAD)" >> $GITHUB_OUTPUT 36 | id: tag 37 | 38 | - name: Log into ${{ env.REGISTRY }} 39 | uses: docker/login-action@v3.3.0 40 | with: 41 | registry: ${{ env.REGISTRY }} 42 | username: ${{ github.actor }} 43 | password: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | - name: Install crossplane CLI 46 | run: | 47 | curl -Lo /usr/local/bin/crossplane "https://releases.crossplane.io/stable/${{ env.CROSSPLANE_VERSION }}/bin/linux_amd64/crank" \ 48 | && chmod +x /usr/local/bin/crossplane 49 | 50 | - name: Build Configuration Package 51 | run: | 52 | crossplane xpkg build --package-root=crossplane/ -o crossplane/back-stack.xpkg 53 | 54 | - name: Push ${{ steps.tag.outputs.VERSION_TAG }} & latest 55 | run: | 56 | crossplane xpkg push -f crossplane/back-stack.xpkg ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-configuration:${{ steps.tag.outputs.VERSION_TAG }} 57 | crossplane xpkg push -f crossplane/back-stack.xpkg ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-configuration:latest 58 | 59 | backstage: 60 | runs-on: ubuntu-latest 61 | 62 | permissions: 63 | contents: read 64 | packages: write 65 | 66 | steps: 67 | - uses: actions/checkout@v4 68 | with: 69 | fetch-depth: 0 70 | 71 | - name: Set tag 72 | run: echo "VERSION_TAG=$(git rev-parse --short=8 HEAD)" >> $GITHUB_OUTPUT 73 | id: tag 74 | 75 | - name: Set Node.js 18.x 76 | uses: actions/setup-node@v4.0.4 77 | with: 78 | node-version: 18.x 79 | 80 | - name: Log in to the Container registry 81 | uses: docker/login-action@v3.3.0 82 | with: 83 | registry: ${{ env.REGISTRY }} 84 | username: ${{ github.actor }} 85 | password: ${{ secrets.GITHUB_TOKEN }} 86 | 87 | - name: Run yarn install 88 | uses: borales/actions-yarn@v5 89 | with: 90 | dir: backstage 91 | cmd: install 92 | 93 | - name: Build backend bundle 94 | uses: borales/actions-yarn@v5 95 | with: 96 | dir: backstage 97 | cmd: build:backend 98 | 99 | - name: Build the image 100 | uses: borales/actions-yarn@v5 101 | with: 102 | dir: backstage 103 | cmd: build-image --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backstage:${{ steps.tag.outputs.VERSION_TAG }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backstage:latest 104 | 105 | # Use docker push command directly to work with docker login action. 106 | - name: Push the image 107 | run: | 108 | docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backstage:${{ steps.tag.outputs.VERSION_TAG }} 109 | docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backstage:latest 110 | 111 | bundle: 112 | runs-on: ubuntu-latest 113 | 114 | permissions: 115 | contents: read 116 | packages: write 117 | 118 | steps: 119 | - uses: actions/checkout@v4 120 | with: 121 | fetch-depth: 0 122 | 123 | - name: Set tag 124 | run: echo "VERSION_TAG=$(git rev-parse --short=8 HEAD)" >> $GITHUB_OUTPUT 125 | id: tag 126 | 127 | - name: Log in to the Container registry 128 | uses: docker/login-action@v3.3.0 129 | with: 130 | registry: ${{ env.REGISTRY }} 131 | username: ${{ github.actor }} 132 | password: ${{ secrets.GITHUB_TOKEN }} 133 | 134 | - name: Install porter 135 | run: | 136 | curl -L https://cdn.porter.sh/$PORTER_VERSION/install-linux.sh | bash 137 | echo "/home/runner/.porter" >> $GITHUB_PATH 138 | 139 | - name: Install porter mixins 140 | run: | 141 | porter mixin install docker --version $PORTER_MIXIN_DOCKER_VERSION && porter mixin install kubernetes --version $PORTER_MIXIN_KUBERNETES_VERSION && porter mixin install helm3 --feed-url $PORTER_MIXIN_HELM_URL --version $PORTER_MIXIN_HELM_VERSION 142 | 143 | - name: Build Porter bundle 144 | run: | 145 | porter build --dir bundle --version $BACK_STACK_VERSION+${{ steps.tag.outputs.VERSION_TAG }} 146 | 147 | - name: Publish Porter bundle 148 | run: | 149 | porter publish --dir bundle --registry ${{ env.REGISTRY }}/back-stack 150 | porter publish --dir bundle --registry ${{ env.REGISTRY }}/back-stack --tag latest --force 151 | -------------------------------------------------------------------------------- /.github/workflows/validate-apps-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Validate applications workflow 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | paths: 8 | - "demo/applications/*.yaml" 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | # This workflow contains a single job to validate applications 13 | validate-application-yaml: 14 | # The type of runner that the job will run on 15 | runs-on: ubuntu-latest 16 | 17 | # Steps represent a sequence of tasks that will be executed as part of the job 18 | steps: 19 | - name: Checkout template 20 | uses: actions/checkout@v4 21 | 22 | - name: Install Kyverno CLI 23 | uses: kyverno/action-install-cli@v0.2.0 24 | 25 | - name: Check Kyverno install 26 | run: kyverno version 27 | 28 | - name: Validate application 29 | run: | 30 | kyverno apply ./kyverno/policies/pod-security -b main --resource=./demo/applications 31 | -------------------------------------------------------------------------------- /.github/workflows/validate-cluster-claim.yml: -------------------------------------------------------------------------------- 1 | name: Validate cluster claim workflow 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | paths: 8 | - "demo/clusters/*.yaml" 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | # This workflow contains a single job to generate the cluster claim yaml 13 | validate-cluster-yaml: 14 | # The type of runner that the job will run on 15 | runs-on: ubuntu-latest 16 | 17 | # Steps represent a sequence of tasks that will be executed as part of the job 18 | steps: 19 | - name: Checkout template 20 | uses: actions/checkout@v4 21 | 22 | - name: Install Kyverno CLI 23 | uses: kyverno/action-install-cli@v0.2.0 24 | 25 | - name: Check Kyverno install 26 | run: kyverno version 27 | 28 | - name: Validate cluster claim 29 | run: | 30 | kyverno apply ./kyverno/policies/crossplane -b main --resource=./demo/clusters 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .cnab 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The BACK Stack 2 | 3 | Find out more [backstack.dev](https://backstack.dev) 4 | 5 | ## Install the BACK Stack 6 | 7 | Refer to our [Documentation](https://backstack.dev/intro) 8 | 9 | ## Keep up with Developments 10 | 11 | Reach out on the #backstack channel on [the CNCF Slack](https://slack.cncf.io/) with any feedback or questions! 12 | Also, catch new developments [News](https://backstack.dev/blog) 13 | 14 | Watch the KubeCon NA 2023 session: [Introducing the BACK Stack!](https://youtu.be/SMlR12uwMLs) 15 | -------------------------------------------------------------------------------- /argocd/application/cluster-application.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: clusters 5 | namespace: argocd 6 | spec: 7 | project: default 8 | source: 9 | repoURL: repository 10 | path: demo/clusters 11 | targetRevision: HEAD 12 | destination: 13 | name: in-cluster 14 | namespace: default -------------------------------------------------------------------------------- /argocd/appsets/appset_kyverno.yaml: -------------------------------------------------------------------------------- 1 | kind: ApplicationSet 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: kyverno 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - clusters: 9 | selector: 10 | matchLabels: 11 | clusterType: spoke 12 | template: 13 | metadata: 14 | name: "{{name}}-kyverno" 15 | annotations: 16 | argocd.argoproj.io/sync-wave: "-1" 17 | spec: 18 | project: "default" 19 | source: 20 | repoURL: https://github.com/kyverno/kyverno 21 | targetRevision: release-1.11 22 | path: charts/kyverno 23 | helm: 24 | releaseName: "{{name}}-kyverno" 25 | valueFiles: 26 | - "values.yaml" 27 | destination: 28 | server: "{{server}}" 29 | namespace: kyverno 30 | syncPolicy: 31 | syncOptions: 32 | - Replace=true 33 | - CreateNamespace=true 34 | automated: 35 | selfHeal: true 36 | retry: 37 | limit: 30 38 | backoff: 39 | duration: 5s 40 | factor: 2 41 | maxDuration: 3m0s 42 | -------------------------------------------------------------------------------- /argocd/appsets/appset_kyverno_policies.yaml: -------------------------------------------------------------------------------- 1 | kind: ApplicationSet 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: kyverno-policies 5 | namespace: argocd 6 | spec: 7 | generators: 8 | - clusters: 9 | selector: 10 | matchLabels: 11 | clusterType: spoke 12 | template: 13 | metadata: 14 | name: "{{name}}-kyverno-policies" 15 | annotations: 16 | argocd.argoproj.io/sync-wave: "-1" 17 | spec: 18 | project: "default" 19 | source: 20 | repoURL: https://github.com/kyverno/kyverno 21 | targetRevision: release-1.11 22 | path: charts/kyverno-policies 23 | helm: 24 | releaseName: "{{name}}-kyverno-policies" 25 | valueFiles: 26 | - "values.yaml" 27 | destination: 28 | server: "{{server}}" 29 | namespace: kyverno 30 | syncPolicy: 31 | syncOptions: 32 | - Replace=true 33 | automated: 34 | selfHeal: true 35 | retry: 36 | limit: 30 37 | backoff: 38 | duration: 5s 39 | factor: 2 40 | maxDuration: 3m0s 41 | -------------------------------------------------------------------------------- /backstage/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .yarn/cache 3 | .yarn/install-state.gz 4 | node_modules 5 | packages/*/src 6 | packages/*/node_modules 7 | plugins 8 | *.local.yaml 9 | -------------------------------------------------------------------------------- /backstage/.eslintignore: -------------------------------------------------------------------------------- 1 | playwright.config.ts 2 | -------------------------------------------------------------------------------- /backstage/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | }; 4 | -------------------------------------------------------------------------------- /backstage/.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | 12 | # Coverage directory generated when running tests with coverage 13 | coverage 14 | 15 | # Dependencies 16 | node_modules/ 17 | 18 | # Yarn 3 files 19 | .pnp.* 20 | .yarn/* 21 | !.yarn/patches 22 | !.yarn/plugins 23 | !.yarn/releases 24 | !.yarn/sdks 25 | !.yarn/versions 26 | 27 | # Node version directives 28 | .nvmrc 29 | 30 | # dotenv environment variables file 31 | .env 32 | .env.test 33 | 34 | # Build output 35 | dist 36 | dist-types 37 | 38 | # Temporary change files created by Vim 39 | *.swp 40 | 41 | # MkDocs build output 42 | site 43 | 44 | # Local configuration files 45 | *.local.yaml 46 | 47 | # Sensitive credentials 48 | *-credentials.yaml 49 | 50 | # vscode database functionality support files 51 | *.session.sql 52 | 53 | # E2E test reports 54 | e2e-test-report/ 55 | -------------------------------------------------------------------------------- /backstage/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-types 3 | coverage 4 | .vscode 5 | -------------------------------------------------------------------------------- /backstage/README.md: -------------------------------------------------------------------------------- 1 | # [Backstage](https://backstage.io) 2 | 3 | This is your newly scaffolded Backstage App, Good Luck! 4 | 5 | To start the app, run: 6 | 7 | ```sh 8 | yarn install 9 | yarn dev 10 | ``` 11 | -------------------------------------------------------------------------------- /backstage/app-config.yaml: -------------------------------------------------------------------------------- 1 | app: 2 | title: Scaffolded Backstage App 3 | baseUrl: http://localhost:3000 4 | 5 | organization: 6 | name: The BACK Stack 7 | 8 | backend: 9 | # Used for enabling authentication, secret is shared by all backend plugins 10 | # See https://backstage.io/docs/auth/service-to-service-auth for 11 | # information on the format 12 | # auth: 13 | # keys: 14 | # - secret: ${BACKEND_SECRET} 15 | baseUrl: http://localhost:7007 16 | listen: 17 | port: 7007 18 | # Uncomment the following host directive to bind to specific interfaces 19 | # host: 127.0.0.1 20 | csp: 21 | connect-src: ["'self'", 'http:', 'https:'] 22 | # Content-Security-Policy directives follow the Helmet format: https://helmetjs.github.io/#reference 23 | # Default Helmet Content-Security-Policy values can be removed by setting the key to false 24 | cors: 25 | origin: http://localhost:3000 26 | methods: [GET, HEAD, PATCH, POST, PUT, DELETE] 27 | credentials: true 28 | # This is for local development only, it is not recommended to use this in production 29 | # The production database configuration is stored in app-config.production.yaml 30 | database: 31 | client: better-sqlite3 32 | connection: ':memory:' 33 | # workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir 34 | 35 | integrations: 36 | github: 37 | - host: github.com 38 | # This is a Personal Access Token or PAT from GitHub. You can find out how to generate this token, and more information 39 | # about setting up the GitHub integration here: https://backstage.io/docs/getting-started/configuration#setting-up-a-github-integration 40 | token: ${GITHUB_TOKEN} 41 | ### Example for how to add your GitHub Enterprise instance using the API: 42 | # - host: ghe.example.net 43 | # apiBaseUrl: https://ghe.example.net/api/v3 44 | # token: ${GHE_TOKEN} 45 | 46 | proxy: 47 | ### Example for how to add a proxy endpoint for the frontend. 48 | ### A typical reason to do this is to handle HTTPS and CORS for internal services. 49 | # endpoints: 50 | # '/test': 51 | # target: 'https://example.com' 52 | # changeOrigin: true 53 | 54 | # Reference documentation http://backstage.io/docs/features/techdocs/configuration 55 | # Note: After experimenting with basic setup, use CI/CD to generate docs 56 | # and an external cloud storage when deploying TechDocs for production use-case. 57 | # https://backstage.io/docs/features/techdocs/how-to-guides#how-to-migrate-from-techdocs-basic-to-recommended-deployment-approach 58 | techdocs: 59 | builder: 'local' # Alternatives - 'external' 60 | generator: 61 | runIn: 'docker' # Alternatives - 'local' 62 | publisher: 63 | type: 'local' # Alternatives - 'googleGcs' or 'awsS3'. Read documentation for using alternatives. 64 | 65 | auth: 66 | # see https://backstage.io/docs/auth/ to learn about auth providers 67 | providers: {} 68 | 69 | scaffolder: 70 | # see https://backstage.io/docs/features/software-templates/configuration for software template options 71 | 72 | catalog: 73 | import: 74 | entityFilename: catalog-info.yaml 75 | pullRequestBranchName: backstage-integration 76 | rules: 77 | - allow: [Component, System, API, Resource, Location] 78 | locations: 79 | # Local example data, file locations are relative to the backend process, typically `packages/backend` 80 | - type: file 81 | target: ../../examples/entities.yaml 82 | 83 | # Local example template 84 | - type: file 85 | target: ../../examples/template/template.yaml 86 | rules: 87 | - allow: [Template] 88 | 89 | # Local example organizational data 90 | - type: file 91 | target: ../../examples/org.yaml 92 | rules: 93 | - allow: [User, Group] 94 | 95 | ## Uncomment these lines to add more example data 96 | # - type: url 97 | # target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all.yaml 98 | 99 | ## Uncomment these lines to add an example org 100 | # - type: url 101 | # target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml 102 | # rules: 103 | # - allow: [User, Group] 104 | -------------------------------------------------------------------------------- /backstage/backstage.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.19.0" 3 | } 4 | -------------------------------------------------------------------------------- /backstage/catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: backstage 5 | description: An example of a Backstage application. 6 | # Example for optional annotations 7 | # annotations: 8 | # github.com/project-slug: backstage/backstage 9 | # backstage.io/techdocs-ref: dir:. 10 | spec: 11 | type: website 12 | owner: john@example.com 13 | lifecycle: experimental 14 | -------------------------------------------------------------------------------- /backstage/catalog/components.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Location 3 | metadata: 4 | name: components 5 | description: A collection of all components 6 | spec: 7 | targets: 8 | - ./components/backstage.yaml 9 | - ./components/argocd.yaml 10 | - ./components/crossplane.yaml 11 | - ./components/kyverno.yaml -------------------------------------------------------------------------------- /backstage/catalog/components/argocd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | annotations: 5 | backstage.io/kubernetes-label-selector: app.kubernetes.io/part-of=argocd 6 | name: argocd 7 | description: Gitops Platform 8 | links: 9 | - url: https://argocd-7f000001.nip.io 10 | title: ArgoCD Dashboard 11 | icon: dashboard 12 | spec: 13 | type: service 14 | lifecycle: production 15 | owner: infrastructure 16 | system: back-stack -------------------------------------------------------------------------------- /backstage/catalog/components/backstage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: backstage 5 | description: Software Catalog 6 | spec: 7 | type: service 8 | lifecycle: production 9 | owner: infrastructure 10 | system: back-stack -------------------------------------------------------------------------------- /backstage/catalog/components/crossplane.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: crossplane 5 | description: Universal Control Plane 6 | spec: 7 | type: service 8 | lifecycle: production 9 | owner: infrastructure 10 | system: back-stack -------------------------------------------------------------------------------- /backstage/catalog/components/kyverno-repo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: kyverno-policy-repository 5 | description: Bring your own Policies 6 | spec: 7 | type: service 8 | lifecycle: production 9 | owner: infrastructure 10 | system: back-stack 11 | -------------------------------------------------------------------------------- /backstage/catalog/components/kyverno.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: kyverno 5 | description: Policy as Code 6 | spec: 7 | type: service 8 | lifecycle: production 9 | owner: infrastructure 10 | system: back-stack -------------------------------------------------------------------------------- /backstage/catalog/resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Location 3 | metadata: 4 | name: resources 5 | description: A collection of all resources 6 | spec: 7 | targets: 8 | - ./resources/hub.yaml 9 | -------------------------------------------------------------------------------- /backstage/catalog/resources/hub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Resource 3 | metadata: 4 | name: hub 5 | annotations: 6 | kubernetes.io/api-server: https://kubernetes.default.svc 7 | kubernetes.io/api-server-certificate-authority: '' 8 | kubernetes.io/auth-provider: serviceAccount 9 | kubernetes.io/skip-metrics-lookup: 'true' 10 | spec: 11 | type: kubernetes-cluster 12 | system: back-stack 13 | owner: infrastructure 14 | -------------------------------------------------------------------------------- /backstage/catalog/system.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: System 3 | metadata: 4 | name: back-stack 5 | description: The BACK stack 6 | spec: 7 | owner: infrastructure -------------------------------------------------------------------------------- /backstage/catalog/templates.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Location 3 | metadata: 4 | name: templates 5 | description: A collection of all templates 6 | spec: 7 | targets: 8 | - ./templates/cluster/eks/template.yaml 9 | - ./templates/cluster/aks/template.yaml 10 | - ./templates/application/template.yaml -------------------------------------------------------------------------------- /backstage/catalog/templates/application/skeleton/${{values.application_name}}.Application.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: ${{ values.application_name }} 5 | namespace: argocd 6 | spec: 7 | project: default 8 | source: 9 | repoURL: ${{ values.application_source }} 10 | path: ${{ values.application_source_path }} 11 | targetRevision: HEAD 12 | destination: 13 | name: ${{ values.cluster_id }} 14 | namespace: default -------------------------------------------------------------------------------- /backstage/catalog/templates/application/template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scaffolder.backstage.io/v1beta3 2 | kind: Template 3 | metadata: 4 | name: application 5 | title: New Application Deployment 6 | description: Deploy an application to a registered cluster 7 | tags: 8 | - argocd 9 | - xks 10 | spec: 11 | owner: infrastructure 12 | type: component 13 | parameters: 14 | - title: Deployment Configuration Parameters 15 | required: 16 | - name 17 | - application_source 18 | - cluster_id 19 | properties: 20 | name: 21 | title: Application Name 22 | type: string 23 | description: The name of the application to deploy 24 | application_source: 25 | title: Application Source 26 | type: string 27 | description: The source repository of the application to deploy 28 | custom_kyverno_policies: 29 | title: Custom Policy Repository Location 30 | type: string 31 | description: The source repository of the custome Kyverno policies specific to the application 32 | application_source_path: 33 | title: Path 34 | type: string 35 | description: Path within the source repository where the application is found 36 | default: / 37 | cluster_id: 38 | title: Cluster ID 39 | type: string 40 | description: ID of the Cluster to target for deployment 41 | ui:field: EntityPicker 42 | ui:options: 43 | catalogFilter: 44 | - kind: Resource 45 | spec.type: kubernetes-cluster 46 | - title: Destination 47 | required: 48 | - repo_url 49 | properties: 50 | repo_url: 51 | title: Repository Location 52 | type: string 53 | ui:field: RepoUrlPicker 54 | ui:options: 55 | allowedHosts: 56 | - github.com 57 | pull_request_branch_name: 58 | title: Branch name for Pull Request 59 | type: string 60 | default: new-application-request 61 | steps: 62 | - id: template-application 63 | name: Fetch application claim template 64 | action: fetch:template 65 | input: 66 | url: ./skeleton 67 | targetPath: demo/applications 68 | values: 69 | cluster_id: ${{ parameters.cluster_id | parseEntityRef | pick('name') }} 70 | application_name: ${{ parameters.name }} 71 | application_source: ${{ parameters.application_source }} 72 | application_source_path: ${{ parameters.application_source_path }} 73 | - id: publish 74 | name: Publish 75 | action: publish:github:pull-request 76 | input: 77 | allowedHosts: ['github.com'] 78 | title: Request for new application deployment 79 | description: | 80 | Application Source: ${{ parameters.application_source }} 81 | Cluster ID: ${{ parameters.cluster_id }} 82 | repoUrl: ${{ parameters.repo_url }} 83 | branchName: ${{ parameters.pull_request_branch_name }} 84 | output: 85 | text: 86 | - title: Accept Pull Request 87 | content: | 88 | Your New Application Request has been published to GitHub 89 | 90 | To finish deploying the application, review and accept the [pull request](${{ steps['publish'].output.remoteUrl }}) 91 | -------------------------------------------------------------------------------- /backstage/catalog/templates/cluster/aks/skeleton/${{values.cluster_id}}.AKSCluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstack.dev/v1alpha1 2 | kind: AKSCluster 3 | metadata: 4 | name: ${{ values.cluster_id }} 5 | namespace: default 6 | spec: 7 | parameters: 8 | id: ${{ values.cluster_id }} 9 | region: ${{ values.region }} 10 | version: ${{ values.version }} 11 | nodes: 12 | count: ${{ values.nodes_count }} 13 | instanceType: ${{ values.nodes_instance_type }} 14 | writeConnectionSecretToRef: 15 | name: ${{ values.cluster_id }}-kubeconfig 16 | publishConnectionDetailsTo: 17 | name: ${{ values.cluster_id }}/${{ values.cluster_id }}-kubeconfig 18 | configRef: 19 | name: vault 20 | -------------------------------------------------------------------------------- /backstage/catalog/templates/cluster/aks/template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scaffolder.backstage.io/v1beta3 2 | kind: Template 3 | metadata: 4 | name: aks 5 | title: New Cluster (AKS) 6 | description: Request an AKS Cluster to host workloads 7 | tags: 8 | - caas 9 | - aks 10 | spec: 11 | owner: infrastructure 12 | type: resource 13 | parameters: 14 | - title: Cluster Configuration Parameters 15 | required: 16 | - cluster_id 17 | - region 18 | properties: 19 | cluster_id: 20 | title: Cluster ID 21 | type: string 22 | description: ID of this Cluster that other objects will use to refer to it. 23 | region: 24 | title: Region 25 | type: string 26 | description: Region is the region you'd like your resource to be created in. 27 | version: 28 | title: Version 29 | type: string 30 | description: Kubernetes version of the Cluster 31 | default: "1.27.3" 32 | enum: 33 | - "1.27.3" 34 | - "1.26.6" 35 | - "1.25.11" 36 | nodes_count: 37 | title: Node Group Size 38 | type: integer 39 | description: Desired node count, from 1 to 100. 40 | default: 3 41 | nodes_instance_type: 42 | title: Node Instance Type 43 | type: string 44 | description: Instance type associated with the Node Group. 45 | default: Standard_B2s 46 | custom_kyverno_policies: 47 | title: Custom Policy Repository Location 48 | type: string 49 | description: The source repository of the custome Kyverno policies specific to the cluster 50 | - title: Destination 51 | required: 52 | - repo_url 53 | properties: 54 | repo_url: 55 | title: Repository Location 56 | type: string 57 | ui:field: RepoUrlPicker 58 | ui:options: 59 | allowedHosts: 60 | - github.com 61 | pull_request_branch_name: 62 | title: Branch name for Pull Request 63 | type: string 64 | default: new-cluster-request 65 | steps: 66 | - id: template-cluster 67 | name: Fetch cluster claim template 68 | action: fetch:template 69 | input: 70 | url: ./skeleton 71 | targetPath: demo/clusters 72 | values: 73 | cluster_id: ${{ parameters.cluster_id }} 74 | region: ${{ parameters.region }} 75 | version: ${{ parameters.version }} 76 | nodes_count: ${{ parameters.nodes_count }} 77 | nodes_instance_type: ${{ parameters.nodes_instance_type }} 78 | - id: publish 79 | name: Publish 80 | action: publish:github:pull-request 81 | input: 82 | allowedHosts: ['github.com'] 83 | title: Request for new cluster 84 | description: | 85 | Cluster ID: ${{ parameters.cluster_id }} 86 | Region: ${{ parameters.region }} 87 | Version: ${{ parameters.version }} 88 | Nodes: ${{ parameters.nodes_instance_type }} (${{ parameters.nodes_count }}) 89 | repoUrl: ${{ parameters.repo_url }} 90 | branchName: ${{ parameters.pull_request_branch_name }} 91 | output: 92 | text: 93 | - title: Accept Pull Request 94 | content: | 95 | Your New Cluster Request has been published to GitHub. 96 | 97 | To finish creating the cluster, review and accept the [pull request](${{ steps['publish'].output.remoteUrl }}) 98 | -------------------------------------------------------------------------------- /backstage/catalog/templates/cluster/eks/skeleton/${{values.cluster_id}}.EKSCluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstack.dev/v1alpha1 2 | kind: EKSCluster 3 | metadata: 4 | name: ${{ values.cluster_id }} 5 | namespace: default 6 | spec: 7 | parameters: 8 | id: ${{ values.cluster_id }} 9 | region: ${{ values.region }} 10 | iam: 11 | roleArn: ${{ values.iam_role_arn}} 12 | version: "${{ values.version }}" 13 | nodes: 14 | count: ${{ values.nodes_count }} 15 | instanceType: ${{ values.nodes_instance_type }} 16 | writeConnectionSecretToRef: 17 | name: ${{ values.cluster_id }}-kubeconfig 18 | publishConnectionDetailsTo: 19 | name: ${{ values.cluster_id }}/${{ values.cluster_id }}-kubeconfig 20 | configRef: 21 | name: vault 22 | -------------------------------------------------------------------------------- /backstage/catalog/templates/cluster/eks/template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scaffolder.backstage.io/v1beta3 2 | kind: Template 3 | metadata: 4 | name: eks 5 | title: New Cluster (EKS) 6 | description: Request an EKS Cluster to host workloads 7 | tags: 8 | - caas 9 | - eks 10 | spec: 11 | owner: infrastructure 12 | type: resource 13 | parameters: 14 | - title: Cluster Configuration Parameters 15 | required: 16 | - cluster_id 17 | - region 18 | properties: 19 | cluster_id: 20 | title: Cluster ID 21 | type: string 22 | description: ID of this Cluster that other objects will use to refer to it. 23 | region: 24 | title: Region 25 | type: string 26 | description: Region is the region you'd like your resource to be created in. 27 | iam_role_arn: 28 | title: IAM Role ARN 29 | type: string 30 | description: The IAM Role ARN to connect as ClusterAdmin. 31 | version: 32 | title: Version 33 | type: string 34 | description: Kubernetes version of the Cluster 35 | default: "1.28" 36 | enum: 37 | - "1.29" 38 | - "1.28" 39 | - "1.27" 40 | - "1.26" 41 | - "1.25" 42 | - "1.24" 43 | nodes_count: 44 | title: Node Group Size 45 | type: integer 46 | description: Desired node count, from 1 to 100. 47 | default: 3 48 | nodes_instance_type: 49 | title: Node Instance Type 50 | type: string 51 | description: Instance type associated with the Node Group. 52 | default: t3.small 53 | custom_kyverno_policies: 54 | title: Custom Policy Repository Location 55 | type: string 56 | description: The source repository of the custome Kyverno policies specific to the cluster 57 | - title: Destination 58 | required: 59 | - repo_url 60 | properties: 61 | repo_url: 62 | title: Repository Location 63 | type: string 64 | ui:field: RepoUrlPicker 65 | ui:options: 66 | allowedHosts: 67 | - github.com 68 | pull_request_branch_name: 69 | title: Branch name for Pull Request 70 | type: string 71 | default: new-cluster-request 72 | steps: 73 | - id: template 74 | name: Fetch cluster claim template 75 | action: fetch:template 76 | input: 77 | url: ./skeleton 78 | targetPath: demo/clusters 79 | values: 80 | cluster_id: ${{ parameters.cluster_id }} 81 | region: ${{ parameters.region }} 82 | version: ${{ parameters.version }} 83 | iam_role_arn: ${{ parameters.iam_role_arn }} 84 | nodes_count: ${{ parameters.nodes_count }} 85 | nodes_instance_type: ${{ parameters.nodes_instance_type }} 86 | - id: publish 87 | name: Publish 88 | action: publish:github:pull-request 89 | input: 90 | allowedHosts: ['github.com'] 91 | title: Request for new cluster 92 | description: | 93 | Cluster ID: ${{ parameters.cluster_id }} 94 | Region: ${{ parameters.region }} 95 | Version: ${{ parameters.version }} 96 | Nodes: ${{ parameters.nodes_instance_type }} (${{ parameters.nodes_count }}) 97 | repoUrl: ${{ parameters.repo_url }} 98 | branchName: ${{ parameters.pull_request_branch_name }} 99 | output: 100 | text: 101 | - title: Accept Pull Request 102 | content: | 103 | Your New Cluster Request has been published to GitHub. 104 | 105 | To finish creating the cluster, review and accept the [pull request](${{ steps['publish'].output.remoteUrl }}) 106 | -------------------------------------------------------------------------------- /backstage/lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*", "plugins/*"], 3 | "npmClient": "yarn", 4 | "version": "0.1.0", 5 | "$schema": "node_modules/lerna/schemas/lerna-schema.json" 6 | } 7 | -------------------------------------------------------------------------------- /backstage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "version": "1.0.0", 4 | "private": true, 5 | "engines": { 6 | "node": "18 || 20" 7 | }, 8 | "scripts": { 9 | "dev": "concurrently \"yarn start\" \"yarn start-backend\"", 10 | "start": "yarn workspace app start", 11 | "start-backend": "yarn workspace backend start", 12 | "build:backend": "yarn workspace backend build", 13 | "build:all": "backstage-cli repo build --all", 14 | "build-image": "yarn workspace backend build-image", 15 | "tsc": "tsc", 16 | "tsc:full": "tsc --skipLibCheck false --incremental false", 17 | "clean": "backstage-cli repo clean", 18 | "test": "backstage-cli repo test", 19 | "test:all": "backstage-cli repo test --coverage", 20 | "test:e2e": "playwright test", 21 | "fix": "backstage-cli repo fix", 22 | "lint": "backstage-cli repo lint --since origin/main", 23 | "lint:all": "backstage-cli repo lint", 24 | "prettier:check": "prettier --check .", 25 | "new": "backstage-cli new --scope internal" 26 | }, 27 | "workspaces": { 28 | "packages": [ 29 | "packages/*", 30 | "plugins/*" 31 | ] 32 | }, 33 | "devDependencies": { 34 | "@backstage/cli": "^0.23.0", 35 | "@backstage/e2e-test-utils": "^0.1.0", 36 | "@playwright/test": "^1.32.3", 37 | "@spotify/prettier-config": "^12.0.0", 38 | "concurrently": "^8.0.0", 39 | "lerna": "^7.3.0", 40 | "node-gyp": "^9.0.0", 41 | "prettier": "^2.3.2", 42 | "typescript": "~5.2.0" 43 | }, 44 | "resolutions": { 45 | "@types/react": "^17", 46 | "@types/react-dom": "^17" 47 | }, 48 | "prettier": "@spotify/prettier-config", 49 | "lint-staged": { 50 | "*.{js,jsx,ts,tsx,mjs,cjs}": [ 51 | "eslint --fix", 52 | "prettier --write" 53 | ], 54 | "*.{json,md}": [ 55 | "prettier --write" 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /backstage/packages/README.md: -------------------------------------------------------------------------------- 1 | # The Packages Folder 2 | 3 | This is where your own applications and centrally managed libraries live, each 4 | in a separate folder of its own. 5 | 6 | From the start there's an `app` folder (for the frontend) and a `backend` folder 7 | (for the Node backend), but you can also add more modules in here that house 8 | your core additions and adaptations, such as themes, common React component 9 | libraries, utilities, and similar. 10 | -------------------------------------------------------------------------------- /backstage/packages/app/.eslintignore: -------------------------------------------------------------------------------- 1 | public 2 | -------------------------------------------------------------------------------- /backstage/packages/app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); 2 | -------------------------------------------------------------------------------- /backstage/packages/app/e2e-tests/app.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 The Backstage Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { test, expect } from '@playwright/test'; 18 | 19 | test('App should render the welcome page', async ({ page }) => { 20 | await page.goto('/'); 21 | 22 | await expect(page.getByText('My Company Catalog')).toBeVisible(); 23 | }); 24 | -------------------------------------------------------------------------------- /backstage/packages/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "bundled": true, 6 | "backstage": { 7 | "role": "frontend" 8 | }, 9 | "scripts": { 10 | "start": "backstage-cli package start", 11 | "build": "backstage-cli package build", 12 | "clean": "backstage-cli package clean", 13 | "test": "backstage-cli package test", 14 | "lint": "backstage-cli package lint" 15 | }, 16 | "dependencies": { 17 | "@backstage/app-defaults": "^1.4.4", 18 | "@backstage/catalog-model": "^1.4.3", 19 | "@backstage/cli": "^0.23.0", 20 | "@backstage/core-app-api": "^1.11.0", 21 | "@backstage/core-components": "^0.13.6", 22 | "@backstage/core-plugin-api": "^1.7.0", 23 | "@backstage/integration-react": "^1.1.20", 24 | "@backstage/plugin-api-docs": "^0.9.12", 25 | "@backstage/plugin-catalog": "^1.14.0", 26 | "@backstage/plugin-catalog-common": "^1.0.17", 27 | "@backstage/plugin-catalog-graph": "^0.2.37", 28 | "@backstage/plugin-catalog-import": "^0.10.1", 29 | "@backstage/plugin-catalog-react": "^1.8.5", 30 | "@backstage/plugin-github-actions": "^0.6.6", 31 | "@backstage/plugin-kubernetes": "^0.11.0", 32 | "@backstage/plugin-org": "^0.6.15", 33 | "@backstage/plugin-permission-react": "^0.4.16", 34 | "@backstage/plugin-scaffolder": "^1.15.1", 35 | "@backstage/plugin-search": "^1.4.1", 36 | "@backstage/plugin-search-react": "^1.7.1", 37 | "@backstage/plugin-tech-radar": "^0.6.9", 38 | "@backstage/plugin-techdocs": "^1.8.0", 39 | "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.1", 40 | "@backstage/plugin-techdocs-react": "^1.1.12", 41 | "@backstage/plugin-user-settings": "^0.7.11", 42 | "@backstage/plugin-vault": "^0.1.20", 43 | "@backstage/theme": "^0.4.3", 44 | "@janus-idp/backstage-plugin-topology": "^1.15.9", 45 | "@material-ui/core": "^4.12.2", 46 | "@material-ui/icons": "^4.9.1", 47 | "history": "^5.0.0", 48 | "react": "^17.0.2", 49 | "react-dom": "^17.0.2", 50 | "react-router": "^6.3.0", 51 | "react-router-dom": "^6.3.0", 52 | "react-use": "^17.2.4" 53 | }, 54 | "devDependencies": { 55 | "@backstage/test-utils": "^1.4.4", 56 | "@playwright/test": "^1.32.3", 57 | "@testing-library/dom": "^8.0.0", 58 | "@testing-library/jest-dom": "^5.10.1", 59 | "@testing-library/react": "^12.1.3", 60 | "@testing-library/user-event": "^14.0.0", 61 | "@types/react-dom": "*", 62 | "cross-env": "^7.0.0" 63 | }, 64 | "browserslist": { 65 | "production": [ 66 | ">0.2%", 67 | "not dead", 68 | "not op_mini all" 69 | ], 70 | "development": [ 71 | "last 1 chrome version", 72 | "last 1 firefox version", 73 | "last 1 safari version" 74 | ] 75 | }, 76 | "files": [ 77 | "dist" 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /backstage/packages/app/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/back-stack/showcase/a8b7071c32621b85e7ffe4e31a3199d20b6ec946/backstage/packages/app/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /backstage/packages/app/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/back-stack/showcase/a8b7071c32621b85e7ffe4e31a3199d20b6ec946/backstage/packages/app/public/apple-touch-icon.png -------------------------------------------------------------------------------- /backstage/packages/app/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/back-stack/showcase/a8b7071c32621b85e7ffe4e31a3199d20b6ec946/backstage/packages/app/public/favicon-16x16.png -------------------------------------------------------------------------------- /backstage/packages/app/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/back-stack/showcase/a8b7071c32621b85e7ffe4e31a3199d20b6ec946/backstage/packages/app/public/favicon-32x32.png -------------------------------------------------------------------------------- /backstage/packages/app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/back-stack/showcase/a8b7071c32621b85e7ffe4e31a3199d20b6ec946/backstage/packages/app/public/favicon.ico -------------------------------------------------------------------------------- /backstage/packages/app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 15 | 20 | 21 | 22 | 27 | 33 | 39 | 44 | <%= config.getString('app.title') %> 45 | 46 | 47 | 48 |
49 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /backstage/packages/app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Backstage", 3 | "name": "Backstage", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "48x48", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /backstage/packages/app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /backstage/packages/app/public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | Created by potrace 1.11, written by Peter Selinger 2001-2013 -------------------------------------------------------------------------------- /backstage/packages/app/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { renderWithEffects } from '@backstage/test-utils'; 3 | import App from './App'; 4 | 5 | describe('App', () => { 6 | it('should render', async () => { 7 | process.env = { 8 | NODE_ENV: 'test', 9 | APP_CONFIG: [ 10 | { 11 | data: { 12 | app: { title: 'Test' }, 13 | backend: { baseUrl: 'http://localhost:7007' }, 14 | techdocs: { 15 | storageUrl: 'http://localhost:7007/api/techdocs/static/docs', 16 | }, 17 | }, 18 | context: 'test', 19 | }, 20 | ] as any, 21 | }; 22 | 23 | const rendered = await renderWithEffects(); 24 | expect(rendered.baseElement).toBeInTheDocument(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /backstage/packages/app/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Navigate, Route } from 'react-router-dom'; 3 | import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; 4 | import { 5 | CatalogEntityPage, 6 | CatalogIndexPage, 7 | catalogPlugin, 8 | } from '@backstage/plugin-catalog'; 9 | import { 10 | CatalogImportPage, 11 | catalogImportPlugin, 12 | } from '@backstage/plugin-catalog-import'; 13 | import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; 14 | import { NextScaffolderPage } from '@backstage/plugin-scaffolder/alpha' 15 | import { orgPlugin } from '@backstage/plugin-org'; 16 | import { SearchPage } from '@backstage/plugin-search'; 17 | import { TechRadarPage } from '@backstage/plugin-tech-radar'; 18 | import { 19 | TechDocsIndexPage, 20 | techdocsPlugin, 21 | TechDocsReaderPage, 22 | } from '@backstage/plugin-techdocs'; 23 | import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; 24 | import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; 25 | import { UserSettingsPage } from '@backstage/plugin-user-settings'; 26 | import { apis } from './apis'; 27 | import { entityPage } from './components/catalog/EntityPage'; 28 | import { searchPage } from './components/search/SearchPage'; 29 | import { Root } from './components/Root'; 30 | 31 | import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; 32 | import { createApp } from '@backstage/app-defaults'; 33 | import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; 34 | import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; 35 | import { RequirePermission } from '@backstage/plugin-permission-react'; 36 | import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; 37 | 38 | const app = createApp({ 39 | apis, 40 | bindRoutes({ bind }) { 41 | bind(catalogPlugin.externalRoutes, { 42 | createComponent: scaffolderPlugin.routes.root, 43 | viewTechDoc: techdocsPlugin.routes.docRoot, 44 | createFromTemplate: scaffolderPlugin.routes.selectedTemplate, 45 | }); 46 | bind(apiDocsPlugin.externalRoutes, { 47 | registerApi: catalogImportPlugin.routes.importPage, 48 | }); 49 | bind(scaffolderPlugin.externalRoutes, { 50 | registerComponent: catalogImportPlugin.routes.importPage, 51 | viewTechDoc: techdocsPlugin.routes.docRoot, 52 | }); 53 | bind(orgPlugin.externalRoutes, { 54 | catalogIndex: catalogPlugin.routes.catalogIndex, 55 | }); 56 | }, 57 | }); 58 | 59 | const routes = ( 60 | 61 | } /> 62 | } /> 63 | } 66 | > 67 | {entityPage} 68 | 69 | } /> 70 | } 73 | > 74 | 75 | 76 | 77 | 78 | } /> 79 | } /> 80 | } 83 | /> 84 | 88 | 89 | 90 | } 91 | /> 92 | }> 93 | {searchPage} 94 | 95 | } /> 96 | } /> 97 | 98 | ); 99 | 100 | export default app.createRoot( 101 | <> 102 | 103 | 104 | 105 | {routes} 106 | 107 | , 108 | ); 109 | -------------------------------------------------------------------------------- /backstage/packages/app/src/apis.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ScmIntegrationsApi, 3 | scmIntegrationsApiRef, 4 | ScmAuth, 5 | } from '@backstage/integration-react'; 6 | import { 7 | AnyApiFactory, 8 | configApiRef, 9 | createApiFactory, 10 | } from '@backstage/core-plugin-api'; 11 | 12 | export const apis: AnyApiFactory[] = [ 13 | createApiFactory({ 14 | api: scmIntegrationsApiRef, 15 | deps: { configApi: configApiRef }, 16 | factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), 17 | }), 18 | ScmAuth.createDefaultApiFactory(), 19 | ]; 20 | -------------------------------------------------------------------------------- /backstage/packages/app/src/components/Root/LogoFull.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core'; 3 | 4 | const useStyles = makeStyles({ 5 | svg: { 6 | width: 'auto', 7 | height: 30, 8 | }, 9 | path: { 10 | fill: '#7df3e1', 11 | }, 12 | }); 13 | const LogoFull = () => { 14 | const classes = useStyles(); 15 | 16 | return ( 17 | 22 | 26 | 27 | ); 28 | }; 29 | 30 | export default LogoFull; 31 | -------------------------------------------------------------------------------- /backstage/packages/app/src/components/Root/LogoIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core'; 3 | 4 | const useStyles = makeStyles({ 5 | svg: { 6 | width: 'auto', 7 | height: 28, 8 | }, 9 | path: { 10 | fill: '#7df3e1', 11 | }, 12 | }); 13 | 14 | const LogoIcon = () => { 15 | const classes = useStyles(); 16 | 17 | return ( 18 | 23 | 27 | 28 | ); 29 | }; 30 | 31 | export default LogoIcon; 32 | -------------------------------------------------------------------------------- /backstage/packages/app/src/components/Root/Root.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import { makeStyles } from '@material-ui/core'; 3 | import HomeIcon from '@material-ui/icons/Home'; 4 | import ExtensionIcon from '@material-ui/icons/Extension'; 5 | import MapIcon from '@material-ui/icons/MyLocation'; 6 | import LibraryBooks from '@material-ui/icons/LibraryBooks'; 7 | import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; 8 | import LogoFull from './LogoFull'; 9 | import LogoIcon from './LogoIcon'; 10 | import { 11 | Settings as SidebarSettings, 12 | UserSettingsSignInAvatar, 13 | } from '@backstage/plugin-user-settings'; 14 | import { SidebarSearchModal } from '@backstage/plugin-search'; 15 | import { 16 | Sidebar, 17 | sidebarConfig, 18 | SidebarDivider, 19 | SidebarGroup, 20 | SidebarItem, 21 | SidebarPage, 22 | SidebarScrollWrapper, 23 | SidebarSpace, 24 | useSidebarOpenState, 25 | Link, 26 | } from '@backstage/core-components'; 27 | import MenuIcon from '@material-ui/icons/Menu'; 28 | import SearchIcon from '@material-ui/icons/Search'; 29 | 30 | const useSidebarLogoStyles = makeStyles({ 31 | root: { 32 | width: sidebarConfig.drawerWidthClosed, 33 | height: 3 * sidebarConfig.logoHeight, 34 | display: 'flex', 35 | flexFlow: 'row nowrap', 36 | alignItems: 'center', 37 | marginBottom: -14, 38 | }, 39 | link: { 40 | width: sidebarConfig.drawerWidthClosed, 41 | marginLeft: 24, 42 | }, 43 | }); 44 | 45 | const SidebarLogo = () => { 46 | const classes = useSidebarLogoStyles(); 47 | const { isOpen } = useSidebarOpenState(); 48 | 49 | return ( 50 |
51 | 52 | {isOpen ? : } 53 | 54 |
55 | ); 56 | }; 57 | 58 | export const Root = ({ children }: PropsWithChildren<{}>) => ( 59 | 60 | 61 | 62 | } to="/search"> 63 | 64 | 65 | 66 | }> 67 | {/* Global nav, not org-specific */} 68 | 69 | 70 | 71 | 72 | {/* End global nav */} 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | } 83 | to="/settings" 84 | > 85 | 86 | 87 | 88 | {children} 89 | 90 | ); 91 | -------------------------------------------------------------------------------- /backstage/packages/app/src/components/Root/index.ts: -------------------------------------------------------------------------------- 1 | export { Root } from './Root'; 2 | -------------------------------------------------------------------------------- /backstage/packages/app/src/components/search/SearchPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles, Theme, Grid, Paper } from '@material-ui/core'; 3 | 4 | import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; 5 | import { 6 | catalogApiRef, 7 | CATALOG_FILTER_EXISTS, 8 | } from '@backstage/plugin-catalog-react'; 9 | import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs'; 10 | 11 | import { SearchType } from '@backstage/plugin-search'; 12 | import { 13 | SearchBar, 14 | SearchFilter, 15 | SearchResult, 16 | SearchPagination, 17 | useSearch, 18 | } from '@backstage/plugin-search-react'; 19 | import { 20 | CatalogIcon, 21 | Content, 22 | DocsIcon, 23 | Header, 24 | Page, 25 | } from '@backstage/core-components'; 26 | import { useApi } from '@backstage/core-plugin-api'; 27 | 28 | const useStyles = makeStyles((theme: Theme) => ({ 29 | bar: { 30 | padding: theme.spacing(1, 0), 31 | }, 32 | filters: { 33 | padding: theme.spacing(2), 34 | marginTop: theme.spacing(2), 35 | }, 36 | filter: { 37 | '& + &': { 38 | marginTop: theme.spacing(2.5), 39 | }, 40 | }, 41 | })); 42 | 43 | const SearchPage = () => { 44 | const classes = useStyles(); 45 | const { types } = useSearch(); 46 | const catalogApi = useApi(catalogApiRef); 47 | 48 | return ( 49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | , 67 | }, 68 | { 69 | value: 'techdocs', 70 | name: 'Documentation', 71 | icon: , 72 | }, 73 | ]} 74 | /> 75 | 76 | {types.includes('techdocs') && ( 77 | { 82 | // Return a list of entities which are documented. 83 | const { items } = await catalogApi.getEntities({ 84 | fields: ['metadata.name'], 85 | filter: { 86 | 'metadata.annotations.backstage.io/techdocs-ref': 87 | CATALOG_FILTER_EXISTS, 88 | }, 89 | }); 90 | 91 | const names = items.map(entity => entity.metadata.name); 92 | names.sort(); 93 | return names; 94 | }} 95 | /> 96 | )} 97 | 103 | 109 | 110 | 111 | 112 | 113 | 114 | } /> 115 | } /> 116 | 117 | 118 | 119 | 120 | 121 | ); 122 | }; 123 | 124 | export const searchPage = ; 125 | -------------------------------------------------------------------------------- /backstage/packages/app/src/index.tsx: -------------------------------------------------------------------------------- 1 | import '@backstage/cli/asset-types'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /backstage/packages/app/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | -------------------------------------------------------------------------------- /backstage/packages/backend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); 2 | -------------------------------------------------------------------------------- /backstage/packages/backend/Dockerfile: -------------------------------------------------------------------------------- 1 | # This dockerfile builds an image for the backend package. 2 | # It should be executed with the root of the repo as docker context. 3 | # 4 | # Before building this image, be sure to have run the following commands in the repo root: 5 | # 6 | # yarn install 7 | # yarn tsc 8 | # yarn build:backend 9 | # 10 | # Once the commands have been run, you can build the image using `yarn build-image` 11 | 12 | FROM node:18-bookworm-slim 13 | 14 | # Install isolate-vm dependencies, these are needed by the @backstage/plugin-scaffolder-backend. 15 | RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ 16 | --mount=type=cache,target=/var/lib/apt,sharing=locked \ 17 | apt-get update && \ 18 | apt-get install -y --no-install-recommends python3 g++ build-essential && \ 19 | yarn config set python /usr/bin/python3 && \ 20 | npm install -g node-gyp 21 | 22 | # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, 23 | # in which case you should also move better-sqlite3 to "devDependencies" in package.json. 24 | RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ 25 | --mount=type=cache,target=/var/lib/apt,sharing=locked \ 26 | apt-get update && \ 27 | apt-get install -y --no-install-recommends libsqlite3-dev 28 | 29 | # From here on we use the least-privileged `node` user to run the backend. 30 | USER node 31 | 32 | # This should create the app dir as `node`. 33 | # If it is instead created as `root` then the `tar` command below will fail: `can't create directory 'packages/': Permission denied`. 34 | # If this occurs, then ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`) so the app dir is correctly created as `node`. 35 | WORKDIR /app 36 | 37 | # This switches many Node.js dependencies to production mode. 38 | ENV NODE_ENV production 39 | 40 | # Copy repo skeleton first, to avoid unnecessary docker cache invalidation. 41 | # The skeleton contains the package.json of each package in the monorepo, 42 | # and along with yarn.lock and the root package.json, that's enough to run yarn install. 43 | COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ 44 | RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz 45 | 46 | RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \ 47 | yarn install --frozen-lockfile --production --network-timeout 300000 48 | 49 | # Then copy the rest of the backend bundle, along with any other files we might want. 50 | COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./ 51 | RUN tar xzf bundle.tar.gz && rm bundle.tar.gz 52 | 53 | CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"] 54 | -------------------------------------------------------------------------------- /backstage/packages/backend/README.md: -------------------------------------------------------------------------------- 1 | # example-backend 2 | 3 | This package is an EXAMPLE of a Backstage backend. 4 | 5 | The main purpose of this package is to provide a test bed for Backstage plugins 6 | that have a backend part. Feel free to experiment locally or within your fork by 7 | adding dependencies and routes to this backend, to try things out. 8 | 9 | Our goal is to eventually amend the create-app flow of the CLI, such that a 10 | production ready version of a backend skeleton is made alongside the frontend 11 | app. Until then, feel free to experiment here! 12 | 13 | ## Development 14 | 15 | To run the example backend, first go to the project root and run 16 | 17 | ```bash 18 | yarn install 19 | ``` 20 | 21 | You should only need to do this once. 22 | 23 | After that, go to the `packages/backend` directory and run 24 | 25 | ```bash 26 | yarn start 27 | ``` 28 | 29 | If you want to override any configuration locally, for example adding any secrets, 30 | you can do so in `app-config.local.yaml`. 31 | 32 | The backend starts up on port 7007 per default. 33 | 34 | ## Populating The Catalog 35 | 36 | If you want to use the catalog functionality, you need to add so called 37 | locations to the backend. These are places where the backend can find some 38 | entity descriptor data to consume and serve. For more information, see 39 | [Software Catalog Overview - Adding Components to the Catalog](https://backstage.io/docs/features/software-catalog/#adding-components-to-the-catalog). 40 | 41 | To get started quickly, this template already includes some statically configured example locations 42 | in `app-config.yaml` under `catalog.locations`. You can remove and replace these locations as you 43 | like, and also override them for local development in `app-config.local.yaml`. 44 | 45 | ## Authentication 46 | 47 | We chose [Passport](http://www.passportjs.org/) as authentication platform due 48 | to its comprehensive set of supported authentication 49 | [strategies](http://www.passportjs.org/packages/). 50 | 51 | Read more about the 52 | [auth-backend](https://github.com/backstage/backstage/blob/master/plugins/auth-backend/README.md) 53 | and 54 | [how to add a new provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md) 55 | 56 | ## Documentation 57 | 58 | - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) 59 | - [Backstage Documentation](https://backstage.io/docs) 60 | -------------------------------------------------------------------------------- /backstage/packages/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "0.0.0", 4 | "main": "dist/index.cjs.js", 5 | "types": "src/index.ts", 6 | "private": true, 7 | "backstage": { 8 | "role": "backend" 9 | }, 10 | "scripts": { 11 | "start": "backstage-cli package start", 12 | "build": "backstage-cli package build", 13 | "lint": "backstage-cli package lint", 14 | "test": "backstage-cli package test", 15 | "clean": "backstage-cli package clean", 16 | "build-image": "docker build ../.. -f Dockerfile --tag backstage" 17 | }, 18 | "dependencies": { 19 | "@backstage/backend-common": "^0.19.8", 20 | "@backstage/backend-tasks": "^0.5.11", 21 | "@backstage/catalog-client": "^1.4.5", 22 | "@backstage/catalog-model": "^1.4.3", 23 | "@backstage/config": "^1.1.1", 24 | "@backstage/plugin-app-backend": "^0.3.54", 25 | "@backstage/plugin-auth-backend": "^0.19.3", 26 | "@backstage/plugin-auth-node": "^0.4.0", 27 | "@backstage/plugin-catalog-backend": "^1.14.0", 28 | "@backstage/plugin-kubernetes-backend": "^0.13.0", 29 | "@backstage/plugin-permission-common": "^0.7.9", 30 | "@backstage/plugin-permission-node": "^0.7.17", 31 | "@backstage/plugin-proxy-backend": "^0.4.3", 32 | "@backstage/plugin-scaffolder-backend": "^1.18.0", 33 | "@backstage/plugin-search-backend": "^1.4.6", 34 | "@backstage/plugin-search-backend-module-pg": "^0.5.15", 35 | "@backstage/plugin-search-backend-node": "^1.2.10", 36 | "@backstage/plugin-techdocs-backend": "^1.8.0", 37 | "@backstage/plugin-vault-backend": "^0.3.11", 38 | "app": "link:../app", 39 | "better-sqlite3": "^8.0.0", 40 | "dockerode": "^3.3.1", 41 | "express": "^4.17.1", 42 | "express-promise-router": "^4.1.0", 43 | "fs-extra": "^11.1.1", 44 | "pg": "^8.3.0", 45 | "winston": "^3.2.1" 46 | }, 47 | "devDependencies": { 48 | "@backstage/cli": "^0.23.0", 49 | "@types/dockerode": "^3.3.0", 50 | "@types/express": "^4.17.6", 51 | "@types/express-serve-static-core": "^4.17.5", 52 | "@types/fs-extra": "^11.0.3", 53 | "@types/luxon": "^2.0.4" 54 | }, 55 | "files": [ 56 | "dist" 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { PluginEnvironment } from './types'; 2 | 3 | describe('test', () => { 4 | it('unbreaks the test runner', () => { 5 | const unbreaker = {} as PluginEnvironment; 6 | expect(unbreaker).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Hi! 3 | * 4 | * Note that this is an EXAMPLE Backstage backend. Please check the README. 5 | * 6 | * Happy hacking! 7 | */ 8 | 9 | import Router from 'express-promise-router'; 10 | import { 11 | createServiceBuilder, 12 | loadBackendConfig, 13 | getRootLogger, 14 | useHotMemoize, 15 | notFoundHandler, 16 | CacheManager, 17 | DatabaseManager, 18 | HostDiscovery, 19 | UrlReaders, 20 | ServerTokenManager, 21 | } from '@backstage/backend-common'; 22 | import { TaskScheduler } from '@backstage/backend-tasks'; 23 | import { Config } from '@backstage/config'; 24 | import app from './plugins/app'; 25 | import auth from './plugins/auth'; 26 | import catalog from './plugins/catalog'; 27 | import scaffolder from './plugins/scaffolder'; 28 | import proxy from './plugins/proxy'; 29 | import techdocs from './plugins/techdocs'; 30 | import search from './plugins/search'; 31 | import { PluginEnvironment } from './types'; 32 | import { ServerPermissionClient } from '@backstage/plugin-permission-node'; 33 | import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; 34 | import kubernetes from './plugins/kubernetes'; 35 | import vault from './plugins/vault'; 36 | 37 | function makeCreateEnv(config: Config) { 38 | const root = getRootLogger(); 39 | const reader = UrlReaders.default({ logger: root, config }); 40 | const discovery = HostDiscovery.fromConfig(config); 41 | const cacheManager = CacheManager.fromConfig(config); 42 | const databaseManager = DatabaseManager.fromConfig(config, { logger: root }); 43 | const tokenManager = ServerTokenManager.noop(); 44 | const taskScheduler = TaskScheduler.fromConfig(config, { databaseManager }); 45 | 46 | const identity = DefaultIdentityClient.create({ 47 | discovery, 48 | }); 49 | const permissions = ServerPermissionClient.fromConfig(config, { 50 | discovery, 51 | tokenManager, 52 | }); 53 | 54 | root.info(`Created UrlReader ${reader}`); 55 | 56 | return (plugin: string): PluginEnvironment => { 57 | const logger = root.child({ type: 'plugin', plugin }); 58 | const database = databaseManager.forPlugin(plugin); 59 | const cache = cacheManager.forPlugin(plugin); 60 | const scheduler = taskScheduler.forPlugin(plugin); 61 | return { 62 | logger, 63 | database, 64 | cache, 65 | config, 66 | reader, 67 | discovery, 68 | tokenManager, 69 | scheduler, 70 | permissions, 71 | identity, 72 | }; 73 | }; 74 | } 75 | 76 | async function main() { 77 | const config = await loadBackendConfig({ 78 | argv: process.argv, 79 | logger: getRootLogger(), 80 | }); 81 | const createEnv = makeCreateEnv(config); 82 | 83 | const catalogEnv = useHotMemoize(module, () => createEnv('catalog')); 84 | const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder')); 85 | const authEnv = useHotMemoize(module, () => createEnv('auth')); 86 | const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); 87 | const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); 88 | const searchEnv = useHotMemoize(module, () => createEnv('search')); 89 | const appEnv = useHotMemoize(module, () => createEnv('app')); 90 | const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes')); 91 | const vaultEnv = useHotMemoize(module, () => createEnv('vault')); 92 | 93 | const apiRouter = Router(); 94 | apiRouter.use('/catalog', await catalog(catalogEnv)); 95 | apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)); 96 | apiRouter.use('/auth', await auth(authEnv)); 97 | apiRouter.use('/techdocs', await techdocs(techdocsEnv)); 98 | apiRouter.use('/proxy', await proxy(proxyEnv)); 99 | apiRouter.use('/search', await search(searchEnv)); 100 | apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv)); 101 | apiRouter.use('/vault', await vault(vaultEnv)); 102 | 103 | // Add backends ABOVE this line; this 404 handler is the catch-all fallback 104 | apiRouter.use(notFoundHandler()); 105 | 106 | const service = createServiceBuilder(module) 107 | .loadConfig(config) 108 | .addRouter('/api', apiRouter) 109 | .addRouter('', await app(appEnv)); 110 | 111 | await service.start().catch(err => { 112 | console.log(err); 113 | process.exit(1); 114 | }); 115 | } 116 | 117 | module.hot?.accept(); 118 | main().catch(error => { 119 | console.error('Backend failed to start up', error); 120 | process.exit(1); 121 | }); 122 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/app.ts: -------------------------------------------------------------------------------- 1 | import { createRouter } from '@backstage/plugin-app-backend'; 2 | import { Router } from 'express'; 3 | import { PluginEnvironment } from '../types'; 4 | 5 | export default async function createPlugin( 6 | env: PluginEnvironment, 7 | ): Promise { 8 | return await createRouter({ 9 | logger: env.logger, 10 | config: env.config, 11 | database: env.database, 12 | appPackageName: 'app', 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/auth.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createRouter, 3 | providers, 4 | defaultAuthProviderFactories, 5 | } from '@backstage/plugin-auth-backend'; 6 | import { Router } from 'express'; 7 | import { PluginEnvironment } from '../types'; 8 | 9 | export default async function createPlugin( 10 | env: PluginEnvironment, 11 | ): Promise { 12 | return await createRouter({ 13 | logger: env.logger, 14 | config: env.config, 15 | database: env.database, 16 | discovery: env.discovery, 17 | tokenManager: env.tokenManager, 18 | providerFactories: { 19 | ...defaultAuthProviderFactories, 20 | 21 | // This replaces the default GitHub auth provider with a customized one. 22 | // The `signIn` option enables sign-in for this provider, using the 23 | // identity resolution logic that's provided in the `resolver` callback. 24 | // 25 | // This particular resolver makes all users share a single "guest" identity. 26 | // It should only be used for testing and trying out Backstage. 27 | // 28 | // If you want to use a production ready resolver you can switch to 29 | // the one that is commented out below, it looks up a user entity in the 30 | // catalog using the GitHub username of the authenticated user. 31 | // That resolver requires you to have user entities populated in the catalog, 32 | // for example using https://backstage.io/docs/integrations/github/org 33 | // 34 | // There are other resolvers to choose from, and you can also create 35 | // your own, see the auth documentation for more details: 36 | // 37 | // https://backstage.io/docs/auth/identity-resolver 38 | github: providers.github.create({ 39 | signIn: { 40 | resolver(_, ctx) { 41 | const userRef = 'user:default/guest'; // Must be a full entity reference 42 | return ctx.issueToken({ 43 | claims: { 44 | sub: userRef, // The user's own identity 45 | ent: [userRef], // A list of identities that the user claims ownership through 46 | }, 47 | }); 48 | }, 49 | // resolver: providers.github.resolvers.usernameMatchingUserEntityName(), 50 | }, 51 | }), 52 | }, 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/catalog.ts: -------------------------------------------------------------------------------- 1 | import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; 2 | import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend'; 3 | import { Router } from 'express'; 4 | import { PluginEnvironment } from '../types'; 5 | import { CrossplaneProvider } from './crossplane'; 6 | 7 | export default async function createPlugin( 8 | env: PluginEnvironment, 9 | ): Promise { 10 | const builder = await CatalogBuilder.create(env); 11 | const crossplane = new CrossplaneProvider(env); 12 | builder.addEntityProvider(crossplane); 13 | builder.addProcessor(new ScaffolderEntitiesProcessor()); 14 | const { processingEngine, router } = await builder.build(); 15 | await processingEngine.start(); 16 | await env.scheduler.scheduleTask({ 17 | id: 'run_crossplane_refresh', 18 | fn: async () => { 19 | await crossplane.run(); 20 | }, 21 | frequency: {minutes: 1}, 22 | timeout: {seconds: 30} 23 | }) 24 | return router; 25 | } 26 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/crossplane.ts: -------------------------------------------------------------------------------- 1 | import { Entity, ANNOTATION_LOCATION, ANNOTATION_ORIGIN_LOCATION } from '@backstage/catalog-model'; 2 | import { 3 | EntityProvider, 4 | EntityProviderConnection, 5 | } from '@backstage/plugin-catalog-node'; 6 | import { KubeConfig, User, Cluster } from '@kubernetes/client-node' 7 | import { PluginEnvironment } from '../types'; 8 | import fs from 'fs-extra'; 9 | import fetch, { RequestInit } from 'node-fetch'; 10 | import * as https from 'https'; 11 | 12 | /** 13 | * Provides entities from crossplane. 14 | */ 15 | export class CrossplaneProvider implements EntityProvider { 16 | private env: PluginEnvironment; 17 | private connection?: EntityProviderConnection; 18 | private kc?: KubeConfig; 19 | 20 | constructor(env: PluginEnvironment) { 21 | this.env = env; 22 | } 23 | 24 | getProviderName(): string { 25 | return `crossplane`; 26 | } 27 | 28 | async connect(connection: EntityProviderConnection): Promise { 29 | this.connection = connection; 30 | this.kc = new KubeConfig(); 31 | this.kc.loadFromCluster(); 32 | } 33 | 34 | async getClusters(clusterType: string): Promise { 35 | if (!this.kc) { 36 | throw new Error('Not initialized'); 37 | } 38 | const user = this.kc.getCurrentUser() as User; 39 | const token = fs.readFileSync(user.authProvider.config.tokenFile).toString(); 40 | 41 | const requestInit: RequestInit = { 42 | method: 'GET', 43 | headers: { 44 | Accept: 'application/json', 45 | 'Content-Type': 'application/json', 46 | Authorization: `Bearer ${token}`, 47 | }, 48 | }; 49 | 50 | const cluster = this.kc.getCurrentCluster() as Cluster; 51 | const url = new URL(cluster.server); 52 | 53 | if (url.protocol === 'https:') { 54 | requestInit.agent = new https.Agent({ 55 | ca: fs.readFileSync(cluster.caFile as string), 56 | }); 57 | } 58 | 59 | if (url.pathname === '/') { 60 | url.pathname = `/apis/backstack.dev/v1alpha1/namespaces/default/${clusterType}`; 61 | } else { 62 | url.pathname += `/apis/backstack.dev/v1alpha1/namespaces/default/${clusterType}`; 63 | } 64 | 65 | const clusters: {kind: any, items: any} = await fetch(url, requestInit).then(res => res.json()); 66 | return clusters.items; 67 | } 68 | 69 | async run(): Promise { 70 | if (!this.connection) { 71 | throw new Error('Not initialized'); 72 | } 73 | 74 | this.env.logger.info("Running crossplane entity import"); 75 | 76 | const aksclusters = await this.getClusters("aksclusters"); 77 | const eksclusters = await this.getClusters("eksclusters"); 78 | 79 | const entities: Entity[] = aksclusters.concat(eksclusters).map((item: any): Entity => ({ 80 | apiVersion: "backstage.io/v1alpha1", 81 | kind: "Resource", 82 | metadata: { 83 | name: item.metadata.name, 84 | annotations: { 85 | [ANNOTATION_LOCATION]: `crossplane:${item.metadata.namespace}/${item.metadata.name}`, 86 | [ANNOTATION_ORIGIN_LOCATION]: `crossplane:${item.metadata.namespace}/${item.metadata.name}`, 87 | "vault.io/secrets-path": `${item.metadata.namespace}/${item.metadata.name}`, 88 | } 89 | }, 90 | spec: { 91 | type: "kubernetes-cluster", 92 | system: "back-stack", 93 | owner: "infrastructure" 94 | } 95 | })); 96 | 97 | await this.connection.applyMutation({ 98 | type: 'full', 99 | entities: entities.map(entity => ({ 100 | entity, 101 | locationKey: `crossplane`, 102 | })), 103 | }); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/kubernetes.ts: -------------------------------------------------------------------------------- 1 | import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend'; 2 | import { Router } from 'express'; 3 | import { PluginEnvironment } from '../types'; 4 | import { CatalogClient } from '@backstage/catalog-client'; 5 | 6 | export default async function createPlugin( 7 | env: PluginEnvironment, 8 | ): Promise { 9 | const catalogApi = new CatalogClient({ discoveryApi: env.discovery }); 10 | const { router } = await KubernetesBuilder.createBuilder({ 11 | logger: env.logger, 12 | config: env.config, 13 | catalogApi, 14 | permissions: env.permissions, 15 | }).build(); 16 | return router; 17 | } 18 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/proxy.ts: -------------------------------------------------------------------------------- 1 | import { createRouter } from '@backstage/plugin-proxy-backend'; 2 | import { Router } from 'express'; 3 | import { PluginEnvironment } from '../types'; 4 | 5 | export default async function createPlugin( 6 | env: PluginEnvironment, 7 | ): Promise { 8 | return await createRouter({ 9 | logger: env.logger, 10 | config: env.config, 11 | discovery: env.discovery, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/scaffolder.ts: -------------------------------------------------------------------------------- 1 | import { CatalogClient } from '@backstage/catalog-client'; 2 | import { createRouter } from '@backstage/plugin-scaffolder-backend'; 3 | import { Router } from 'express'; 4 | import type { PluginEnvironment } from '../types'; 5 | 6 | export default async function createPlugin( 7 | env: PluginEnvironment, 8 | ): Promise { 9 | const catalogClient = new CatalogClient({ 10 | discoveryApi: env.discovery, 11 | }); 12 | 13 | return await createRouter({ 14 | logger: env.logger, 15 | config: env.config, 16 | database: env.database, 17 | reader: env.reader, 18 | catalogClient, 19 | identity: env.identity, 20 | permissions: env.permissions, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/search.ts: -------------------------------------------------------------------------------- 1 | import { useHotCleanup } from '@backstage/backend-common'; 2 | import { createRouter } from '@backstage/plugin-search-backend'; 3 | import { 4 | IndexBuilder, 5 | LunrSearchEngine, 6 | } from '@backstage/plugin-search-backend-node'; 7 | import { PluginEnvironment } from '../types'; 8 | import { DefaultCatalogCollatorFactory } from '@backstage/plugin-catalog-backend'; 9 | import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-techdocs-backend'; 10 | import { Router } from 'express'; 11 | 12 | export default async function createPlugin( 13 | env: PluginEnvironment, 14 | ): Promise { 15 | // Initialize a connection to a search engine. 16 | const searchEngine = new LunrSearchEngine({ 17 | logger: env.logger, 18 | }); 19 | const indexBuilder = new IndexBuilder({ 20 | logger: env.logger, 21 | searchEngine, 22 | }); 23 | 24 | const schedule = env.scheduler.createScheduledTaskRunner({ 25 | frequency: { minutes: 10 }, 26 | timeout: { minutes: 15 }, 27 | // A 3 second delay gives the backend server a chance to initialize before 28 | // any collators are executed, which may attempt requests against the API. 29 | initialDelay: { seconds: 3 }, 30 | }); 31 | 32 | // Collators are responsible for gathering documents known to plugins. This 33 | // collator gathers entities from the software catalog. 34 | indexBuilder.addCollator({ 35 | schedule, 36 | factory: DefaultCatalogCollatorFactory.fromConfig(env.config, { 37 | discovery: env.discovery, 38 | tokenManager: env.tokenManager, 39 | }), 40 | }); 41 | 42 | // collator gathers entities from techdocs. 43 | indexBuilder.addCollator({ 44 | schedule, 45 | factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, { 46 | discovery: env.discovery, 47 | logger: env.logger, 48 | tokenManager: env.tokenManager, 49 | }), 50 | }); 51 | 52 | // The scheduler controls when documents are gathered from collators and sent 53 | // to the search engine for indexing. 54 | const { scheduler } = await indexBuilder.build(); 55 | scheduler.start(); 56 | 57 | useHotCleanup(module, () => scheduler.stop()); 58 | 59 | return await createRouter({ 60 | engine: indexBuilder.getSearchEngine(), 61 | types: indexBuilder.getDocumentTypes(), 62 | permissions: env.permissions, 63 | config: env.config, 64 | logger: env.logger, 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/techdocs.ts: -------------------------------------------------------------------------------- 1 | import { DockerContainerRunner } from '@backstage/backend-common'; 2 | import { 3 | createRouter, 4 | Generators, 5 | Preparers, 6 | Publisher, 7 | } from '@backstage/plugin-techdocs-backend'; 8 | import Docker from 'dockerode'; 9 | import { Router } from 'express'; 10 | import { PluginEnvironment } from '../types'; 11 | 12 | export default async function createPlugin( 13 | env: PluginEnvironment, 14 | ): Promise { 15 | // Preparers are responsible for fetching source files for documentation. 16 | const preparers = await Preparers.fromConfig(env.config, { 17 | logger: env.logger, 18 | reader: env.reader, 19 | }); 20 | 21 | // Docker client (conditionally) used by the generators, based on techdocs.generators config. 22 | const dockerClient = new Docker(); 23 | const containerRunner = new DockerContainerRunner({ dockerClient }); 24 | 25 | // Generators are used for generating documentation sites. 26 | const generators = await Generators.fromConfig(env.config, { 27 | logger: env.logger, 28 | containerRunner, 29 | }); 30 | 31 | // Publisher is used for 32 | // 1. Publishing generated files to storage 33 | // 2. Fetching files from storage and passing them to TechDocs frontend. 34 | const publisher = await Publisher.fromConfig(env.config, { 35 | logger: env.logger, 36 | discovery: env.discovery, 37 | }); 38 | 39 | // checks if the publisher is working and logs the result 40 | await publisher.getReadiness(); 41 | 42 | return await createRouter({ 43 | preparers, 44 | generators, 45 | publisher, 46 | logger: env.logger, 47 | config: env.config, 48 | discovery: env.discovery, 49 | cache: env.cache, 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /backstage/packages/backend/src/plugins/vault.ts: -------------------------------------------------------------------------------- 1 | // In packages/backend/src/plugins/vault.ts 2 | import { createRouter } from '@backstage/plugin-vault-backend'; 3 | import { Router } from 'express'; 4 | import { PluginEnvironment } from '../types'; 5 | 6 | export default async function createPlugin( 7 | env: PluginEnvironment, 8 | ): Promise { 9 | return await createRouter({ 10 | logger: env.logger, 11 | config: env.config, 12 | scheduler: env.scheduler, 13 | }); 14 | } -------------------------------------------------------------------------------- /backstage/packages/backend/src/types.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from 'winston'; 2 | import { Config } from '@backstage/config'; 3 | import { 4 | PluginCacheManager, 5 | PluginDatabaseManager, 6 | PluginEndpointDiscovery, 7 | TokenManager, 8 | UrlReader, 9 | } from '@backstage/backend-common'; 10 | import { PluginTaskScheduler } from '@backstage/backend-tasks'; 11 | import { PermissionEvaluator } from '@backstage/plugin-permission-common'; 12 | import { IdentityApi } from '@backstage/plugin-auth-node'; 13 | 14 | export type PluginEnvironment = { 15 | logger: Logger; 16 | database: PluginDatabaseManager; 17 | cache: PluginCacheManager; 18 | config: Config; 19 | reader: UrlReader; 20 | discovery: PluginEndpointDiscovery; 21 | tokenManager: TokenManager; 22 | scheduler: PluginTaskScheduler; 23 | permissions: PermissionEvaluator; 24 | identity: IdentityApi; 25 | }; 26 | -------------------------------------------------------------------------------- /backstage/playwright.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Backstage Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { defineConfig } from '@playwright/test'; 18 | import { generateProjects } from '@backstage/e2e-test-utils/playwright'; 19 | 20 | /** 21 | * See https://playwright.dev/docs/test-configuration. 22 | */ 23 | export default defineConfig({ 24 | timeout: 60_000, 25 | 26 | expect: { 27 | timeout: 5_000, 28 | }, 29 | 30 | // Run your local dev server before starting the tests 31 | webServer: process.env.CI 32 | ? [] 33 | : [ 34 | { 35 | command: 'yarn start', 36 | port: 3000, 37 | reuseExistingServer: true, 38 | timeout: 60_000, 39 | }, 40 | ], 41 | 42 | forbidOnly: !!process.env.CI, 43 | 44 | retries: process.env.CI ? 2 : 0, 45 | 46 | reporter: [['html', { open: 'never', outputFolder: 'e2e-test-report' }]], 47 | 48 | use: { 49 | actionTimeout: 0, 50 | baseURL: 51 | process.env.PLAYWRIGHT_URL ?? 52 | (process.env.CI ? 'http://localhost:7007' : 'http://localhost:3000'), 53 | screenshot: 'only-on-failure', 54 | trace: 'on-first-retry', 55 | }, 56 | 57 | outputDir: 'node_modules/.cache/e2e-test-results', 58 | 59 | projects: generateProjects(), // Find all packages with e2e-test folders 60 | }); 61 | -------------------------------------------------------------------------------- /backstage/plugins/README.md: -------------------------------------------------------------------------------- 1 | # The Plugins Folder 2 | 3 | This is where your own plugins and their associated modules live, each in a 4 | separate folder of its own. 5 | 6 | If you want to create a new plugin here, go to your project root directory, run 7 | the command `yarn new`, and follow the on-screen instructions. 8 | 9 | You can also check out existing plugins on [the plugin marketplace](https://backstage.io/plugins)! 10 | -------------------------------------------------------------------------------- /backstage/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@backstage/cli/config/tsconfig.json", 3 | "include": [ 4 | "packages/*/src", 5 | "plugins/*/src", 6 | "plugins/*/dev", 7 | "plugins/*/migrations" 8 | ], 9 | "exclude": ["node_modules"], 10 | "compilerOptions": { 11 | "outDir": "dist-types", 12 | "rootDir": "." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bundle/.dockerignore: -------------------------------------------------------------------------------- 1 | # See https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Put files here that you don't want copied into your bundle's invocation image 3 | .gitignore 4 | template.Dockerfile 5 | -------------------------------------------------------------------------------- /bundle/helpers.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | K8S_CFG_INTERNAL=/home/nonroot/.kube/config 4 | K8S_CFG_EXTERNAL=/home/nonroot/.kube/config-external 5 | CLUSTER_NAME=backstack 6 | 7 | validate_providers() { 8 | for provider in {crossplane-contrib-provider-{helm,kubernetes},upbound-provider-{family-{aws,azure},aws-{ec2,eks,iam},azure-{containerservice,network}}}; do 9 | kubectl wait providers.pkg.crossplane.io/${provider} --for='condition=healthy' --timeout=5m 10 | done 11 | } 12 | 13 | validate_configuration() { 14 | kubectl wait configuration/back-stack --for='condition=healthy' --timeout=10m 15 | } 16 | 17 | deploy_backstack_hub() { 18 | # this is a silly way to solve the problem of waiting for the custom CRDs to 19 | # show up and be ready, but for now its an easy enough way to get around it 20 | # not dropping out of the install. This is NOT a fool proof way of solving this 21 | echo "Waiting for Backstack CRDs to be ready" 22 | while ! kubectl get crd hubs.backstack.dev &>/dev/null; do sleep 1; done 23 | # deploy hub 24 | kubectl apply -f - <<-EOF 25 | apiVersion: backstack.dev/v1alpha1 26 | kind: Hub 27 | metadata: 28 | name: hub 29 | spec: 30 | parameters: 31 | clusterId: local 32 | repository: ${REPOSITORY} 33 | backstage: 34 | host: ${BACKSTAGE_HOST} 35 | image: 36 | registry: ghcr.io 37 | repository: back-stack/showcase-backstage 38 | tag: latest 39 | pullPolicy: Always 40 | argocd: 41 | host: ${ARGOCD_HOST} 42 | vault: 43 | host: ${VAULT_HOST} 44 | EOF 45 | } 46 | 47 | deploy_secrets() { 48 | ensure_namespace argocd 49 | kubectl apply -f - <<-EOF 50 | apiVersion: v1 51 | kind: Secret 52 | metadata: 53 | name: clusters-repository 54 | namespace: argocd 55 | labels: 56 | argocd.argoproj.io/secret-type: repository 57 | stringData: 58 | type: git 59 | url: ${REPOSITORY} 60 | password: ${GITHUB_TOKEN} 61 | username: back-stack 62 | EOF 63 | 64 | ensure_namespace backstage 65 | kubectl apply -f - <<-EOF 66 | apiVersion: v1 67 | kind: Secret 68 | metadata: 69 | name: backstage 70 | namespace: backstage 71 | stringData: 72 | GITHUB_TOKEN: ${GITHUB_TOKEN} 73 | VAULT_TOKEN: ${VAULT_TOKEN} 74 | EOF 75 | 76 | ensure_namespace crossplane-system 77 | kubectl apply -f - <<-EOF 78 | apiVersion: v1 79 | kind: Secret 80 | metadata: 81 | name: azure-secret 82 | namespace: crossplane-system 83 | data: 84 | credentials: $(echo -n "${AZURE_CREDENTIALS}" | base64 -w 0) 85 | EOF 86 | 87 | kubectl apply -f - <<-EOF 88 | apiVersion: v1 89 | kind: Secret 90 | metadata: 91 | name: aws-secret 92 | namespace: crossplane-system 93 | data: 94 | credentials: $(echo -n "$AWS_CREDENTIALS" | base64 -w 0) 95 | EOF 96 | } 97 | 98 | upgrade() { 99 | echo World 2.0 100 | } 101 | 102 | uninstall() { 103 | if [ "$CLUSTER_TYPE" = "kind" ]; then 104 | kind delete cluster --name ${CLUSTER_NAME} 105 | fi 106 | } 107 | 108 | ensure_namespace() { 109 | kubectl get namespaces -o name | grep -q $1 || kubectl create namespace $1 110 | } 111 | 112 | ensure_kubernetes() { 113 | if [ "$CLUSTER_TYPE" = "kind" ]; then 114 | if $(kind get clusters | grep -q ${CLUSTER_NAME}); then 115 | echo KinD Cluster Exists 116 | kind export kubeconfig --name ${CLUSTER_NAME} --kubeconfig=${K8S_CFG_INTERNAL} 117 | kind export kubeconfig --name ${CLUSTER_NAME} --kubeconfig=${K8S_CFG_EXTERNAL} 118 | else 119 | echo Create KinD Cluster 120 | kind create cluster --name ${CLUSTER_NAME} --kubeconfig=${K8S_CFG_INTERNAL} --config=/cnab/app/kind.cluster.config --wait=40s 121 | kind export kubeconfig --name ${CLUSTER_NAME} --kubeconfig=${K8S_CFG_EXTERNAL} 122 | fi 123 | docker network connect kind ${HOSTNAME} 124 | KIND_DIND_IP=$(docker inspect -f "{{ .NetworkSettings.Networks.kind.IPAddress }}" ${CLUSTER_NAME}-control-plane) 125 | sed -i -e "s@server: .*@server: https://${KIND_DIND_IP}:6443@" ${K8S_CFG_INTERNAL} 126 | elif [ "$CLUSTER_TYPE" = "eks" ]; then 127 | if [ ! -d "~/.aws" ]; then 128 | mkdir ~/.aws 129 | echo -n "$AWS_CREDENTIALS" > ~/.aws/credentials 130 | fi 131 | # there is no difference between internal and external 132 | # when we are dealing with anything other than KinD 133 | cp ${K8S_CFG_INTERNAL} ${K8S_CFG_EXTERNAL} 134 | kubectl get ns >/dev/null 135 | else 136 | cp ${K8S_CFG_INTERNAL} ${K8S_CFG_EXTERNAL} 137 | kubectl get ns >/dev/null 138 | fi 139 | } 140 | 141 | restart_pod() { 142 | kubectl rollout restart deployment $2 -n $1 143 | } 144 | 145 | return_argo_initial_pass() { 146 | while ! kubectl -n argocd get secret argocd-initial-admin-secret &>/dev/null; do sleep 1; done 147 | kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d >~/argo_initial_passwd 148 | } 149 | 150 | # Call the requested function and pass the arguments as-is 151 | "$@" 152 | -------------------------------------------------------------------------------- /bundle/kind.cluster.config: -------------------------------------------------------------------------------- 1 | apiVersion: kind.x-k8s.io/v1alpha4 2 | kind: Cluster 3 | nodes: 4 | - role: control-plane 5 | kubeadmConfigPatches: 6 | - | 7 | kind: InitConfiguration 8 | nodeRegistration: 9 | kubeletExtraArgs: 10 | node-labels: "ingress-ready=true" 11 | extraPortMappings: 12 | - containerPort: 80 13 | hostPort: 80 14 | protocol: TCP 15 | - containerPort: 443 16 | hostPort: 443 17 | protocol: TCP -------------------------------------------------------------------------------- /bundle/manifests/back-stack.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1 2 | kind: Configuration 3 | metadata: 4 | name: back-stack 5 | spec: 6 | package: ghcr.io/back-stack/showcase-configuration:latest 7 | skipDependencyResolution: true 8 | packagePullPolicy: Always 9 | -------------------------------------------------------------------------------- /bundle/manifests/ess-plugin-vault.yaml: -------------------------------------------------------------------------------- 1 | podAnnotations: 2 | vault.hashicorp.com/agent-inject: "true" 3 | vault.hashicorp.com/agent-inject-token: "true" 4 | vault.hashicorp.com/role: "crossplane" 5 | vault.hashicorp.com/agent-run-as-user: "65532" 6 | -------------------------------------------------------------------------------- /bundle/manifests/ingress-nginx/values-eks.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | updateStrategy: 3 | type: RollingUpdate 4 | rollingUpdate: 5 | maxUnavailable: 1 6 | service: 7 | type: LoadBalancer 8 | annotations: 9 | service.beta.kubernetes.io/aws-load-balancer-type: external 10 | service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing 11 | service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip 12 | service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: http 13 | service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: /healthz 14 | service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: 10254 15 | -------------------------------------------------------------------------------- /bundle/manifests/ingress-nginx/values-kind.yaml: -------------------------------------------------------------------------------- 1 | # Kind - https://kind.sigs.k8s.io/docs/user/ingress/ 2 | controller: 3 | updateStrategy: 4 | type: RollingUpdate 5 | rollingUpdate: 6 | maxUnavailable: 1 7 | hostPort: 8 | enabled: true 9 | terminationGracePeriodSeconds: 0 10 | service: 11 | type: NodePort 12 | watchIngressWithoutClass: true 13 | 14 | nodeSelector: 15 | ingress-ready: "true" 16 | tolerations: 17 | - key: "node-role.kubernetes.io/master" 18 | operator: "Equal" 19 | effect: "NoSchedule" 20 | - key: "node-role.kubernetes.io/control-plane" 21 | operator: "Equal" 22 | effect: "NoSchedule" 23 | 24 | publishService: 25 | enabled: false 26 | extraArgs: 27 | publish-status-address: localhost -------------------------------------------------------------------------------- /bundle/manifests/providers/provider-configs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.crossplane.io/v1beta1 2 | kind: ProviderConfig 3 | metadata: 4 | name: local 5 | spec: 6 | credentials: 7 | source: InjectedIdentity 8 | --- 9 | apiVersion: kubernetes.crossplane.io/v1alpha1 10 | kind: ProviderConfig 11 | metadata: 12 | name: local 13 | spec: 14 | credentials: 15 | source: InjectedIdentity 16 | --- 17 | apiVersion: azure.upbound.io/v1beta1 18 | kind: ProviderConfig 19 | metadata: 20 | name: default 21 | spec: 22 | credentials: 23 | source: Secret 24 | secretRef: 25 | namespace: crossplane-system 26 | name: azure-secret 27 | key: credentials 28 | --- 29 | apiVersion: aws.upbound.io/v1beta1 30 | kind: ProviderConfig 31 | metadata: 32 | name: default 33 | spec: 34 | credentials: 35 | source: Secret 36 | secretRef: 37 | namespace: crossplane-system 38 | name: aws-secret 39 | key: credentials 40 | -------------------------------------------------------------------------------- /bundle/manifests/providers/providers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: Function 3 | metadata: 4 | name: crossplane-contrib-function-patch-and-transform 5 | spec: 6 | package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.2.1 7 | --- 8 | apiVersion: pkg.crossplane.io/v1 9 | kind: Provider 10 | metadata: 11 | name: crossplane-contrib-provider-helm 12 | spec: 13 | package: xpkg.upbound.io/crossplane-contrib/provider-helm:v0.16.0 14 | runtimeConfigRef: 15 | name: provider-local-admin 16 | skipDependencyResolution: true 17 | --- 18 | apiVersion: pkg.crossplane.io/v1 19 | kind: Provider 20 | metadata: 21 | name: crossplane-contrib-provider-kubernetes 22 | spec: 23 | package: xpkg.upbound.io/crossplane-contrib/provider-kubernetes:v0.9.0 24 | runtimeConfigRef: 25 | name: provider-local-admin 26 | skipDependencyResolution: true 27 | --- 28 | apiVersion: pkg.crossplane.io/v1 29 | kind: Provider 30 | metadata: 31 | name: upbound-provider-family-aws 32 | spec: 33 | package: xpkg.upbound.io/upbound/provider-family-aws:v0.46.1 34 | skipDependencyResolution: true 35 | --- 36 | apiVersion: pkg.crossplane.io/v1 37 | kind: Provider 38 | metadata: 39 | name: upbound-provider-aws-ec2 40 | spec: 41 | package: xpkg.upbound.io/upbound/provider-aws-ec2:v0.46.1 42 | skipDependencyResolution: true 43 | --- 44 | apiVersion: pkg.crossplane.io/v1 45 | kind: Provider 46 | metadata: 47 | name: upbound-provider-aws-eks 48 | spec: 49 | package: xpkg.upbound.io/upbound/provider-aws-eks:v0.46.1 50 | skipDependencyResolution: true 51 | --- 52 | apiVersion: pkg.crossplane.io/v1 53 | kind: Provider 54 | metadata: 55 | name: upbound-provider-aws-iam 56 | spec: 57 | package: xpkg.upbound.io/upbound/provider-aws-iam:v0.46.1 58 | skipDependencyResolution: true 59 | --- 60 | apiVersion: pkg.crossplane.io/v1 61 | kind: Provider 62 | metadata: 63 | name: upbound-provider-family-azure 64 | spec: 65 | package: xpkg.upbound.io/upbound/provider-family-azure:v0.39.0 66 | skipDependencyResolution: true 67 | --- 68 | apiVersion: pkg.crossplane.io/v1 69 | kind: Provider 70 | metadata: 71 | name: upbound-provider-azure-containerservice 72 | spec: 73 | package: xpkg.upbound.io/upbound/provider-azure-containerservice:v0.39.0 74 | skipDependencyResolution: true 75 | --- 76 | apiVersion: pkg.crossplane.io/v1 77 | kind: Provider 78 | metadata: 79 | name: upbound-provider-azure-network 80 | spec: 81 | package: xpkg.upbound.io/upbound/provider-azure-network:v0.39.0 82 | skipDependencyResolution: true -------------------------------------------------------------------------------- /bundle/manifests/providers/runtime-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: DeploymentRuntimeConfig 3 | metadata: 4 | name: provider-local-admin 5 | spec: 6 | serviceAccountTemplate: 7 | metadata: 8 | name: provider-local-admin 9 | --- 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | kind: ClusterRoleBinding 12 | metadata: 13 | name: provider-local-admin-binding 14 | roleRef: 15 | apiGroup: rbac.authorization.k8s.io 16 | kind: ClusterRole 17 | name: cluster-admin 18 | subjects: 19 | - kind: ServiceAccount 20 | name: provider-local-admin 21 | namespace: crossplane-system -------------------------------------------------------------------------------- /bundle/porter.yaml: -------------------------------------------------------------------------------- 1 | schemaType: Bundle 2 | schemaVersion: 1.0.1 3 | registry: "ghcr.io/back-stack" 4 | name: showcase-bundle 5 | description: "The BACK stack showcase bundle" 6 | 7 | dockerfile: template.Dockerfile 8 | 9 | credentials: 10 | # required 11 | - name: github-token 12 | env: GITHUB_TOKEN 13 | applyTo: 14 | - install 15 | - name: vault-token 16 | env: VAULT_TOKEN 17 | applyTo: 18 | - install 19 | - name: azure-credentials 20 | env: AZURE_CREDENTIALS 21 | applyTo: 22 | - install 23 | - name: aws-credentials 24 | env: AWS_CREDENTIALS 25 | applyTo: 26 | - install 27 | # optional 28 | - name: kubeconfig 29 | path: /home/nonroot/.kube/config 30 | required: false 31 | applyTo: 32 | - install 33 | 34 | parameters: 35 | - name: cluster-type 36 | env: CLUSTER_TYPE 37 | type: string 38 | default: kind 39 | enum: 40 | - kind 41 | - eks 42 | - aks 43 | - gke 44 | - name: repository 45 | env: REPOSITORY 46 | type: string 47 | default: https://github.com/back-stack/showcase 48 | - name: backstage-host 49 | env: BACKSTAGE_HOST 50 | type: string 51 | default: backstage-7f000001.nip.io 52 | - name: argocd-host 53 | env: ARGOCD_HOST 54 | type: string 55 | default: argocd-7f000001.nip.io 56 | - name: vault-host 57 | env: VAULT_HOST 58 | type: string 59 | default: vault-7f000001.nip.io 60 | 61 | outputs: 62 | - name: kubeconfig 63 | type: file 64 | path: /home/nonroot/.kube/config 65 | applyTo: 66 | - install 67 | - name: kubeconfig-external 68 | type: file 69 | path: /home/nonroot/.kube/config-external 70 | applyTo: 71 | - install 72 | - name: ingress 73 | type: string 74 | applyTo: 75 | - install 76 | - name: argo-initial-passwd 77 | type: file 78 | path: /home/nonroot/argo_initial_passwd 79 | applyTo: 80 | - install 81 | 82 | mixins: 83 | - exec 84 | - docker 85 | - kubernetes 86 | - helm3: 87 | clientVersion: v3.13.2 88 | repositories: 89 | crossplane-stable: 90 | url: https://charts.crossplane.io/stable 91 | ingress-nginx: 92 | url: https://kubernetes.github.io/ingress-nginx 93 | 94 | # Define the steps that should execute when the bundle is installed 95 | install: 96 | - exec: 97 | description: "Ensure Kubernetes Connection" 98 | command: ./helpers.sh 99 | arguments: 100 | - ensure_kubernetes 101 | - helm3: 102 | description: "Install ingress-nginx" 103 | name: ingress-nginx 104 | namespace: ingress-nginx 105 | chart: ingress-nginx/ingress-nginx 106 | version: 4.8.3 107 | wait: true 108 | values: 109 | - ./manifests/ingress-nginx/values-${bundle.parameters.cluster-type}.yaml 110 | outputs: 111 | - name: ingress 112 | resourceType: service 113 | resourceName: ingress-nginx-controller 114 | namespace: ingress-nginx 115 | jsonPath: "{.status.loadBalancer.ingress}" 116 | - helm3: 117 | description: "Install crossplane" 118 | name: crossplane 119 | namespace: crossplane-system 120 | chart: crossplane-stable/crossplane 121 | wait: true 122 | set: 123 | args: "{--enable-external-secret-stores}" 124 | - helm3: 125 | description: "Install vault ess plugin" 126 | name: ess-plugin-vault 127 | namespace: crossplane-system 128 | chart: oci://xpkg.upbound.io/crossplane-contrib/ess-plugin-vault 129 | wait: true 130 | values: 131 | - ./manifests/ess-plugin-vault.yaml 132 | - kubernetes: 133 | description: "Define Crossplane RuntimeConfig" 134 | manifests: 135 | - ./manifests/providers/runtime-config.yaml 136 | - kubernetes: 137 | description: "Define Crossplane Providers" 138 | manifests: 139 | - ./manifests/providers/providers.yaml 140 | - exec: 141 | description: "Wait for providers to become healthy" 142 | command: ./helpers.sh 143 | arguments: 144 | - validate_providers 145 | - kubernetes: 146 | description: "Configure Crossplane Providers" 147 | manifests: 148 | - ./manifests/providers/provider-configs.yaml 149 | - kubernetes: 150 | description: "Install BACK Stack Configuration" 151 | manifests: 152 | - ./manifests/back-stack.yaml 153 | - exec: 154 | description: "Validate BACK Stack Configuration" 155 | command: ./helpers.sh 156 | arguments: 157 | - validate_configuration 158 | - exec: 159 | description: "Deploy BACK Stack" 160 | command: ./helpers.sh 161 | arguments: 162 | - deploy_backstack_hub 163 | - exec: 164 | description: "Install Secrets" 165 | command: ./helpers.sh 166 | arguments: 167 | - deploy_secrets 168 | - exec: 169 | description: "Restart Crossplane ESS Vault Plugin" 170 | command: ./helpers.sh 171 | arguments: 172 | - restart_pod 173 | - crossplane-system 174 | - ess-plugin-vault 175 | - exec: 176 | description: "Grab ArgoCD Initial Password" 177 | command: ./helpers.sh 178 | arguments: 179 | - return_argo_initial_pass 180 | 181 | # Define the steps that should execute when the bundle is upgraded 182 | upgrade: 183 | - exec: 184 | description: "Not implemented at this time" 185 | command: ./helpers.sh 186 | arguments: 187 | - upgrade 188 | 189 | # Define the steps that should execute when the bundle is uninstalled 190 | uninstall: 191 | - exec: 192 | description: "Uninstall BACK Stack" 193 | command: ./helpers.sh 194 | arguments: 195 | - uninstall 196 | -------------------------------------------------------------------------------- /bundle/template.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:1.4.0 2 | # This is a template Dockerfile for the bundle's invocation image 3 | # You can customize it to use different base images, install tools and copy configuration files. 4 | # 5 | # Porter will use it as a template and append lines to it for the mixins 6 | # and to set the CMD appropriately for the CNAB specification. 7 | # 8 | # Add the following line to porter.yaml to instruct Porter to use this template 9 | # dockerfile: template.Dockerfile 10 | 11 | # You can control where the mixin's Dockerfile lines are inserted into this file by moving the "# PORTER_*" tokens 12 | # another location in this file. If you remove a token, its content is appended to the end of the Dockerfile. 13 | 14 | # Porter targets linux/amd64 by default. Change the --platform flag to target a different platform 15 | FROM --platform=linux/amd64 debian:stable-slim 16 | 17 | # PORTER_INIT 18 | 19 | RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache 20 | RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \ 21 | apt-get update && apt-get install -y ca-certificates curl awscli 22 | 23 | # Install KinD 24 | RUN curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 && \ 25 | chmod +x ./kind && \ 26 | mv ./kind /usr/local/bin/kind 27 | 28 | # PORTER_MIXINS 29 | 30 | # Use the BUNDLE_DIR build argument to copy files into the bundle's working directory 31 | COPY --link . ${BUNDLE_DIR} 32 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Location 3 | metadata: 4 | name: back-stack 5 | description: The BACK stack entity locations 6 | spec: 7 | targets: 8 | - ./backstage/catalog/system.yaml 9 | - ./backstage/catalog/components.yaml 10 | - ./backstage/catalog/resources.yaml 11 | - ./backstage/catalog/templates.yaml -------------------------------------------------------------------------------- /crossplane/apis/cluster/aks/aks/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xaks.azure.backstack.dev 5 | labels: 6 | provider: azure 7 | spec: 8 | writeConnectionSecretsToNamespace: crossplane-system 9 | compositeTypeRef: 10 | apiVersion: azure.backstack.dev/v1alpha1 11 | kind: XAKS 12 | patchSets: 13 | - name: region 14 | patches: 15 | - type: FromCompositeFieldPath 16 | fromFieldPath: spec.parameters.region 17 | toFieldPath: spec.forProvider.location 18 | resources: 19 | - name: kubernetes-cluster 20 | base: 21 | apiVersion: containerservice.azure.upbound.io/v1beta1 22 | kind: KubernetesCluster 23 | spec: 24 | forProvider: 25 | defaultNodePool: 26 | - name: default 27 | identity: 28 | - type: "SystemAssigned" 29 | patches: 30 | - type: PatchSet 31 | patchSetName: region 32 | - fromFieldPath: spec.parameters.version 33 | toFieldPath: spec.forProvider.kubernetesVersion 34 | - fromFieldPath: spec.parameters.id 35 | toFieldPath: metadata.name 36 | transforms: 37 | - type: string 38 | string: 39 | fmt: "%s-aks" 40 | - fromFieldPath: spec.parameters.id 41 | toFieldPath: spec.forProvider.resourceGroupNameSelector.matchLabels[azure.caas.upbound.io/network-id] 42 | - fromFieldPath: spec.parameters.id 43 | toFieldPath: spec.forProvider.defaultNodePool[0].vnetSubnetIdSelector.matchLabels[azure.caas.upbound.io/network-id] 44 | - fromFieldPath: spec.parameters.id 45 | toFieldPath: spec.forProvider.dnsPrefix 46 | - fromFieldPath: spec.parameters.nodes.instanceType 47 | toFieldPath: spec.forProvider.defaultNodePool[0].vmSize 48 | - fromFieldPath: spec.parameters.nodes.count 49 | toFieldPath: spec.forProvider.defaultNodePool[0].nodeCount 50 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 51 | toFieldPath: spec.writeConnectionSecretToRef.namespace 52 | - fromFieldPath: metadata.uid 53 | toFieldPath: spec.writeConnectionSecretToRef.name 54 | transforms: 55 | - type: string 56 | string: 57 | fmt: "%s-akscluster" 58 | connectionDetails: 59 | - fromConnectionSecretKey: kubeconfig 60 | - name: providerConfig-helm 61 | base: 62 | apiVersion: helm.crossplane.io/v1beta1 63 | kind: ProviderConfig 64 | spec: 65 | credentials: 66 | source: Secret 67 | secretRef: 68 | key: kubeconfig 69 | patches: 70 | - fromFieldPath: spec.parameters.id 71 | toFieldPath: metadata.name 72 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 73 | toFieldPath: spec.credentials.secretRef.namespace 74 | - fromFieldPath: metadata.uid 75 | toFieldPath: spec.credentials.secretRef.name 76 | transforms: 77 | - type: string 78 | string: 79 | fmt: "%s-akscluster" 80 | readinessChecks: 81 | - type: None 82 | - name: providerConfig-kubernetes 83 | base: 84 | apiVersion: kubernetes.crossplane.io/v1alpha1 85 | kind: ProviderConfig 86 | spec: 87 | credentials: 88 | source: Secret 89 | secretRef: 90 | key: kubeconfig 91 | patches: 92 | - fromFieldPath: spec.parameters.id 93 | toFieldPath: metadata.name 94 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 95 | toFieldPath: spec.credentials.secretRef.namespace 96 | - fromFieldPath: metadata.uid 97 | toFieldPath: spec.credentials.secretRef.name 98 | transforms: 99 | - type: string 100 | string: 101 | fmt: "%s-akscluster" 102 | readinessChecks: 103 | - type: None 104 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/aks/aks/definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: CompositeResourceDefinition 3 | metadata: 4 | name: xaks.azure.backstack.dev 5 | spec: 6 | group: azure.backstack.dev 7 | names: 8 | kind: XAKS 9 | plural: xaks 10 | connectionSecretKeys: 11 | - kubeconfig 12 | versions: 13 | - name: v1alpha1 14 | served: true 15 | referenceable: true 16 | schema: 17 | openAPIV3Schema: 18 | type: object 19 | properties: 20 | spec: 21 | type: object 22 | properties: 23 | parameters: 24 | type: object 25 | description: AKS configuration parameters. 26 | properties: 27 | id: 28 | type: string 29 | description: ID of this Cluster that other objects will use to refer to it. 30 | region: 31 | type: string 32 | description: Region is the region you'd like your resource to be created in. 33 | version: 34 | description: Kubernetes version 35 | type: string 36 | enum: 37 | - "1.28.5" 38 | - "1.27.3" 39 | - "1.26.6" 40 | - "1.25.11" 41 | default: "1.27.3" 42 | nodes: 43 | type: object 44 | description: AKS node configuration parameters. 45 | properties: 46 | count: 47 | type: integer 48 | description: Desired node count 49 | instanceType: 50 | type: string 51 | description: instance types associated with the Node Group. 52 | default: Standard_B2s 53 | required: 54 | - count 55 | - instanceType 56 | required: 57 | - id 58 | - region 59 | - nodes 60 | required: 61 | - parameters 62 | status: 63 | description: A Status represents the observed state 64 | properties: 65 | aks: 66 | description: Freeform field containing status information for aks 67 | type: object 68 | x-kubernetes-preserve-unknown-fields: true 69 | type: object 70 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/aks/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xaksclusters.backstack.dev 5 | spec: 6 | writeConnectionSecretsToNamespace: crossplane-system 7 | publishConnectionDetailsWithStoreConfigRef: 8 | name: vault 9 | compositeTypeRef: 10 | apiVersion: backstack.dev/v1alpha1 11 | kind: XAKSCluster 12 | resources: 13 | - name: network 14 | base: 15 | apiVersion: azure.backstack.dev/v1alpha1 16 | kind: XNetwork 17 | patches: 18 | - fromFieldPath: spec.parameters.id 19 | toFieldPath: spec.parameters.id 20 | - fromFieldPath: spec.parameters.region 21 | toFieldPath: spec.parameters.region 22 | - fromFieldPath: spec.parameters.networkSelector 23 | toFieldPath: spec.compositionSelector.matchLabels[type] 24 | - name: cluster 25 | base: 26 | apiVersion: azure.backstack.dev/v1alpha1 27 | kind: XAKS 28 | connectionDetails: 29 | - fromConnectionSecretKey: kubeconfig 30 | patches: 31 | - fromFieldPath: spec.parameters.id 32 | toFieldPath: spec.parameters.id 33 | - fromFieldPath: spec.parameters.region 34 | toFieldPath: spec.parameters.region 35 | - fromFieldPath: metadata.uid 36 | toFieldPath: spec.writeConnectionSecretToRef.name 37 | transforms: 38 | - type: string 39 | string: 40 | fmt: "%s-aks" 41 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 42 | toFieldPath: spec.writeConnectionSecretToRef.namespace 43 | - fromFieldPath: spec.parameters.version 44 | toFieldPath: spec.parameters.version 45 | - fromFieldPath: spec.parameters.nodes.count 46 | toFieldPath: spec.parameters.nodes.count 47 | - fromFieldPath: spec.parameters.nodes.instanceType 48 | toFieldPath: spec.parameters.nodes.instanceType 49 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/aks/definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: CompositeResourceDefinition 3 | metadata: 4 | name: xaksclusters.backstack.dev 5 | spec: 6 | group: backstack.dev 7 | names: 8 | kind: XAKSCluster 9 | plural: xaksclusters 10 | claimNames: 11 | kind: AKSCluster 12 | plural: aksclusters 13 | connectionSecretKeys: 14 | - kubeconfig 15 | versions: 16 | - name: v1alpha1 17 | served: true 18 | referenceable: true 19 | schema: 20 | openAPIV3Schema: 21 | type: object 22 | properties: 23 | spec: 24 | type: object 25 | properties: 26 | parameters: 27 | type: object 28 | description: Cluster configuration parameters. 29 | properties: 30 | id: 31 | type: string 32 | description: ID of this Cluster that other objects will use to refer to it. 33 | custom_kyverno_policies: 34 | type: string 35 | description: The source repository of the custom Kyverno policies specific to the application 36 | region: 37 | type: string 38 | description: Region is the region you'd like your resource to be created in. 39 | networkSelector: 40 | type: string 41 | description: NetworkSelector employs a specific type of network architecture. 42 | enum: 43 | - basic 44 | default: basic 45 | version: 46 | type: string 47 | description: Kubernetes version of the Cluster 48 | enum: 49 | - "1.28.5" 50 | - "1.27.3" 51 | - "1.26.6" 52 | - "1.25.11" 53 | default: "1.27.3" 54 | nodes: 55 | type: object 56 | description: Cluster node configuration parameters. 57 | properties: 58 | count: 59 | type: integer 60 | description: Desired node count, from 1 to 100. 61 | instanceType: 62 | type: string 63 | description: instance types associated with the Node Group. 64 | default: Standard_B2s 65 | required: 66 | - count 67 | - instanceType 68 | required: 69 | - nodes 70 | - id 71 | - region 72 | required: 73 | - parameters 74 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/aks/network/basic/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xnetworks.azure.backstack.dev 5 | labels: 6 | provider: azure 7 | type: basic 8 | spec: 9 | writeConnectionSecretsToNamespace: crossplane-system 10 | compositeTypeRef: 11 | apiVersion: azure.backstack.dev/v1alpha1 12 | kind: XNetwork 13 | patchSets: 14 | - name: network-id 15 | patches: 16 | - type: FromCompositeFieldPath 17 | fromFieldPath: spec.parameters.id 18 | toFieldPath: metadata.labels[azure.caas.upbound.io/network-id] 19 | - name: region 20 | patches: 21 | - type: FromCompositeFieldPath 22 | fromFieldPath: spec.parameters.region 23 | toFieldPath: spec.forProvider.location 24 | resources: 25 | - name: resource-group 26 | base: 27 | apiVersion: azure.upbound.io/v1beta1 28 | kind: ResourceGroup 29 | patches: 30 | - type: PatchSet 31 | patchSetName: network-id 32 | - type: PatchSet 33 | patchSetName: region 34 | - name: virtual-network 35 | base: 36 | apiVersion: network.azure.upbound.io/v1beta1 37 | kind: VirtualNetwork 38 | spec: 39 | forProvider: 40 | resourceGroupNameSelector: 41 | matchControllerRef: true 42 | addressSpace: 43 | - 192.168.0.0/16 44 | patches: 45 | - type: PatchSet 46 | patchSetName: network-id 47 | - type: PatchSet 48 | patchSetName: region 49 | - fromFieldPath: spec.parameters.id 50 | toFieldPath: metadata.name 51 | transforms: 52 | - type: string 53 | string: 54 | fmt: "%s-vnet" 55 | - name: subnet 56 | base: 57 | apiVersion: network.azure.upbound.io/v1beta1 58 | kind: Subnet 59 | spec: 60 | forProvider: 61 | resourceGroupNameSelector: 62 | matchControllerRef: true 63 | virtualNetworkNameSelector: 64 | matchControllerRef: true 65 | addressPrefixes: 66 | - 192.168.1.0/24 67 | patches: 68 | - type: PatchSet 69 | patchSetName: network-id 70 | - fromFieldPath: spec.parameters.id 71 | toFieldPath: metadata.name 72 | transforms: 73 | - type: string 74 | string: 75 | fmt: "%s-sn" 76 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/aks/network/definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: CompositeResourceDefinition 3 | metadata: 4 | name: xnetworks.azure.backstack.dev 5 | spec: 6 | group: azure.backstack.dev 7 | names: 8 | kind: XNetwork 9 | plural: xnetworks 10 | versions: 11 | - name: v1alpha1 12 | served: true 13 | referenceable: true 14 | schema: 15 | openAPIV3Schema: 16 | type: object 17 | properties: 18 | spec: 19 | type: object 20 | properties: 21 | parameters: 22 | description: Network Parameters 23 | properties: 24 | id: 25 | type: string 26 | description: ID of this Network that other objects will use to refer to it. 27 | region: 28 | type: string 29 | description: Region is the region you'd like your resource to be created in. 30 | required: 31 | - id 32 | - region 33 | type: object 34 | required: 35 | - parameters 36 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/eks/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xeksclusters.backstack.dev 5 | spec: 6 | writeConnectionSecretsToNamespace: crossplane-system 7 | publishConnectionDetailsWithStoreConfigRef: 8 | name: vault 9 | compositeTypeRef: 10 | apiVersion: backstack.dev/v1alpha1 11 | kind: XEKSCluster 12 | resources: 13 | - base: 14 | apiVersion: aws.caas.upbound.io/v1alpha1 15 | kind: XNetwork 16 | patches: 17 | - fromFieldPath: spec.parameters.id 18 | toFieldPath: spec.parameters.id 19 | - fromFieldPath: spec.parameters.region 20 | toFieldPath: spec.parameters.region 21 | - fromFieldPath: spec.parameters.networkSelector 22 | toFieldPath: spec.compositionSelector.matchLabels[type] 23 | - type: ToCompositeFieldPath 24 | fromFieldPath: status.subnetIds 25 | toFieldPath: status.subnetIds 26 | policy: 27 | fromFieldPath: Required 28 | name: compositeNetworkEKS 29 | - base: 30 | apiVersion: aws.caas.upbound.io/v1alpha1 31 | kind: XEKS 32 | connectionDetails: 33 | - fromConnectionSecretKey: kubeconfig 34 | name: compositeClusterEKS 35 | patches: 36 | - fromFieldPath: spec.parameters.id 37 | toFieldPath: spec.parameters.id 38 | - fromFieldPath: spec.parameters.region 39 | toFieldPath: spec.parameters.region 40 | - fromFieldPath: spec.parameters.id 41 | toFieldPath: metadata.annotations[crossplane.io/external-name] 42 | - fromFieldPath: metadata.uid 43 | toFieldPath: spec.writeConnectionSecretToRef.name 44 | transforms: 45 | - type: string 46 | string: 47 | fmt: "%s-eks" 48 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 49 | toFieldPath: spec.writeConnectionSecretToRef.namespace 50 | - fromFieldPath: spec.parameters.version 51 | toFieldPath: spec.parameters.version 52 | - fromFieldPath: spec.parameters.nodes.count 53 | toFieldPath: spec.parameters.nodes.count 54 | - fromFieldPath: spec.parameters.nodes.instanceType 55 | toFieldPath: spec.parameters.nodes.instanceType 56 | - fromFieldPath: spec.parameters.iam.roleArn 57 | toFieldPath: spec.parameters.iam.roleArn 58 | - fromFieldPath: status.subnetIds 59 | toFieldPath: spec.parameters.subnetIds 60 | policy: 61 | fromFieldPath: Required 62 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/eks/definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: CompositeResourceDefinition 3 | metadata: 4 | name: xeksclusters.backstack.dev 5 | spec: 6 | group: backstack.dev 7 | names: 8 | kind: XEKSCluster 9 | plural: xeksclusters 10 | claimNames: 11 | kind: EKSCluster 12 | plural: eksclusters 13 | connectionSecretKeys: 14 | - kubeconfig 15 | versions: 16 | - name: v1alpha1 17 | served: true 18 | referenceable: true 19 | schema: 20 | openAPIV3Schema: 21 | type: object 22 | properties: 23 | spec: 24 | type: object 25 | properties: 26 | parameters: 27 | type: object 28 | description: Cluster configuration parameters. 29 | properties: 30 | id: 31 | type: string 32 | description: ID of this Cluster that other objects will use to refer to it. 33 | custom_kyverno_policies: 34 | type: string 35 | description: The source repository of the custom Kyverno policies specific to the application 36 | region: 37 | type: string 38 | description: Region is the region you'd like your resource to be created in. 39 | iam: 40 | type: object 41 | description: IAM configuration to connect as ClusterAdmin. 42 | properties: 43 | roleArn: 44 | description: The IAM Role ARN to connect as ClusterAdmin. 45 | type: string 46 | networkSelector: 47 | type: string 48 | description: NetworkSelector employs a specific type of network architecture. 49 | enum: 50 | - basic 51 | default: basic 52 | version: 53 | type: string 54 | description: Kubernetes version of the Cluster 55 | enum: 56 | - "1.29" 57 | - "1.28" 58 | - "1.27" 59 | - "1.26" 60 | - "1.25" 61 | - "1.24" 62 | default: "1.29" 63 | nodes: 64 | type: object 65 | description: Cluster node configuration parameters. 66 | properties: 67 | count: 68 | type: integer 69 | description: Desired node count, from 1 to 100. 70 | instanceType: 71 | type: string 72 | description: instance types associated with the Node Group. 73 | default: t3.small 74 | required: 75 | - count 76 | - instanceType 77 | required: 78 | - nodes 79 | - id 80 | - region 81 | required: 82 | - parameters 83 | status: 84 | type: object 85 | properties: 86 | subnetIds: 87 | type: array 88 | items: 89 | type: string 90 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/eks/eks/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xeks.aws.caas.upbound.io 5 | labels: 6 | provider: aws 7 | spec: 8 | writeConnectionSecretsToNamespace: crossplane-system 9 | compositeTypeRef: 10 | apiVersion: aws.caas.upbound.io/v1alpha1 11 | kind: XEKS 12 | patchSets: 13 | - name: region 14 | patches: 15 | - type: FromCompositeFieldPath 16 | fromFieldPath: spec.parameters.region 17 | toFieldPath: spec.forProvider.region 18 | resources: 19 | - base: 20 | apiVersion: iam.aws.upbound.io/v1beta1 21 | kind: Role 22 | metadata: 23 | labels: 24 | role: controlplane 25 | spec: 26 | forProvider: 27 | assumeRolePolicy: | 28 | { 29 | "Version": "2012-10-17", 30 | "Statement": [ 31 | { 32 | "Effect": "Allow", 33 | "Principal": { 34 | "Service": [ 35 | "eks.amazonaws.com" 36 | ] 37 | }, 38 | "Action": [ 39 | "sts:AssumeRole" 40 | ] 41 | } 42 | ] 43 | } 44 | name: controlplaneRole 45 | - base: 46 | apiVersion: iam.aws.upbound.io/v1beta1 47 | kind: RolePolicyAttachment 48 | spec: 49 | forProvider: 50 | policyArn: arn:aws:iam::aws:policy/AmazonEKSClusterPolicy 51 | roleSelector: 52 | matchControllerRef: true 53 | matchLabels: 54 | role: controlplane 55 | name: clusterRolePolicyAttachment 56 | - base: 57 | apiVersion: eks.aws.upbound.io/v1beta1 58 | kind: Cluster 59 | spec: 60 | forProvider: 61 | roleArnSelector: 62 | matchControllerRef: true 63 | matchLabels: 64 | role: controlplane 65 | vpcConfig: 66 | - endpointPrivateAccess: true 67 | endpointPublicAccess: true 68 | name: kubernetesCluster 69 | patches: 70 | - type: PatchSet 71 | patchSetName: region 72 | - fromFieldPath: spec.parameters.subnetIds 73 | toFieldPath: spec.forProvider.vpcConfig[0].subnetIds 74 | - fromFieldPath: spec.parameters.version 75 | toFieldPath: spec.forProvider.version 76 | - type: ToCompositeFieldPath 77 | fromFieldPath: status.atProvider.identity[0].oidc[0].issuer 78 | toFieldPath: status.eks.oidc 79 | policy: 80 | fromFieldPath: Optional 81 | - type: ToCompositeFieldPath 82 | fromFieldPath: status.atProvider.identity[0].oidc[0].issuer 83 | toFieldPath: status.eks.oidcUri 84 | transforms: 85 | - type: string 86 | string: 87 | type: TrimPrefix 88 | trim: 'https://' 89 | policy: 90 | fromFieldPath: Optional 91 | - type: ToCompositeFieldPath 92 | fromFieldPath: status.atProvider.roleArn 93 | toFieldPath: status.eks.accountId 94 | transforms: 95 | - type: string 96 | string: 97 | type: Regexp 98 | regexp: 99 | match: 'arn:aws:iam::(\d+):.*' 100 | group: 1 101 | policy: 102 | fromFieldPath: Optional 103 | - base: 104 | apiVersion: eks.aws.upbound.io/v1beta1 105 | kind: ClusterAuth 106 | spec: 107 | forProvider: 108 | clusterNameSelector: 109 | matchControllerRef: true 110 | name: kubernetesClusterAuth 111 | patches: 112 | - type: PatchSet 113 | patchSetName: region 114 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 115 | toFieldPath: spec.writeConnectionSecretToRef.namespace 116 | - fromFieldPath: metadata.uid 117 | toFieldPath: spec.writeConnectionSecretToRef.name 118 | transforms: 119 | - type: string 120 | string: 121 | fmt: "%s-ekscluster" 122 | connectionDetails: 123 | - fromConnectionSecretKey: kubeconfig 124 | - base: 125 | apiVersion: iam.aws.upbound.io/v1beta1 126 | kind: Role 127 | metadata: 128 | labels: 129 | role: nodegroup 130 | spec: 131 | forProvider: 132 | assumeRolePolicy: | 133 | { 134 | "Version": "2012-10-17", 135 | "Statement": [ 136 | { 137 | "Effect": "Allow", 138 | "Principal": { 139 | "Service": [ 140 | "ec2.amazonaws.com" 141 | ] 142 | }, 143 | "Action": [ 144 | "sts:AssumeRole" 145 | ] 146 | } 147 | ] 148 | } 149 | name: nodegroupRole 150 | patches: 151 | - type: ToCompositeFieldPath 152 | fromFieldPath: status.atProvider.arn 153 | toFieldPath: status.eks.nodeGroupRoleArn 154 | policy: 155 | fromFieldPath: Optional 156 | - base: 157 | apiVersion: iam.aws.upbound.io/v1beta1 158 | kind: RolePolicyAttachment 159 | spec: 160 | forProvider: 161 | policyArn: arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy 162 | roleSelector: 163 | matchControllerRef: true 164 | matchLabels: 165 | role: nodegroup 166 | name: workerNodeRolePolicyAttachment 167 | - base: 168 | apiVersion: iam.aws.upbound.io/v1beta1 169 | kind: RolePolicyAttachment 170 | spec: 171 | forProvider: 172 | policyArn: arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy 173 | roleSelector: 174 | matchControllerRef: true 175 | matchLabels: 176 | role: nodegroup 177 | name: cniRolePolicyAttachment 178 | - base: 179 | apiVersion: iam.aws.upbound.io/v1beta1 180 | kind: RolePolicyAttachment 181 | spec: 182 | forProvider: 183 | policyArn: arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy 184 | roleSelector: 185 | matchControllerRef: true 186 | matchLabels: 187 | role: nodegroup 188 | name: ebsCsiRolePolicyAttachment 189 | - base: 190 | apiVersion: iam.aws.upbound.io/v1beta1 191 | kind: RolePolicyAttachment 192 | spec: 193 | forProvider: 194 | policyArn: arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly 195 | roleSelector: 196 | matchControllerRef: true 197 | matchLabels: 198 | role: nodegroup 199 | name: containerRegistryRolePolicyAttachment 200 | - base: 201 | apiVersion: eks.aws.upbound.io/v1beta1 202 | kind: NodeGroup 203 | spec: 204 | forProvider: 205 | clusterNameSelector: 206 | matchControllerRef: true 207 | nodeRoleArnSelector: 208 | matchControllerRef: true 209 | matchLabels: 210 | role: nodegroup 211 | subnetIdSelector: 212 | matchLabels: 213 | access: public 214 | scalingConfig: 215 | - minSize: 1 216 | maxSize: 100 217 | desiredSize: 1 218 | instanceTypes: 219 | - t3.medium 220 | name: nodeGroupPublic 221 | patches: 222 | - type: PatchSet 223 | patchSetName: region 224 | - fromFieldPath: spec.parameters.nodes.count 225 | toFieldPath: spec.forProvider.scalingConfig[0].desiredSize 226 | - fromFieldPath: spec.parameters.nodes.instanceType 227 | toFieldPath: spec.forProvider.instanceTypes[0] 228 | - fromFieldPath: spec.parameters.id 229 | toFieldPath: spec.forProvider.subnetIdSelector.matchLabels[networks.aws.caas.upbound.io/network-id] 230 | - base: 231 | apiVersion: eks.aws.upbound.io/v1beta1 232 | kind: Addon 233 | metadata: 234 | annotations: 235 | crossplane.io/external-name: aws-ebs-csi-driver 236 | spec: 237 | forProvider: 238 | addonName: aws-ebs-csi-driver 239 | clusterNameSelector: 240 | matchControllerRef: true 241 | name: ebsCsiAddon 242 | patches: 243 | - type: PatchSet 244 | patchSetName: region 245 | - base: 246 | apiVersion: iam.aws.upbound.io/v1beta1 247 | kind: OpenIDConnectProvider 248 | spec: 249 | forProvider: 250 | clientIdList: 251 | - sts.amazonaws.com 252 | thumbprintList: 253 | - "9e99a48a9960b14926bb7f3b02e22da2b0ab7280" 254 | name: oidcProvider 255 | patches: 256 | - fromFieldPath: status.eks.oidc 257 | toFieldPath: spec.forProvider.url 258 | policy: 259 | fromFieldPath: Required 260 | - type: ToCompositeFieldPath 261 | fromFieldPath: status.atProvider.arn 262 | toFieldPath: status.eks.oidcArn 263 | policy: 264 | fromFieldPath: Optional 265 | - base: 266 | apiVersion: helm.crossplane.io/v1beta1 267 | kind: ProviderConfig 268 | spec: 269 | credentials: 270 | source: Secret 271 | secretRef: 272 | key: kubeconfig 273 | name: providerConfig-helm 274 | patches: 275 | - fromFieldPath: spec.parameters.id 276 | toFieldPath: metadata.name 277 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 278 | toFieldPath: spec.credentials.secretRef.namespace 279 | - fromFieldPath: metadata.uid 280 | toFieldPath: spec.credentials.secretRef.name 281 | transforms: 282 | - type: string 283 | string: 284 | fmt: "%s-ekscluster" 285 | readinessChecks: 286 | - type: None 287 | - base: 288 | apiVersion: kubernetes.crossplane.io/v1alpha1 289 | kind: ProviderConfig 290 | spec: 291 | credentials: 292 | source: Secret 293 | secretRef: 294 | key: kubeconfig 295 | name: providerConfig-kubernetes 296 | patches: 297 | - fromFieldPath: spec.parameters.id 298 | toFieldPath: metadata.name 299 | - fromFieldPath: spec.writeConnectionSecretToRef.namespace 300 | toFieldPath: spec.credentials.secretRef.namespace 301 | - fromFieldPath: metadata.uid 302 | toFieldPath: spec.credentials.secretRef.name 303 | transforms: 304 | - type: string 305 | string: 306 | fmt: "%s-ekscluster" 307 | readinessChecks: 308 | - type: None 309 | - name: aws-auth 310 | base: 311 | apiVersion: kubernetes.crossplane.io/v1alpha1 312 | kind: Object 313 | spec: 314 | deletionPolicy: Orphan 315 | forProvider: 316 | manifest: 317 | apiVersion: v1 318 | kind: ConfigMap 319 | metadata: 320 | namespace: kube-system 321 | name: aws-auth 322 | patches: 323 | - fromFieldPath: spec.parameters.id 324 | toFieldPath: spec.providerConfigRef.name 325 | - fromFieldPath: spec.parameters.id 326 | toFieldPath: metadata.name 327 | transforms: 328 | - type: string 329 | string: 330 | fmt: "%s-aws-auth" 331 | - type: CombineFromComposite 332 | combine: 333 | variables: 334 | - fromFieldPath: status.eks.nodeGroupRoleArn 335 | - fromFieldPath: spec.parameters.iam.roleArn 336 | strategy: string 337 | string: 338 | fmt: | 339 | - groups: 340 | - system:bootstrappers 341 | - system:nodes 342 | rolearn: %s 343 | username: system:node:{{EC2PrivateDNSName}} 344 | - groups: 345 | - system:masters 346 | rolearn: %s 347 | username: adminrole 348 | toFieldPath: spec.forProvider.manifest.data.mapRoles 349 | policy: 350 | fromFieldPath: Optional 351 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/eks/eks/definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: CompositeResourceDefinition 3 | metadata: 4 | name: xeks.aws.caas.upbound.io 5 | spec: 6 | connectionSecretKeys: 7 | - kubeconfig 8 | group: aws.caas.upbound.io 9 | names: 10 | kind: XEKS 11 | plural: xeks 12 | versions: 13 | - name: v1alpha1 14 | served: true 15 | referenceable: true 16 | schema: 17 | openAPIV3Schema: 18 | type: object 19 | properties: 20 | spec: 21 | type: object 22 | properties: 23 | parameters: 24 | type: object 25 | description: EKS configuration parameters. 26 | properties: 27 | id: 28 | type: string 29 | description: ID of this Cluster that other objects will use to refer to it. 30 | region: 31 | type: string 32 | description: Region is the region you'd like your resource to be created in. 33 | iam: 34 | type: object 35 | description: IAM configuration to connect as ClusterAdmin. 36 | properties: 37 | roleArn: 38 | description: The IAM Role ARN to connect as ClusterAdmin. 39 | type: string 40 | default: roleArn 41 | version: 42 | description: Kubernetes version 43 | type: string 44 | enum: 45 | - "1.28" 46 | - "1.27" 47 | - "1.26" 48 | - "1.25" 49 | - "1.24" 50 | default: "1.28" 51 | subnetIds: 52 | type: array 53 | items: 54 | type: string 55 | nodes: 56 | type: object 57 | description: EKS node configuration parameters. 58 | properties: 59 | count: 60 | type: integer 61 | description: Desired node count, from 1 to 100. 62 | instanceType: 63 | type: string 64 | description: instance types associated with the Node Group. 65 | default: t3.small 66 | required: 67 | - count 68 | - instanceType 69 | required: 70 | - id 71 | - region 72 | - nodes 73 | - subnetIds 74 | required: 75 | - parameters 76 | status: 77 | description: A Status represents the observed state 78 | properties: 79 | eks: 80 | description: Freeform field containing status information for eks 81 | type: object 82 | x-kubernetes-preserve-unknown-fields: true 83 | type: object 84 | -------------------------------------------------------------------------------- /crossplane/apis/cluster/eks/network/basic/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: xnetworks.aws.caas.upbound.io 5 | labels: 6 | provider: aws 7 | type: basic 8 | spec: 9 | writeConnectionSecretsToNamespace: crossplane-system 10 | compositeTypeRef: 11 | apiVersion: aws.caas.upbound.io/v1alpha1 12 | kind: XNetwork 13 | patchSets: 14 | - name: network-id 15 | patches: 16 | - type: FromCompositeFieldPath 17 | fromFieldPath: spec.parameters.id 18 | toFieldPath: metadata.labels[networks.aws.caas.upbound.io/network-id] 19 | - name: region 20 | patches: 21 | - type: FromCompositeFieldPath 22 | fromFieldPath: spec.parameters.region 23 | toFieldPath: spec.forProvider.region 24 | resources: 25 | - base: 26 | apiVersion: ec2.aws.upbound.io/v1beta1 27 | kind: VPC 28 | spec: 29 | forProvider: 30 | cidrBlock: 192.168.0.0/16 31 | enableDnsSupport: true 32 | enableDnsHostnames: true 33 | tags: 34 | Name: "" 35 | name: caas-vcp 36 | patches: 37 | - type: PatchSet 38 | patchSetName: network-id 39 | - type: PatchSet 40 | patchSetName: region 41 | - fromFieldPath: metadata.name 42 | toFieldPath: spec.forProvider.tags["Name"] 43 | - base: 44 | apiVersion: ec2.aws.upbound.io/v1beta1 45 | kind: InternetGateway 46 | spec: 47 | forProvider: 48 | vpcIdSelector: 49 | matchControllerRef: true 50 | name: gateway 51 | patches: 52 | - type: PatchSet 53 | patchSetName: network-id 54 | - type: PatchSet 55 | patchSetName: region 56 | - base: 57 | apiVersion: ec2.aws.upbound.io/v1beta1 58 | kind: Subnet 59 | metadata: 60 | labels: 61 | access: public 62 | spec: 63 | forProvider: 64 | mapPublicIpOnLaunch: true 65 | cidrBlock: 192.168.0.0/18 66 | vpcIdSelector: 67 | matchControllerRef: true 68 | tags: 69 | kubernetes.io/role/elb: "1" 70 | name: subnet-public-a 71 | patches: 72 | - type: PatchSet 73 | patchSetName: network-id 74 | - type: PatchSet 75 | patchSetName: region 76 | - fromFieldPath: spec.parameters.region 77 | toFieldPath: spec.forProvider.availabilityZone 78 | transforms: 79 | - type: string 80 | string: 81 | fmt: "%sa" 82 | - fromFieldPath: spec.parameters.region 83 | toFieldPath: metadata.labels[zone] 84 | transforms: 85 | - type: string 86 | string: 87 | fmt: "%sa" 88 | - type: ToCompositeFieldPath 89 | fromFieldPath: metadata.annotations[crossplane.io/external-name] 90 | toFieldPath: status.subnetIds[0] 91 | - base: 92 | apiVersion: ec2.aws.upbound.io/v1beta1 93 | kind: Subnet 94 | metadata: 95 | labels: 96 | access: public 97 | spec: 98 | forProvider: 99 | mapPublicIpOnLaunch: true 100 | cidrBlock: 192.168.64.0/18 101 | vpcIdSelector: 102 | matchControllerRef: true 103 | tags: 104 | kubernetes.io/role/elb: "1" 105 | name: subnet-public-b 106 | patches: 107 | - type: PatchSet 108 | patchSetName: network-id 109 | - type: PatchSet 110 | patchSetName: region 111 | - fromFieldPath: spec.parameters.region 112 | toFieldPath: spec.forProvider.availabilityZone 113 | transforms: 114 | - type: string 115 | string: 116 | fmt: "%sb" 117 | - fromFieldPath: spec.parameters.region 118 | toFieldPath: metadata.labels[zone] 119 | transforms: 120 | - type: string 121 | string: 122 | fmt: "%sb" 123 | - type: ToCompositeFieldPath 124 | fromFieldPath: metadata.annotations[crossplane.io/external-name] 125 | toFieldPath: status.subnetIds[1] 126 | - base: 127 | apiVersion: ec2.aws.upbound.io/v1beta1 128 | kind: Subnet 129 | metadata: 130 | labels: 131 | access: private 132 | spec: 133 | forProvider: 134 | cidrBlock: 192.168.128.0/18 135 | vpcIdSelector: 136 | matchControllerRef: true 137 | tags: 138 | kubernetes.io/role/internal-elb: "1" 139 | name: subnet-private-a 140 | patches: 141 | - type: PatchSet 142 | patchSetName: network-id 143 | - type: PatchSet 144 | patchSetName: region 145 | - fromFieldPath: spec.parameters.region 146 | toFieldPath: spec.forProvider.availabilityZone 147 | transforms: 148 | - type: string 149 | string: 150 | fmt: "%sa" 151 | - fromFieldPath: spec.parameters.region 152 | toFieldPath: metadata.labels[zone] 153 | transforms: 154 | - type: string 155 | string: 156 | fmt: "%sa" 157 | - type: ToCompositeFieldPath 158 | fromFieldPath: metadata.annotations[crossplane.io/external-name] 159 | toFieldPath: status.subnetIds[2] 160 | - base: 161 | apiVersion: ec2.aws.upbound.io/v1beta1 162 | kind: Subnet 163 | metadata: 164 | labels: 165 | access: private 166 | spec: 167 | forProvider: 168 | cidrBlock: 192.168.192.0/18 169 | vpcIdSelector: 170 | matchControllerRef: true 171 | tags: 172 | kubernetes.io/role/internal-elb: "1" 173 | name: subnet-private-b 174 | patches: 175 | - type: PatchSet 176 | patchSetName: network-id 177 | - type: PatchSet 178 | patchSetName: region 179 | - fromFieldPath: spec.parameters.region 180 | toFieldPath: spec.forProvider.availabilityZone 181 | transforms: 182 | - type: string 183 | string: 184 | fmt: "%sb" 185 | - fromFieldPath: spec.parameters.region 186 | toFieldPath: metadata.labels[zone] 187 | transforms: 188 | - type: string 189 | string: 190 | fmt: "%sb" 191 | - type: ToCompositeFieldPath 192 | fromFieldPath: metadata.annotations[crossplane.io/external-name] 193 | toFieldPath: status.subnetIds[3] 194 | - base: 195 | apiVersion: ec2.aws.upbound.io/v1beta1 196 | kind: RouteTable 197 | spec: 198 | forProvider: 199 | vpcIdSelector: 200 | matchControllerRef: true 201 | name: routeTable 202 | patches: 203 | - type: PatchSet 204 | patchSetName: network-id 205 | - type: PatchSet 206 | patchSetName: region 207 | - base: 208 | apiVersion: ec2.aws.upbound.io/v1beta1 209 | kind: Route 210 | spec: 211 | forProvider: 212 | destinationCidrBlock: 0.0.0.0/0 213 | gatewayIdSelector: 214 | matchControllerRef: true 215 | routeTableIdSelector: 216 | matchControllerRef: true 217 | name: route 218 | patches: 219 | - type: PatchSet 220 | patchSetName: network-id 221 | - type: PatchSet 222 | patchSetName: region 223 | - base: 224 | apiVersion: ec2.aws.upbound.io/v1beta1 225 | kind: MainRouteTableAssociation 226 | spec: 227 | forProvider: 228 | routeTableIdSelector: 229 | matchControllerRef: true 230 | vpcIdSelector: 231 | matchControllerRef: true 232 | name: mainRouteTableAssociation 233 | patches: 234 | - type: PatchSet 235 | patchSetName: network-id 236 | - type: PatchSet 237 | patchSetName: region 238 | - base: 239 | apiVersion: ec2.aws.upbound.io/v1beta1 240 | kind: RouteTableAssociation 241 | spec: 242 | forProvider: 243 | routeTableIdSelector: 244 | matchControllerRef: true 245 | subnetIdSelector: 246 | matchControllerRef: true 247 | matchLabels: 248 | access: public 249 | name: RouteTableAssociation-public-a 250 | patches: 251 | - type: PatchSet 252 | patchSetName: network-id 253 | - type: PatchSet 254 | patchSetName: region 255 | - fromFieldPath: spec.parameters.region 256 | toFieldPath: spec.forProvider.subnetIdSelector.matchLabels[zone] 257 | transforms: 258 | - type: string 259 | string: 260 | fmt: "%sa" 261 | - base: 262 | apiVersion: ec2.aws.upbound.io/v1beta1 263 | kind: RouteTableAssociation 264 | spec: 265 | forProvider: 266 | routeTableIdSelector: 267 | matchControllerRef: true 268 | subnetIdSelector: 269 | matchControllerRef: true 270 | matchLabels: 271 | access: public 272 | name: RouteTableAssociation-public-b 273 | patches: 274 | - type: PatchSet 275 | patchSetName: network-id 276 | - type: PatchSet 277 | patchSetName: region 278 | - fromFieldPath: spec.parameters.region 279 | toFieldPath: spec.forProvider.subnetIdSelector.matchLabels[zone] 280 | transforms: 281 | - type: string 282 | string: 283 | fmt: "%sb" 284 | - base: 285 | apiVersion: ec2.aws.upbound.io/v1beta1 286 | kind: RouteTableAssociation 287 | spec: 288 | forProvider: 289 | routeTableIdSelector: 290 | matchControllerRef: true 291 | subnetIdSelector: 292 | matchControllerRef: true 293 | matchLabels: 294 | access: private 295 | name: RouteTableAssociation-private-a 296 | patches: 297 | - type: PatchSet 298 | patchSetName: network-id 299 | - type: PatchSet 300 | patchSetName: region 301 | - fromFieldPath: spec.parameters.region 302 | toFieldPath: spec.forProvider.subnetIdSelector.matchLabels[zone] 303 | transforms: 304 | - type: string 305 | string: 306 | fmt: "%sa" 307 | - base: 308 | apiVersion: ec2.aws.upbound.io/v1beta1 309 | kind: RouteTableAssociation 310 | spec: 311 | forProvider: 312 | routeTableIdSelector: 313 | matchControllerRef: true 314 | subnetIdSelector: 315 | matchControllerRef: true 316 | matchLabels: 317 | access: private 318 | name: RouteTableAssociation-private-b 319 | patches: 320 | - type: PatchSet 321 | patchSetName: network-id 322 | - type: PatchSet 323 | patchSetName: region 324 | - fromFieldPath: spec.parameters.region 325 | toFieldPath: spec.forProvider.subnetIdSelector.matchLabels[zone] 326 | transforms: 327 | - type: string 328 | string: 329 | fmt: "%sb" -------------------------------------------------------------------------------- /crossplane/apis/cluster/eks/network/definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: CompositeResourceDefinition 3 | metadata: 4 | name: xnetworks.aws.caas.upbound.io 5 | spec: 6 | group: aws.caas.upbound.io 7 | names: 8 | kind: XNetwork 9 | plural: xnetworks 10 | versions: 11 | - name: v1alpha1 12 | served: true 13 | referenceable: true 14 | schema: 15 | openAPIV3Schema: 16 | type: object 17 | properties: 18 | spec: 19 | type: object 20 | properties: 21 | parameters: 22 | description: Network Parameters 23 | properties: 24 | id: 25 | type: string 26 | description: ID of this Network that other objects will use to refer to it. 27 | region: 28 | type: string 29 | description: Region is the region you'd like your resource to be created in. 30 | required: 31 | - id 32 | - region 33 | type: object 34 | required: 35 | - parameters 36 | status: 37 | type: object 38 | properties: 39 | subnetIds: 40 | type: array 41 | items: 42 | type: string 43 | securityGroupIds: 44 | type: array 45 | items: 46 | type: string 47 | -------------------------------------------------------------------------------- /crossplane/apis/hub/definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: CompositeResourceDefinition 3 | metadata: 4 | name: xhubs.backstack.dev 5 | spec: 6 | group: backstack.dev 7 | names: 8 | kind: XHub 9 | plural: xhubs 10 | claimNames: 11 | kind: Hub 12 | plural: hubs 13 | versions: 14 | - name: v1alpha1 15 | served: true 16 | referenceable: true 17 | schema: 18 | openAPIV3Schema: 19 | type: object 20 | description: A BACK stack hub installs Backstage, Argo, and Kyverno and connects them all together with Crossplane 21 | properties: 22 | spec: 23 | type: object 24 | description: Specification of the desired state of the Hub 25 | properties: 26 | parameters: 27 | type: object 28 | description: Input parameters for provisioning a Hub 29 | properties: 30 | clusterId: 31 | type: string 32 | description: ID of the target cluster for the BACK stack. There should be helm and kubernetes ProviderConfigs of the same name present in the control plane. 33 | repository: 34 | type: string 35 | description: The URL of the git repository where claims will be synchronized 36 | kyverno-policy-repository: 37 | type: string 38 | # TODO: describe what format the repository is expected to be in 39 | description: The URL of the git repository where custom kyverno policies are stored 40 | backstage: 41 | type: object 42 | description: Configuration for the Backstage deployment 43 | properties: 44 | host: 45 | type: string 46 | description: Ingress URL for Backstage 47 | image: 48 | type: object 49 | description: Backstage image configuration 50 | x-kubernetes-preserve-unknown-fields: true 51 | argocd: 52 | type: object 53 | description: Configuration for the ArgoCD deployment 54 | properties: 55 | host: 56 | type: string 57 | description: Ingress URL for ArgoCD 58 | vault: 59 | type: object 60 | description: Configuration for the Vault deployment 61 | properties: 62 | host: 63 | type: string 64 | description: Ingress URL for Vault 65 | status: 66 | type: object 67 | description: Observed status of the Hub 68 | -------------------------------------------------------------------------------- /crossplane/crossplane.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: meta.pkg.crossplane.io/v1 2 | kind: Configuration 3 | metadata: 4 | name: backstack 5 | annotations: 6 | meta.crossplane.io/maintainer: Back Stack 7 | meta.crossplane.io/source: github.com/back-stack 8 | meta.crossplane.io/license: Apache-2.0 9 | meta.crossplane.io/description: | 10 | This configuration provides apis for the BACK stack 11 | spec: 12 | crossplane: 13 | version: ">=v1.14.0-0" 14 | dependsOn: 15 | - function: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform 16 | version: ">=v0.1.4" 17 | - provider: xpkg.upbound.io/crossplane-contrib/provider-helm 18 | version: ">=v0.15.0" 19 | - provider: xpkg.upbound.io/crossplane-contrib/provider-kubernetes 20 | version: ">=v0.9.0" 21 | - provider: xpkg.upbound.io/upbound/provider-aws-ec2 22 | version: ">=v0.42.0" 23 | - provider: xpkg.upbound.io/upbound/provider-aws-eks 24 | version: ">=v0.42.0" 25 | - provider: xpkg.upbound.io/upbound/provider-aws-iam 26 | version: ">=v0.42.0" 27 | - provider: xpkg.upbound.io/upbound/provider-azure-containerservice 28 | version: ">=v0.37.1" 29 | - provider: xpkg.upbound.io/upbound/provider-azure-network 30 | version: ">=v0.37.1" 31 | -------------------------------------------------------------------------------- /kyverno/policies/crossplane/limit-node-count-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v2beta1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: limit-node-count-policy 5 | annotations: 6 | policies.kyverno.io/title: Limit Node Count 7 | policies.kyverno.io/category: Upbound Crossplane 8 | policies.kyverno.io/minversion: 1.9.0 9 | kyverno.io/kyverno-version: 1.9.0 10 | kyverno.io/kubernetes-version: "1.24" 11 | policies.kyverno.io/subject: Cluster 12 | policies.kyverno.io/description: >- 13 | User should not be able to create clusters with more than 3 nodes 14 | spec: 15 | validationFailureAction: Enforce 16 | background: true 17 | rules: 18 | - name: limit-node-count 19 | match: 20 | any: 21 | - resources: 22 | kinds: 23 | - aws.platformref.upbound.io/v1alpha1/Cluster 24 | validate: 25 | message: >- 26 | You cannot create a cluster with more than 3 nodes. 27 | Please reduce the node count and create the cluster. 28 | deny: 29 | conditions: 30 | any: 31 | - key: "{{request.object.spec.parameters.nodes.count}}" 32 | operator: GreaterThan 33 | value: 3 34 | -------------------------------------------------------------------------------- /kyverno/policies/crossplane/restrict-node-size-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v2beta1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: restrict-node-size-policy 5 | annotations: 6 | policies.kyverno.io/title: Restrict Node Size 7 | policies.kyverno.io/category: Upbound Crossplane 8 | policies.kyverno.io/minversion: 1.9.0 9 | kyverno.io/kyverno-version: 1.9.0 10 | kyverno.io/kubernetes-version: "1.24" 11 | policies.kyverno.io/subject: Cluster 12 | policies.kyverno.io/description: >- 13 | Create clusters with node size large is not permitted 14 | spec: 15 | validationFailureAction: Enforce 16 | background: true 17 | rules: 18 | - name: restrict-node-size 19 | match: 20 | any: 21 | - resources: 22 | kinds: 23 | - aws.platformref.upbound.io/v1alpha1/Cluster 24 | validate: 25 | message: >- 26 | You cannot create a cluster with node size large 27 | Please change the node size to small or medium 28 | deny: 29 | conditions: 30 | any: 31 | - key: "{{request.object.spec.parameters.nodes.size}}" 32 | operator: Equals 33 | value: "large" 34 | -------------------------------------------------------------------------------- /kyverno/policies/crossplane/validate-cluster-name-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v2beta1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: validate-cluster-name-policy 5 | annotations: 6 | policies.kyverno.io/title: validate-cluster-name 7 | policies.kyverno.io/category: Upbound Crossplane 8 | policies.kyverno.io/minversion: 1.9.0 9 | kyverno.io/kyverno-version: 1.9.0 10 | kyverno.io/kubernetes-version: "1.24" 11 | policies.kyverno.io/subject: Cluster 12 | policies.kyverno.io/description: >- 13 | Cluster name should start with department name as the prefix 14 | spec: 15 | validationFailureAction: Enforce 16 | background: false 17 | rules: 18 | - name: validate-cluster-name 19 | match: 20 | any: 21 | - resources: 22 | kinds: 23 | - aws.platformref.upbound.io/v1alpha1/Cluster 24 | context: 25 | - name: prefix 26 | variable: 27 | jmesPath: request.object.spec.id.starts_with(@,'dev-') || request.object.spec.id.starts_with(@,'test-') 28 | validate: 29 | message: >- 30 | You can only create clusters with your department name as the prefix. 31 | Please change the cluster name. 32 | deny: 33 | conditions: 34 | any: 35 | - key: "{{ prefix }}" 36 | operator: NotEquals 37 | value: true 38 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/disallow-capabilities.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: disallow-capabilities 5 | annotations: 6 | policies.kyverno.io/title: Disallow Capabilities 7 | policies.kyverno.io/category: Pod Security Standards (Baseline) 8 | policies.kyverno.io/severity: medium 9 | kyverno.io/kyverno-version: 1.6.0 10 | policies.kyverno.io/minversion: 1.6.0 11 | kyverno.io/kubernetes-version: "1.22-1.23" 12 | policies.kyverno.io/subject: Pod 13 | policies.nirmata.io/remediation: "https://docs.nirmata.io/policysets/podsecurity/baseline/disallow-capabilities/" 14 | policies.kyverno.io/description: >- 15 | Any additional capabilities not mentioned in the allowed list, which includes AUDIT_WRITE, CHOWN, DAC_OVERRIDE, FOWNER, FSETID, KILL, MKNOD, NET_BIND_SERVICE, SETFCAP, SETGID, SETPCAP, SETUID, SYS_CHROOT, are prohibited and not permitted. 16 | spec: 17 | validationFailureAction: audit 18 | background: true 19 | rules: 20 | - name: adding-capabilities 21 | match: 22 | any: 23 | - resources: 24 | kinds: 25 | - Pod 26 | validate: 27 | message: >- 28 | Adding capabilities beyond those listed in the policy rule is disallowed. 29 | deny: 30 | conditions: 31 | all: 32 | - key: "{{ request.object.spec.[ephemeralContainers, initContainers, containers][].securityContext.capabilities.add[] }}" 33 | operator: AnyNotIn 34 | value: 35 | - AUDIT_WRITE 36 | - CHOWN 37 | - DAC_OVERRIDE 38 | - FOWNER 39 | - FSETID 40 | - KILL 41 | - MKNOD 42 | - NET_BIND_SERVICE 43 | - SETFCAP 44 | - SETGID 45 | - SETPCAP 46 | - SETUID 47 | - SYS_CHROOT 48 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/disallow-host-namespaces.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: disallow-host-namespaces 5 | annotations: 6 | policies.kyverno.io/title: Disallow Host Namespaces 7 | policies.kyverno.io/category: Pod Security Standards (Baseline) 8 | policies.kyverno.io/severity: medium 9 | kyverno.io/kyverno-version: 1.6.0 10 | kyverno.io/kubernetes-version: "1.22-1.23" 11 | policies.kyverno.io/subject: Pod 12 | policies.kyverno.io/description: >- 13 | Host namespaces (Process ID namespace, Inter-Process Communication namespace, and 14 | network namespace) allow access to shared information and can be used to elevate 15 | privileges. Pods should not be allowed access to host namespaces. This policy ensures 16 | fields which make use of these host namespaces are unset or set to `false`. 17 | spec: 18 | validationFailureAction: audit 19 | background: true 20 | rules: 21 | - name: host-namespaces 22 | match: 23 | any: 24 | - resources: 25 | kinds: 26 | - Pod 27 | validate: 28 | message: >- 29 | Sharing the host namespaces is disallowed. The fields spec.hostNetwork, 30 | spec.hostIPC, and spec.hostPID must be unset or set to `false`. (CCI-001090) 31 | pattern: 32 | spec: 33 | =(hostPID): "false" 34 | =(hostIPC): "false" 35 | =(hostNetwork): "false" 36 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/disallow-host-path.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: disallow-host-path 5 | annotations: 6 | policies.kyverno.io/title: Disallow hostPath 7 | policies.kyverno.io/category: Pod Security Standards (Baseline) 8 | policies.kyverno.io/severity: medium 9 | policies.kyverno.io/subject: Pod,Volume 10 | kyverno.io/kyverno-version: 1.6.0 11 | kyverno.io/kubernetes-version: "1.22-1.23" 12 | policies.nirmata.io/remediation: "https://docs.nirmata.io/policysets/podsecurity/baseline/disallow-host-path/" 13 | policies.kyverno.io/description: >- 14 | HostPath volumes let Pods use host directories and volumes in containers. 15 | Using host resources can be used to access shared data or escalate privileges 16 | and should not be allowed. This policy ensures no hostPath volumes are in use. 17 | spec: 18 | validationFailureAction: audit 19 | background: true 20 | rules: 21 | - name: host-path 22 | match: 23 | any: 24 | - resources: 25 | kinds: 26 | - Pod 27 | validate: 28 | message: >- 29 | HostPath volumes are forbidden. (CCI-001090) 30 | pattern: 31 | spec: 32 | =(volumes): 33 | - X(hostPath): "null" 34 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/disallow-privileged-containers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: disallow-privileged-containers 5 | annotations: 6 | policies.kyverno.io/title: Disallow Privileged Containers 7 | policies.kyverno.io/category: Pod Security Standards (Baseline) 8 | policies.kyverno.io/severity: medium 9 | policies.kyverno.io/subject: Pod 10 | kyverno.io/kyverno-version: 1.6.0 11 | kyverno.io/kubernetes-version: "1.22-1.23" 12 | policies.nirmata.io/remediation: "https://docs.nirmata.io/policysets/podsecurity/baseline/disallow-privileged-containers/" 13 | policies.kyverno.io/description: >- 14 | Privileged mode disables most security mechanisms and must not be allowed. This policy 15 | ensures the fields spec.containers[*].securityContext.privileged and spec.initContainers[*].securityContext.privileged must be unset or set to `false`. 16 | spec: 17 | validationFailureAction: audit 18 | background: true 19 | rules: 20 | - name: privileged-containers 21 | match: 22 | any: 23 | - resources: 24 | kinds: 25 | - Pod 26 | validate: 27 | message: >- 28 | Privileged mode is disallowed. (CCI-002235) 29 | pattern: 30 | spec: 31 | =(ephemeralContainers): 32 | - =(securityContext): 33 | =(privileged): "false" 34 | =(initContainers): 35 | - =(securityContext): 36 | =(privileged): "false" 37 | containers: 38 | - =(securityContext): 39 | =(privileged): "false" 40 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/require-run-as-nonroot.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: require-run-as-nonroot 5 | annotations: 6 | policies.kyverno.io/title: Require runAsNonRoot 7 | policies.kyverno.io/category: Pod Security Standards (Restricted) 8 | policies.kyverno.io/severity: medium 9 | policies.kyverno.io/subject: Pod 10 | kyverno.io/kyverno-version: 1.6.0 11 | kyverno.io/kubernetes-version: "1.22-1.23" 12 | policies.kyverno.io/description: >- 13 | Containers must be required to run as non-root users. This policy ensures 14 | `runAsNonRoot` is set to `true`. A known issue prevents a policy such as this 15 | using `anyPattern` from being persisted properly in Kubernetes 1.23.0-1.23.2. 16 | spec: 17 | validationFailureAction: audit 18 | background: true 19 | rules: 20 | - name: run-as-non-root 21 | match: 22 | any: 23 | - resources: 24 | kinds: 25 | - Pod 26 | validate: 27 | message: >- 28 | Running as root is not allowed. Either the field spec.securityContext.runAsNonRoot 29 | must be set to `true`, or the fields spec.containers[*].securityContext.runAsNonRoot, 30 | spec.initContainers[*].securityContext.runAsNonRoot, and spec.ephemeralContainers[*].securityContext.runAsNonRoot 31 | must be set to `true`. (CCI-002235) 32 | anyPattern: 33 | - spec: 34 | securityContext: 35 | runAsNonRoot: "true" 36 | =(ephemeralContainers): 37 | - =(securityContext): 38 | =(runAsNonRoot): "true" 39 | =(initContainers): 40 | - =(securityContext): 41 | =(runAsNonRoot): "true" 42 | containers: 43 | - =(securityContext): 44 | =(runAsNonRoot): "true" 45 | - spec: 46 | =(ephemeralContainers): 47 | - securityContext: 48 | runAsNonRoot: "true" 49 | =(initContainers): 50 | - securityContext: 51 | runAsNonRoot: "true" 52 | containers: 53 | - securityContext: 54 | runAsNonRoot: "true" 55 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/require_pod_requests_limits.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: require-requests-limits 5 | annotations: 6 | policies.kyverno.io/title: Require Limits and Requests 7 | policies.kyverno.io/category: Best Practices, EKS Best Practices 8 | policies.kyverno.io/severity: medium 9 | policies.kyverno.io/subject: Pod 10 | policies.kyverno.io/minversion: 1.6.0 11 | policies.kyverno.io/description: >- 12 | As application workloads share cluster resources, it is important to limit resources 13 | requested and consumed by each Pod. It is recommended to require resource requests and 14 | limits per Pod, especially for memory and CPU. If a Namespace level request or limit is specified, 15 | defaults will automatically be applied to each Pod based on the LimitRange configuration. 16 | This policy validates that all containers have something specified for memory and CPU 17 | requests and memory limits. 18 | spec: 19 | validationFailureAction: audit 20 | background: true 21 | rules: 22 | - name: validate-resources 23 | match: 24 | any: 25 | - resources: 26 | kinds: 27 | - Pod 28 | validate: 29 | message: "CPU and memory resource requests and limits are required. (CCI-001094, CCI-001095)" 30 | pattern: 31 | spec: 32 | containers: 33 | - resources: 34 | requests: 35 | memory: "?*" 36 | cpu: "?*" 37 | limits: 38 | memory: "?*" -------------------------------------------------------------------------------- /kyverno/policies/pod-security/require_probes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: require-pod-probes 5 | annotations: 6 | pod-policies.kyverno.io/autogen-controllers: DaemonSet,Deployment,StatefulSet 7 | policies.kyverno.io/title: Require Pod Probes 8 | policies.kyverno.io/category: Best Practices, EKS Best Practices 9 | policies.kyverno.io/severity: medium 10 | policies.kyverno.io/subject: Pod 11 | policies.kyverno.io/description: >- 12 | Liveness and readiness probes need to be configured to correctly manage a Pod's 13 | lifecycle during deployments, restarts, and upgrades. For each Pod, a periodic 14 | `livenessProbe` is performed by the kubelet to determine if the Pod's containers 15 | are running or need to be restarted. A `readinessProbe` is used by Services 16 | and Deployments to determine if the Pod is ready to receive network traffic. 17 | This policy validates that all containers have one of livenessProbe, readinessProbe, 18 | or startupProbe defined. 19 | spec: 20 | validationFailureAction: audit 21 | background: true 22 | rules: 23 | - name: validate-probes 24 | match: 25 | any: 26 | - resources: 27 | kinds: 28 | - Pod 29 | validate: 30 | message: "Liveness, readiness, or startup probes are required for all containers. (CCI-002385)" 31 | foreach: 32 | - list: request.object.spec.containers[] 33 | deny: 34 | conditions: 35 | all: 36 | - key: livenessProbe 37 | operator: AllNotIn 38 | value: "{{ element.keys(@)[] }}" 39 | - key: startupProbe 40 | operator: AllNotIn 41 | value: "{{ element.keys(@)[] }}" 42 | - key: readinessProbe 43 | operator: AllNotIn 44 | value: "{{ element.keys(@)[] }}" -------------------------------------------------------------------------------- /kyverno/policies/pod-security/require_ro_rootfs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: require-ro-rootfs 5 | annotations: 6 | policies.kyverno.io/title: Require Read-Only Root Filesystem 7 | policies.kyverno.io/category: Best Practices, EKS Best Practices 8 | policies.kyverno.io/severity: medium 9 | policies.kyverno.io/subject: Pod 10 | policies.kyverno.io/minversion: 1.6.0 11 | policies.kyverno.io/description: >- 12 | A read-only root file system helps to enforce an immutable infrastructure strategy; 13 | the container only needs to write on the mounted volume that persists the state. 14 | An immutable root filesystem can also prevent malicious binaries from writing to the 15 | host system. This policy validates that containers define a securityContext 16 | with `readOnlyRootFilesystem: true`. 17 | spec: 18 | validationFailureAction: audit 19 | background: true 20 | rules: 21 | - name: validate-readOnlyRootFilesystem 22 | match: 23 | any: 24 | - resources: 25 | kinds: 26 | - Pod 27 | validate: 28 | message: "Root filesystem must be read-only. (CCI-001813)" 29 | pattern: 30 | spec: 31 | containers: 32 | - securityContext: 33 | readOnlyRootFilesystem: true -------------------------------------------------------------------------------- /kyverno/policies/pod-security/restrict-privileged-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: restric-privileged-port 5 | annotations: 6 | policies.kyverno.io/title: Restrict Privileged Ports 7 | policies.kyverno.io/category: Security 8 | policies.kyverno.io/severity: medium 9 | kyverno.io/kyverno-version: 1.7.2 10 | kyverno.io/kubernetes-version: "1.23" 11 | policies.kyverno.io/subject: Pod 12 | policies.kyverno.io/description: >- 13 | Privileged ports should not be used. Use port number greater than 1024 14 | spec: 15 | validationFailureAction: audit 16 | background: true 17 | rules: 18 | - name: check-privileged-ports 19 | match: 20 | any: 21 | - resources: 22 | kinds: 23 | - Pod 24 | validate: 25 | message: Privileged ports should not be used. (CCI-0017620) 26 | pattern: 27 | spec: 28 | containers: 29 | - name: "*" 30 | =(ports): 31 | - containerPort: ">=1024" 32 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/restrict-seccomp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: restrict-seccomp 5 | annotations: 6 | policies.kyverno.io/title: Restrict Seccomp 7 | policies.kyverno.io/category: Pod Security Standards (Baseline) 8 | policies.kyverno.io/severity: medium 9 | policies.kyverno.io/subject: Pod 10 | kyverno.io/kyverno-version: 1.6.0 11 | kyverno.io/kubernetes-version: "1.22-1.23" 12 | policies.nirmata.io/remediation: "https://docs.nirmata.io/policysets/podsecurity/baseline/restrict-seccomp/" 13 | policies.kyverno.io/description: >- 14 | The seccomp profile must not be explicitly set to Unconfined. This policy, 15 | requiring Kubernetes v1.19 or later, ensures that the fields 16 | spec.securityContext.seccompProfile.type, 17 | spec.containers[*].securityContext.seccompProfile.type, 18 | spec.initContainers[*].securityContext.seccompProfile.type, and 19 | spec.ephemeralContainers[*].securityContext.seccompProfile.type 20 | must be unset or set to `RuntimeDefault` or `Localhost`. 21 | spec: 22 | background: true 23 | validationFailureAction: audit 24 | rules: 25 | - name: check-seccomp 26 | match: 27 | any: 28 | - resources: 29 | kinds: 30 | - Pod 31 | validate: 32 | message: >- 33 | Use of custom Seccomp profiles is disallowed. (CCI-001090) 34 | pattern: 35 | spec: 36 | =(securityContext): 37 | =(seccompProfile): 38 | =(type): "RuntimeDefault | Localhost" 39 | =(ephemeralContainers): 40 | - =(securityContext): 41 | =(seccompProfile): 42 | =(type): "RuntimeDefault | Localhost" 43 | =(initContainers): 44 | - =(securityContext): 45 | =(seccompProfile): 46 | =(type): "RuntimeDefault | Localhost" 47 | containers: 48 | - =(securityContext): 49 | =(seccompProfile): 50 | =(type): "RuntimeDefault | Localhost" 51 | -------------------------------------------------------------------------------- /kyverno/policies/pod-security/restrict_image_registries.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kyverno.io/v1 2 | kind: ClusterPolicy 3 | metadata: 4 | name: restrict-image-registries 5 | annotations: 6 | policies.kyverno.io/title: Restrict Image Registries 7 | policies.kyverno.io/category: Best Practices, EKS Best Practices 8 | policies.kyverno.io/severity: medium 9 | policies.kyverno.io/minversion: 1.6.0 10 | policies.kyverno.io/subject: Pod 11 | policies.kyverno.io/description: >- 12 | Images from unknown, public registries can be of dubious quality and may not be 13 | scanned and secured, representing a high degree of risk. Requiring use of known, approved 14 | registries helps reduce threat exposure by ensuring image pulls only come from them. This 15 | policy validates that container images only originate from the registry `eu.foo.io` or 16 | `bar.io`. Use of this policy requires customization to define your allowable registries. 17 | spec: 18 | validationFailureAction: audit 19 | background: true 20 | rules: 21 | - name: validate-registries 22 | match: 23 | any: 24 | - resources: 25 | kinds: 26 | - Pod 27 | validate: 28 | message: "Unknown image registry. A trusted image registry should be used. (CCI-000185)" 29 | pattern: 30 | spec: 31 | containers: 32 | - image: "ghcr.io/* | aws.com/*" 33 | --------------------------------------------------------------------------------