diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 23efb61..063d23e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -45,4 +45,4 @@ jobs: - name: "Pytest" working-directory: src - run: uv run pytest --cov=. --cov-fail-under=96 + run: uv run pytest --cov=. --cov-fail-under=94 diff --git a/README.md b/README.md index ac1edc5..e0cb66d 100644 --- a/README.md +++ b/README.md @@ -1 +1,41 @@ -# STACKIT Database ACL Validation \ No newline at end of file +# STACKIT Database ACL Validation + +A GitHub Action to validate the ACL configuration of all postgres databases in a STACKIT Organisation or Project. This +makes sure that the databases are only accessible via the cluster. + +## Usage + +### Validate all DBs in an organisation + +The action will fail as soon as at least one database has other ACLs than the cluster egress CIDR range. The output will +contain more details about what project and what database is causing the problem. + +```yaml +jobs: + db-validation: + name: "STACKIT DB ACL Validation" + runs-on: ubuntu-latest + steps: + - name: "Run validation" + uses: digitalservicebund/stackit-database-validation@main + with: + organisation_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + stackit_service_account_key: ${{ secrets.STACKIT_SERVICE_ACCOUNT_KEY }} +``` + +### Validate all DBs in a project + +```yaml +jobs: + db-validation: + name: "STACKIT DB ACL Validation" + runs-on: ubuntu-latest + steps: + - name: "Run validation" + uses: digitalservicebund/stackit-database-validation@main + with: + project_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + prod_egress_range: 10.0.0.0/32 # get this from the platfrom team + non_prod_egress_range: 10.0.0.1/32 # get this from the platfrom team + stackit_service_account_key: ${{ secrets.STACKIT_SERVICE_ACCOUNT_KEY }} +``` diff --git a/action.yaml b/action.yaml index ed2ccf8..c8673c7 100644 --- a/action.yaml +++ b/action.yaml @@ -2,7 +2,19 @@ name: 'Validate STACKIT Database ACLs' description: 'A github action that uses the STACKIT API to ensure that database ACLs only allow the cluster to connect to the database instances.' inputs: organisation_id: - description: 'The Organisation ID, to validate all Databases in your STACKIT organisation.' + description: 'The Organisation ID, to validate all Databases in your STACKIT organisation. Either provide organisation_id or project_id' + required: false + project_id: + description: 'The Project ID, to validate all Databases in your STACKIT project.' + required: false + prod_egress_range: + description: 'The CIDR Range of the production cluster' + required: false + non_prod_egress_range: + description: 'The CIDR Range of the non-prod cluster' + required: false + stackit_service_account_key: + description: "STACKIT Service Account Key to authenticate against STACKIT API" required: true runs: @@ -10,8 +22,44 @@ runs: steps: - name: "Install Python" uses: actions/setup-python@v5 + with: + python-version-file: ${{ github.action_path }}/.python-version - name: "Install uv" uses: astral-sh/setup-uv@v6 - - name: "Validate STACKIT Database ACLs" + with: + ignore-empty-workdir: true + working-directory: ${{ github.action_path }} + - name: "Install STACKIT CLI" + shell: bash + run: | + sudo apt-get install curl gnupg + curl https://packages.stackit.cloud/keys/key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/stackit.gpg + echo "deb [signed-by=/usr/share/keyrings/stackit.gpg] https://packages.stackit.cloud/apt/cli stackit main" | sudo tee -a /etc/apt/sources.list.d/stackit.list + sudo apt-get update + sudo apt-get install stackit + - name: "Install dependencies" + run: uv sync + working-directory: ${{ github.action_path }} + shell: bash + - name: "Setup STACKIT Credentials" + shell: bash + env: + STACKIT_SERVICE_ACCOUNT_KEY: ${{ inputs.stackit_service_account_key }} + run: | + echo "$STACKIT_SERVICE_ACCOUNT_KEY" > ${{ github.action_path }}/stackit-credentials.json + - name: "Validate STACKIT Database ACLs in Organisation" + if: ${{ inputs.organisation_id }} + shell: bash run: uv run src/main.py validate-org ${{ inputs.organisation_id }} - shell: bash \ No newline at end of file + working-directory: ${{ github.action_path }} + env: + STACKIT_SERVICE_ACCOUNT_KEY_PATH: ${{ github.action_path }}/stackit-credentials.json + - name: "Validate STACKIT Database ACLs in Project" + if: ${{ inputs.project_id }} + shell: bash + run: uv run src/main.py validate-projects ${{ inputs.project_id }} + working-directory: ${{ github.action_path }} + env: + STACKIT_SERVICE_ACCOUNT_KEY_PATH: ${{ github.action_path }}/stackit-credentials.json + PROD_EGRESS_RANGE: ${{ inputs.prod_egress_range }} + NON_PROD_EGRESS_RANGE: ${{ inputs.non_prod_egress_range }} \ No newline at end of file diff --git a/src/main.py b/src/main.py index 4b5c569..b1be70a 100644 --- a/src/main.py +++ b/src/main.py @@ -47,19 +47,22 @@ class OrgSettings(StackITSettings): @lru_cache def get_bearer_token(stackit_service_account_key_path: str): - result = subprocess.run( - [ - "stackit", - "auth", - "activate-service-account", - "--service-account-key-path", - stackit_service_account_key_path, - "--only-print-access-token", - ], - capture_output=True, - text=True, - check=True, - ) + try: + result = subprocess.run( + [ + "stackit", + "auth", + "activate-service-account", + "--service-account-key-path", + stackit_service_account_key_path, + "--only-print-access-token", + ], + capture_output=True, + text=True, + check=True, + ) + except subprocess.CalledProcessError as e: + raise Exception(f"Could not get auth token: {e.stderr}") from e token = result.stdout.strip() return token