├── .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 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
--------------------------------------------------------------------------------