Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63f7c1d666 |
No files matched your search
@@ -44,16 +44,7 @@ jobs:
|
||||
|
||||
container-build-push:
|
||||
needs: setup
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
arch: amd64
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -72,17 +63,17 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Build and push API container for ${{ matrix.arch }}
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
- name: Build and push API container (latest)
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: ${{ env.WORKING_DIRECTORY }}
|
||||
push: true
|
||||
platforms: ${{ matrix.platform }}
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-${{ matrix.arch }}
|
||||
cache-from: type=gha,scope=${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify container push started
|
||||
if: github.event_name == 'release'
|
||||
@@ -98,6 +89,19 @@ jobs:
|
||||
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-started.json"
|
||||
|
||||
- name: Build and push API container (release)
|
||||
if: github.event_name == 'release'
|
||||
id: container-push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: ${{ env.WORKING_DIRECTORY }}
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.RELEASE_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.STABLE_TAG }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify container push completed
|
||||
if: github.event_name == 'release' && always()
|
||||
uses: ./.github/actions/slack-notification
|
||||
@@ -113,52 +117,6 @@ jobs:
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-completed.json"
|
||||
step-outcome: ${{ steps.container-push.outcome }}
|
||||
|
||||
# Create and push multi-architecture manifest
|
||||
create-manifest:
|
||||
needs: [setup, container-build-push]
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Create and push manifests for push event
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64
|
||||
|
||||
- name: Create and push manifests for release event
|
||||
if: github.event_name == 'release'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.RELEASE_TAG }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.STABLE_TAG }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64
|
||||
|
||||
- name: Install regctl
|
||||
if: always()
|
||||
uses: regclient/actions/regctl-installer@f61d18f46c86af724a9c804cb9ff2a6fec741c7c # main
|
||||
|
||||
- name: Cleanup intermediate architecture tags
|
||||
if: always()
|
||||
run: |
|
||||
echo "Cleaning up intermediate tags..."
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64" || true
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64" || true
|
||||
echo "Cleanup completed"
|
||||
|
||||
trigger-deployment:
|
||||
if: github.event_name == 'push'
|
||||
needs: [setup, container-build-push]
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
name: 'Tools: Comment Label Update'
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types:
|
||||
- 'created'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.issue.number }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
update-labels:
|
||||
if: contains(github.event.issue.labels.*.name, 'status/awaiting-response')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Remove 'status/awaiting-response' label
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
run: |
|
||||
echo "Removing 'status/awaiting-response' label from #$ISSUE_NUMBER"
|
||||
gh api /repos/${{ github.repository }}/issues/$ISSUE_NUMBER/labels/status%2Fawaiting-response \
|
||||
-X DELETE
|
||||
|
||||
- name: Add 'status/waiting-for-revision' label
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
run: |
|
||||
echo "Adding 'status/waiting-for-revision' label to #$ISSUE_NUMBER"
|
||||
gh api /repos/${{ github.repository }}/issues/$ISSUE_NUMBER/labels \
|
||||
-X POST \
|
||||
-f labels[]='status/waiting-for-revision'
|
||||
@@ -43,16 +43,7 @@ jobs:
|
||||
|
||||
container-build-push:
|
||||
needs: setup
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
arch: amd64
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -70,25 +61,24 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Build and push MCP container for ${{ matrix.arch }}
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
- name: Build and push MCP container (latest)
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: ${{ env.WORKING_DIRECTORY }}
|
||||
push: true
|
||||
platforms: ${{ matrix.platform }}
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-${{ matrix.arch }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}
|
||||
labels: |
|
||||
org.opencontainers.image.title=Prowler MCP Server
|
||||
org.opencontainers.image.description=Model Context Protocol server for Prowler
|
||||
org.opencontainers.image.vendor=ProwlerPro, Inc.
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.created=${{ github.event_name == 'release' && github.event.release.published_at || github.event.head_commit.timestamp }}
|
||||
${{ github.event_name == 'release' && format('org.opencontainers.image.version={0}', env.RELEASE_TAG) || '' }}
|
||||
cache-from: type=gha,scope=${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
|
||||
org.opencontainers.image.created=${{ github.event.head_commit.timestamp }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify container push started
|
||||
if: github.event_name == 'release'
|
||||
@@ -104,6 +94,27 @@ jobs:
|
||||
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-started.json"
|
||||
|
||||
- name: Build and push MCP container (release)
|
||||
if: github.event_name == 'release'
|
||||
id: container-push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: ${{ env.WORKING_DIRECTORY }}
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.RELEASE_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.STABLE_TAG }}
|
||||
labels: |
|
||||
org.opencontainers.image.title=Prowler MCP Server
|
||||
org.opencontainers.image.description=Model Context Protocol server for Prowler
|
||||
org.opencontainers.image.vendor=ProwlerPro, Inc.
|
||||
org.opencontainers.image.version=${{ env.RELEASE_TAG }}
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.created=${{ github.event.release.published_at }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify container push completed
|
||||
if: github.event_name == 'release' && always()
|
||||
uses: ./.github/actions/slack-notification
|
||||
@@ -119,52 +130,6 @@ jobs:
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-completed.json"
|
||||
step-outcome: ${{ steps.container-push.outcome }}
|
||||
|
||||
# Create and push multi-architecture manifest
|
||||
create-manifest:
|
||||
needs: [setup, container-build-push]
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Create and push manifests for push event
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64
|
||||
|
||||
- name: Create and push manifests for release event
|
||||
if: github.event_name == 'release'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.RELEASE_TAG }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.STABLE_TAG }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64
|
||||
|
||||
- name: Install regctl
|
||||
if: always()
|
||||
uses: regclient/actions/regctl-installer@main
|
||||
|
||||
- name: Cleanup intermediate architecture tags
|
||||
if: always()
|
||||
run: |
|
||||
echo "Cleaning up intermediate tags..."
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64" || true
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64" || true
|
||||
echo "Cleanup completed"
|
||||
|
||||
trigger-deployment:
|
||||
if: github.event_name == 'push'
|
||||
needs: [setup, container-build-push]
|
||||
|
||||
@@ -46,16 +46,7 @@ env:
|
||||
jobs:
|
||||
container-build-push:
|
||||
if: github.repository == 'prowler-cloud/prowler'
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
arch: amd64
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -63,8 +54,6 @@ jobs:
|
||||
outputs:
|
||||
prowler_version: ${{ steps.get-prowler-version.outputs.prowler_version }}
|
||||
prowler_version_major: ${{ steps.get-prowler-version.outputs.prowler_version_major }}
|
||||
latest_tag: ${{ steps.get-prowler-version.outputs.latest_tag }}
|
||||
stable_tag: ${{ steps.get-prowler-version.outputs.stable_tag }}
|
||||
env:
|
||||
POETRY_VIRTUALENVS_CREATE: 'false'
|
||||
|
||||
@@ -99,22 +88,16 @@ jobs:
|
||||
3)
|
||||
echo "LATEST_TAG=v3-latest" >> "${GITHUB_ENV}"
|
||||
echo "STABLE_TAG=v3-stable" >> "${GITHUB_ENV}"
|
||||
echo "latest_tag=v3-latest" >> "${GITHUB_OUTPUT}"
|
||||
echo "stable_tag=v3-stable" >> "${GITHUB_OUTPUT}"
|
||||
echo "✓ Prowler v3 detected - tags: v3-latest, v3-stable"
|
||||
;;
|
||||
4)
|
||||
echo "LATEST_TAG=v4-latest" >> "${GITHUB_ENV}"
|
||||
echo "STABLE_TAG=v4-stable" >> "${GITHUB_ENV}"
|
||||
echo "latest_tag=v4-latest" >> "${GITHUB_OUTPUT}"
|
||||
echo "stable_tag=v4-stable" >> "${GITHUB_OUTPUT}"
|
||||
echo "✓ Prowler v4 detected - tags: v4-latest, v4-stable"
|
||||
;;
|
||||
5)
|
||||
echo "LATEST_TAG=latest" >> "${GITHUB_ENV}"
|
||||
echo "STABLE_TAG=stable" >> "${GITHUB_ENV}"
|
||||
echo "latest_tag=latest" >> "${GITHUB_OUTPUT}"
|
||||
echo "stable_tag=stable" >> "${GITHUB_OUTPUT}"
|
||||
echo "✓ Prowler v5 detected - tags: latest, stable"
|
||||
;;
|
||||
*)
|
||||
@@ -141,18 +124,19 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Build and push SDK container for ${{ matrix.arch }}
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
- name: Build and push SDK container (latest)
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
file: ${{ env.DOCKERFILE_PATH }}
|
||||
push: true
|
||||
platforms: ${{ matrix.platform }}
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }}-${{ matrix.arch }}
|
||||
cache-from: type=gha,scope=${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
|
||||
${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.LATEST_TAG }}
|
||||
${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.LATEST_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify container push started
|
||||
if: github.event_name == 'release'
|
||||
@@ -167,7 +151,25 @@ jobs:
|
||||
with:
|
||||
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-started.json"
|
||||
|
||||
|
||||
- name: Build and push SDK container (release)
|
||||
if: github.event_name == 'release'
|
||||
id: container-push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
file: ${{ env.DOCKERFILE_PATH }}
|
||||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.PROWLER_VERSION }}
|
||||
${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.STABLE_TAG }}
|
||||
${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.PROWLER_VERSION }}
|
||||
${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.STABLE_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.PROWLER_VERSION }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.STABLE_TAG }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify container push completed
|
||||
if: github.event_name == 'release' && always()
|
||||
uses: ./.github/actions/slack-notification
|
||||
@@ -183,57 +185,6 @@ jobs:
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-completed.json"
|
||||
step-outcome: ${{ steps.container-push.outcome }}
|
||||
|
||||
# Create and push multi-architecture manifest
|
||||
create-manifest:
|
||||
needs: [container-build-push]
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Create and push manifests for push event
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }} \
|
||||
-t ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }} \
|
||||
-t ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }}-arm64
|
||||
|
||||
- name: Create and push manifests for release event
|
||||
if: github.event_name == 'release'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ needs.container-build-push.outputs.prowler_version }} \
|
||||
-t ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ needs.container-build-push.outputs.stable_tag }} \
|
||||
-t ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ needs.container-build-push.outputs.prowler_version }} \
|
||||
-t ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ needs.container-build-push.outputs.stable_tag }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.prowler_version }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.stable_tag }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }}-arm64
|
||||
|
||||
- name: Install regctl
|
||||
if: always()
|
||||
uses: regclient/actions/regctl-installer@main
|
||||
|
||||
- name: Cleanup intermediate architecture tags
|
||||
if: always()
|
||||
run: |
|
||||
echo "Cleaning up intermediate tags..."
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }}-amd64" || true
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.container-build-push.outputs.latest_tag }}-arm64" || true
|
||||
echo "Cleanup completed"
|
||||
|
||||
dispatch-v3-deployment:
|
||||
if: needs.container-build-push.outputs.prowler_version_major == '3'
|
||||
needs: container-build-push
|
||||
|
||||
@@ -46,16 +46,7 @@ jobs:
|
||||
|
||||
container-build-push:
|
||||
needs: setup
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
arch: amd64
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -74,7 +65,7 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Build and push UI container for ${{ matrix.arch }}
|
||||
- name: Build and push UI container (latest)
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
@@ -83,22 +74,8 @@ jobs:
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=${{ needs.setup.outputs.short-sha }}
|
||||
NEXT_PUBLIC_API_BASE_URL=${{ env.NEXT_PUBLIC_API_BASE_URL }}
|
||||
push: true
|
||||
platforms: ${{ matrix.platform }}
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-${{ matrix.arch }}
|
||||
cache-from: type=gha,scope=${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
|
||||
|
||||
- name: Build and push UI container for ${{ matrix.arch }}
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: ${{ env.WORKING_DIRECTORY }}
|
||||
build-args: |
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=${{ github.event_name == 'release' && format('v{0}', env.RELEASE_TAG) || needs.setup.outputs.short_sha }}
|
||||
NEXT_PUBLIC_API_BASE_URL=${{ env.NEXT_PUBLIC_API_BASE_URL }}
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -117,6 +94,22 @@ jobs:
|
||||
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-started.json"
|
||||
|
||||
- name: Build and push UI container (release)
|
||||
if: github.event_name == 'release'
|
||||
id: container-push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: ${{ env.WORKING_DIRECTORY }}
|
||||
build-args: |
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v${{ env.RELEASE_TAG }}
|
||||
NEXT_PUBLIC_API_BASE_URL=${{ env.NEXT_PUBLIC_API_BASE_URL }}
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.RELEASE_TAG }}
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.STABLE_TAG }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify container push completed
|
||||
if: github.event_name == 'release' && always()
|
||||
uses: ./.github/actions/slack-notification
|
||||
@@ -132,52 +125,6 @@ jobs:
|
||||
payload-file-path: "./.github/scripts/slack-messages/container-release-completed.json"
|
||||
step-outcome: ${{ steps.container-push.outcome }}
|
||||
|
||||
# Create and push multi-architecture manifest
|
||||
create-manifest:
|
||||
needs: [setup, container-build-push]
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Create and push manifests for push event
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.LATEST_TAG }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64
|
||||
|
||||
- name: Create and push manifests for release event
|
||||
if: github.event_name == 'release'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.RELEASE_TAG }} \
|
||||
-t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ env.STABLE_TAG }} \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64 \
|
||||
${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64
|
||||
|
||||
- name: Install regctl
|
||||
if: always()
|
||||
uses: regclient/actions/regctl-installer@main
|
||||
|
||||
- name: Cleanup intermediate architecture tags
|
||||
if: always()
|
||||
run: |
|
||||
echo "Cleaning up intermediate tags..."
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-amd64" || true
|
||||
regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.short-sha }}-arm64" || true
|
||||
echo "Cleanup completed"
|
||||
|
||||
trigger-deployment:
|
||||
if: github.event_name == 'push'
|
||||
needs: [setup, container-build-push]
|
||||
|
||||
@@ -45,86 +45,21 @@ pytest_*.xml
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# VSCode files and settings
|
||||
# VSCode files
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
.vscode-test/
|
||||
|
||||
# VSCode extension settings and workspaces
|
||||
.history/
|
||||
.ionide/
|
||||
|
||||
# MCP Server Settings (various locations)
|
||||
**/cline_mcp_settings.json
|
||||
**/mcp_settings.json
|
||||
**/mcp-config.json
|
||||
**/mcpServers.json
|
||||
.mcp/
|
||||
|
||||
# AI Coding Assistants - Cursor
|
||||
# Cursor files
|
||||
.cursorignore
|
||||
.cursor/
|
||||
.cursorrules
|
||||
|
||||
# AI Coding Assistants - RooCode
|
||||
# RooCode files
|
||||
.roo/
|
||||
.rooignore
|
||||
.roomodes
|
||||
|
||||
# AI Coding Assistants - Cline (formerly Claude Dev)
|
||||
# Cline files
|
||||
.cline/
|
||||
.clineignore
|
||||
.clinerules
|
||||
|
||||
# AI Coding Assistants - Continue
|
||||
.continue/
|
||||
continue.json
|
||||
.continuerc
|
||||
.continuerc.json
|
||||
|
||||
# AI Coding Assistants - GitHub Copilot
|
||||
.copilot/
|
||||
.github/copilot/
|
||||
|
||||
# AI Coding Assistants - Amazon Q Developer (formerly CodeWhisperer)
|
||||
.aws/
|
||||
.codewhisperer/
|
||||
.amazonq/
|
||||
.aws-toolkit/
|
||||
|
||||
# AI Coding Assistants - Tabnine
|
||||
.tabnine/
|
||||
tabnine_config.json
|
||||
|
||||
# AI Coding Assistants - Kiro
|
||||
.kiro/
|
||||
.kiroignore
|
||||
kiro.config.json
|
||||
|
||||
# AI Coding Assistants - Aider
|
||||
.aider/
|
||||
.aider.chat.history.md
|
||||
.aider.input.history
|
||||
.aider.tags.cache.v3/
|
||||
|
||||
# AI Coding Assistants - Windsurf
|
||||
.windsurf/
|
||||
.windsurfignore
|
||||
|
||||
# AI Coding Assistants - Replit Agent
|
||||
.replit
|
||||
.replitignore
|
||||
|
||||
# AI Coding Assistants - Supermaven
|
||||
.supermaven/
|
||||
|
||||
# AI Coding Assistants - Sourcegraph Cody
|
||||
.cody/
|
||||
|
||||
# AI Coding Assistants - General
|
||||
.ai/
|
||||
.aiconfig
|
||||
ai-config.json
|
||||
|
||||
# Terraform
|
||||
.terraform*
|
||||
|
||||
@@ -4,10 +4,6 @@ LABEL maintainer="https://github.com/prowler-cloud/prowler"
|
||||
LABEL org.opencontainers.image.source="https://github.com/prowler-cloud/prowler"
|
||||
|
||||
ARG POWERSHELL_VERSION=7.5.0
|
||||
ENV POWERSHELL_VERSION=${POWERSHELL_VERSION}
|
||||
|
||||
ARG TRIVY_VERSION=0.66.0
|
||||
ENV TRIVY_VERSION=${TRIVY_VERSION}
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
@@ -29,24 +25,6 @@ RUN ARCH=$(uname -m) && \
|
||||
ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh && \
|
||||
rm /tmp/powershell.tar.gz
|
||||
|
||||
# Install Trivy for IaC scanning
|
||||
RUN ARCH=$(uname -m) && \
|
||||
if [ "$ARCH" = "x86_64" ]; then \
|
||||
TRIVY_ARCH="Linux-64bit" ; \
|
||||
elif [ "$ARCH" = "aarch64" ]; then \
|
||||
TRIVY_ARCH="Linux-ARM64" ; \
|
||||
else \
|
||||
echo "Unsupported architecture for Trivy: $ARCH" && exit 1 ; \
|
||||
fi && \
|
||||
wget --progress=dot:giga "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_${TRIVY_ARCH}.tar.gz" -O /tmp/trivy.tar.gz && \
|
||||
tar zxf /tmp/trivy.tar.gz -C /tmp && \
|
||||
mv /tmp/trivy /usr/local/bin/trivy && \
|
||||
chmod +x /usr/local/bin/trivy && \
|
||||
rm /tmp/trivy.tar.gz && \
|
||||
# Create trivy cache directory with proper permissions
|
||||
mkdir -p /tmp/.cache/trivy && \
|
||||
chmod 777 /tmp/.cache/trivy
|
||||
|
||||
# Add prowler user
|
||||
RUN addgroup --gid 1000 prowler && \
|
||||
adduser --uid 1000 --gid 1000 --disabled-password --gecos "" prowler
|
||||
|
||||
@@ -15,24 +15,17 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
- Support C5 compliance framework for the GCP provider [(#9097)](https://github.com/prowler-cloud/prowler/pull/9097)
|
||||
- Support for Amazon Bedrock and OpenAI compatible providers in Lighthouse AI [(#8957)](https://github.com/prowler-cloud/prowler/pull/8957)
|
||||
- Tenant-wide ThreatScore overview aggregation and snapshot persistence with backfill support [(#9148)](https://github.com/prowler-cloud/prowler/pull/9148)
|
||||
- Added `metadata`, `details`, and `partition` attributes to `/resources` endpoint & `details`, and `partition` to `/findings` endpoint [(#9098)](https://github.com/prowler-cloud/prowler/pull/9098)
|
||||
- Support for MongoDB Atlas provider [(#9167)](https://github.com/prowler-cloud/prowler/pull/9167)
|
||||
|
||||
### Changed
|
||||
- Optimized database write queries for scan related tasks [(#9190)](https://github.com/prowler-cloud/prowler/pull/9190)
|
||||
|
||||
### Fixed
|
||||
- Scans no longer fail when findings have UIDs exceeding 300 characters; such findings are now skipped with detailed logging [(#9246)](https://github.com/prowler-cloud/prowler/pull/9246)
|
||||
|
||||
### Security
|
||||
- Django updated to the latest 5.1 security release, 5.1.14, due to problems with potential [SQL injection](https://github.com/prowler-cloud/prowler/security/dependabot/113) and [denial-of-service vulnerability](https://github.com/prowler-cloud/prowler/security/dependabot/114) [(#9176)](https://github.com/prowler-cloud/prowler/pull/9176)
|
||||
|
||||
---
|
||||
|
||||
## [1.14.2] (Prowler 5.13.2)
|
||||
## [1.14.2] (Prowler UNRELEASED)
|
||||
|
||||
### Fixed
|
||||
- Update unique constraint for `Provider` model to exclude soft-deleted entries, resolving duplicate errors when re-deleting providers [(#9054)](https://github.com/prowler-cloud/prowler/pull/9054)
|
||||
- Update unique constraint for `Provider` model to exclude soft-deleted entries, resolving duplicate errors when re-deleting providers.[(#9054)](https://github.com/prowler-cloud/prowler/pull/9054)
|
||||
- Remove compliance generation for providers without compliance frameworks [(#9208)](https://github.com/prowler-cloud/prowler/pull/9208)
|
||||
|
||||
## [1.14.1] (Prowler 5.13.1)
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
from django.contrib.postgres.operations import RemoveIndexConcurrently
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
("api", "0057_threatscoresnapshot"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
RemoveIndexConcurrently(
|
||||
model_name="compliancerequirementoverview",
|
||||
name="cro_tenant_scan_idx",
|
||||
),
|
||||
RemoveIndexConcurrently(
|
||||
model_name="compliancerequirementoverview",
|
||||
name="cro_scan_comp_idx",
|
||||
),
|
||||
RemoveIndexConcurrently(
|
||||
model_name="compliancerequirementoverview",
|
||||
name="cro_scan_comp_req_idx",
|
||||
),
|
||||
]
|
||||
@@ -1,75 +0,0 @@
|
||||
# Generated by Django 5.1.13 on 2025-10-30 15:23
|
||||
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import api.rls
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("api", "0058_drop_redundant_compliance_requirement_indexes"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ComplianceOverviewSummary",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("inserted_at", models.DateTimeField(auto_now_add=True)),
|
||||
("compliance_id", models.TextField()),
|
||||
("requirements_passed", models.IntegerField(default=0)),
|
||||
("requirements_failed", models.IntegerField(default=0)),
|
||||
("requirements_manual", models.IntegerField(default=0)),
|
||||
("total_requirements", models.IntegerField(default=0)),
|
||||
(
|
||||
"scan",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="compliance_summaries",
|
||||
related_query_name="compliance_summary",
|
||||
to="api.scan",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tenant",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="api.tenant"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"db_table": "compliance_overview_summaries",
|
||||
"abstract": False,
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["tenant_id", "scan_id"], name="cos_tenant_scan_idx"
|
||||
)
|
||||
],
|
||||
"constraints": [
|
||||
models.UniqueConstraint(
|
||||
fields=("tenant_id", "scan_id", "compliance_id"),
|
||||
name="unique_compliance_summary_per_scan",
|
||||
)
|
||||
],
|
||||
},
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="complianceoverviewsummary",
|
||||
constraint=api.rls.RowLevelSecurityConstraint(
|
||||
"tenant_id",
|
||||
name="rls_on_complianceoverviewsummary",
|
||||
statements=["SELECT", "INSERT", "UPDATE", "DELETE"],
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1371,70 +1371,35 @@ class ComplianceRequirementOverview(RowLevelSecurityProtectedModel):
|
||||
),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=["tenant_id", "scan_id"], name="cro_tenant_scan_idx"),
|
||||
models.Index(
|
||||
fields=["tenant_id", "scan_id", "compliance_id"],
|
||||
name="cro_scan_comp_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["tenant_id", "scan_id", "compliance_id", "region"],
|
||||
name="cro_scan_comp_reg_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["tenant_id", "scan_id", "compliance_id", "requirement_id"],
|
||||
name="cro_scan_comp_req_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=[
|
||||
"tenant_id",
|
||||
"scan_id",
|
||||
"compliance_id",
|
||||
"requirement_id",
|
||||
"region",
|
||||
],
|
||||
name="cro_scan_comp_req_reg_idx",
|
||||
),
|
||||
]
|
||||
|
||||
class JSONAPIMeta:
|
||||
resource_name = "compliance-requirements-overviews"
|
||||
|
||||
|
||||
class ComplianceOverviewSummary(RowLevelSecurityProtectedModel):
|
||||
"""
|
||||
Pre-aggregated compliance overview aggregated across ALL regions.
|
||||
One row per (scan_id, compliance_id) combination.
|
||||
|
||||
This table optimizes the common case where users view overall compliance
|
||||
without filtering by region. For region-specific views, the detailed
|
||||
ComplianceRequirementOverview table is used instead.
|
||||
"""
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
inserted_at = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
|
||||
scan = models.ForeignKey(
|
||||
Scan,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="compliance_summaries",
|
||||
related_query_name="compliance_summary",
|
||||
)
|
||||
|
||||
compliance_id = models.TextField(blank=False)
|
||||
|
||||
# Pre-aggregated scores (computed across ALL regions)
|
||||
requirements_passed = models.IntegerField(default=0)
|
||||
requirements_failed = models.IntegerField(default=0)
|
||||
requirements_manual = models.IntegerField(default=0)
|
||||
total_requirements = models.IntegerField(default=0)
|
||||
|
||||
class Meta(RowLevelSecurityProtectedModel.Meta):
|
||||
db_table = "compliance_overview_summaries"
|
||||
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=("tenant_id", "scan_id", "compliance_id"),
|
||||
name="unique_compliance_summary_per_scan",
|
||||
),
|
||||
RowLevelSecurityConstraint(
|
||||
field="tenant_id",
|
||||
name="rls_on_%(class)s",
|
||||
statements=["SELECT", "INSERT", "DELETE"],
|
||||
),
|
||||
]
|
||||
|
||||
indexes = [
|
||||
models.Index(
|
||||
fields=["tenant_id", "scan_id"],
|
||||
name="cos_tenant_scan_idx",
|
||||
),
|
||||
]
|
||||
|
||||
class JSONAPIMeta:
|
||||
resource_name = "compliance-overview-summaries"
|
||||
|
||||
|
||||
class ScanSummary(RowLevelSecurityProtectedModel):
|
||||
objects = ActiveProviderManager()
|
||||
all_objects = models.Manager()
|
||||
|
||||
@@ -869,16 +869,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -896,16 +896,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -1408,16 +1407,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -1435,16 +1434,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -1855,16 +1853,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -1882,16 +1880,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -2300,16 +2297,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -2327,16 +2324,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -2733,16 +2729,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -2760,16 +2756,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -4570,16 +4565,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -4597,16 +4592,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -4759,16 +4753,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -4786,16 +4780,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5014,16 +5007,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5041,16 +5034,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5749,16 +5741,16 @@ paths:
|
||||
name: filter[provider]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5776,16 +5768,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5797,7 +5788,6 @@ paths:
|
||||
* `m365` - M365
|
||||
* `github` - GitHub
|
||||
* `mongodbatlas` - MongoDB Atlas
|
||||
* `iac` - IaC
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
explode: false
|
||||
style: form
|
||||
@@ -5805,16 +5795,14 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5823,8 +5811,6 @@ paths:
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
* `github` - GitHub
|
||||
* `mongodbatlas` - MongoDB Atlas
|
||||
* `iac` - IaC
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
@@ -5832,16 +5818,14 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5852,8 +5836,6 @@ paths:
|
||||
* `kubernetes` - Kubernetes
|
||||
* `m365` - M365
|
||||
* `github` - GitHub
|
||||
* `mongodbatlas` - MongoDB Atlas
|
||||
* `iac` - IaC
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
explode: false
|
||||
style: form
|
||||
@@ -6389,9 +6371,6 @@ paths:
|
||||
- findings
|
||||
- failed_findings_count
|
||||
- url
|
||||
- metadata
|
||||
- details
|
||||
- partition
|
||||
- type
|
||||
description: endpoint return only specific fields in the response on a per-type
|
||||
basis by including a fields[TYPE] query parameter.
|
||||
@@ -6455,16 +6434,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -6482,16 +6461,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -6725,9 +6703,6 @@ paths:
|
||||
- findings
|
||||
- failed_findings_count
|
||||
- url
|
||||
- metadata
|
||||
- details
|
||||
- partition
|
||||
- type
|
||||
description: endpoint return only specific fields in the response on a per-type
|
||||
basis by including a fields[TYPE] query parameter.
|
||||
@@ -6787,9 +6762,6 @@ paths:
|
||||
- findings
|
||||
- failed_findings_count
|
||||
- url
|
||||
- metadata
|
||||
- details
|
||||
- partition
|
||||
- type
|
||||
description: endpoint return only specific fields in the response on a per-type
|
||||
basis by including a fields[TYPE] query parameter.
|
||||
@@ -6838,16 +6810,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -6865,16 +6837,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -7116,16 +7087,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -7143,16 +7114,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -7400,16 +7370,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -7427,16 +7397,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -8247,16 +8216,16 @@ paths:
|
||||
name: filter[provider_type]
|
||||
schema:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -8274,16 +8243,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
enum:
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- github
|
||||
- iac
|
||||
- mongodbatlas
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -14317,7 +14285,7 @@ components:
|
||||
type: string
|
||||
description: GitHub OAuth App token for authentication.
|
||||
required:
|
||||
- oauth_app_token
|
||||
- oauth_app_token
|
||||
- type: object
|
||||
title: GitHub App Credentials
|
||||
properties:
|
||||
@@ -14330,6 +14298,18 @@ components:
|
||||
required:
|
||||
- github_app_id
|
||||
- github_app_key
|
||||
- type: object
|
||||
title: MongoDB Atlas API Key
|
||||
properties:
|
||||
atlas_public_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API public key.
|
||||
atlas_private_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API private key.
|
||||
required:
|
||||
- atlas_public_key
|
||||
- atlas_private_key
|
||||
- type: object
|
||||
title: IaC Repository Credentials
|
||||
properties:
|
||||
@@ -14374,18 +14354,6 @@ components:
|
||||
- fingerprint
|
||||
- tenancy
|
||||
- region
|
||||
- type: object
|
||||
title: MongoDB Atlas API Key
|
||||
properties:
|
||||
atlas_public_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API public key.
|
||||
atlas_private_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API private key.
|
||||
required:
|
||||
- atlas_public_key
|
||||
- atlas_private_key
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret
|
||||
@@ -15381,7 +15349,6 @@ components:
|
||||
- m365
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- oraclecloud
|
||||
type: string
|
||||
description: |-
|
||||
@@ -15392,9 +15359,8 @@ components:
|
||||
* `m365` - M365
|
||||
* `github` - GitHub
|
||||
* `mongodbatlas` - MongoDB Atlas
|
||||
* `iac` - IaC
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
uid:
|
||||
type: string
|
||||
title: Unique identifier for the provider, set by the provider
|
||||
@@ -15507,10 +15473,9 @@ components:
|
||||
- m365
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- oraclecloud
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
description: |-
|
||||
Type of provider to create.
|
||||
|
||||
@@ -15521,13 +15486,12 @@ components:
|
||||
* `m365` - M365
|
||||
* `github` - GitHub
|
||||
* `mongodbatlas` - MongoDB Atlas
|
||||
* `iac` - IaC
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
uid:
|
||||
type: string
|
||||
title: Unique identifier for the provider, set by the provider
|
||||
description: Unique identifier for the provider, set by the provider,
|
||||
e.g. AWS account ID, Azure subscription ID, GCP project ID, etc.
|
||||
e.g. AWS account ID, Azure subscription ID, GCP project ID, MongoDB Atlas organization ID, etc.
|
||||
maxLength: 250
|
||||
minLength: 3
|
||||
required:
|
||||
@@ -15567,10 +15531,9 @@ components:
|
||||
- m365
|
||||
- github
|
||||
- mongodbatlas
|
||||
- iac
|
||||
- oraclecloud
|
||||
type: string
|
||||
x-spec-enum-id: eca8c51e6bd28935
|
||||
x-spec-enum-id: 7087703783970b51
|
||||
description: |-
|
||||
Type of provider to create.
|
||||
|
||||
@@ -15581,14 +15544,13 @@ components:
|
||||
* `m365` - M365
|
||||
* `github` - GitHub
|
||||
* `mongodbatlas` - MongoDB Atlas
|
||||
* `iac` - IaC
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
uid:
|
||||
type: string
|
||||
minLength: 3
|
||||
title: Unique identifier for the provider, set by the provider
|
||||
description: Unique identifier for the provider, set by the provider,
|
||||
e.g. AWS account ID, Azure subscription ID, GCP project ID, etc.
|
||||
e.g. AWS account ID, Azure subscription ID, GCP project ID, MongoDB Atlas organization ID, etc.
|
||||
maxLength: 250
|
||||
required:
|
||||
- uid
|
||||
@@ -16333,18 +16295,6 @@ components:
|
||||
- fingerprint
|
||||
- tenancy
|
||||
- region
|
||||
- type: object
|
||||
title: MongoDB Atlas API Key
|
||||
properties:
|
||||
atlas_public_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API public key.
|
||||
atlas_private_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API private key.
|
||||
required:
|
||||
- atlas_public_key
|
||||
- atlas_private_key
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret_type
|
||||
@@ -16658,18 +16608,6 @@ components:
|
||||
- fingerprint
|
||||
- tenancy
|
||||
- region
|
||||
- type: object
|
||||
title: MongoDB Atlas API Key
|
||||
properties:
|
||||
atlas_public_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API public key.
|
||||
atlas_private_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API private key.
|
||||
required:
|
||||
- atlas_public_key
|
||||
- atlas_private_key
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret_type
|
||||
@@ -16997,18 +16935,6 @@ components:
|
||||
- fingerprint
|
||||
- tenancy
|
||||
- region
|
||||
- type: object
|
||||
title: MongoDB Atlas API Key
|
||||
properties:
|
||||
atlas_public_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API public key.
|
||||
atlas_private_key:
|
||||
type: string
|
||||
description: MongoDB Atlas API private key.
|
||||
required:
|
||||
- atlas_public_key
|
||||
- atlas_private_key
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret
|
||||
@@ -17097,18 +17023,6 @@ components:
|
||||
failed_findings_count:
|
||||
type: integer
|
||||
readOnly: true
|
||||
metadata:
|
||||
type: string
|
||||
readOnly: true
|
||||
nullable: true
|
||||
details:
|
||||
type: string
|
||||
readOnly: true
|
||||
nullable: true
|
||||
partition:
|
||||
type: string
|
||||
readOnly: true
|
||||
nullable: true
|
||||
type:
|
||||
type: string
|
||||
readOnly: true
|
||||
|
||||
@@ -35,8 +35,6 @@ from rest_framework.response import Response
|
||||
from api.compliance import get_compliance_frameworks
|
||||
from api.db_router import MainRouter
|
||||
from api.models import (
|
||||
ComplianceOverviewSummary,
|
||||
ComplianceRequirementOverview,
|
||||
Finding,
|
||||
Integration,
|
||||
Invitation,
|
||||
@@ -57,7 +55,6 @@ from api.models import (
|
||||
Scan,
|
||||
ScanSummary,
|
||||
StateChoices,
|
||||
StatusChoices,
|
||||
Task,
|
||||
TenantAPIKey,
|
||||
ThreatScoreSnapshot,
|
||||
@@ -3545,9 +3542,6 @@ class TestResourceViewSet:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()["data"]) == len(resources_fixture)
|
||||
assert "metadata" in response.json()["data"][0]["attributes"]
|
||||
assert "details" in response.json()["data"][0]["attributes"]
|
||||
assert "partition" in response.json()["data"][0]["attributes"]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"include_values, expected_resources",
|
||||
@@ -5820,44 +5814,16 @@ class TestProviderGroupMembershipViewSet:
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestComplianceOverviewViewSet:
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_backfill_task(self):
|
||||
with patch("api.v1.views.backfill_compliance_summaries_task.delay") as mock:
|
||||
yield mock
|
||||
|
||||
def test_compliance_overview_list_none(
|
||||
self,
|
||||
authenticated_client,
|
||||
tenants_fixture,
|
||||
providers_fixture,
|
||||
mock_backfill_task,
|
||||
):
|
||||
tenant = tenants_fixture[0]
|
||||
provider = providers_fixture[0]
|
||||
scan = Scan.objects.create(
|
||||
name="empty-compliance-scan",
|
||||
provider=provider,
|
||||
trigger=Scan.TriggerChoices.MANUAL,
|
||||
state=StateChoices.COMPLETED,
|
||||
tenant=tenant,
|
||||
)
|
||||
|
||||
def test_compliance_overview_list_none(self, authenticated_client):
|
||||
response = authenticated_client.get(
|
||||
reverse("complianceoverview-list"),
|
||||
{"filter[scan_id]": str(scan.id)},
|
||||
{"filter[scan_id]": "8d20ac7d-4cbc-435e-85f4-359be37af821"},
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()["data"]) == 0
|
||||
mock_backfill_task.assert_called_once()
|
||||
_, kwargs = mock_backfill_task.call_args
|
||||
assert kwargs["scan_id"] == str(scan.id)
|
||||
assert str(kwargs["tenant_id"]) == str(tenant.id)
|
||||
|
||||
def test_compliance_overview_list(
|
||||
self,
|
||||
authenticated_client,
|
||||
compliance_requirements_overviews_fixture,
|
||||
mock_backfill_task,
|
||||
self, authenticated_client, compliance_requirements_overviews_fixture
|
||||
):
|
||||
# List compliance overviews with existing data
|
||||
requirement_overview1 = compliance_requirements_overviews_fixture[0]
|
||||
@@ -5887,90 +5853,6 @@ class TestComplianceOverviewViewSet:
|
||||
assert "requirements_failed" in attributes
|
||||
assert "requirements_manual" in attributes
|
||||
assert "total_requirements" in attributes
|
||||
mock_backfill_task.assert_called_once()
|
||||
_, kwargs = mock_backfill_task.call_args
|
||||
assert kwargs["scan_id"] == scan_id
|
||||
|
||||
def test_compliance_overview_list_uses_preaggregated_summaries(
|
||||
self,
|
||||
authenticated_client,
|
||||
tenants_fixture,
|
||||
providers_fixture,
|
||||
mock_backfill_task,
|
||||
):
|
||||
tenant = tenants_fixture[0]
|
||||
provider = providers_fixture[0]
|
||||
scan = Scan.objects.create(
|
||||
name="preaggregated-scan",
|
||||
provider=provider,
|
||||
trigger=Scan.TriggerChoices.MANUAL,
|
||||
state=StateChoices.COMPLETED,
|
||||
tenant=tenant,
|
||||
)
|
||||
|
||||
ComplianceRequirementOverview.objects.create(
|
||||
tenant=tenant,
|
||||
scan=scan,
|
||||
compliance_id="cis_1.4_aws",
|
||||
framework="CIS-1.4-AWS",
|
||||
version="1.4",
|
||||
description="CIS AWS Foundations Benchmark v1.4.0",
|
||||
region="eu-west-1",
|
||||
requirement_id="framework-metadata",
|
||||
requirement_status=StatusChoices.PASS,
|
||||
passed_checks=1,
|
||||
failed_checks=0,
|
||||
total_checks=1,
|
||||
)
|
||||
|
||||
ComplianceOverviewSummary.objects.create(
|
||||
tenant=tenant,
|
||||
scan=scan,
|
||||
compliance_id="cis_1.4_aws",
|
||||
requirements_passed=5,
|
||||
requirements_failed=1,
|
||||
requirements_manual=2,
|
||||
total_requirements=8,
|
||||
)
|
||||
|
||||
response = authenticated_client.get(
|
||||
reverse("complianceoverview-list"),
|
||||
{"filter[scan_id]": str(scan.id)},
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()["data"]
|
||||
assert len(data) == 1
|
||||
overview = data[0]
|
||||
assert overview["id"] == "cis_1.4_aws"
|
||||
assert overview["attributes"]["requirements_passed"] == 5
|
||||
assert overview["attributes"]["requirements_failed"] == 1
|
||||
assert overview["attributes"]["requirements_manual"] == 2
|
||||
assert overview["attributes"]["total_requirements"] == 8
|
||||
assert "framework" in overview["attributes"]
|
||||
assert "version" in overview["attributes"]
|
||||
mock_backfill_task.assert_not_called()
|
||||
|
||||
def test_compliance_overview_region_filter_skips_backfill(
|
||||
self,
|
||||
authenticated_client,
|
||||
compliance_requirements_overviews_fixture,
|
||||
mock_backfill_task,
|
||||
):
|
||||
requirement_overview = compliance_requirements_overviews_fixture[0]
|
||||
scan_id = str(requirement_overview.scan.id)
|
||||
|
||||
response = authenticated_client.get(
|
||||
reverse("complianceoverview-list"),
|
||||
{
|
||||
"filter[scan_id]": scan_id,
|
||||
"filter[region]": requirement_overview.region,
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.json()["data"]) >= 1
|
||||
mock_backfill_task.assert_not_called()
|
||||
|
||||
def test_compliance_overview_metadata(
|
||||
self, authenticated_client, compliance_requirements_overviews_fixture
|
||||
@@ -6124,11 +6006,6 @@ class TestComplianceOverviewViewSet:
|
||||
requirement_overview1 = compliance_requirements_overviews_fixture[0]
|
||||
scan_id = str(requirement_overview1.scan.id)
|
||||
|
||||
# Remove existing compliance data so the view falls back to task checks
|
||||
scan = requirement_overview1.scan
|
||||
ComplianceOverviewSummary.objects.filter(scan=scan).delete()
|
||||
ComplianceRequirementOverview.objects.filter(scan=scan).delete()
|
||||
|
||||
# Mock a running task
|
||||
with patch.object(
|
||||
ComplianceOverviewViewSet, "get_task_response_if_running"
|
||||
@@ -6156,11 +6033,6 @@ class TestComplianceOverviewViewSet:
|
||||
requirement_overview1 = compliance_requirements_overviews_fixture[0]
|
||||
scan_id = str(requirement_overview1.scan.id)
|
||||
|
||||
# Remove existing compliance data so the view falls back to task checks
|
||||
scan = requirement_overview1.scan
|
||||
ComplianceOverviewSummary.objects.filter(scan=scan).delete()
|
||||
ComplianceRequirementOverview.objects.filter(scan=scan).delete()
|
||||
|
||||
# Mock a failed task
|
||||
with patch.object(
|
||||
ComplianceOverviewViewSet, "get_task_response_if_running"
|
||||
@@ -6184,8 +6056,6 @@ class TestComplianceOverviewViewSet:
|
||||
("framework", "framework", 1),
|
||||
("version", "version", 1),
|
||||
("region", "region", 1),
|
||||
("region__in", "region", 1),
|
||||
("region.in", "region", 1),
|
||||
],
|
||||
)
|
||||
def test_compliance_overview_filters(
|
||||
|
||||
@@ -1167,17 +1167,11 @@ class ResourceSerializer(RLSSerializer):
|
||||
"findings",
|
||||
"failed_findings_count",
|
||||
"url",
|
||||
"metadata",
|
||||
"details",
|
||||
"partition",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"id": {"read_only": True},
|
||||
"inserted_at": {"read_only": True},
|
||||
"updated_at": {"read_only": True},
|
||||
"metadata": {"read_only": True},
|
||||
"details": {"read_only": True},
|
||||
"partition": {"read_only": True},
|
||||
}
|
||||
|
||||
included_serializers = {
|
||||
@@ -1234,15 +1228,11 @@ class ResourceIncludeSerializer(RLSSerializer):
|
||||
"service",
|
||||
"type_",
|
||||
"tags",
|
||||
"details",
|
||||
"partition",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"id": {"read_only": True},
|
||||
"inserted_at": {"read_only": True},
|
||||
"updated_at": {"read_only": True},
|
||||
"details": {"read_only": True},
|
||||
"partition": {"read_only": True},
|
||||
}
|
||||
|
||||
@extend_schema_field(
|
||||
|
||||
@@ -75,7 +75,6 @@ from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
|
||||
from tasks.beat import schedule_provider_scan
|
||||
from tasks.jobs.export import get_s3_client
|
||||
from tasks.tasks import (
|
||||
backfill_compliance_summaries_task,
|
||||
backfill_scan_resource_summaries_task,
|
||||
check_integration_connection_task,
|
||||
check_lighthouse_connection_task,
|
||||
@@ -127,7 +126,6 @@ from api.filters import (
|
||||
UserFilter,
|
||||
)
|
||||
from api.models import (
|
||||
ComplianceOverviewSummary,
|
||||
ComplianceRequirementOverview,
|
||||
Finding,
|
||||
Integration,
|
||||
@@ -3400,168 +3398,6 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
raise MethodNotAllowed(method="GET")
|
||||
|
||||
def _compliance_summaries_queryset(self, scan_id):
|
||||
"""Return pre-aggregated summaries constrained by RBAC visibility."""
|
||||
role = get_role(self.request.user)
|
||||
unlimited_visibility = getattr(
|
||||
role, Permissions.UNLIMITED_VISIBILITY.value, False
|
||||
)
|
||||
summaries = ComplianceOverviewSummary.objects.filter(
|
||||
tenant_id=self.request.tenant_id,
|
||||
scan_id=scan_id,
|
||||
)
|
||||
|
||||
if not unlimited_visibility:
|
||||
providers = Provider.all_objects.filter(
|
||||
provider_groups__in=role.provider_groups.all()
|
||||
).distinct()
|
||||
summaries = summaries.filter(scan__provider__in=providers)
|
||||
|
||||
return summaries
|
||||
|
||||
def _get_compliance_template(self, *, provider=None, scan_id=None):
|
||||
"""Return the compliance template for the given provider or scan."""
|
||||
if provider is None and scan_id is not None:
|
||||
scan = Scan.all_objects.select_related("provider").get(pk=scan_id)
|
||||
provider = scan.provider
|
||||
|
||||
if not provider:
|
||||
return {}
|
||||
|
||||
return PROWLER_COMPLIANCE_OVERVIEW_TEMPLATE.get(provider.provider, {})
|
||||
|
||||
def _aggregate_compliance_overview(self, queryset, template_metadata=None):
|
||||
"""
|
||||
Aggregate requirement rows into compliance overview dictionaries.
|
||||
|
||||
Args:
|
||||
queryset: ComplianceRequirementOverview queryset already filtered.
|
||||
template_metadata: Optional dict mapping compliance_id -> metadata.
|
||||
"""
|
||||
template_metadata = template_metadata or {}
|
||||
requirement_status_subquery = queryset.values(
|
||||
"compliance_id", "requirement_id"
|
||||
).annotate(
|
||||
fail_count=Count("id", filter=Q(requirement_status="FAIL")),
|
||||
pass_count=Count("id", filter=Q(requirement_status="PASS")),
|
||||
total_count=Count("id"),
|
||||
)
|
||||
|
||||
compliance_data = {}
|
||||
fallback_metadata = {
|
||||
item["compliance_id"]: {
|
||||
"framework": item["framework"],
|
||||
"version": item["version"],
|
||||
}
|
||||
for item in queryset.values(
|
||||
"compliance_id", "framework", "version"
|
||||
).distinct()
|
||||
}
|
||||
|
||||
for item in requirement_status_subquery:
|
||||
compliance_id = item["compliance_id"]
|
||||
|
||||
if item["fail_count"] > 0:
|
||||
req_status = "FAIL"
|
||||
elif item["pass_count"] == item["total_count"]:
|
||||
req_status = "PASS"
|
||||
else:
|
||||
req_status = "MANUAL"
|
||||
|
||||
compliance_status = compliance_data.setdefault(
|
||||
compliance_id,
|
||||
{
|
||||
"total_requirements": 0,
|
||||
"requirements_passed": 0,
|
||||
"requirements_failed": 0,
|
||||
"requirements_manual": 0,
|
||||
},
|
||||
)
|
||||
|
||||
compliance_status["total_requirements"] += 1
|
||||
if req_status == "PASS":
|
||||
compliance_status["requirements_passed"] += 1
|
||||
elif req_status == "FAIL":
|
||||
compliance_status["requirements_failed"] += 1
|
||||
else:
|
||||
compliance_status["requirements_manual"] += 1
|
||||
|
||||
response_data = []
|
||||
for compliance_id, data in compliance_data.items():
|
||||
template = template_metadata.get(compliance_id, {})
|
||||
fallback = fallback_metadata.get(compliance_id, {})
|
||||
|
||||
response_data.append(
|
||||
{
|
||||
"id": compliance_id,
|
||||
"compliance_id": compliance_id,
|
||||
"framework": template.get("framework")
|
||||
or fallback.get("framework", ""),
|
||||
"version": template.get("version") or fallback.get("version", ""),
|
||||
"requirements_passed": data["requirements_passed"],
|
||||
"requirements_failed": data["requirements_failed"],
|
||||
"requirements_manual": data["requirements_manual"],
|
||||
"total_requirements": data["total_requirements"],
|
||||
}
|
||||
)
|
||||
|
||||
serializer = self.get_serializer(response_data, many=True)
|
||||
return serializer.data
|
||||
|
||||
def _task_response_if_running(self, scan_id):
|
||||
"""Check for an in-progress task only when no compliance data exists."""
|
||||
try:
|
||||
return self.get_task_response_if_running(
|
||||
task_name="scan-compliance-overviews",
|
||||
task_kwargs={"tenant_id": self.request.tenant_id, "scan_id": scan_id},
|
||||
raise_on_not_found=False,
|
||||
)
|
||||
except TaskFailedException:
|
||||
return Response(
|
||||
{"detail": "Task failed to generate compliance overview data."},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
|
||||
def _list_with_region_filter(self, scan_id, region_filter):
|
||||
"""
|
||||
Fall back to detailed ComplianceRequirementOverview query when region filter is applied.
|
||||
This uses the original aggregation logic across filtered regions.
|
||||
"""
|
||||
regions = region_filter.split(",") if "," in region_filter else [region_filter]
|
||||
queryset = self.filter_queryset(self.get_queryset()).filter(
|
||||
scan_id=scan_id,
|
||||
region__in=regions,
|
||||
)
|
||||
|
||||
data = self._aggregate_compliance_overview(queryset)
|
||||
if data:
|
||||
return Response(data)
|
||||
|
||||
task_response = self._task_response_if_running(scan_id)
|
||||
if task_response:
|
||||
return task_response
|
||||
|
||||
return Response(data)
|
||||
|
||||
def _list_without_region_aggregation(self, scan_id):
|
||||
"""
|
||||
Fall back aggregation when compliance summaries don't exist yet.
|
||||
Aggregates ComplianceRequirementOverview data across ALL regions.
|
||||
"""
|
||||
queryset = self.filter_queryset(self.get_queryset()).filter(scan_id=scan_id)
|
||||
compliance_template = self._get_compliance_template(scan_id=scan_id)
|
||||
data = self._aggregate_compliance_overview(
|
||||
queryset, template_metadata=compliance_template
|
||||
)
|
||||
if data:
|
||||
return Response(data)
|
||||
|
||||
task_response = self._task_response_if_running(scan_id)
|
||||
if task_response:
|
||||
return task_response
|
||||
|
||||
return Response(data)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
scan_id = request.query_params.get("filter[scan_id]")
|
||||
if not scan_id:
|
||||
@@ -3575,41 +3411,77 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
region_filter = request.query_params.get(
|
||||
"filter[region]"
|
||||
) or request.query_params.get("filter[region__in]")
|
||||
|
||||
if region_filter:
|
||||
# Fall back to detailed query with region filtering
|
||||
return self._list_with_region_filter(scan_id, region_filter)
|
||||
|
||||
summaries = list(self._compliance_summaries_queryset(scan_id))
|
||||
if not summaries:
|
||||
# Trigger async backfill for next time
|
||||
backfill_compliance_summaries_task.delay(
|
||||
tenant_id=self.request.tenant_id, scan_id=scan_id
|
||||
try:
|
||||
if task := self.get_task_response_if_running(
|
||||
task_name="scan-compliance-overviews",
|
||||
task_kwargs={"tenant_id": self.request.tenant_id, "scan_id": scan_id},
|
||||
raise_on_not_found=False,
|
||||
):
|
||||
return task
|
||||
except TaskFailedException:
|
||||
return Response(
|
||||
{"detail": "Task failed to generate compliance overview data."},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
# Use fallback aggregation for this request
|
||||
return self._list_without_region_aggregation(scan_id)
|
||||
queryset = self.filter_queryset(self.filter_queryset(self.get_queryset()))
|
||||
|
||||
# Get compliance template for provider to enrich with framework/version
|
||||
compliance_template = self._get_compliance_template(scan_id=scan_id)
|
||||
requirement_status_subquery = queryset.values(
|
||||
"compliance_id", "requirement_id"
|
||||
).annotate(
|
||||
fail_count=Count("id", filter=Q(requirement_status="FAIL")),
|
||||
pass_count=Count("id", filter=Q(requirement_status="PASS")),
|
||||
total_count=Count("id"),
|
||||
)
|
||||
|
||||
compliance_data = {}
|
||||
framework_info = {}
|
||||
|
||||
for item in queryset.values("compliance_id", "framework", "version").distinct():
|
||||
framework_info[item["compliance_id"]] = {
|
||||
"framework": item["framework"],
|
||||
"version": item["version"],
|
||||
}
|
||||
|
||||
for item in requirement_status_subquery:
|
||||
compliance_id = item["compliance_id"]
|
||||
|
||||
if item["fail_count"] > 0:
|
||||
req_status = "FAIL"
|
||||
elif item["pass_count"] == item["total_count"]:
|
||||
req_status = "PASS"
|
||||
else:
|
||||
req_status = "MANUAL"
|
||||
|
||||
if compliance_id not in compliance_data:
|
||||
compliance_data[compliance_id] = {
|
||||
"total_requirements": 0,
|
||||
"requirements_passed": 0,
|
||||
"requirements_failed": 0,
|
||||
"requirements_manual": 0,
|
||||
}
|
||||
|
||||
compliance_data[compliance_id]["total_requirements"] += 1
|
||||
if req_status == "PASS":
|
||||
compliance_data[compliance_id]["requirements_passed"] += 1
|
||||
elif req_status == "FAIL":
|
||||
compliance_data[compliance_id]["requirements_failed"] += 1
|
||||
else:
|
||||
compliance_data[compliance_id]["requirements_manual"] += 1
|
||||
|
||||
# Convert to response format with framework/version enrichment
|
||||
response_data = []
|
||||
for summary in summaries:
|
||||
compliance_metadata = compliance_template.get(summary.compliance_id, {})
|
||||
for compliance_id, data in compliance_data.items():
|
||||
framework = framework_info.get(compliance_id, {})
|
||||
|
||||
response_data.append(
|
||||
{
|
||||
"id": summary.compliance_id,
|
||||
"compliance_id": summary.compliance_id,
|
||||
"framework": compliance_metadata.get("framework", ""),
|
||||
"version": compliance_metadata.get("version", ""),
|
||||
"requirements_passed": summary.requirements_passed,
|
||||
"requirements_failed": summary.requirements_failed,
|
||||
"requirements_manual": summary.requirements_manual,
|
||||
"total_requirements": summary.total_requirements,
|
||||
"id": compliance_id,
|
||||
"compliance_id": compliance_id,
|
||||
"framework": framework.get("framework", ""),
|
||||
"version": framework.get("version", ""),
|
||||
"requirements_passed": data["requirements_passed"],
|
||||
"requirements_failed": data["requirements_failed"],
|
||||
"requirements_manual": data["requirements_manual"],
|
||||
"total_requirements": data["total_requirements"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -3630,6 +3502,18 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
|
||||
}
|
||||
]
|
||||
)
|
||||
try:
|
||||
if task := self.get_task_response_if_running(
|
||||
task_name="scan-compliance-overviews",
|
||||
task_kwargs={"tenant_id": self.request.tenant_id, "scan_id": scan_id},
|
||||
raise_on_not_found=False,
|
||||
):
|
||||
return task
|
||||
except TaskFailedException:
|
||||
return Response(
|
||||
{"detail": "Task failed to generate compliance overview data."},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
regions = list(
|
||||
self.get_queryset()
|
||||
.filter(scan_id=scan_id)
|
||||
@@ -3639,15 +3523,6 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
|
||||
)
|
||||
result = {"regions": regions}
|
||||
|
||||
if regions:
|
||||
serializer = self.get_serializer(data=result)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
task_response = self._task_response_if_running(scan_id)
|
||||
if task_response:
|
||||
return task_response
|
||||
|
||||
serializer = self.get_serializer(data=result)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
@@ -3680,6 +3555,18 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
|
||||
}
|
||||
]
|
||||
)
|
||||
try:
|
||||
if task := self.get_task_response_if_running(
|
||||
task_name="scan-compliance-overviews",
|
||||
task_kwargs={"tenant_id": self.request.tenant_id, "scan_id": scan_id},
|
||||
raise_on_not_found=False,
|
||||
):
|
||||
return task
|
||||
except TaskFailedException:
|
||||
return Response(
|
||||
{"detail": "Task failed to generate compliance overview data."},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
filtered_queryset = self.filter_queryset(self.get_queryset())
|
||||
|
||||
all_requirements = filtered_queryset.values(
|
||||
@@ -3739,13 +3626,6 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
|
||||
requirements_summary, many=True
|
||||
)
|
||||
|
||||
if requirements_summary:
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
task_response = self._task_response_if_running(scan_id)
|
||||
if task_response:
|
||||
return task_response
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@action(detail=False, methods=["get"], url_name="attributes")
|
||||
@@ -3764,6 +3644,15 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
|
||||
)
|
||||
|
||||
provider_type = None
|
||||
try:
|
||||
sample_requirement = (
|
||||
self.get_queryset().filter(compliance_id=compliance_id).first()
|
||||
)
|
||||
|
||||
if sample_requirement:
|
||||
provider_type = sample_requirement.scan.provider.provider
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If we couldn't determine from database, try each provider type
|
||||
if not provider_type:
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from api.db_router import READ_REPLICA_ALIAS
|
||||
from api.db_utils import rls_transaction
|
||||
from api.models import (
|
||||
ComplianceOverviewSummary,
|
||||
ComplianceRequirementOverview,
|
||||
Resource,
|
||||
ResourceFindingMapping,
|
||||
ResourceScanSummary,
|
||||
@@ -14,7 +9,7 @@ from api.models import (
|
||||
|
||||
|
||||
def backfill_resource_scan_summaries(tenant_id: str, scan_id: str):
|
||||
with rls_transaction(tenant_id, using=READ_REPLICA_ALIAS):
|
||||
with rls_transaction(tenant_id):
|
||||
if ResourceScanSummary.objects.filter(
|
||||
tenant_id=tenant_id, scan_id=scan_id
|
||||
).exists():
|
||||
@@ -64,114 +59,3 @@ def backfill_resource_scan_summaries(tenant_id: str, scan_id: str):
|
||||
)
|
||||
|
||||
return {"status": "backfilled", "inserted": len(summaries)}
|
||||
|
||||
|
||||
def backfill_compliance_summaries(tenant_id: str, scan_id: str):
|
||||
"""
|
||||
Backfill ComplianceOverviewSummary records for a completed scan.
|
||||
|
||||
This function checks if summary records already exist for the scan.
|
||||
If not, it aggregates compliance requirement data and creates the summaries.
|
||||
|
||||
Args:
|
||||
tenant_id: Target tenant UUID
|
||||
scan_id: Scan UUID to backfill
|
||||
|
||||
Returns:
|
||||
dict: Status indicating whether backfill was performed
|
||||
"""
|
||||
with rls_transaction(tenant_id):
|
||||
if ComplianceOverviewSummary.objects.filter(
|
||||
tenant_id=tenant_id, scan_id=scan_id
|
||||
).exists():
|
||||
return {"status": "already backfilled"}
|
||||
|
||||
with rls_transaction(tenant_id):
|
||||
if not Scan.objects.filter(
|
||||
tenant_id=tenant_id,
|
||||
id=scan_id,
|
||||
state__in=(StateChoices.COMPLETED, StateChoices.FAILED),
|
||||
).exists():
|
||||
return {"status": "scan is not completed"}
|
||||
|
||||
# Fetch all compliance requirement overview rows for this scan
|
||||
requirement_rows = ComplianceRequirementOverview.objects.filter(
|
||||
tenant_id=tenant_id, scan_id=scan_id
|
||||
).values(
|
||||
"compliance_id",
|
||||
"requirement_id",
|
||||
"requirement_status",
|
||||
)
|
||||
|
||||
if not requirement_rows:
|
||||
return {"status": "no compliance data to backfill"}
|
||||
|
||||
# Group by (compliance_id, requirement_id) across regions
|
||||
requirement_statuses = defaultdict(
|
||||
lambda: {"fail_count": 0, "pass_count": 0, "total_count": 0}
|
||||
)
|
||||
|
||||
for row in requirement_rows:
|
||||
compliance_id = row["compliance_id"]
|
||||
requirement_id = row["requirement_id"]
|
||||
requirement_status = row["requirement_status"]
|
||||
|
||||
# Aggregate requirement status across regions
|
||||
key = (compliance_id, requirement_id)
|
||||
requirement_statuses[key]["total_count"] += 1
|
||||
|
||||
if requirement_status == "FAIL":
|
||||
requirement_statuses[key]["fail_count"] += 1
|
||||
elif requirement_status == "PASS":
|
||||
requirement_statuses[key]["pass_count"] += 1
|
||||
|
||||
# Determine per-requirement status and aggregate to compliance level
|
||||
compliance_summaries = defaultdict(
|
||||
lambda: {
|
||||
"total_requirements": 0,
|
||||
"requirements_passed": 0,
|
||||
"requirements_failed": 0,
|
||||
"requirements_manual": 0,
|
||||
}
|
||||
)
|
||||
|
||||
for (compliance_id, requirement_id), counts in requirement_statuses.items():
|
||||
# Apply business rule: any FAIL → requirement fails
|
||||
if counts["fail_count"] > 0:
|
||||
req_status = "FAIL"
|
||||
elif counts["pass_count"] == counts["total_count"]:
|
||||
req_status = "PASS"
|
||||
else:
|
||||
req_status = "MANUAL"
|
||||
|
||||
# Aggregate to compliance level
|
||||
compliance_summaries[compliance_id]["total_requirements"] += 1
|
||||
if req_status == "PASS":
|
||||
compliance_summaries[compliance_id]["requirements_passed"] += 1
|
||||
elif req_status == "FAIL":
|
||||
compliance_summaries[compliance_id]["requirements_failed"] += 1
|
||||
else:
|
||||
compliance_summaries[compliance_id]["requirements_manual"] += 1
|
||||
|
||||
# Create summary objects
|
||||
summary_objects = []
|
||||
for compliance_id, data in compliance_summaries.items():
|
||||
summary_objects.append(
|
||||
ComplianceOverviewSummary(
|
||||
tenant_id=tenant_id,
|
||||
scan_id=scan_id,
|
||||
compliance_id=compliance_id,
|
||||
requirements_passed=data["requirements_passed"],
|
||||
requirements_failed=data["requirements_failed"],
|
||||
requirements_manual=data["requirements_manual"],
|
||||
total_requirements=data["total_requirements"],
|
||||
)
|
||||
)
|
||||
|
||||
# Bulk insert summaries
|
||||
if summary_objects:
|
||||
ComplianceOverviewSummary.objects.bulk_create(
|
||||
summary_objects, batch_size=500, ignore_conflicts=True
|
||||
)
|
||||
|
||||
return {"status": "backfilled", "inserted": len(summary_objects)}
|
||||
@@ -8,10 +8,7 @@ from celery.utils.log import get_task_logger
|
||||
from config.celery import RLSTask
|
||||
from config.django.base import DJANGO_FINDINGS_BATCH_SIZE, DJANGO_TMP_OUTPUT_DIRECTORY
|
||||
from django_celery_beat.models import PeriodicTask
|
||||
from tasks.jobs.backfill import (
|
||||
backfill_compliance_summaries,
|
||||
backfill_resource_scan_summaries,
|
||||
)
|
||||
from tasks.jobs.backfill import backfill_resource_scan_summaries
|
||||
from tasks.jobs.connection import (
|
||||
check_integration_connection,
|
||||
check_lighthouse_connection,
|
||||
@@ -497,21 +494,6 @@ def backfill_scan_resource_summaries_task(tenant_id: str, scan_id: str):
|
||||
return backfill_resource_scan_summaries(tenant_id=tenant_id, scan_id=scan_id)
|
||||
|
||||
|
||||
@shared_task(name="backfill-compliance-summaries", queue="backfill")
|
||||
def backfill_compliance_summaries_task(tenant_id: str, scan_id: str):
|
||||
"""
|
||||
Tries to backfill compliance overview summaries for a completed scan.
|
||||
|
||||
This task aggregates compliance requirement data across regions
|
||||
to create pre-computed summary records for fast compliance overview queries.
|
||||
|
||||
Args:
|
||||
tenant_id (str): The tenant identifier.
|
||||
scan_id (str): The scan identifier.
|
||||
"""
|
||||
return backfill_compliance_summaries(tenant_id=tenant_id, scan_id=scan_id)
|
||||
|
||||
|
||||
@shared_task(base=RLSTask, name="scan-compliance-overviews", queue="compliance")
|
||||
def create_compliance_requirements_task(tenant_id: str, scan_id: str):
|
||||
"""
|
||||
|
||||
@@ -1,53 +1,43 @@
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from tasks.jobs.backfill import (
|
||||
backfill_compliance_summaries,
|
||||
backfill_resource_scan_summaries,
|
||||
)
|
||||
from tasks.jobs.backfill import backfill_resource_scan_summaries
|
||||
|
||||
from api.models import (
|
||||
ComplianceOverviewSummary,
|
||||
ResourceScanSummary,
|
||||
Scan,
|
||||
StateChoices,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def resource_scan_summary_data(scans_fixture):
|
||||
scan = scans_fixture[0]
|
||||
return ResourceScanSummary.objects.create(
|
||||
tenant_id=scan.tenant_id,
|
||||
scan_id=scan.id,
|
||||
resource_id=str(uuid4()),
|
||||
service="aws",
|
||||
region="us-east-1",
|
||||
resource_type="instance",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def get_not_completed_scans(providers_fixture):
|
||||
provider_id = providers_fixture[0].id
|
||||
tenant_id = providers_fixture[0].tenant_id
|
||||
scan_1 = Scan.objects.create(
|
||||
tenant_id=tenant_id,
|
||||
trigger=Scan.TriggerChoices.MANUAL,
|
||||
state=StateChoices.EXECUTING,
|
||||
provider_id=provider_id,
|
||||
)
|
||||
scan_2 = Scan.objects.create(
|
||||
tenant_id=tenant_id,
|
||||
trigger=Scan.TriggerChoices.MANUAL,
|
||||
state=StateChoices.AVAILABLE,
|
||||
provider_id=provider_id,
|
||||
)
|
||||
return scan_1, scan_2
|
||||
from api.models import ResourceScanSummary, Scan, StateChoices
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestBackfillResourceScanSummaries:
|
||||
@pytest.fixture(scope="function")
|
||||
def resource_scan_summary_data(self, scans_fixture):
|
||||
scan = scans_fixture[0]
|
||||
return ResourceScanSummary.objects.create(
|
||||
tenant_id=scan.tenant_id,
|
||||
scan_id=scan.id,
|
||||
resource_id=str(uuid4()),
|
||||
service="aws",
|
||||
region="us-east-1",
|
||||
resource_type="instance",
|
||||
)
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def get_not_completed_scans(self, providers_fixture):
|
||||
provider_id = providers_fixture[0].id
|
||||
tenant_id = providers_fixture[0].tenant_id
|
||||
scan_1 = Scan.objects.create(
|
||||
tenant_id=tenant_id,
|
||||
trigger=Scan.TriggerChoices.MANUAL,
|
||||
state=StateChoices.EXECUTING,
|
||||
provider_id=provider_id,
|
||||
)
|
||||
scan_2 = Scan.objects.create(
|
||||
tenant_id=tenant_id,
|
||||
trigger=Scan.TriggerChoices.MANUAL,
|
||||
state=StateChoices.AVAILABLE,
|
||||
provider_id=provider_id,
|
||||
)
|
||||
return scan_1, scan_2
|
||||
|
||||
def test_already_backfilled(self, resource_scan_summary_data):
|
||||
tenant_id = resource_scan_summary_data.tenant_id
|
||||
scan_id = resource_scan_summary_data.scan_id
|
||||
@@ -87,88 +77,3 @@ class TestBackfillResourceScanSummaries:
|
||||
assert summary.service == resource.service
|
||||
assert summary.region == resource.region
|
||||
assert summary.resource_type == resource.type
|
||||
|
||||
def test_no_resources_to_backfill(self, scans_fixture):
|
||||
scan = scans_fixture[1] # Failed scan with no findings/resources
|
||||
tenant_id = str(scan.tenant_id)
|
||||
scan_id = str(scan.id)
|
||||
|
||||
result = backfill_resource_scan_summaries(tenant_id, scan_id)
|
||||
|
||||
assert result == {"status": "no resources to backfill"}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestBackfillComplianceSummaries:
|
||||
def test_already_backfilled(self, scans_fixture):
|
||||
scan = scans_fixture[0]
|
||||
tenant_id = str(scan.tenant_id)
|
||||
ComplianceOverviewSummary.objects.create(
|
||||
tenant_id=scan.tenant_id,
|
||||
scan=scan,
|
||||
compliance_id="aws_account_security_onboarding_aws",
|
||||
requirements_passed=1,
|
||||
requirements_failed=0,
|
||||
requirements_manual=0,
|
||||
total_requirements=1,
|
||||
)
|
||||
|
||||
result = backfill_compliance_summaries(tenant_id, str(scan.id))
|
||||
|
||||
assert result == {"status": "already backfilled"}
|
||||
|
||||
def test_not_completed_scan(self, get_not_completed_scans):
|
||||
for scan in get_not_completed_scans:
|
||||
result = backfill_compliance_summaries(str(scan.tenant_id), str(scan.id))
|
||||
assert result == {"status": "scan is not completed"}
|
||||
|
||||
def test_no_compliance_data(self, scans_fixture):
|
||||
scan = scans_fixture[1] # Failed scan with no compliance rows
|
||||
|
||||
result = backfill_compliance_summaries(str(scan.tenant_id), str(scan.id))
|
||||
|
||||
assert result == {"status": "no compliance data to backfill"}
|
||||
|
||||
def test_backfill_creates_compliance_summaries(
|
||||
self, tenants_fixture, scans_fixture, compliance_requirements_overviews_fixture
|
||||
):
|
||||
tenant = tenants_fixture[0]
|
||||
scan = scans_fixture[0]
|
||||
|
||||
result = backfill_compliance_summaries(str(tenant.id), str(scan.id))
|
||||
|
||||
expected = {
|
||||
"aws_account_security_onboarding_aws": {
|
||||
"requirements_passed": 1,
|
||||
"requirements_failed": 1,
|
||||
"requirements_manual": 1,
|
||||
"total_requirements": 3,
|
||||
},
|
||||
"cis_1.4_aws": {
|
||||
"requirements_passed": 0,
|
||||
"requirements_failed": 1,
|
||||
"requirements_manual": 0,
|
||||
"total_requirements": 1,
|
||||
},
|
||||
"mitre_attack_aws": {
|
||||
"requirements_passed": 0,
|
||||
"requirements_failed": 1,
|
||||
"requirements_manual": 0,
|
||||
"total_requirements": 1,
|
||||
},
|
||||
}
|
||||
|
||||
assert result == {"status": "backfilled", "inserted": len(expected)}
|
||||
|
||||
summaries = ComplianceOverviewSummary.objects.filter(
|
||||
tenant_id=str(tenant.id), scan_id=str(scan.id)
|
||||
)
|
||||
assert summaries.count() == len(expected)
|
||||
|
||||
for summary in summaries:
|
||||
assert summary.compliance_id in expected
|
||||
expected_counts = expected[summary.compliance_id]
|
||||
assert summary.requirements_passed == expected_counts["requirements_passed"]
|
||||
assert summary.requirements_failed == expected_counts["requirements_failed"]
|
||||
assert summary.requirements_manual == expected_counts["requirements_manual"]
|
||||
assert summary.total_requirements == expected_counts["total_requirements"]
|
||||
@@ -24,7 +24,7 @@ Standard results will be shown and additionally the framework information as the
|
||||
**If Prowler can't find a resource related with a check from a compliance requirement, this requirement won't appear on the output**
|
||||
</Note>
|
||||
|
||||
## List Available Compliance Frameworks
|
||||
## List Available Compliance Frameworks
|
||||
|
||||
To see which compliance frameworks are covered by Prowler, use the `--list-compliance` option:
|
||||
|
||||
@@ -34,7 +34,7 @@ prowler <provider> --list-compliance
|
||||
|
||||
Or you can visit [Prowler Hub](https://hub.prowler.com/compliance).
|
||||
|
||||
## List Requirements of Compliance Frameworks
|
||||
## List Requirements of Compliance Frameworks
|
||||
To list requirements for a compliance framework, use the `--list-compliance-requirements` option:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -94,7 +94,7 @@ The following list includes all the Azure checks with configurable variables tha
|
||||
|
||||
### Configurable Checks
|
||||
|
||||
## Kubernetes
|
||||
## Kubernetes
|
||||
|
||||
### Configurable Checks
|
||||
The following list includes all the Kubernetes checks with configurable variables that can be changed in the configuration yaml file:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: 'Integrations'
|
||||
---
|
||||
|
||||
## Integration with Slack
|
||||
## Integration with Slack
|
||||
|
||||
Prowler can be integrated with [Slack](https://slack.com/) to send a summary of the execution having configured a Slack APP in your channel with the following command:
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ prowler <provider> -V/-v/--version
|
||||
|
||||
Prowler provides various execution settings.
|
||||
|
||||
### Verbose Execution
|
||||
### Verbose Execution
|
||||
|
||||
To enable verbose mode in Prowler, similar to Version 2, use:
|
||||
|
||||
@@ -54,7 +54,7 @@ To run Prowler without color formatting:
|
||||
prowler <provider> --no-color
|
||||
```
|
||||
|
||||
### Checks in Prowler
|
||||
### Checks in Prowler
|
||||
|
||||
Prowler provides various security checks per cloud provider. Use the following options to list, execute, or exclude specific checks:
|
||||
|
||||
@@ -96,7 +96,7 @@ prowler <provider> -e/--excluded-checks ec2 rds
|
||||
prowler <provider> -C/--checks-file <checks_list>.json
|
||||
```
|
||||
|
||||
## Custom Checks in Prowler
|
||||
## Custom Checks in Prowler
|
||||
|
||||
Prowler supports custom security checks, allowing users to define their own logic.
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Tags have special matching behavior:
|
||||
Remember that mutelist can be used with regular expressions.
|
||||
|
||||
</Note>
|
||||
## Mutelist Specification
|
||||
## Mutelist Specification
|
||||
|
||||
<Note>
|
||||
- For Azure provider, the Account ID is the Subscription Name and the Region is the Location.
|
||||
@@ -72,7 +72,7 @@ The Mutelist file uses the [YAML](https://en.wikipedia.org/wiki/YAML) format wit
|
||||
### Resources and tags are lists that can have either Regex or Keywords.
|
||||
### Multiple tags in the list are "ANDed" together (ALL must match).
|
||||
### Use regex alternation (|) within a single tag for "OR" logic (e.g., "env=dev|env=stg").
|
||||
### For each check you can use Exceptions to unmute specific Accounts, Regions, Resources and/or Tags.
|
||||
### For each check you can use Exceptions to unmute specific Accounts, Regions, Resources and/or Tags.
|
||||
### All conditions (Account, Check, Region, Resource, Tags) are ANDed together.
|
||||
########################### MUTELIST EXAMPLE ###########################
|
||||
Mutelist:
|
||||
|
||||
@@ -10,7 +10,7 @@ This can help for really large accounts, but please be aware of AWS API rate lim
|
||||
2. **API Rate Limits**: Most of the rate limits in AWS are applied at the API level. Each API call to an AWS service counts towards the rate limit for that service.
|
||||
3. **Throttling Responses**: When you exceed the rate limit for a service, AWS responds with a throttling error. In AWS SDKs, these are typically represented as `ThrottlingException` or `RateLimitExceeded` errors.
|
||||
|
||||
For information on Prowler's retrier configuration please refer to this [page](https://docs.prowler.com/user-guide/providers/aws/boto3-configuration/).
|
||||
For information on Prowler's retrier configuration please refer to this [page](https://docs.prowler.cloud/en/latest/tutorials/aws/boto3-configuration/).
|
||||
|
||||
<Note>
|
||||
You might need to increase the `--aws-retries-max-attempts` parameter from the default value of 3. The retrier follows an exponential backoff strategy.
|
||||
|
||||
@@ -24,6 +24,6 @@ By default, it extracts resources from all the regions, you could use `-f`/`--fi
|
||||
|
||||

|
||||
|
||||
## Objections
|
||||
## Objections
|
||||
|
||||
The inventorying process is carried out with `resourcegroupstaggingapi` calls, which means that only resources they have or have had tags will appear (except for the IAM and S3 resources which are done with Boto3 API calls).
|
||||
@@ -22,7 +22,7 @@ prowler <provider> --output-formats json-asff
|
||||
|
||||
All compliance-related reports are automatically generated when Prowler is executed. These outputs are stored in the `/output/compliance` directory.
|
||||
|
||||
## Custom Output Flags
|
||||
## Custom Output Flags
|
||||
|
||||
By default, Prowler creates a file inside the `output` directory named: `prowler-output-ACCOUNT_NUM-OUTPUT_DATE.format`.
|
||||
|
||||
@@ -53,13 +53,13 @@ Both flags can be used simultaneously to provide a custom directory and filename
|
||||
|
||||
By default, the timestamp format of the output files is ISO 8601. This can be changed with the flag `--unix-timestamp` generating the timestamp fields in pure unix timestamp format.
|
||||
|
||||
## Supported Output Formats
|
||||
## Supported Output Formats
|
||||
|
||||
Prowler natively supports the following reporting output formats:
|
||||
|
||||
- CSV
|
||||
- JSON-OCSF
|
||||
- JSON-ASFF (AWS only)
|
||||
- JSON-ASFF
|
||||
- HTML
|
||||
|
||||
Hereunder is the structure for each of the supported report formats by Prowler:
|
||||
@@ -285,10 +285,10 @@ The JSON-OCSF output format implements the [Detection Finding](https://schema.oc
|
||||
Each finding is a `json` object within a list.
|
||||
|
||||
</Note>
|
||||
### JSON-ASFF (AWS Only)
|
||||
### JSON-ASFF
|
||||
|
||||
<Note>
|
||||
Only available when using `--security-hub` or `--output-formats json-asff` with the AWS provider.
|
||||
Only available when using `--security-hub` or `--output-formats json-asff`
|
||||
|
||||
</Note>
|
||||
The following code is an example output of the [JSON-ASFF](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-findings-format-syntax.html) format:
|
||||
|
||||
@@ -14,7 +14,7 @@ prowler <provider> --scan-unused-services
|
||||
|
||||
## Services Ignored
|
||||
|
||||
### AWS
|
||||
### AWS
|
||||
|
||||
#### ACM (AWS Certificate Manager)
|
||||
|
||||
@@ -22,21 +22,21 @@ Certificates stored in ACM without active usage in AWS resources are excluded. B
|
||||
|
||||
- `acm_certificates_expiration_check`
|
||||
|
||||
#### Athena
|
||||
#### Athena
|
||||
|
||||
Upon AWS account creation, Athena provisions a default primary workgroup for the user. Prowler verifies if this workgroup is enabled and used by checking for queries within the last 45 days. If Athena is unused, findings related to its checks will not appear.
|
||||
|
||||
- `athena_workgroup_encryption`
|
||||
- `athena_workgroup_enforce_configuration`
|
||||
|
||||
#### AWS CloudTrail
|
||||
#### AWS CloudTrail
|
||||
|
||||
AWS CloudTrail should have at least one trail with a data event to record all S3 object-level API operations. Before flagging this issue, Prowler verifies if S3 buckets exist in the account.
|
||||
|
||||
- `cloudtrail_s3_dataevents_read_enabled`
|
||||
- `cloudtrail_s3_dataevents_write_enabled`
|
||||
|
||||
#### AWS Elastic Compute Cloud (EC2)
|
||||
#### AWS Elastic Compute Cloud (EC2)
|
||||
|
||||
If Amazon Elastic Block Store (EBS) default encyption is not enabled, sensitive data at rest will remain unprotected in EC2. However, Prowler will only generate a finding if EBS volumes exist where default encryption could be enforced.
|
||||
|
||||
@@ -56,7 +56,7 @@ Prowler scans only attached security groups to report vulnerabilities in activel
|
||||
|
||||
- `ec2_networkacl_allow_ingress_X_port`
|
||||
|
||||
#### AWS Glue
|
||||
#### AWS Glue
|
||||
|
||||
AWS Glue best practices recommend encrypting metadata and connection passwords in Data Catalogs.
|
||||
|
||||
@@ -71,7 +71,7 @@ Amazon Inspector is a vulnerability discovery service that automates continuous
|
||||
|
||||
- `inspector2_is_enabled`
|
||||
|
||||
#### Amazon Macie
|
||||
#### Amazon Macie
|
||||
|
||||
Amazon Macie leverages machine learning to automatically discover, classify, and protect sensitive data in S3 buckets. Prowler only generates findings if Macie is disabled and there are S3 buckets in the AWS account.
|
||||
|
||||
@@ -83,7 +83,7 @@ A network firewall is essential for monitoring and controlling traffic within a
|
||||
|
||||
- `networkfirewall_in_all_vpc`
|
||||
|
||||
#### Amazon S3
|
||||
#### Amazon S3
|
||||
|
||||
To prevent unintended data exposure:
|
||||
|
||||
@@ -91,7 +91,7 @@ Public Access Block should be enabled at the account level. Prowler only checks
|
||||
|
||||
- `s3_account_level_public_access_blocks`
|
||||
|
||||
#### Virtual Private Cloud (VPC)
|
||||
#### Virtual Private Cloud (VPC)
|
||||
|
||||
VPC settings directly impact network security and availability.
|
||||
|
||||
|
||||
@@ -84,17 +84,6 @@ For detailed instructions on how to create the role, see [Authentication > Assum
|
||||

|
||||

|
||||
|
||||
<Note>
|
||||
Check if your AWS Security Token Service (STS) has the EU (Ireland) endpoint active. If not, we will not be able to connect to your AWS account.
|
||||
|
||||
If that is the case your STS configuration may look like this:
|
||||
|
||||
<img src="/images/sts-configuration.png" alt="AWS Role" width="800" />
|
||||
|
||||
To solve this issue, please activate the EU (Ireland) STS endpoint.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
#### Credentials (Static Access Keys)
|
||||
|
||||
@@ -108,16 +108,58 @@ This method uses a service account with a downloaded key file for authentication
|
||||
|
||||
### Create Service Account and Key
|
||||
|
||||
1. Go to the [Service Accounts page](https://console.cloud.google.com/iam-admin/serviceaccounts) in the GCP Console
|
||||
2. Click "Create Service Account"
|
||||
3. Fill in the service account details and click "Create and Continue"
|
||||
4. Grant the service account the "Reader" role
|
||||
5. Click "Done"
|
||||
6. Find your service account in the list and click on it
|
||||
7. Go to the "Keys" tab
|
||||
8. Click "Add Key" > "Create new key"
|
||||
9. Select "JSON" and click "Create"
|
||||
10. Save the downloaded key file securely
|
||||
1. Go to the [Service Accounts page](https://console.cloud.google.com/iam-admin/serviceaccounts) in the GCP Console.
|
||||
|
||||

|
||||
|
||||
2. Click **Create Service Account**.
|
||||
|
||||

|
||||
|
||||
3. Click **Done** to skip the optional steps for now.
|
||||
4. Go to the service account page, look for the service account you just created and click on it.
|
||||
5. Go to the **Keys** tab.
|
||||
|
||||

|
||||
|
||||
6. Click **Add Key** > **Create new key**.
|
||||
7. Select **JSON** and click **Create**.
|
||||
|
||||

|
||||
|
||||
8. Save the downloaded key file securely.
|
||||
|
||||
### Create and assign the needed Roles
|
||||
|
||||
Prowler need the following roles to be assigned to the service account:
|
||||
- **Viewer (`roles/viewer`)**
|
||||
- **Service Usage Consumer (`roles/serviceusage.serviceUsageConsumer`)**
|
||||
- **ProwlerRole** (custom role to be created in the steps below)
|
||||
|
||||
|
||||
#### Create the ProwlerRole
|
||||
|
||||
1. Open the Google Cloud Console and navigate to **IAM & Admin → Roles**.
|
||||

|
||||
2. Click **Create role**, then fill in:
|
||||
- **Title**: `ProwlerRole`
|
||||
- **ID**: `ProwlerRole`
|
||||
- **Description**: short reminder such as “Needed role to execute Prowler with Service Account authentication.”
|
||||
- **Role launch stage**: `Alpha` (or another stage approved by your org).
|
||||
3. Select **Add permissions**, search for each permission listed above (filter by **Storage Admin** if that is faster), check the boxes, and click **Add**.
|
||||

|
||||
4. Click **Create** to publish the role.
|
||||
|
||||
#### Assign the roles to the service account
|
||||
|
||||
1. Go to **IAM & Admin → Service Accounts** and open the account used by Prowler.
|
||||
2. On the **Permissions** tab choose **Manage access**.
|
||||
3. Add the following roles to the service account:
|
||||
- **Viewer (`roles/viewer`)**
|
||||
- **Service Usage Consumer (`roles/serviceusage.serviceUsageConsumer`)**
|
||||
- **ProwlerRole (custom)**
|
||||
4. Save the changes.
|
||||

|
||||
|
||||
### Using with Prowler CLI
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 167 KiB |
@@ -67,7 +67,7 @@ The Mutelist configuration takes effect on the next scans.
|
||||
</Note>
|
||||
## Mutelist Ready To Use Examples
|
||||
|
||||
Below are examples for different cloud providers supported by Prowler App. Check how the mutelist works [here](/user-guide/cli/tutorials/mutelist#how-the-mutelist-works).
|
||||
Below are examples for different cloud providers supported by Prowler App. Check how the mutelist works [here](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/mutelist/#how-the-mutelist-works).
|
||||
|
||||
### AWS Provider
|
||||
|
||||
|
||||
@@ -621,24 +621,6 @@ azure-mgmt-core = ">=1.3.2"
|
||||
isodate = ">=0.6.1"
|
||||
typing-extensions = ">=4.6.0"
|
||||
|
||||
[[package]]
|
||||
name = "azure-mgmt-postgresqlflexibleservers"
|
||||
version = "1.1.0"
|
||||
description = "Microsoft Azure Postgresqlflexibleservers Management Client Library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "azure_mgmt_postgresqlflexibleservers-1.1.0-py3-none-any.whl", hash = "sha256:87ddb5a5e6d12c45769485d234cfe0322140e3a0a7636d0e61fb00ac544b5d20"},
|
||||
{file = "azure_mgmt_postgresqlflexibleservers-1.1.0.tar.gz", hash = "sha256:9ede9d8ba63e9d2879cb74adc903c649af3bc5460a02787287b0cd18d754af14"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
azure-common = ">=1.1"
|
||||
azure-mgmt-core = ">=1.3.2"
|
||||
isodate = ">=0.6.1"
|
||||
typing-extensions = ">=4.6.0"
|
||||
|
||||
[[package]]
|
||||
name = "azure-mgmt-rdbms"
|
||||
version = "10.1.0"
|
||||
@@ -2366,6 +2348,8 @@ python-versions = "*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"},
|
||||
{file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"},
|
||||
{file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -4440,7 +4424,7 @@ version = "2025.9.18"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
groups = ["dev", "docs"]
|
||||
files = [
|
||||
{file = "regex-2025.9.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:12296202480c201c98a84aecc4d210592b2f55e200a1d193235c4db92b9f6788"},
|
||||
{file = "regex-2025.9.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:220381f1464a581f2ea988f2220cf2a67927adcef107d47d6897ba5a2f6d51a4"},
|
||||
@@ -4884,6 +4868,7 @@ files = [
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"},
|
||||
@@ -4892,6 +4877,7 @@ files = [
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"},
|
||||
@@ -4900,6 +4886,7 @@ files = [
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"},
|
||||
@@ -4908,6 +4895,7 @@ files = [
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"},
|
||||
@@ -4916,6 +4904,7 @@ files = [
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"},
|
||||
{file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"},
|
||||
{file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"},
|
||||
@@ -5688,4 +5677,4 @@ type = ["pytest-mypy"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">3.9.1,<3.13"
|
||||
content-hash = "a367e65bc43c0a16495a3d0f6eab8b356cc49b509e329b61c6641cd87f374ff4"
|
||||
content-hash = "ea79d82b4e255ec4604f440a507da6dac38b57af93356761ac793678aa615cf5"
|
||||
@@ -22,7 +22,6 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- NIST CSF 2.0 compliance framework for the AWS provider [(#9185)](https://github.com/prowler-cloud/prowler/pull/9185)
|
||||
- Add FedRAMP 20x KSI Low for AWS, Azure and GCP [(#9198)](https://github.com/prowler-cloud/prowler/pull/9198)
|
||||
- Add verification for provider ID in MongoDB Atlas provider [(#9211)](https://github.com/prowler-cloud/prowler/pull/9211)
|
||||
- Add `postgresql_flexible_server_entra_id_authentication_enabled` check for Azure provider [(#8764)](https://github.com/prowler-cloud/prowler/pull/8764)
|
||||
|
||||
### Changed
|
||||
- Update AWS Direct Connect service metadata to new format [(#8855)](https://github.com/prowler-cloud/prowler/pull/8855)
|
||||
@@ -33,7 +32,6 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Update AWS EKS service metadata to new format [(#8890)](https://github.com/prowler-cloud/prowler/pull/8890)
|
||||
- Update AWS Elastic Beanstalk service metadata to new format [(#8934)](https://github.com/prowler-cloud/prowler/pull/8934)
|
||||
- Update AWS ElastiCache service metadata to new format [(#8933)](https://github.com/prowler-cloud/prowler/pull/8933)
|
||||
- Update Kubernetes etcd service metadata to new format [(#9096)](https://github.com/prowler-cloud/prowler/pull/9096)
|
||||
- Update MongoDB Atlas projects service metadata to new format [(#9093)](https://github.com/prowler-cloud/prowler/pull/9093)
|
||||
- Update GitHub Organization service metadata to new format [(#9094)](https://github.com/prowler-cloud/prowler/pull/9094)
|
||||
- Update AWS CodeBuild service metadata to new format [(#8851)](https://github.com/prowler-cloud/prowler/pull/8851)
|
||||
@@ -49,13 +47,10 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Update AWS CodeArtifact service metadata to new format [(#8850)](https://github.com/prowler-cloud/prowler/pull/8850)
|
||||
- Rename OCI provider to oraclecloud with oci alias [(#9126)](https://github.com/prowler-cloud/prowler/pull/9126)
|
||||
- Remove unnecessary tests for M365_PowerShell module [(#9204)](https://github.com/prowler-cloud/prowler/pull/9204)
|
||||
- Update AWS ELB v2 service metadata to new format [(#9001)](https://github.com/prowler-cloud/prowler/pull/9001)
|
||||
- Update oraclecloud cloudguard service metadata to new format [(#9223)](https://github.com/prowler-cloud/prowler/pull/9223)
|
||||
- Update oraclecloud blockstorage service metadata to new format [(#9222)](https://github.com/prowler-cloud/prowler/pull/9222)
|
||||
- Update oraclecloud audit service metadata to new format [(#9221)](https://github.com/prowler-cloud/prowler/pull/9221)
|
||||
- Raise ASFF output error for non-AWS providers [(#9225)](https://github.com/prowler-cloud/prowler/pull/9225)
|
||||
- Update AWS ECR service metadata to new format [(#8872)](https://github.com/prowler-cloud/prowler/pull/8872)
|
||||
- Update AWS ECS service metadata to new format [(#8888)](https://github.com/prowler-cloud/prowler/pull/8888)
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -67,8 +62,6 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- False negative in `iam_role_cross_service_confused_deputy_prevention` check [(#9213)](https://github.com/prowler-cloud/prowler/pull/9213)
|
||||
- Fix M365 Teams `--sp-env-auth` connection error and enhanced timeout logging [(#9191)](https://github.com/prowler-cloud/prowler/pull/9191)
|
||||
- Rename `get_oci_assessment_summary` to `get_oraclecloud_assessment_summary` in HTML output [(#9200)](https://github.com/prowler-cloud/prowler/pull/9200)
|
||||
- Fix Validation and other errors in Azure provider [(#8915)](https://github.com/prowler-cloud/prowler/pull/8915)
|
||||
- Update documentation URLs from docs.prowler.cloud to docs.prowler.com [(#9240)](https://github.com/prowler-cloud/prowler/pull/9240)
|
||||
|
||||
---
|
||||
|
||||
@@ -131,7 +124,6 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Update AWS CloudFront service metadata to new format [(#8829)](https://github.com/prowler-cloud/prowler/pull/8829)
|
||||
- Deprecate user authentication for M365 provider [(#8865)](https://github.com/prowler-cloud/prowler/pull/8865)
|
||||
|
||||
|
||||
### Fixed
|
||||
- Fix SNS topics showing empty AWS_ResourceID in Quick Inventory output [(#8762)](https://github.com/prowler-cloud/prowler/issues/8762)
|
||||
- Fix HTML Markdown output for long strings [(#8803)](https://github.com/prowler-cloud/prowler/pull/8803)
|
||||
|
||||
@@ -430,7 +430,7 @@ def prowler():
|
||||
else:
|
||||
# Refactor(CLI)
|
||||
logger.critical(
|
||||
"Slack integration needs SLACK_API_TOKEN and SLACK_CHANNEL_NAME environment variables (see more in https://docs.prowler.com/user-guide/cli/tutorials/integrations#configuration-of-the-integration-with-slack)."
|
||||
"Slack integration needs SLACK_API_TOKEN and SLACK_CHANNEL_NAME environment variables (see more in https://docs.prowler.cloud/en/latest/tutorials/integrations/#slack)."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -573,6 +573,7 @@ def prowler():
|
||||
generated_outputs["compliance"].append(prowler_threatscore)
|
||||
prowler_threatscore.batch_write_data_to_file()
|
||||
elif compliance_name.startswith("ccc_"):
|
||||
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
|
||||
@@ -15,7 +15,6 @@ from prowler.lib.check.models import Severity
|
||||
from prowler.lib.outputs.common import Status
|
||||
from prowler.providers.common.arguments import (
|
||||
init_providers_parser,
|
||||
validate_asff_usage,
|
||||
validate_provider_arguments,
|
||||
)
|
||||
|
||||
@@ -136,12 +135,6 @@ Detailed documentation at https://docs.prowler.com
|
||||
if not valid:
|
||||
self.parser.error(f"{args.provider}: {message}")
|
||||
|
||||
asff_is_valid, asff_error = validate_asff_usage(
|
||||
args.provider, getattr(args, "output_formats", None)
|
||||
)
|
||||
if not asff_is_valid:
|
||||
self.parser.error(asff_error)
|
||||
|
||||
return args
|
||||
|
||||
def __set_default_provider__(self, args: list) -> list:
|
||||
@@ -311,7 +304,7 @@ Detailed documentation at https://docs.prowler.com
|
||||
"--checks-folder",
|
||||
"-x",
|
||||
nargs="?",
|
||||
help="Specify external directory with custom checks (each check must have a folder with the required files, see more in https://docs.prowler.com/user-guide/cli/tutorials/misc#custom-checks-in-prowler).",
|
||||
help="Specify external directory with custom checks (each check must have a folder with the required files, see more in https://docs.prowler.cloud/en/latest/tutorials/misc/#custom-checks).",
|
||||
)
|
||||
|
||||
def __init_list_checks_parser__(self):
|
||||
@@ -364,7 +357,7 @@ Detailed documentation at https://docs.prowler.com
|
||||
"--mutelist-file",
|
||||
"-w",
|
||||
nargs="?",
|
||||
help="Path for mutelist YAML file. See example prowler/config/<provider>_mutelist.yaml for reference and format. For AWS provider, it also accepts AWS DynamoDB Table, Lambda ARNs or S3 URIs, see more in https://docs.prowler.com/user-guide/cli/tutorials/mutelist",
|
||||
help="Path for mutelist YAML file. See example prowler/config/<provider>_mutelist.yaml for reference and format. For AWS provider, it also accepts AWS DynamoDB Table, Lambda ARNs or S3 URIs, see more in https://docs.prowler.cloud/en/latest/tutorials/mutelist/",
|
||||
)
|
||||
|
||||
def __init_config_parser__(self):
|
||||
@@ -391,7 +384,7 @@ Detailed documentation at https://docs.prowler.com
|
||||
"--custom-checks-metadata-file",
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Path for the custom checks metadata YAML file. See example prowler/config/custom_checks_metadata_example.yaml for reference and format. See more in https://docs.prowler.com/user-guide/cli/tutorials/custom-checks-metadata/",
|
||||
help="Path for the custom checks metadata YAML file. See example prowler/config/custom_checks_metadata_example.yaml for reference and format. See more in https://docs.prowler.cloud/en/latest/tutorials/custom-checks-metadata/",
|
||||
)
|
||||
|
||||
def __init_third_party_integrations_parser__(self):
|
||||
@@ -409,5 +402,5 @@ Detailed documentation at https://docs.prowler.com
|
||||
third_party_subparser.add_argument(
|
||||
"--slack",
|
||||
action="store_true",
|
||||
help="Send a summary of the execution with a Slack APP in your channel. Environment variables SLACK_API_TOKEN and SLACK_CHANNEL_NAME are required (see more in https://docs.prowler.com/user-guide/cli/tutorials/integrations#configuration-of-the-integration-with-slack/).",
|
||||
help="Send a summary of the execution with a Slack APP in your channel. Environment variables SLACK_API_TOKEN and SLACK_CHANNEL_NAME are required (see more in https://docs.prowler.cloud/en/latest/tutorials/integrations/#slack).",
|
||||
)
|
||||
@@ -64,7 +64,7 @@ def open_file(input_file: str, mode: str = "r") -> TextIOWrapper:
|
||||
except OSError as os_error:
|
||||
if os_error.strerror == "Too many open files":
|
||||
logger.critical(
|
||||
"Ooops! You reached your user session maximum open files. To solve this issue, increase the shell session limit by running this command `ulimit -n 4096`. For more info visit https://docs.prowler.com/troubleshooting/"
|
||||
"Ooops! You reached your user session maximum open files. To solve this issue, increase the shell session limit by running this command `ulimit -n 4096`. For more info visit https://docs.prowler.cloud/en/latest/troubleshooting/"
|
||||
)
|
||||
else:
|
||||
logger.critical(
|
||||
|
||||
@@ -1454,23 +1454,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"bedrock-agentcore": {
|
||||
"regions": {
|
||||
"aws": [
|
||||
"ap-northeast-1",
|
||||
"ap-south-1",
|
||||
"ap-southeast-1",
|
||||
"ap-southeast-2",
|
||||
"eu-central-1",
|
||||
"eu-west-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
"us-west-2"
|
||||
],
|
||||
"aws-cn": [],
|
||||
"aws-us-gov": []
|
||||
}
|
||||
},
|
||||
"bedrock-data-automation": {
|
||||
"regions": {
|
||||
"aws": [
|
||||
@@ -6990,6 +6973,21 @@
|
||||
"aws-us-gov": []
|
||||
}
|
||||
},
|
||||
"lookoutvision": {
|
||||
"regions": {
|
||||
"aws": [
|
||||
"ap-northeast-1",
|
||||
"ap-northeast-2",
|
||||
"eu-central-1",
|
||||
"eu-west-1",
|
||||
"us-east-1",
|
||||
"us-east-2",
|
||||
"us-west-2"
|
||||
],
|
||||
"aws-cn": [],
|
||||
"aws-us-gov": []
|
||||
}
|
||||
},
|
||||
"lumberyard": {
|
||||
"regions": {
|
||||
"aws": [
|
||||
@@ -7901,7 +7899,6 @@
|
||||
"ap-southeast-3",
|
||||
"ap-southeast-4",
|
||||
"ap-southeast-5",
|
||||
"ap-southeast-6",
|
||||
"ap-southeast-7",
|
||||
"ca-central-1",
|
||||
"ca-west-1",
|
||||
|
||||
@@ -297,7 +297,7 @@ def create_output(resources: list, provider: AwsProvider, args):
|
||||
|
||||
csv_file.close()
|
||||
print(
|
||||
f"\n{Fore.YELLOW}WARNING: Only resources that have or have had tags will appear (except for IAM and S3).\nSee more in https://docs.prowler.com/user-guide/cli/tutorials/quick-inventory/#objections{Style.RESET_ALL}"
|
||||
f"\n{Fore.YELLOW}WARNING: Only resources that have or have had tags will appear (except for IAM and S3).\nSee more in https://docs.prowler.cloud/en/latest/tutorials/quick-inventory/#objections{Style.RESET_ALL}"
|
||||
)
|
||||
print("\nMore details in files:")
|
||||
print(f" - CSV: {args.output_directory}/{output_file + csv_file_suffix}")
|
||||
|
||||
@@ -256,7 +256,7 @@ class SecurityHub:
|
||||
security_hub_client.list_enabled_products_for_import()
|
||||
):
|
||||
logger.warning(
|
||||
f"Security Hub is enabled in {region} but Prowler integration does not accept findings. More info: https://docs.prowler.com/user-guide/providers/aws/securityhub#aws-security-hub-integration-with-prowler"
|
||||
f"Security Hub is enabled in {region} but Prowler integration does not accept findings. More info: https://docs.prowler.cloud/en/latest/tutorials/aws/securityhub/"
|
||||
)
|
||||
return region, None
|
||||
else:
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
"Url": "https://hub.prowler.com/check/awslambda_function_using_supported_runtimes"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Categories": [
|
||||
"container-security"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecr_registry_scan_images_on_push_enabled",
|
||||
"CheckTitle": "ECR registry has image scanning on push enabled for all repositories",
|
||||
"CheckTitle": "Check if ECR Registry has scan on push enabled",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
|
||||
"Identify",
|
||||
"Vulnerability, patch, and version management"
|
||||
],
|
||||
"ServiceName": "ecr",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:ecr:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Other",
|
||||
"Description": "Amazon ECR registries with repositories are evaluated for image scanning configured as `scan on push` at the registry level, with scan rules that cover all repositories (no restrictive filters), for either **basic** or **enhanced** scanning.",
|
||||
"Risk": "Absent or filtered `scan on push` lets **vulnerable images** be pushed and deployed without timely detection, enabling exploitation of known CVEs (RCE, privilege escalation), supply chain compromise, and lateral movement - threatening workload integrity and data confidentiality.",
|
||||
"Description": "Check if ECR Registry has scan on push enabled",
|
||||
"Risk": "Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings. ",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecr put-registry-scanning-configuration --rules 'scanFrequency=SCAN_ON_PUSH,repositoryFilters=[{filter=string,filterType=WILDCARD}]'",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. Open the AWS Management Console and go to Amazon ECR\n2. In the left menu, click Account settings (or Settings), then find Registry scanning\n3. Click Edit\n4. Set Scanning type to Enhanced scanning\n5. Enable Scan on push\n6. Under Repository filters, set Filter type to WILDCARD and Filter to *\n7. Click Save",
|
||||
"Terraform": "```hcl\nresource \"aws_ecr_registry_scanning_configuration\" \"<example_resource_name>\" {\n scan_type = \"ENHANCED\"\n\n rule {\n scan_frequency = \"SCAN_ON_PUSH\" # Ensures scan on push\n repository_filter {\n filter = \"*\" # Applies to all repositories\n filter_type = \"WILDCARD\"\n }\n }\n}\n```"
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable registry-wide `scan on push` and ensure rules apply to all repositories (no filters). Prefer **enhanced scanning** for broader coverage, and pair with continuous scans when available. Integrate findings into CI/CD gates and alerts to enforce **defense in depth** and block promotion of risky images.",
|
||||
"Url": "https://hub.prowler.com/check/ecr_registry_scan_images_on_push_enabled"
|
||||
"Text": "Enable ECR image scanning and review the scan findings for information about the security of the container images that are being deployed.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,41 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecr_repositories_lifecycle_policy_enabled",
|
||||
"CheckTitle": "ECR repository has a lifecycle policy configured",
|
||||
"CheckTitle": "Check if ECR repositories have lifecycle policies enabled",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Effects/Resource Consumption"
|
||||
"Identify",
|
||||
"Resource configuration"
|
||||
],
|
||||
"ServiceName": "ecr",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "low",
|
||||
"ResourceType": "AwsEcrRepository",
|
||||
"Description": "Amazon ECR repositories have a **lifecycle policy** configured to automatically expire container images based on age, count, or tags.",
|
||||
"Risk": "Without **lifecycle policies**, images accumulate indefinitely, leading to:\n- **Availability** issues when quotas block pushes and CI/CD\n- **Integrity** risk from redeploying outdated, vulnerable images\n- **Cost** growth from unnecessary storage",
|
||||
"Description": "Check if ECR repositories have lifecycle policies enabled",
|
||||
"Risk": "Amazon ECR repositories run the risk of retaining huge volumes of images, increasing unnecessary cost.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html",
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/lp_creation.html",
|
||||
"https://aws.plainenglish.io/automation-deletion-untagged-container-image-in-amazon-ecr-using-ecr-lifecycle-policy-995eae2f5b8d",
|
||||
"https://blog.stackademic.com/title-implementing-lifecycle-policies-in-aws-ecr-a-practical-guide-3860b612b477",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ECR/lifecycle-policy-in-use.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecr put-lifecycle-policy --repository-name <REPOSITORY_NAME> --lifecycle-policy-text '{\"rules\":[{\"rulePriority\":1,\"selection\":{\"tagStatus\":\"untagged\",\"countType\":\"imageCountMoreThan\",\"countNumber\":1},\"action\":{\"type\":\"expire\"}}]}'",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ECR::Repository\n Properties:\n # Critical: Adding a lifecycle policy makes the repo PASS this check\n LifecyclePolicy:\n # Critical: The policy content; any valid rule satisfies the requirement\n LifecyclePolicyText: >-\n {\"rules\":[{\"rulePriority\":1,\"selection\":{\"tagStatus\":\"untagged\",\"countType\":\"imageCountMoreThan\",\"countNumber\":1},\"action\":{\"type\":\"expire\"}}]}\n```",
|
||||
"Other": "1. Open the AWS Console and go to Amazon ECR > Repositories\n2. Select the target repository\n3. From Actions, choose \"Lifecycle policies\"\n4. Click \"Create rule\"\n5. Set Image status: Untagged, Match criteria: Image count more than = 1, Action: Expire\n6. Click \"Save\" to apply the lifecycle policy",
|
||||
"Terraform": "```hcl\nresource \"aws_ecr_lifecycle_policy\" \"<example_resource_name>\" {\n repository = \"<example_resource_name>\"\n # Critical: The policy ensures a lifecycle policy is configured for the repo\n policy = <<POLICY\n{\"rules\":[{\"rulePriority\":1,\"selection\":{\"tagStatus\":\"untagged\",\"countType\":\"imageCountMoreThan\",\"countNumber\":1},\"action\":{\"type\":\"expire\"}}]}\nPOLICY\n}\n```"
|
||||
"CLI": "aws ecr put-lifecycle-policy --repository-name <REPOSITORY_NAME> --lifecycle-policy-text <LIFECYCLE_POLICY> [--registry-id <REGISTRY_ID>]",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ECR/lifecycle-policy-in-use.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Implement **lifecycle policies** per repository to expire untagged, old, or excess images and retain a small set of trusted releases. Validate outcomes before applying, review rules regularly, and apply consistently across Regions when replicating. This supports **defense in depth** by reducing image attack surface and operational risk.",
|
||||
"Url": "https://hub.prowler.com/check/ecr_repositories_lifecycle_policy_enabled"
|
||||
"Text": "Open the Amazon ECR console. Create an ECR lifecycle policy.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,39 +1,33 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecr_repositories_not_publicly_accessible",
|
||||
"CheckTitle": "ECR repository is not publicly accessible",
|
||||
"CheckTitle": "Ensure there are no ECR repositories set as Public",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Reachability",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"TTPs/Initial Access",
|
||||
"Effects/Data Exposure"
|
||||
"Protect",
|
||||
"Secure Access Management"
|
||||
],
|
||||
"ServiceName": "ecr",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsEcrRepository",
|
||||
"Description": "**Amazon ECR repositories** are evaluated for **public exposure** via repository policies that allow anonymous principals (e.g., `Principal: \"*\"`) to access the repo, including image listing, pulling, or modification.",
|
||||
"Risk": "**Public access to ECR repositories** weakens **confidentiality** and **integrity**.\n\nAnyone can pull images, exposing proprietary code or embedded secrets; if pushes are allowed, attackers can poison images, enabling supply-chain compromise. Uncontrolled pulls can raise **egress costs** and leak repository metadata.",
|
||||
"Description": "Ensure there are no ECR repositories set as Public",
|
||||
"Risk": "A repository policy that allows anonymous access may allow anonymous users to perform actions.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/public/security_iam_service-with-iam.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecr delete-repository-policy --repository-name <example_resource_name>",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ECR::Repository\n Properties:\n RepositoryPolicyText:\n Version: \"2012-10-17\"\n Statement:\n - Effect: Allow\n Principal:\n AWS: \"arn:aws:iam::<example_resource_id>:root\" # Critical: restricts access to a specific AWS account; removes public (*) access\n Action: \"ecr:*\"\n```",
|
||||
"Other": "1. In the AWS Console, go to Amazon ECR > Repositories\n2. Select the repository\n3. Open the Permissions tab and click Edit\n4. Remove any statement with Principal set to \"*\", or replace it with specific AWS ARN(s) (e.g., arn:aws:iam::<example_resource_id>:root)\n5. Save changes",
|
||||
"Terraform": "```hcl\nresource \"aws_ecr_repository_policy\" \"<example_resource_name>\" {\n repository = \"<example_resource_name>\"\n policy = jsonencode({\n Version = \"2012-10-17\"\n Statement = [{\n Effect = \"Allow\"\n Principal = { AWS = \"arn:aws:iam::<example_resource_id>:root\" } # Critical: restricts access to a specific AWS principal; removes public (*) access\n Action = \"ecr:*\"\n }]\n })\n}\n```"
|
||||
"CLI": "",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/aws/public-policies/public_1-ecr-repositories-not-public#cloudformation",
|
||||
"Other": "https://docs.prowler.com/checks/aws/public-policies/public_1-ecr-repositories-not-public#aws-console",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Apply **least privilege** to repository policies:\n- Avoid `Principal:\"*\"` and block anonymous access\n- Grant minimal actions to specific accounts/roles\n- Require authenticated pulls/pushes via IAM\n- Use **private connectivity** (e.g., VPC endpoints)\n- Add **defense in depth** with image scanning and signing",
|
||||
"Url": "https://hub.prowler.com/check/ecr_repositories_not_publicly_accessible"
|
||||
"Text": "Ensure the repository and its contents are not publicly accessible",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/public/security_iam_service-with-iam.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"internet-exposed",
|
||||
"container-security"
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,40 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecr_repositories_scan_images_on_push_enabled",
|
||||
"CheckTitle": "[DEPRECATED] ECR repository has image scanning on push enabled",
|
||||
"CheckTitle": "[DEPRECATED] Check if ECR image scan on push is enabled",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/Vulnerabilities/CVE",
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
|
||||
"Identify",
|
||||
"Vulnerability, patch, and version management"
|
||||
],
|
||||
"ServiceName": "ecr",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEcrRepository",
|
||||
"Description": "[DEPRECATED]\n**Amazon ECR repositories** are evaluated for **image scanning on push**; when configured, new image uploads automatically trigger a vulnerability scan (`scan_on_push`).",
|
||||
"Risk": "Without **scan on push**, images with known CVEs can enter registries and reach runtime unnoticed, undermining **integrity** and **confidentiality** through exploitable packages. Attackers may achieve code execution and lateral movement. Delayed detection increases operational risk and extends remediation timelines.",
|
||||
"Description": "[DEPRECATED] Check if ECR image scan on push is enabled",
|
||||
"Risk": "Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings. ",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ECR/scan-on-push.html",
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning-basic-enabling.html",
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecr put-image-scanning-configuration --repository-name <repo_name> --image-scanning-configuration scanOnPush=true",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ECR::Repository\n Properties:\n ImageScanningConfiguration:\n ScanOnPush: true # Critical: enables image scanning on push for this repository\n```",
|
||||
"Other": "1. Open the AWS Console and go to Amazon ECR\n2. Click Repositories and select the target repository\n3. Click Edit\n4. Enable the Scan on push toggle\n5. Click Save",
|
||||
"Terraform": "```hcl\nresource \"aws_ecr_repository\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n\n image_scanning_configuration {\n scan_on_push = true # Critical: enables scanning on image push\n }\n}\n```"
|
||||
"CLI": "aws ecr create-repository --repository-name <repo_name> --image-scanning-configuration scanOnPush=true--region <region_name>",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/aws/general-policies/general_8#cli-command",
|
||||
"Other": "",
|
||||
"Terraform": "https://docs.prowler.com/checks/aws/general-policies/general_8#fix---buildtime"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **image scanning on push** (`scan_on_push`) for all repositories and use findings as promotion gates. Prefer **continuous/enhanced scanning** for defense in depth, set severity thresholds, and block or quarantine noncompliant images. Integrate results with CI/CD and adopt **shift-left** vulnerability management.",
|
||||
"Url": "https://hub.prowler.com/check/ecr_repositories_scan_images_on_push_enabled"
|
||||
"Text": "Enable ECR image scanning and review the scan findings for information about the security of the container images that are being deployed.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,42 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecr_repositories_scan_vulnerabilities_in_latest_image",
|
||||
"CheckTitle": "ECR repository latest image is scanned with no vulnerabilities at or above the configured minimum severity",
|
||||
"CheckTitle": "Check if ECR image scan found vulnerabilities in the newest image version",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/Vulnerabilities/CVE",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
|
||||
"Identify",
|
||||
"Vulnerability, patch, and version management"
|
||||
],
|
||||
"ServiceName": "ecr",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEcrRepository",
|
||||
"Description": "**Amazon ECR repositories** are assessed on the most recent pushed image to confirm a vulnerability scan exists, completed successfully, and that no results meet or exceed the configured minimum severity (e.g., `CRITICAL`, `HIGH`, `MEDIUM`).",
|
||||
"Risk": "Unscanned or high-severity findings in container images expose workloads to exploitation of known CVEs.\n\nAttackers can gain code execution, exfiltrate data, alter services, or disrupt operations, enabling **lateral movement** and supply-chain compromise-impacting **confidentiality**, **integrity**, and **availability**.",
|
||||
"Description": "Check if ECR image scan found vulnerabilities in the newest image version",
|
||||
"Risk": "Amazon ECR image scanning helps in identifying software vulnerabilities in your container images. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project and provides a list of scan findings.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://www.geeksforgeeks.org/devops/how-to-manage-image-security-and-vulnerabilities-in-ecr/",
|
||||
"https://aws.amazon.com/blogs/aws/amazon-inspector-enhances-container-security-by-mapping-amazon-ecr-images-to-running-containers/",
|
||||
"https://docs.aws.amazon.com/inspector/latest/user/scanning-ecr.html",
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning-enhanced.html",
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html",
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning-basic.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "```yaml\n# Enable scan on push so the latest image is automatically scanned\nResources:\n EcrRepository:\n Type: AWS::ECR::Repository\n Properties:\n RepositoryName: <example_resource_name>\n ImageScanningConfiguration:\n ScanOnPush: true # CRITICAL: ensures each pushed image is scanned so the latest has scan results\n```",
|
||||
"Other": "1. In the AWS Console, go to ECR > Repositories > <example_resource_name>\n2. Click Edit and enable Scan on push, then Save\n3. Rebuild the container image to remove vulnerabilities and push a new tag to the repository\n4. Open the image details and click Scan image (if not auto-scanned)\n5. Confirm Findings show 0 vulnerabilities at or above the required severity",
|
||||
"Terraform": "```hcl\n# Enable scan on push so the latest image is automatically scanned\nresource \"aws_ecr_repository\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n\n image_scanning_configuration {\n scan_on_push = true # CRITICAL: ensures each pushed image is scanned so the latest has scan results\n }\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **continuous scanning** for repositories and enforce deployment gates at your policy threshold (e.g., `MEDIUM`+).\n\nRebuild images with patched components and updated bases, keep images minimal, and apply **least privilege**. Use **image signing** and CI/CD checks so only scanned, compliant images can run.",
|
||||
"Url": "https://hub.prowler.com/check/ecr_repositories_scan_vulnerabilities_in_latest_image"
|
||||
"Text": "Open the Amazon ECR console. Then look for vulnerabilities and fix them.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html#describe-scan-findings"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,39 +1,31 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecr_repositories_tag_immutability",
|
||||
"CheckTitle": "ECR repository has image tag immutability enabled",
|
||||
"CheckTitle": "ECR repositories should have tag immutability configured",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecr",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecr:{region}:{account-id}:repository/{repository-name}",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEcrRepository",
|
||||
"Description": "Amazon ECR repositories are assessed for **image tag immutability**. Repositories permitting tag updates (`MUTABLE`) are identified; those enforcing immutable tags (such as `IMMUTABLE`) are recognized.",
|
||||
"Risk": "Mutable tags allow replacing the image behind a trusted tag, undermining release **integrity**. This enables supply-chain injection, unintended rollouts, and backdoored deployments, harming **availability**. Malicious images can exfiltrate data, impacting **confidentiality**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecr-private-tag-immutability-enabled.html",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecr-controls.html#ecr-2",
|
||||
"https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-tag-mutability.html"
|
||||
],
|
||||
"Description": "This control checks whether a ECR repository has tag immutability enabled. The control fails if a ECR repository has tag immutability disabled and passes if tag immutability is set to IMMUTABLE.",
|
||||
"Risk": "Disabling tag immutability in an ECR repository increases the risk of image tampering by allowing tags to be overridden, which can lead to deploying unintended images and security vulnerabilities.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecr-private-tag-immutability-enabled.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecr put-image-tag-mutability --repository-name <repository-name> --image-tag-mutability IMMUTABLE",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ECR::Repository\n Properties:\n ImageTagMutability: IMMUTABLE # Critical: enables tag immutability to prevent tag overwrites\n```",
|
||||
"Other": "1. Open the Amazon ECR console\n2. Go to Repositories (Private) and select the repository\n3. Click Actions > Edit\n4. Set Image tag immutability to Immutable\n5. Click Save",
|
||||
"Terraform": "```hcl\nresource \"aws_ecr_repository\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n image_tag_mutability = \"IMMUTABLE\" # Critical: enables tag immutability to prevent tag overwrites\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecr-controls.html#ecr-2",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **tag immutability** so tags map to a single artifact. Use **versioned tags** per build, block retagging in CI/CD, and apply **least privilege** for push actions. Layer **image signing** and admission controls to run only trusted images. *If exceptions are needed, keep them narrow and monitored.*",
|
||||
"Url": "https://hub.prowler.com/check/ecr_repositories_tag_immutability"
|
||||
"Text": "Enable tag immutability on your ECR repositories to prevent image tags from being overwritten.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-tag-mutability.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,38 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_cluster_container_insights_enabled",
|
||||
"CheckTitle": "ECS cluster has Container Insights enabled or enhanced",
|
||||
"CheckTitle": "ECS clusters should use Container Insights",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Runtime Behavior Analysis"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:cluster/{cluster-name}",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEcsCluster",
|
||||
"Description": "**ECS clusters** have CloudWatch **Container Insights** configured via the `containerInsights` setting, accepting `enabled` or `enhanced` values to emit cluster, service, task, and container telemetry.",
|
||||
"Risk": "Without **Container Insights**, ECS operations lack **telemetry** to spot failures and anomalies. Missed CPU/memory/network spikes and restart loops degrade **availability** and delay response. Absent baselines impede detecting abuse (e.g., **cryptomining** or data egress bursts), risking **confidentiality** and unexpected **costs**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-12",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cloudwatch-container-insights.html",
|
||||
"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-metrics-ECS.html",
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-container-insights-enabled.html",
|
||||
"https://aws.amazon.com/blogs/aws/container-insights-with-enhanced-observability-now-available-in-amazon-ecs/",
|
||||
"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/deploy-container-insights-ECS-cluster.html",
|
||||
"https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights.html"
|
||||
],
|
||||
"Description": "This control checks if ECS clusters use Container Insights. This control fails if Container Insights are not set up for a cluster.",
|
||||
"Risk": "Without Container Insights, important performance metrics and diagnostic information from containerized applications may not be captured, which can hinder monitoring and troubleshooting.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-container-insights-enabled.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs update-cluster-settings --cluster <cluster-name> --settings name=containerInsights,value=enabled",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: Enable Container Insights on an ECS cluster\nResources:\n <example_resource_name>:\n Type: AWS::ECS::Cluster\n Properties:\n ClusterSettings:\n - Name: containerInsights # Critical: enables CloudWatch Container Insights for the cluster\n Value: enabled # Critical: setting that passes the check\n```",
|
||||
"Other": "1. Open the Amazon ECS console\n2. Go to Clusters and select the target cluster\n3. Click Update cluster\n4. Under CloudWatch Container Insights, enable Container Insights (or Enhanced)\n5. Click Save changes",
|
||||
"Terraform": "```hcl\n# Terraform: Enable Container Insights on an ECS cluster\nresource \"aws_ecs_cluster\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n\n setting {\n name = \"containerInsights\" # Critical: enables CloudWatch Container Insights for the cluster\n value = \"enabled\" # Critical: setting that passes the check\n }\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-12",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **Container Insights** on all clusters-prefer `enhanced` for deeper visibility. Apply at account level for new clusters and enforce via automation.\n\nUse **least privilege** for access to metrics/logs, encrypt logs, and set **alarms** on critical metrics. Correlate with app logs and tracing for **defense in depth** and faster incident detection.",
|
||||
"Url": "https://hub.prowler.com/check/ecs_cluster_container_insights_enabled"
|
||||
"Text": "Enable Container Insights for your ECS clusters to collect and monitor key performance metrics and diagnostic data from your containers.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/deploy-container-insights-ECS-cluster.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,41 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_service_fargate_latest_platform_version",
|
||||
"CheckTitle": "ECS Fargate service uses the latest Fargate platform version",
|
||||
"CheckTitle": "ECS Fargate services should run on the latest Fargate platform version",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:service/{service-name}",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsEcsService",
|
||||
"Description": "**ECS Fargate services** use the **latest Fargate platform version** via `platformVersion`=`LATEST` or an explicit value matching the current release for their `platformFamily` (Linux/Windows).",
|
||||
"Risk": "Running on an outdated platform leaves known CVEs in the kernel/runtime unpatched, risking:\n- **Confidentiality**: data exposure via container escape\n- **Integrity**: privilege escalation and tampering\n- **Availability**: crashes/DoS and instability under load",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://servian.dev/setting-up-fargate-for-ecs-exec-8f5cc8d7d80e",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/platform-fargate.html",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ECS/platform-version.html",
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-fargate-latest-platform-version.html",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-10"
|
||||
],
|
||||
"Description": "This control checks if Amazon ECS Fargate services are running the latest Fargate platform version. The control fails if the platform version is not the latest.",
|
||||
"Risk": "Not running the latest Fargate platform version may expose your services to security vulnerabilities and bugs that are resolved in newer versions.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-fargate-latest-platform-version.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs update-service --cluster <cluster-name> --service <service-name> --platform-version LATEST",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: set ECS Fargate service to latest platform version\nResources:\n <example_resource_name>:\n Type: AWS::ECS::Service\n Properties:\n Cluster: <example_resource_id>\n TaskDefinition: <example_resource_name>\n LaunchType: FARGATE\n PlatformVersion: LATEST # Critical: use the latest Fargate platform version\n NetworkConfiguration:\n AwsvpcConfiguration:\n Subnets:\n - <example_resource_id>\n```",
|
||||
"Other": "1. In the AWS Console, go to Amazon ECS\n2. Open your cluster and select the service\n3. Click Update\n4. Set Platform version to LATEST\n5. Click Update service (or Deploy) to apply",
|
||||
"Terraform": "```hcl\n# ECS Fargate service using the latest platform version\nresource \"aws_ecs_service\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n cluster = \"<example_resource_id>\"\n task_definition = \"<example_resource_name>\"\n launch_type = \"FARGATE\"\n platform_version = \"LATEST\" # Critical: ensures the latest Fargate platform version\n\n network_configuration {\n subnets = [\"<example_resource_id>\"]\n }\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-10",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "- Prefer `platformVersion` `LATEST` to receive patches.\n- If pinning, monitor releases and redeploy quickly to the current version.\n- Automate updates with staged rollouts in CI/CD.\n- Apply **defense in depth** and **least privilege** to limit runtime exploit impact.",
|
||||
"Url": "https://hub.prowler.com/check/ecs_service_fargate_latest_platform_version"
|
||||
"Text": "Update your ECS Fargate services to the latest platform version to ensure they are running in a secure and optimized environment.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"vulnerabilities",
|
||||
"container-security"
|
||||
"vulnerabilities"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_service_no_assign_public_ip",
|
||||
"CheckTitle": "ECS service does not have automatic public IP assignment",
|
||||
"CheckTitle": "ECS services should not assign public IPs automatically",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Reachability"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:service/{service-name}",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsService",
|
||||
"Description": "**ECS services** are assessed for automatic public IP assignment via the `assignPublicIp` setting in their network configuration.\n\nThe finding indicates whether tasks launched by the service receive a public IP or are limited to private addressing.",
|
||||
"Risk": "Automatic **public IPs** make tasks directly reachable from the Internet, enabling:\n- Port scanning and remote exploitation\n- Brute-force against admin endpoints\n- Data exfiltration via exposed APIs\nThis jeopardizes **confidentiality**, **integrity**, and **availability**, and can facilitate lateral movement within the VPC.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-2",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security.html",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html"
|
||||
],
|
||||
"Description": "This control checks whether Amazon ECS services are configured to automatically assign public IP addresses. The control fails if AssignPublicIP is ENABLED and passes if it is DISABLED.",
|
||||
"Risk": "Having public IP addresses assigned to ECS services automatically can expose services to the internet, increasing the risk of unauthorized access, data breaches, and cyberattacks.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs update-service --cluster <cluster-name> --service <service-name> --network-configuration \"awsvpcConfiguration={subnets=[<subnet-id>],assignPublicIp=DISABLED}\"",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ECS::Service\n Properties:\n Cluster: <example_resource_id>\n TaskDefinition: <example_resource_id>\n NetworkConfiguration:\n AwsvpcConfiguration:\n Subnets:\n - <example_resource_id>\n AssignPublicIp: DISABLED # Critical: disables automatic public IP assignment for the service\n```",
|
||||
"Other": "1. In the AWS Console, go to ECS > Clusters and open your cluster\n2. Select the service and click Update\n3. Under Networking (awsvpc), set Assign public IP to Disabled\n4. Click Update service to apply",
|
||||
"Terraform": "```hcl\nresource \"aws_ecs_service\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n cluster = \"<example_resource_id>\"\n task_definition = \"<example_resource_id>\"\n\n network_configuration {\n subnets = [\"<example_resource_id>\"]\n assign_public_ip = false # Critical: disables automatic public IP assignment\n }\n}\n```"
|
||||
"CLI": "aws ecs update-service --cluster <cluster-name> --service <service-name> --network-configuration 'awsvpcConfiguration={assignPublicIp=DISABLED}'",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-2",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable `assignPublicIp` to keep tasks private. Expose services through **load balancers** or **private endpoints**, restrict ingress with **least-privilege** security groups, and route egress via **NAT**. Apply **defense in depth** (WAF, TLS, monitoring) and segment networks to minimize blast radius.",
|
||||
"Url": "https://hub.prowler.com/check/ecs_service_no_assign_public_ip"
|
||||
"Text": "Disable automatic public IP address assignment for your ECS services to ensure they are not publicly accessible.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,39 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_containers_readonly_access",
|
||||
"CheckTitle": "ECS task definition has all containers with read-only root filesystems",
|
||||
"CheckTitle": "ECS containers should be limited to read-only access to root filesystems",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/CIS Host Hardening Benchmarks"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"SubServiceName": "taskDefinition",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:task-definition/{task-definition-name}",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "Amazon ECS task definitions specify whether container root filesystems are **read-only** via `readonlyRootFilesystem`. Containers where this setting is absent or set to `false` effectively have write access to the root filesystem.",
|
||||
"Risk": "A **writable root filesystem** enables runtime tampering and persistence. Attackers can modify binaries or configs, drop implants, or delete critical files, degrading **integrity** and **availability**. Access to writable paths can also expose secrets and logs, eroding **confidentiality** and complicating incident response.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-containers-readonly-access.html",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/userguide/task_definition_parameters.html#container_definition_readonly",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-5"
|
||||
],
|
||||
"Description": "This control checks if Amazon ECS containers are limited to read-only access to mounted root filesystems. The control fails if the readonlyRootFilesystem parameter is set to false or if the parameter doesn't exist in the container definition.",
|
||||
"Risk": "If ECS containers have write access to root filesystems, it increases the risk of filesystem tampering and exploitation of vulnerabilities, violating the principle of least privilege.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-containers-readonly-access.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs register-task-definition --family <task-family> --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\",\"readonlyRootFilesystem\":true}]'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: ECS task definition with read-only root filesystem\nResources:\n <example_resource_name>:\n Type: AWS::ECS::TaskDefinition\n Properties:\n Family: <example_resource_name>\n ContainerDefinitions:\n - Name: <example_resource_name>\n Image: <image>\n ReadonlyRootFilesystem: true # Critical: enforces read-only root FS for the container\n```",
|
||||
"Other": "1. In the AWS Console, go to Amazon ECS > Task Definitions\n2. Select the task family <task-family> and click Create new revision\n3. For each container, edit and enable Read-only root filesystem (readonlyRootFilesystem = true)\n4. Click Create to register the new revision\n5. (If needed) Update services to use the new revision",
|
||||
"Terraform": "```hcl\n# ECS task definition with read-only root filesystem\nresource \"aws_ecs_task_definition\" \"<example_resource_name>\" {\n family = \"<example_resource_name>\"\n container_definitions = jsonencode([\n {\n name = \"<example_resource_name>\"\n image = \"<image>\"\n readonlyRootFilesystem = true # Critical: enforces read-only root FS for the container\n }\n ])\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-5",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce `readonlyRootFilesystem: true` for containers.\n- Grant write access only via specific volumes required by the app\n- Apply **least privilege** and **defense in depth**: run as non-root, drop unnecessary capabilities, and keep images immutable so runtime writes aren't needed",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_definitions_containers_readonly_access"
|
||||
"Text": "Ensure that containers have read-only access to their root filesystems to limit write access and reduce the attack surface.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/userguide/task_definition_parameters.html#container_definition_readonly"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
"vulnerabilities"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,40 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_host_namespace_not_shared",
|
||||
"CheckTitle": "ECS task definition does not share the host's process namespace with its containers",
|
||||
"CheckTitle": "ECS task definitions should not share the host's process namespace",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/CIS Host Hardening Benchmarks",
|
||||
"TTPs/Privilege Escalation",
|
||||
"TTPs/Discovery"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:task-definition/{task-definition-name}",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "**ECS task definitions** where `pidMode` is `host` are configured to share the host's **process namespace** with containers, rather than using isolated task or private namespaces.",
|
||||
"Risk": "**Host PID sharing** lets containers view and interact with host processes, eroding isolation.\n- Confidentiality: process enumeration and metadata leakage\n- Integrity/Availability: signal or `ptrace` tampering, killing services\n\nEnables lateral movement and privilege escalation from a compromised container.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-3",
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-pid-mode-check.html",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/userguide/task_definition_parameters.html#container_definition_pid_mode"
|
||||
],
|
||||
"Description": "This control checks if Amazon ECS task definitions are configured to share a host's process namespace with its containers. The control fails if the task definition shares the host's process namespace.",
|
||||
"Risk": "Sharing the host's process namespace with containers exposes host processes, potentially allowing unauthorized access or manipulation of host-level processes, undermining container isolation.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-pid-mode-check.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs register-task-definition --family <example_resource_name> --pid-mode task --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\"}]'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: ECS Task Definition without host PID namespace\nResources:\n <example_resource_name>:\n Type: AWS::ECS::TaskDefinition\n Properties:\n Family: <example_resource_name>\n ContainerDefinitions:\n - Name: <container-name>\n Image: <image>\n PidMode: task # Critical: ensures containers use task PID namespace, not host\n```",
|
||||
"Other": "1. In the AWS Console, go to Amazon ECS > Task Definitions\n2. Select the task definition and click Create new revision\n3. Set Process namespace sharing (PID mode) to Task (not Host)\n4. Save the new revision\n5. (If the previous Host PID revision remains active) Select that revision and click Deregister",
|
||||
"Terraform": "```hcl\n# ECS Task Definition without host PID namespace\nresource \"aws_ecs_task_definition\" \"example\" {\n family = \"<example_resource_name>\"\n container_definitions = jsonencode([{ name = \"<container-name>\", image = \"<image>\" }])\n pid_mode = \"task\" # Critical: prevents sharing the host's process namespace\n}\n```"
|
||||
"CLI": "aws ecs register-task-definition --family <task-family> --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\",\"pidMode\":\"task\"}]'",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-3",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Prefer **isolated PID namespaces**: set `pidMode=task` or use the default per-container namespace. Avoid `host` PID sharing except for tightly controlled diagnostics.\n\nApply **least privilege**: non-root users, minimal capabilities, read-only filesystems; and **defense in depth** with network and runtime controls.",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_definitions_host_namespace_not_shared"
|
||||
"Text": "Avoid sharing the host's process namespace with containers to maintain process isolation and enhance security.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/userguide/task_definition_parameters.html#container_definition_pid_mode"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
"vulnerabilities"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,45 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_host_networking_mode_users",
|
||||
"CheckTitle": "Amazon ECS task definition does not use host network mode, or non-privileged containers specify a non-root user",
|
||||
"CheckTitle": "Amazon ECS task definitions should have secure networking modes and user definitions",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Reachability",
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Runtime Behavior Analysis",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/CIS Host Hardening Benchmarks",
|
||||
"TTPs/Privilege Escalation",
|
||||
"TTPs/Lateral Movement"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:region:account-id:task-definition/resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "**Amazon ECS task definitions** in `host` network mode are assessed for containers where `privileged=false` and the container `user` is `root` or unset.",
|
||||
"Risk": "Sharing the host network lets containers reach host interfaces directly. Running as **root** (or with no user set) increases the chance to bind low ports, sniff traffic, or impersonate services, and makes kernel flaws more exploitable-enabling data exfiltration, tampering, and outages, impacting **confidentiality**, **integrity**, and **availability**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-6",
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-user-for-host-mode-check.html",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/update-task-definition-console-v2.html"
|
||||
],
|
||||
"Description": "This control checks whether an active Amazon ECS task definition with host networking mode has privileged or user container definitions. The control fails for task definitions that have host network mode and container definitions of privileged=false or empty, and user=root or empty.",
|
||||
"Risk": "If ECS tasks are configured with host networking and either lack a defined user or run with elevated privileges, this can lead to privilege escalation, unauthorized access to resources, and increased exposure to vulnerabilities.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-user-for-host-mode-check.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: ECS task definition not using host network mode\nResources:\n <example_resource_name>:\n Type: AWS::ECS::TaskDefinition\n Properties:\n NetworkMode: awsvpc # CRITICAL: avoids host mode to pass the check\n ContainerDefinitions:\n - Name: <example_resource_name>\n Image: <image>\n```",
|
||||
"Other": "1. Open the Amazon ECS console and go to Task definitions\n2. Select the task definition and choose the latest revision\n3. Click Create new revision\n4. Set Network mode to awsvpc (not host)\n5. Save the revision and, if used by a service, update the service to this new revision\n6. If you must keep host mode: edit each non-privileged container and set User to a non-root value (e.g., 1000) and save a new revision",
|
||||
"Terraform": "```hcl\n# ECS task definition not using host network mode\nresource \"aws_ecs_task_definition\" \"<example_resource_name>\" {\n family = \"<example_resource_name>\"\n network_mode = \"awsvpc\" # CRITICAL: avoids host mode to pass the check\n container_definitions = jsonencode([\n {\n name = \"<example_resource_name>\"\n image = \"nginx\"\n }\n ])\n}\n```"
|
||||
"CLI": "aws ecs update-task-definition --task-definition <task-definition-name> --network-mode awsvpc --requires-compatibilities FARGATE --user <user-name>",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-6",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Prefer **`awsvpc`** for isolation. If `host` is required, enforce **least privilege**:\n- Run as a non-root `user`\n- Avoid `privileged` unless strictly justified\n- Limit capabilities and exposed ports\n\nApply **defense in depth** with network segmentation and minimal IAM permissions.",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_definitions_host_networking_mode_users"
|
||||
"Text": "Ensure your ECS task definitions use the awsvpc networking mode and have secure user configurations when using host networking mode.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/update-task-definition-console-v2.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security",
|
||||
"trust-boundaries"
|
||||
"trustboundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,39 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_logging_block_mode",
|
||||
"CheckTitle": "ECS task definition has container logging in non-blocking mode",
|
||||
"CheckTitle": "ECS task definitions containers should have a logging configured with non blocking mode",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Runtime Behavior Analysis",
|
||||
"Effects/Denial of Service"
|
||||
"Resilience"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:task-definition/{task-definition-name}",
|
||||
"Severity": "low",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "**ECS task definition containers** use **non-blocking logging mode** via the `logConfiguration.mode` option on the latest active revision",
|
||||
"Risk": "**Blocking log mode** can stall writes to stdout/stderr, making containers unresponsive, failing health checks, and causing task restarts or startup failures if log groups/streams can't be created. This reduces **availability** and may trigger cascading instability across dependent services.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-log-configuration.html",
|
||||
"https://www.amazonaws.cn/en/blog-selection/preventing-log-loss-with-non-blocking-mode-in-the-awslogs-container-log-driver/",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html#specify-log-config"
|
||||
],
|
||||
"Description": "This control checks if the latest active Amazon ECS task definition has a logging configuration set to non blocking mode.",
|
||||
"Risk": "When logs cannot be immediately sent to Amazon CloudWatch, calls from container code to write to stdout or stderr will block and halt execution of the code. The logging thread in the application will block, which may prevent the application from functioning and lead to health check failures and task termination. Container startup fails if the required log group or log stream cannot be created.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-log-configuration.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs register-task-definition --family <task-family> --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\",\"logConfiguration\":{\"logDriver\":\"awslogs\",\"options\":{\"awslogs-group\":\"<log-group>\",\"awslogs-region\":\"<region>\",\"awslogs-stream-prefix\":\"ecs\",\"mode\":\"non-blocking\"}}}]'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: ECS Task Definition with non-blocking container logging\nResources:\n <example_resource_name>:\n Type: AWS::ECS::TaskDefinition\n Properties:\n Family: <example_resource_name>\n ContainerDefinitions:\n - Name: <example_resource_name>\n Image: <image>\n LogConfiguration:\n LogDriver: awslogs\n Options:\n awslogs-group: <log-group>\n awslogs-region: <region>\n awslogs-stream-prefix: ecs\n mode: non-blocking # CRITICAL: sets logging to non-blocking to pass the check\n```",
|
||||
"Other": "1. Open the AWS Console and go to ECS > Task Definitions\n2. Select the failing task definition and choose Create new revision\n3. Edit the affected container > Log configuration\n4. Set Log driver to awslogs and add option: mode = non-blocking\n5. Ensure awslogs-group, awslogs-region, and (if needed) awslogs-stream-prefix are set\n6. Save and Create; the new revision will have non-blocking logging",
|
||||
"Terraform": "```hcl\n# ECS Task Definition with container logging set to non-blocking\nresource \"aws_ecs_task_definition\" \"<example_resource_name>\" {\n family = \"<example_resource_name>\"\n\n # CRITICAL: \"mode\": \"non-blocking\" in logConfiguration options enforces non-blocking logging\n container_definitions = jsonencode([\n {\n name = \"<example_resource_name>\"\n image = \"<image>\"\n logConfiguration = {\n logDriver = \"awslogs\"\n options = {\n awslogs-group = \"<log-group>\"\n awslogs-region = \"<region>\"\n awslogs-stream-prefix = \"ecs\"\n mode = \"non-blocking\" # CRITICAL: required to pass the check\n }\n }\n }\n ])\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/awssupport/latest/user/fault-tolerance-checks.html#amazon-ec2-awslogs-driver-blockingmode",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Set `logConfiguration.mode` to `non-blocking` for all containers and size `max-buffer-size` to handle bursts. Keep log destinations in-Region to lower latency. Apply **defense in depth**: decouple application execution from logging, monitor log throughput, and design for backpressure so logging never blocks runtime.",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_definitions_logging_block_mode"
|
||||
"Text": "Configure logging in ECS task definition to non blocking mode to ensure any issues writing logs will not block or halt the container execution.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html#specify-log-config"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"logging",
|
||||
"resilience"
|
||||
"logging"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_logging_enabled",
|
||||
"CheckTitle": "ECS task definition has logging configured for all containers",
|
||||
"CheckTitle": "ECS task definitions containers should have a logging configuration",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Runtime Behavior Analysis",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:task-definition/{task-definition-name}",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "**Amazon ECS task definition** containers specify a **logging configuration** with a non-null `logDriver` for every container in the latest active revision.",
|
||||
"Risk": "Absent container logs erode visibility, letting intrusions, data exfiltration, and configuration tampering go undetected.\n\nMissing audit trails weaken confidentiality and integrity, hinder forensics, and increase MTTR during outages, impacting availability and compliance evidence.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-9",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html#specify-log-config",
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-log-configuration.html"
|
||||
],
|
||||
"Description": "This control checks if the latest active Amazon ECS task definition has a logging configuration specified. The control fails if the task definition doesn't have the logConfiguration property defined or if the value for logDriver is null in at least one container definition.",
|
||||
"Risk": "Without a logging configuration, important data may be lost, making it difficult to troubleshoot issues, monitor performance, and ensure compliance with auditing requirements.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-log-configuration.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs register-task-definition --family <task-family> --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\",\"logConfiguration\":{\"logDriver\":\"awslogs\",\"options\":{\"awslogs-group\":\"<log-group>\",\"awslogs-region\":\"<region>\"}}}]'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: ECS task definition with logging enabled for the container\nResources:\n ExampleTaskDefinition:\n Type: AWS::ECS::TaskDefinition\n Properties:\n ContainerDefinitions:\n - Name: \"<example_resource_name>\"\n Image: \"<image>\"\n LogConfiguration: # Critical: ensures container has logging configured\n LogDriver: awslogs # Critical: non-null log driver passes the check\n Options:\n awslogs-group: \"<log-group>\" # Critical: CloudWatch Logs group\n awslogs-region: \"<region>\"\n```",
|
||||
"Other": "1. In the AWS Console, go to Amazon ECS > Task Definitions\n2. Select your task definition and click Create new revision\n3. For each container, open Edit and set Log configuration to awslogs\n4. Set Log group to the desired CloudWatch Logs group and select the Region\n5. Save and Create to register the new revision (ensure all containers have logging)",
|
||||
"Terraform": "```hcl\n# ECS task definition with logging enabled for the container\nresource \"aws_ecs_task_definition\" \"<example_resource_name>\" {\n family = \"<example_resource_name>\"\n container_definitions = jsonencode([\n {\n name = \"<example_resource_name>\"\n image = \"<image>\"\n logConfiguration = { # Critical: enables container logging\n logDriver = \"awslogs\" # Critical: non-null log driver passes the check\n options = {\n awslogs-group = \"<log-group>\" # Critical: CloudWatch Logs group\n awslogs-region = \"<region>\"\n }\n }\n }\n ])\n}\n```"
|
||||
"CLI": "aws ecs register-task-definition --family <task-family> --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\",\"logConfiguration\":{\"logDriver\":\"awslogs\",\"options\":{\"awslogs-group\":\"<log-group>\",\"awslogs-region\":\"<region>\",\"awslogs-stream-prefix\":\"ecs\"}}}]'",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-9",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Implement centralized, tamper-resistant **container logging** for all tasks. Define a `logDriver` per container and ship logs to a managed destination with restricted access. Apply **least privilege**, encryption, and retention. Monitor and alert on anomalies. *If using external collectors, ensure equivalent coverage and durability.*",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_definitions_logging_enabled"
|
||||
"Text": "Define a logging configuration in the ECS task definition to ensure important data is captured and available for debugging, monitoring, and auditing purposes.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html#specify-log-config"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_no_environment_secrets",
|
||||
"CheckTitle": "ECS task definition has no secrets in environment variables",
|
||||
"CheckTitle": "Check if secrets exists in ECS task definitions environment variables",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Sensitive Data Identifications/Passwords",
|
||||
"TTPs/Credential Access"
|
||||
"Protect",
|
||||
"Secure development",
|
||||
"Credentials not hard-coded"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "**ECS task definitions** are analyzed for **plaintext secrets** placed in container `environment` variables. It identifies values that resemble credentials (keys, tokens, passwords) within container definitions.",
|
||||
"Risk": "Exposed secrets in env vars undermine confidentiality via logs, task metadata, and introspection.\n\nWith container or read-only API access, attackers can reuse credentials to read databases, modify records (integrity), pivot to other services, and trigger outages or unauthorized costs (availability).",
|
||||
"Description": "Check if secrets exists in ECS task definitions environment variables.",
|
||||
"Risk": "The use of a hard-coded password increases the possibility of password guessing. If hard-coded passwords are used, it is possible that malicious users gain access through the account in question.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ECS::TaskDefinition\n Properties:\n Family: <example_resource_name>\n ContainerDefinitions:\n - Name: app\n Image: <image>\n Secrets: # Critical: use Secrets instead of plaintext env vars\n - Name: DB_PASSWORD # Critical: inject secret at runtime\n ValueFrom: <secret_arn_or_parameter_arn> # Critical: reference Secrets Manager/SSM parameter\n```",
|
||||
"Other": "1. In the AWS Console, go to ECS > Task Definitions and open your task definition\n2. Create a new revision\n3. For each container, remove any sensitive values from Environment variables\n4. Under Environment variables, add a new entry in the Secrets section with Name (e.g., DB_PASSWORD) and ValueFrom pointing to your Secrets Manager/SSM parameter\n5. Save to create the new revision\n6. If using a service, update the service to use the new task definition revision and deploy",
|
||||
"Terraform": "```hcl\nresource \"aws_ecs_task_definition\" \"<example_resource_name>\" {\n family = \"<example_resource_name>\"\n # Critical: define container secrets instead of plaintext env vars\n container_definitions = jsonencode([\n {\n name = \"app\"\n image = \"<image>\"\n secrets = [\n { name = \"DB_PASSWORD\", valueFrom = \"<secret_arn_or_parameter_arn>\" } # Critical: inject secret at runtime\n ]\n }\n ])\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Store secrets in **AWS Secrets Manager** or **SSM Parameter Store** and inject them at runtime instead of plaintext env vars.\n\nApply **least privilege** via task roles, enable regular **rotation**, avoid logging secret values, and prefer **ephemeral credentials** for downstream services.",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_definitions_no_environment_secrets"
|
||||
"Text": "Use Secrets Manager or Parameter Store to securely provide credentials to containers without hardcoding the secrets in code or passing them through environment variables. It is currently not possible to delete task definition revisions which contain plaintext secrets. AWS is looking into implementing this feature in 2023, and it is therefore recommended that all plaintext secrets are rotated at the same time as moving the secrets to Secrets Manager or Parameter Store.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,39 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_no_privileged_containers",
|
||||
"CheckTitle": "ECS task definition has no privileged containers",
|
||||
"CheckTitle": "ECS task definitions shouldn't have privileged containers",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/CIS Host Hardening Benchmarks",
|
||||
"TTPs/Privilege Escalation"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"SubServiceName": "taskDefinition",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:task-definition/{task-definition-name}",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "**Amazon ECS task definitions** are evaluated for containers configured with **privileged mode** (`privileged: true`).\n\nThe outcome indicates whether any container definition enables this setting.",
|
||||
"Risk": "**Privileged containers** can act with host-level root, breaking isolation. A foothold lets attackers achieve **container escape**, mount host devices, read secrets, alter configs, and control other workloads-impacting confidentiality, integrity, and availability via data theft, tampering, and service disruption.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/config/latest/developerguide/ecs-containers-nonprivileged.html",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-4",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definition_security"
|
||||
],
|
||||
"Description": "This control checks if the privileged parameter in the container definition of Amazon ECS Task Definitions is set to true. The control fails if this parameter is equal to true.",
|
||||
"Risk": "Running containers with elevated privileges increases the risk of privilege escalation attacks, potentially allowing unauthorized access to the host and other containers.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-containers-nonprivileged.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs deregister-task-definition --task-definition <task-family>:<revision>",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: ECS task definition with non-privileged container\nResources:\n <example_resource_name>:\n Type: AWS::ECS::TaskDefinition\n Properties:\n Family: <example_resource_name>\n ContainerDefinitions:\n - Name: <example_resource_name>\n Image: <image>\n Privileged: false # Critical: ensures container is non-privileged to pass the check\n```",
|
||||
"Other": "1. Open the Amazon ECS console and go to Task definitions\n2. Select the failing task definition family and open the failing revision\n3. Click Create new revision\n4. Edit the affected container and uncheck Privileged (set it to false)\n5. Click Create to register the new revision",
|
||||
"Terraform": "```hcl\n# ECS task definition with non-privileged container\nresource \"aws_ecs_task_definition\" \"<example_resource_name>\" {\n family = \"<example_resource_name>\"\n container_definitions = jsonencode([\n {\n name = \"<example_resource_name>\"\n image = \"<image>\"\n privileged = false # Critical: ensures container is non-privileged to pass the check\n }\n ])\n}\n```"
|
||||
"CLI": "aws ecs register-task-definition --family <task-family> --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\",\"privileged\":false}]'",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-4",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Run containers without elevated rights (`privileged: false`) and as non-root (`user`). Apply **least privilege**:\n- Grant only required Linux capabilities via `capDrop`/`capAdd`\n- Prefer `readonlyRootFilesystem: true`\n- Isolate networks and separate duties\n- Monitor with logging to support defense in depth",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_definitions_no_privileged_containers"
|
||||
"Text": "Ensure that containers are running without elevated privileges to minimize the risk of privilege escalation.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definition_security"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"container-security"
|
||||
"vulnerabilities"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_set_no_assign_public_ip",
|
||||
"CheckTitle": "ECS task set does not automatically assign a public IP address",
|
||||
"CheckTitle": "ECS task sets should not automatically assign public IP addresses",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Reachability",
|
||||
"Effects/Data Exposure"
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:task-set/{cluster-name}/{service-name}/{task-set-id}",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsService",
|
||||
"Description": "**ECS task sets** are assessed for **automatic public IP assignment** via `AssignPublicIP`. When set to `ENABLED`, tasks are given public addresses in their network configuration.",
|
||||
"Risk": "Public IPs make tasks directly reachable from the Internet, enabling scanning, brute force, and exploit attempts.\n\nImpacts: **confidentiality** (data exposure), **integrity** (unauthorized actions), **availability** (DoS). Attackers can bypass internal controls and pivot for lateral movement.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/developerguide/update-task-definition-console-v2.html",
|
||||
"https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskSet.html",
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-16"
|
||||
],
|
||||
"ResourceType": "AwsEcsTaskSet",
|
||||
"Description": "This control checks whether an Amazon ECS task set is configured to automatically assign public IP addresses. The control fails if AssignPublicIP is set to ENABLED.",
|
||||
"Risk": "A public IP address is reachable from the internet, potentially exposing resources associated with the ECS task set. ECS task sets shouldn't be publicly accessible, as this may allow unintended access to container application servers.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskSet.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "```yaml\n# CloudFormation to ensure ECS Task Set does not auto-assign public IP\nResources:\n <example_resource_name>:\n Type: AWS::ECS::TaskSet\n Properties:\n Cluster: \"<example_resource_id>\"\n Service: \"<example_resource_id>\"\n TaskDefinition: \"<example_resource_id>\"\n NetworkConfiguration:\n AwsvpcConfiguration:\n AssignPublicIp: DISABLED # CRITICAL: disables automatic public IP assignment\n Subnets:\n - \"<example_resource_id>\"\n```",
|
||||
"Other": "1. In the AWS Console, go to ECS > Clusters > select your cluster\n2. Open your Service and choose Update (or Edit)\n3. In Networking, set Public IP assignment to Disabled\n4. Save/Deploy the update to create a new deployment/task set\n5. After the new task set is Primary and stable, delete the old task set that had Public IP enabled",
|
||||
"Terraform": "```hcl\n# ECS Task Set with public IP assignment disabled\nresource \"aws_ecs_task_set\" \"<example_resource_name>\" {\n cluster = \"<example_resource_id>\"\n service = \"<example_resource_id>\"\n task_definition = \"<example_resource_id>\"\n\n network_configuration {\n subnets = [\"<example_resource_id>\"]\n assign_public_ip = false # CRITICAL: disables automatic public IP assignment\n }\n}\n```"
|
||||
"CLI": "aws ecs update-service --cluster <cluster-name> --service <service-name> --network-configuration 'awsvpcConfiguration={assignPublicIp=\"DISABLED\"}'",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-16",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable **automatic public IPs** on task sets.\n\nUse private subnets behind controlled entry points (load balancers, API gateways, or service discovery). Enforce **least privilege** security groups and **defense in depth**. Prefer private connectivity (VPC endpoints/VPN). *Expose only frontends, not tasks.*",
|
||||
"Url": "https://hub.prowler.com/check/ecs_task_set_no_assign_public_ip"
|
||||
"Text": "Configure ECS task sets to not assign public IP addresses to prevent unintended public access to your containerized applications.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/update-task-definition-console-v2.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,37 +1,29 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_cross_zone_load_balancing_enabled",
|
||||
"CheckTitle": "ELBv2 Network or Gateway Load Balancer has cross-zone load balancing enabled",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"CheckTitle": "Ensure Cross-Zone Load Balancing is enabled for Network (NLBs) and Gateway (GWLB) Load Balancers",
|
||||
"CheckType": [],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**Network and Gateway Load Balancers** have **cross-zone load balancing** enabled (`load_balancing.cross_zone.enabled`), so each node distributes requests to targets in all enabled Availability Zones rather than only its own.",
|
||||
"Risk": "Without cross-zone distribution, traffic can concentrate in one zone, degrading **availability** through target saturation, uneven failover, and connection drops. Zonal impairment can cause partial outages and increase **latency** under load.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/enable-cross-zone-load-balancing.html#",
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#cross-zone-load-balancing"
|
||||
],
|
||||
"Description": "Checks whether cross-zone load balancing is enabled for Network Load Balancers (NLBs) and Gateway Load Balancers (GWLB). Cross-zone load balancing ensures even distribution of traffic across all registered targets in all Availability Zones, improving fault tolerance and load distribution.",
|
||||
"Risk": "If cross-zone load balancing is not enabled, traffic may not be evenly distributed across Availability Zones, leading to over-utilization of resources in certain zones and potential application performance degradation or outages.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#cross-zone-load-balancing",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <load-balancer-arn> --attributes Key=load_balancing.cross_zone.enabled,Value=true",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::LoadBalancer\n Properties:\n Type: network\n Subnets:\n - <subnet-id-1>\n - <subnet-id-2>\n LoadBalancerAttributes:\n - Key: load_balancing.cross_zone.enabled # Critical: enable cross-zone load balancing\n Value: true # Ensures the check passes for NLB/GLB\n```",
|
||||
"Other": "1. Open the AWS EC2 console and go to Load Balancers\n2. Select your Network or Gateway Load Balancer\n3. Choose the Attributes tab > Edit attributes\n4. Turn on Cross-zone load balancing\n5. Save changes",
|
||||
"Terraform": "```hcl\nresource \"aws_lb\" \"<example_resource_name>\" {\n load_balancer_type = \"network\"\n subnets = [\"<subnet-id-1>\", \"<subnet-id-2>\"]\n enable_cross_zone_load_balancing = true # Critical: enable cross-zone load balancing\n}\n```"
|
||||
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-name <load-balancer-name> --attributes Key=load_balancing.cross_zone.enabled,Value=true",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/enable-cross-zone-load-balancing.html#",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **cross-zone load balancing** to spread load across zones and design for AZ redundancy.\n\n- Balance capacity per AZ and use health-based routing\n- Avoid single-AZ dependencies and sticky designs\n- Monitor zonal health to sustain **fault tolerance**",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_cross_zone_load_balancing_enabled"
|
||||
"Text": "Enable cross-zone load balancing for Network and Gateway Load Balancers to ensure even traffic distribution and enhance fault tolerance across Availability Zones.",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#cross-zone-load-balancing"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"resilience"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,39 +1,31 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_deletion_protection",
|
||||
"CheckTitle": "ELBv2 load balancer has deletion protection enabled",
|
||||
"CheckTitle": "Check if Elastic Load Balancers have deletion protection enabled.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Effects/Denial of Service"
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**ELBv2 load balancers** with **deletion protection** (`deletion_protection.enabled`) are resistant to deletion through standard APIs.\n\nThe assessment determines whether this attribute is enabled on each load balancer.",
|
||||
"Risk": "Without **deletion protection**, a user or automated process can delete the load balancer, cutting off service endpoints and breaking routing, harming **availability**.\n\nMalicious or mistaken deletes enable **DoS**, disrupt blue/green rollbacks, and increase incident recovery time.",
|
||||
"Description": "Check if Elastic Load Balancers have deletion protection enabled.",
|
||||
"Risk": "If deletion protection is not enabled, the resource is not protected against deletion.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#deletion-protection",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/deletion-protection.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb_arn> --attributes Key=deletion_protection.enabled,Value=true",
|
||||
"NativeIaC": "```yaml\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::LoadBalancer\n Properties:\n Subnets:\n - <example_subnet_id_1>\n - <example_subnet_id_2>\n LoadBalancerAttributes:\n - Key: deletion_protection.enabled # Critical: enable deletion protection\n Value: \"true\" # Ensures the LB cannot be deleted accidentally\n```",
|
||||
"Other": "1. In the AWS Console, go to EC2 > Load Balancers (under Load Balancing)\n2. Select the target load balancer\n3. Open the Attributes tab and click Edit attributes\n4. Enable Deletion protection\n5. Click Save changes",
|
||||
"Terraform": "```hcl\nresource \"aws_lb\" \"<example_resource_name>\" {\n subnets = [\"<example_subnet_id_1>\", \"<example_subnet_id_2>\"]\n\n enable_deletion_protection = true # Critical: enables deletion protection to pass the check\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/deletion-protection.html",
|
||||
"Terraform": "https://docs.prowler.com/checks/aws/general-policies/bc_aws_networking_62#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **deletion protection** for production and other critical load balancers.\n\nEnforce **least privilege** to restrict delete actions, apply governance (tags and policy guardrails) for protected assets, and require **change control** with approvals. *For pipelines*, add checks that block deletion of protected resources.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_deletion_protection"
|
||||
"Text": "Enable deletion protection attribute, this is not enabled by default.",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#deletion-protection"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"resilience"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,37 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_desync_mitigation_mode",
|
||||
"CheckTitle": "Application Load Balancer has desync mitigation mode set to strictest or defensive, or drops invalid header fields",
|
||||
"CheckTitle": "Check whether the Application Load Balancer is configured with strictest desync mitigation mode, if not check if at least is configured with the drop_invalid_header_fields attribute",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Reachability",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"TTPs/Initial Access",
|
||||
"Effects/Data Exposure"
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**Application Load Balancer** settings are reviewed for **HTTP desync protections**. It evaluates `routing.http.desync_mitigation_mode` for `strictest` or `defensive`; when neither is configured, it checks `routing.http.drop_invalid_header_fields.enabled` is `true` as a compensating control.",
|
||||
"Risk": "Lacking robust desync mitigation enables inconsistent HTTP parsing and **request smuggling**:\n- **Confidentiality**: token theft, data exfiltration\n- **Integrity**: cache/queue poisoning, unauthorized actions\n- **Availability**: backend exhaustion and outages\n\nOnly dropping invalid headers reduces but does not eliminate this exposure.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/elb-controls.html#elb-12",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/drop-invalid-header-fields-enabled.html",
|
||||
"https://support.icompaas.com/support/solutions/articles/62000233515-ensure-the-application-load-balancer-is-configured-with-strictest-desync-mitigation-mode",
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#desync-mitigation-mode"
|
||||
],
|
||||
"Description": "Check whether the Application Load Balancer is configured with strictest desync mitigation mode, if not check if at least is configured with the drop_invalid_header_fields attribute",
|
||||
"Risk": "HTTP Desync issues can lead to request smuggling and make your applications vulnerable to request queue or cache poisoning, which could lead to credential hijacking or execution of unauthorized commands.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#desync-mitigation-mode",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <ALB_ARN> --attributes Key=routing.http.desync_mitigation_mode,Value=strictest",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: Set ALB desync mitigation mode\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::LoadBalancer\n Properties:\n Type: application\n Subnets:\n - <example_subnet_id1>\n - <example_subnet_id2>\n LoadBalancerAttributes:\n - Key: routing.http.desync_mitigation_mode # Critical: enforce strictest/defensive desync mitigation to pass the check\n Value: strictest\n```",
|
||||
"Other": "1. Open the AWS Console and go to EC2 > Load Balancers\n2. Select your Application Load Balancer\n3. Choose Actions > Edit attributes (or the Attributes tab > Edit)\n4. Set Desync mitigation mode to Strictest (or Defensive)\n5. Save changes",
|
||||
"Terraform": "```hcl\n# Terraform: Set ALB desync mitigation mode\nresource \"aws_lb\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n subnets = [\"<example_subnet_id1>\", \"<example_subnet_id2>\"]\n\n desync_mitigation_mode = \"strictest\" # Critical: enforce strictest/defensive desync mitigation to pass the check\n}\n```"
|
||||
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <alb arn> --attributes Key=routing.http.desync_mitigation_mode,Value=<defensive/strictest>",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/elb-controls.html#elb-12",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Set ALBs to `desync_mitigation_mode`=`strictest` (*or* `defensive` if compatibility is required) and keep `routing.http.drop_invalid_header_fields.enabled`=`true`.\n\nApply **defense in depth**: validate RFC-compliant requests, roll out changes gradually with monitoring, and enforce **least privilege** on downstream services.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_desync_mitigation_mode"
|
||||
"Text": "Ensure Application Load Balancer is configured with strictest desync mitigation mode or with the drop_invalid_header_fields attribute enabled",
|
||||
"Url": "https://aws.amazon.com/about-aws/whats-new/2020/08/application-and-classic-load-balancers-adding-defense-in-depth-with-introduction-of-desync-mitigation-mode/"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
|
||||
@@ -1,35 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_insecure_ssl_ciphers",
|
||||
"CheckTitle": "ELBv2 load balancer uses a secure SSL policy on HTTPS listeners",
|
||||
"CheckTitle": "Check if Elastic Load Balancers have insecure SSL ciphers.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/PCI-DSS"
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**ELBv2 HTTPS listeners** are assessed for use of **strong TLS policies**. Listeners whose `ssl_policy` is not in the approved set (TLS 1.2/1.3-focused policies) may include weak protocols or ciphers.",
|
||||
"Risk": "Legacy or weak ciphers enable **downgrade** and **man-in-the-middle** attacks, allowing decryption of sessions, credential theft, and request tampering. This undermines **confidentiality** and **integrity** of data in transit and can expose cookies or tokens for **account takeover**.",
|
||||
"Description": "Check if Elastic Load Balancers have insecure SSL ciphers.",
|
||||
"Risk": "Using insecure ciphers could affect privacy of in transit information.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/security-policy.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 modify-listener --listener-arn <listener_arn> --ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: Set a secure SSL policy on an HTTPS listener\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::Listener\n Properties:\n LoadBalancerArn: <example_resource_arn>\n Protocol: HTTPS\n Port: 443\n DefaultActions:\n - Type: forward\n TargetGroupArn: <example_resource_arn>\n Certificates:\n - CertificateArn: <example_certificate_arn>\n SslPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06 # FIX: uses an approved secure policy to eliminate insecure ciphers\n```",
|
||||
"Other": "1. In the AWS Console, go to EC2 > Load Balancers\n2. Select the load balancer and open the Listeners tab\n3. Select the HTTPS listener and choose Edit\n4. Set Security policy to ELBSecurityPolicy-TLS13-1-2-2021-06 (or any approved policy)\n5. Save changes",
|
||||
"Terraform": "```hcl\n# Terraform: Ensure HTTPS listener uses a secure SSL policy\nresource \"aws_lb_listener\" \"<example_resource_name>\" {\n load_balancer_arn = \"<example_resource_arn>\"\n port = 443\n protocol = \"HTTPS\"\n ssl_policy = \"ELBSecurityPolicy-TLS13-1-2-2021-06\" # FIX: approved secure policy\n certificate_arn = \"<example_certificate_arn>\"\n\n default_action {\n type = \"forward\"\n target_group_arn = \"<example_resource_arn>\"\n }\n}\n```"
|
||||
"CLI": "aws elbv2 modify-listener --listener-arn <lb_arn> --ssl-policy ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/security-policy.html",
|
||||
"Terraform": "https://docs.prowler.com/checks/aws/general-policies/bc_aws_general_43#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce **modern TLS** on load balancer listeners:\n- Use AWS recommended policies like `ELBSecurityPolicy-TLS13-1-2-2021-06`\n- Disable TLS 1.0/1.1 and weak ciphers; prefer suites with **forward secrecy**\n- Periodically review and update policies\n\nApply **defense in depth** with strict client access and **least privilege** for changes.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_insecure_ssl_ciphers"
|
||||
"Text": "Use a Security policy with ciphers that are as strong as possible. Drop legacy and insecure ciphers.",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,35 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_internet_facing",
|
||||
"CheckTitle": "Application Load Balancer is not publicly accessible (no inbound TCP from 0.0.0.0/0 or ::/0)",
|
||||
"CheckTitle": "Check for internet facing Elastic Load Balancers.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Reachability",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark",
|
||||
"TTPs/Initial Access"
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**ELBv2 Application Load Balancers** configured as `internet-facing` are assessed for exposure by reviewing attached **security groups**.\n\nInbound TCP rules that allow `0.0.0.0/0` or `::/0` indicate unrestricted internet reachability.",
|
||||
"Risk": "**Unrestricted ALB access** lets any client reach exposed endpoints, enabling **credential stuffing**, automated scanning, and **web exploits**.\n\nImpacts:\n- Confidentiality: data exfiltration\n- Integrity: unauthorized changes\n- Availability: increased attack surface and **DoS** potential",
|
||||
"Description": "Check for internet facing Elastic Load Balancers.",
|
||||
"Risk": "Publicly accessible load balancers could expose sensitive data to bad actors.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/internet-facing-load-balancers.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "```yaml\n# CloudFormation Security Group for ALB with no public (0.0.0.0/0 or ::/0) TCP ingress\nResources:\n <example_resource_name>:\n Type: AWS::EC2::SecurityGroup\n Properties:\n GroupDescription: ALB SG restricted ingress\n VpcId: \"<example_resource_id>\"\n SecurityGroupIngress:\n - IpProtocol: tcp\n FromPort: 80\n ToPort: 80\n CidrIp: 10.0.0.0/8 # Critical: restricts inbound to private CIDR, preventing public access\n```",
|
||||
"Other": "1. In AWS Console, go to EC2 > Load Balancers and select the ALB\n2. In the Description tab, note the attached Security Group and open it\n3. Click Edit inbound rules\n4. Delete any TCP rule with Source 0.0.0.0/0 or ::/0\n5. If access is needed, add only specific private CIDRs or trusted security groups\n6. Click Save rules",
|
||||
"Terraform": "```hcl\n# Security Group for ALB with no public (0.0.0.0/0 or ::/0) TCP ingress\nresource \"aws_security_group\" \"<example_resource_name>\" {\n name = \"alb-restricted-sg\"\n vpc_id = \"<example_resource_id>\"\n\n ingress {\n from_port = 80\n to_port = 80\n protocol = \"tcp\"\n cidr_blocks = [\"10.0.0.0/8\"] # Critical: restricts inbound to private CIDR, preventing public access\n }\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/internet-facing-load-balancers.html",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce **least privilege** on security groups: avoid `0.0.0.0/0`; allow only trusted CIDRs or upstream services.\n\nUse an `internal` load balancer for non-public apps.\n\nFor public endpoints, layer **WAF** rules, strict TLS, and rate limiting; consider **CloudFront/Shield** for defense in depth and reduced direct exposure.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_internet_facing"
|
||||
"Text": "Ensure the load balancer should be publicly accessible. If publicly exposed ensure a WAF ACL is implemented.",
|
||||
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,39 +1,30 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_is_in_multiple_az",
|
||||
"CheckTitle": "ELBv2 load balancer is configured across multiple Availability Zones",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Effects/Denial of Service"
|
||||
],
|
||||
"CheckTitle": "Elastic Load Balancer V2 (ELBv2) is Configured Across Multiple Availability Zones (AZs)",
|
||||
"CheckType": [],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "ELBv2 load balancers (Application, Network, or Gateway) are assessed for distribution across multiple **Availability Zones**. The finding indicates whether each load balancer spans at least the configured minimum number of AZs (default `2`).",
|
||||
"Risk": "Limiting a load balancer to one AZ introduces a single point of failure. An AZ outage, zonal degradation, or imbalanced target capacity can cause downtime, dropped connections, and deployment risk, undermining service **availability** and resiliency.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/network/availability-zones.html",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/enable-multi-az.html",
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html#availability-zones"
|
||||
],
|
||||
"Description": "Ensure whether Elastic Load Balancer V2 (Application, Network, or Gateway Load Balancer) is configured to operate across multiple Availability Zones (AZs). Ensuring that your load balancer is spread across at least two AZs helps maintain high availability and fault tolerance in case of an AZ failure.",
|
||||
"Risk": "If an ELBv2 is not configured across multiple AZs, there is a risk that an Availability Zone failure could lead to downtime for your application. This could result in a single point of failure, impacting the availability and reliability of your services.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html#availability-zones",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 set-subnets --load-balancer-arn <LOAD_BALANCER_ARN> --subnets <SUBNET_ID_A> <SUBNET_ID_B>",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: ensure the ELBv2 spans at least two AZs by specifying two subnets\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::LoadBalancer\n Properties:\n Subnets:\n - <subnet_id_a> # critical: add a second AZ/subnet\n - <subnet_id_b> # critical: ensures the load balancer spans >=2 AZs\n```",
|
||||
"Other": "1. Open AWS Console > EC2 > Load Balancers\n2. Select the load balancer\n3. Go to the Network mapping tab and click Edit subnets\n4. Enable at least two Availability Zones by selecting one subnet in each of two AZs\n5. Click Save changes",
|
||||
"Terraform": "```hcl\n# Ensure ELBv2 spans at least two Availability Zones\nresource \"aws_lb\" \"<example_resource_name>\" {\n subnets = [\n \"<subnet_id_a>\", # critical: add a second AZ/subnet\n \"<subnet_id_b>\" # critical: ensures the load balancer spans >=2 AZs\n ]\n}\n```"
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/elb-controls.html#elb-13",
|
||||
"Terraform": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/enable-multi-az.html"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Operate each load balancer across at least **two AZs** and ensure every enabled AZ has healthy, scaled targets.\n- Distribute capacity per AZ; use autoscaling\n- Keep health checks effective\n- Consider cross-zone load balancing to absorb bursts\n- Regularly test failover",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_is_in_multiple_az"
|
||||
"Text": "It is recommended to configure your ELBv2 to operate across at least two Availability Zones to enhance fault tolerance and availability.",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-subnets.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"resilience"
|
||||
"redundancy"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,37 +1,31 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_listeners_underneath",
|
||||
"CheckTitle": "ELBv2 load balancer has at least one listener",
|
||||
"CheckTitle": "Check if ELBV2 has listeners underneath.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Reachability",
|
||||
"Effects/Denial of Service"
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**ELBv2 load balancer** requires at least one **listener** (protocol and port) to accept client connections and route requests to target groups. The finding indicates whether listeners are defined on the load balancer.",
|
||||
"Risk": "Without a listener, the load balancer cannot accept connections, making back-end services unreachable. This harms **availability**, leads to client timeouts and errors, and disrupts integrations that rely on the load balancer's DNS endpoint.",
|
||||
"Description": "Check if ELBV2 has listeners underneath.",
|
||||
"Risk": "The rules that are defined for a listener determine how the load balancer routes requests to its registered targets.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 create-listener --load-balancer-arn <LOAD_BALANCER_ARN> --protocol HTTP --port 80 --default-actions 'Type=fixed-response,FixedResponseConfig={StatusCode=200}'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: add a minimal listener to the ELBv2\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::Listener\n Properties:\n LoadBalancerArn: <example_load_balancer_arn> # Critical: attaches the listener to the load balancer\n Port: 80 # Critical: defines the listener port\n Protocol: HTTP # Critical: defines the listener protocol\n DefaultActions:\n - Type: fixed-response # Critical: minimal required default action so the listener is valid\n FixedResponseConfig:\n StatusCode: '200' # Critical: required for fixed-response action\n```",
|
||||
"Other": "1. In the AWS Console, go to EC2 > Load Balancing > Load Balancers\n2. Select the load balancer with the finding\n3. Open the Listeners tab and click Add listener\n4. Set Protocol to HTTP and Port to 80\n5. For Default action, choose Return fixed response and set Status code to 200\n6. Click Create/Save to add the listener",
|
||||
"Terraform": "```hcl\n# Terraform: add a minimal listener to the ELBv2\nresource \"aws_lb_listener\" \"<example_resource_name>\" {\n load_balancer_arn = \"<example_load_balancer_arn>\" # Critical: attaches the listener to the load balancer\n port = 80 # Critical: defines the listener port\n protocol = \"HTTP\" # Critical: defines the listener protocol\n\n default_action { # Critical: required default action so the listener is valid\n type = \"fixed-response\"\n fixed_response {\n status_code = \"200\" # Critical: required for fixed-response action\n }\n }\n}\n```"
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Define at least one listener per load balancer. Prefer **HTTPS** on `443` to protect data in transit, and expose only required ports. Apply **least privilege** by limiting protocols and rules to intended traffic, and set an explicit default action to avoid unintended routing.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_listeners_underneath"
|
||||
"Text": "Add listeners to Elastic Load Balancers V2.",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"resilience"
|
||||
],
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_logging_enabled",
|
||||
"CheckTitle": "ELBv2 Application Load Balancer has access logs to S3 configured",
|
||||
"CheckTitle": "Check if Elastic Load Balancers have logging enabled.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
"Logging and Monitoring"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**ELBv2 Application Load Balancers** are evaluated for **access logging** enabled to Amazon S3, capturing request details such as timestamps, client IPs, paths, and response codes.",
|
||||
"Risk": "Absent **ALB access logs** reduces **visibility** and hampers **incident detection** and **forensics**. Malicious requests, credential stuffing, or data exfiltration via the load balancer can go unnoticed, undermining **confidentiality** and **integrity**, and delaying recovery from **availability** incidents.",
|
||||
"Description": "Check if Elastic Load Balancers have logging enabled.",
|
||||
"Risk": "If logs are not enabled monitoring of service use and threat analysis is not possible.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/access-log.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb_arn> --attributes Key=access_logs.s3.enabled,Value=true Key=access_logs.s3.bucket,Value=<bucket_name>",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: enable ALB access logs to S3\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::LoadBalancer\n Properties:\n Subnets:\n - <subnet_id_1>\n - <subnet_id_2>\n SecurityGroups:\n - <example_security_group_id>\n LoadBalancerAttributes:\n - Key: access_logs.s3.enabled # critical: enable ALB access logging\n Value: \"true\"\n - Key: access_logs.s3.bucket # critical: destination S3 bucket for logs\n Value: \"<example_resource_name>\"\n```",
|
||||
"Other": "1. In AWS Console, go to EC2 > Load Balancers and select your Application Load Balancer\n2. Open the Attributes (or Edit attributes) section and find Access logs\n3. Check Enable access logs and choose the S3 bucket for delivery\n4. Save changes",
|
||||
"Terraform": "```hcl\n# Terraform: enable ALB access logs to S3\nresource \"aws_lb\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n security_groups = [\"<example_security_group_id>\"]\n subnets = [\"<subnet_id_1>\", \"<subnet_id_2>\"]\n\n access_logs {\n bucket = \"<example_resource_name>\" # critical: destination S3 bucket for logs\n enabled = true # critical: enable ALB access logging\n }\n}\n```"
|
||||
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb_arn> --attributes Key=access_logs.s3.enabled,Value=true Key=access_logs.s3.bucket,Value=<bucket_name> Key=access_logs.s3.prefix,Value=<prefix>",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/aws/logging-policies/bc_aws_logging_22#cloudformation",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/access-log.html",
|
||||
"Terraform": "https://docs.prowler.com/checks/aws/logging-policies/bc_aws_logging_22#terraform"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **ALB access logging** to a dedicated, encrypted S3 bucket. Apply **least privilege** to the bucket for delivery and readers, set lifecycle policies for retention, and consider `Object Lock` to deter tampering. Centralize logs in a **SIEM** and alert on anomalies as part of **defense in depth**.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_logging_enabled"
|
||||
"Text": "Enable ELB logging, create a log lifecycle and define use cases.",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"forensics-ready",
|
||||
"logging"
|
||||
],
|
||||
"DependsOn": [],
|
||||
|
||||
@@ -1,35 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_nlb_tls_termination_enabled",
|
||||
"CheckTitle": "ELBv2 Network Load Balancer has TLS termination enabled",
|
||||
"CheckTitle": "Check if Network Load Balancers (NLB) has TLS termination enabled.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/PCI-DSS",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53 Controls (USA)"
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**Network Load Balancers** with listeners using the `TLS` protocol indicate **TLS termination** at the load balancer. The evaluation identifies NLBs that have at least one `TLS` listener versus those using plain `TCP`/`UDP` or deferring encryption to targets.",
|
||||
"Risk": "Lack of NLB-level TLS termination can leave transit data unencrypted or managed inconsistently on instances, undermining **confidentiality** and **integrity**. It also shifts handshake CPU cost to targets, reducing **availability** and making them more susceptible to connection floods and downgrade or weak-cipher exposures.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/network/listener-update-rules.html",
|
||||
"https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/network-load-balancer-listener-security.html#"
|
||||
],
|
||||
"Description": "Check if Network Load Balancers (NLB) has TLS listeners.",
|
||||
"Risk": "Ensure that your Amazon Network Load Balancers (NLBs) are configured to terminate TLS connections in order to optimize the performance of the backend servers while encrypting the communication between the load balancer and the associated targets (i.e. server instances).",
|
||||
"RelatedUrl": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/network-load-balancer-listener-security.html#",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 create-listener --load-balancer-arn <nlb_arn> --protocol TLS --port 443 --ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 --certificates CertificateArn=<certificate_arn> --default-actions Type=forward,TargetGroupArn=<target_group_arn>",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: Add a TLS listener to enable TLS termination on the NLB\nResources:\n \"<example_resource_name>\":\n Type: AWS::ElasticLoadBalancingV2::Listener\n Properties:\n LoadBalancerArn: \"<example_resource_arn>\"\n Protocol: TLS # critical: enables TLS termination on the NLB\n Port: 443\n SslPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06 # critical: required when Protocol is TLS\n Certificates:\n - CertificateArn: \"<example_resource_arn>\" # critical: server certificate for TLS termination\n DefaultActions:\n - Type: forward\n TargetGroupArn: \"<example_resource_arn>\"\n```",
|
||||
"Other": "1. In the AWS Console, go to EC2 > Load Balancers and select your Network Load Balancer\n2. Open the Listeners tab and click Add listener\n3. Set Protocol to TLS and Port to 443\n4. Select an ACM certificate and a security policy\n5. Set Default action to Forward to your target group\n6. Click Save changes",
|
||||
"Terraform": "```hcl\n# Terraform: Add a TLS listener to enable TLS termination on the NLB\nresource \"aws_lb_listener\" \"<example_resource_name>\" {\n load_balancer_arn = \"<example_resource_arn>\"\n port = 443\n protocol = \"TLS\" # critical: enables TLS termination\n ssl_policy = \"ELBSecurityPolicy-TLS13-1-2-2021-06\" # critical: required for TLS\n certificate_arn = \"<example_resource_arn>\" # critical: server certificate for TLS termination\n\n default_action {\n type = \"forward\"\n target_group_arn = \"<example_resource_arn>\"\n }\n}\n```"
|
||||
"NativeIaC": "",
|
||||
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/network-load-balancer-listener-security.html#",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable **TLS listeners** to terminate client encryption at the NLB and enforce centralized, modern cipher policies and certificate rotation. Apply **defense in depth** by re-encrypting to targets when needed, limit backend access to the NLB, and automate certificate lifecycle with secure storage and monitoring for deprecated protocols.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_nlb_tls_termination_enabled"
|
||||
"Text": "When Transport Layer Security (TLS) termination is enabled, you can offload the encryption and decryption of the TLS traffic from your backend application servers to your Amazon Network Load Balancer, enhancing the performance of your backend servers while keeping the workload secure. ",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/network/listener-update-rules.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_ssl_listeners",
|
||||
"CheckTitle": "ELBv2 Application Load Balancer listeners use HTTPS or redirect HTTP to HTTPS",
|
||||
"CheckTitle": "Check if Elastic Load Balancers have SSL listeners.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"Effects/Data Exposure"
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "**Application Load Balancer listeners** are assessed for **encrypted ingress**: either only `HTTPS` listeners are present, or any `HTTP` listener redirects to `HTTPS`.",
|
||||
"Risk": "Exposed `HTTP` paths allow traffic to travel in plaintext, enabling interception, credential theft, session hijacking, and response tampering. This weakens confidentiality and integrity and makes **MITM** on public or shared networks feasible.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/securityhub/latest/userguide/elb-controls.html#elb-1",
|
||||
"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html"
|
||||
],
|
||||
"Description": "Check if Elastic Load Balancers have SSL listeners.",
|
||||
"Risk": "Clear text communication could affect privacy of information in transit.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/securityhub/latest/userguide/elb-controls.html#elb-1",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws elbv2 modify-listener --listener-arn <listener_arn> --default-actions '[{\"Type\":\"redirect\",\"RedirectConfig\":{\"Protocol\":\"HTTPS\",\"Port\":\"443\",\"StatusCode\":\"HTTP_301\"}}]'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: Redirect HTTP listener to HTTPS\nResources:\n <example_resource_name>:\n Type: AWS::ElasticLoadBalancingV2::Listener\n Properties:\n LoadBalancerArn: <example_resource_id>\n Protocol: HTTP\n Port: 80\n DefaultActions:\n - Type: redirect\n RedirectConfig:\n Protocol: HTTPS # Critical: redirect HTTP to HTTPS\n Port: '443' # Critical: target HTTPS port\n StatusCode: HTTP_301 # Critical: enforce redirect\n```",
|
||||
"Other": "1. Open the EC2 console and go to Load Balancers\n2. Select the Application Load Balancer and open the Listeners tab\n3. Select the HTTP:80 listener and choose Edit (or View/edit rules)\n4. Set the default action to Redirect to, Protocol: HTTPS, Port: 443, Status code: HTTP_301\n5. Save changes",
|
||||
"Terraform": "```hcl\n# Terraform: Redirect HTTP listener to HTTPS\nresource \"aws_lb_listener\" \"<example_resource_name>\" {\n load_balancer_arn = \"<example_resource_id>\"\n protocol = \"HTTP\"\n port = 80\n\n default_action {\n type = \"redirect\"\n redirect {\n protocol = \"HTTPS\" # Critical: redirect to HTTPS\n port = \"443\" # Critical: target HTTPS port\n status_code = \"HTTP_301\" # Critical: enforce redirect\n }\n }\n}\n```"
|
||||
"CLI": "aws elbv2 create-listener --load-balancer-arn <lb_arn> --protocol HTTPS --port 443 --ssl-policy <ssl_policy> --certificates CertificateArn=<certificate_arn>,IsDefault=true",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.prowler.com/checks/aws/networking-policies/networking_36#aws-ec2-console",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce **TLS everywhere**: use `HTTPS` listeners and make all `HTTP` listeners redirect to `HTTPS` only. Do not forward plaintext. Apply **defense in depth** with strong TLS policies and managed certificates, and consider `HSTS` to prevent users from reaching `http`.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_ssl_listeners"
|
||||
"Text": "Scan for Load Balancers with HTTP or TCP listeners and understand the reason for each of them. Check if the listener can be implemented as TLS instead.",
|
||||
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "elbv2_waf_acl_attached",
|
||||
"CheckTitle": "Application Load Balancer has a WAF Web ACL attached",
|
||||
"CheckTitle": "Check if Application Load Balancer has a WAF ACL attached.",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
"TTPs/Initial Access"
|
||||
"Infrastructure Security"
|
||||
],
|
||||
"ServiceName": "elbv2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsElbv2LoadBalancer",
|
||||
"Description": "Application Load Balancers are evaluated for an associated **AWS WAF web ACL** that governs HTTP(S) requests. The evaluation detects ALBs missing a web ACL and recognizes associations from **WAFv2** or regional **WAF Classic**.",
|
||||
"Risk": "Absent a **WAF web ACL**, ALBs accept unfiltered Layer 7 traffic, enabling:\n- **Injection** (SQLi/XSS) harming confidentiality and integrity\n- **Credential stuffing** and **bot abuse**\n- **Resource exhaustion** degrading availability",
|
||||
"Description": "Check if Application Load Balancer has a WAF ACL attached.",
|
||||
"Risk": "If not WAF ACL is attached risk of web attacks increases.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws wafv2 associate-web-acl --web-acl-arn <WEB_ACL_ARN> --resource-arn <ALB_ARN>",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: associate an existing WAFv2 Web ACL to an ALB\nResources:\n <example_resource_name>:\n Type: AWS::WAFv2::WebACLAssociation\n Properties:\n ResourceArn: <example_resource_id> # CRITICAL: ALB ARN to protect\n WebACLArn: <example_resource_id> # CRITICAL: WAFv2 Web ACL ARN to attach\n```",
|
||||
"Other": "1. In the AWS Console, open **WAF & Shield**\n2. Go to **Web ACLs** and select your regional Web ACL\n3. Click **Associated AWS resources** > **Associate resource**\n4. Select the target **Application Load Balancer** and click **Associate**",
|
||||
"Terraform": "```hcl\n# Associate WAFv2 Web ACL with an ALB\nresource \"aws_wafv2_web_acl_association\" \"<example_resource_name>\" {\n resource_arn = \"<example_resource_id>\" # CRITICAL: ALB ARN\n web_acl_arn = \"<example_resource_id>\" # CRITICAL: WAFv2 Web ACL ARN\n}\n```"
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Associate a **WAF web ACL** with each ALB as **defense in depth**. Use managed and custom rules, IP reputation lists, and rate limiting to block attacks. Continuously tune policies and monitor logs. *Apply least privilege* by scoping rules to required paths, methods, and sources.",
|
||||
"Url": "https://hub.prowler.com/check/elbv2_waf_acl_attached"
|
||||
"Text": "Using the AWS Management Console open the AWS WAF console to attach an ACL.",
|
||||
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
|
||||
@@ -36,14 +36,9 @@ class CosmosDB(AzureService):
|
||||
name=private_endpoint_connection.name,
|
||||
type=private_endpoint_connection.type,
|
||||
)
|
||||
for private_endpoint_connection in getattr(
|
||||
account, "private_endpoint_connections", []
|
||||
)
|
||||
if private_endpoint_connection
|
||||
for private_endpoint_connection in account.private_endpoint_connections
|
||||
],
|
||||
disable_local_auth=getattr(
|
||||
account, "disable_local_auth", False
|
||||
),
|
||||
disable_local_auth=account.disable_local_auth,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
|
||||
@@ -112,9 +112,7 @@ class Defender(AzureService):
|
||||
assessment.display_name: Assesment(
|
||||
resource_id=assessment.id,
|
||||
resource_name=assessment.name,
|
||||
status=getattr(
|
||||
getattr(assessment, "status", None), "code", None
|
||||
),
|
||||
status=assessment.status.code,
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -306,7 +304,7 @@ class AutoProvisioningSetting(BaseModel):
|
||||
class Assesment(BaseModel):
|
||||
resource_id: str
|
||||
resource_name: str
|
||||
status: Optional[str] = None
|
||||
status: str
|
||||
|
||||
|
||||
class Setting(BaseModel):
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"Provider": "azure",
|
||||
"CheckID": "postgresql_flexible_server_entra_id_authentication_enabled",
|
||||
"CheckTitle": "PostgreSQL Flexible Server enforces Microsoft Entra ID authentication with administrators",
|
||||
"CheckType": [],
|
||||
"ServiceName": "postgresql",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "PostgreSQL",
|
||||
"Description": "**PostgreSQL Flexible Servers** must set `authConfig.activeDirectoryAuth` to `Enabled` and keep at least one **Microsoft Entra administrator** assigned so database sessions inherit centrally governed identities instead of unmanaged PostgreSQL accounts.",
|
||||
"Risk": "Without Entra ID authentication, stolen local passwords bypass **MFA** and conditional access, enabling persistent database logins. Missing administrators leaves the feature unusable, blocking security teams from rotating duties and allowing unauthorized access or **privilege escalation**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/security-entra-concepts",
|
||||
"https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/security-entra-configure"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "az postgres flexible-server update --resource-group <resourceGroupName> --name <serverName> --active-directory-auth Enabled\naz postgres flexible-server microsoft-entra-admin create --resource-group <resourceGroupName> --server-name <serverName> --object-id <objectId> --display-name <displayName>",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. In the Azure Portal, open Azure Database for PostgreSQL flexible server and select the target server.\n2. Under Security > Authentication, set Microsoft Entra ID authentication (or combined mode) to Enabled and save the change.\n3. Under Security > Microsoft Entra ID, add at least one administrator (user or group) linked to an Entra object ID and confirm the assignment.",
|
||||
"Terraform": "```hcl\ndata \"azurerm_client_config\" \"current\" {}\n\nresource \"azurerm_postgresql_flexible_server\" \"example\" {\n name = \"pg-flex\"\n resource_group_name = azurerm_resource_group.example.name\n location = azurerm_resource_group.example.location\n sku_name = \"GP_Standard_D4s_v3\"\n administrator_login = \"pgadmin\"\n administrator_password = \"<complexPassword>\"\n storage_mb = 131072\n version = \"16\"\n\n authentication {\n active_directory_auth_enabled = true\n tenant_id = data.azurerm_client_config.current.tenant_id\n }\n}\n\nresource \"azurerm_postgresql_flexible_server_active_directory_administrator\" \"entra_admin\" {\n server_id = azurerm_postgresql_flexible_server.example.id\n object_id = var.entra_object_id\n principal_name = var.entra_principal_name\n principal_type = \"User\"\n tenant_id = data.azurerm_client_config.current.tenant_id\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Federate PostgreSQL Flexible Server access through **Microsoft Entra ID** so MFA, conditional access, and centralized RBAC govern logins. Maintain at least one delegated administrator group and rotate membership through identity governance processes.",
|
||||
"Url": "https://hub.prowler.com/check/postgresql_flexible_server_entra_id_authentication_enabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"identity-access"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "This check fails when Microsoft Entra ID authentication is disabled or no administrators are returned by the flexible server microsoft-entra-admin API."
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Azure
|
||||
from prowler.providers.azure.services.postgresql.postgresql_client import (
|
||||
postgresql_client,
|
||||
)
|
||||
|
||||
|
||||
class postgresql_flexible_server_entra_id_authentication_enabled(Check):
|
||||
def execute(self) -> Check_Report_Azure:
|
||||
findings = []
|
||||
for (
|
||||
subscription,
|
||||
flexible_servers,
|
||||
) in postgresql_client.flexible_servers.items():
|
||||
for server in flexible_servers:
|
||||
report = Check_Report_Azure(metadata=self.metadata(), resource=server)
|
||||
report.subscription = subscription
|
||||
# Default to FAIL
|
||||
report.status = "FAIL"
|
||||
|
||||
# Check if Entra ID authentication is enabled
|
||||
# Note: active_directory_auth is already normalized to uppercase in service layer
|
||||
if (
|
||||
not server.active_directory_auth
|
||||
or server.active_directory_auth != "ENABLED"
|
||||
):
|
||||
report.status_extended = f"Flexible Postgresql server {server.name} from subscription {subscription} has Microsoft Entra ID authentication disabled"
|
||||
else:
|
||||
# Authentication is enabled, now check for admins
|
||||
admin_count = (
|
||||
len(server.entra_id_admins) if server.entra_id_admins else 0
|
||||
)
|
||||
|
||||
if admin_count == 0:
|
||||
report.status_extended = f"Flexible Postgresql server {server.name} from subscription {subscription} has Microsoft Entra ID authentication enabled but no Entra ID administrators configured"
|
||||
else:
|
||||
report.status = "PASS"
|
||||
admin_text = (
|
||||
"administrator" if admin_count == 1 else "administrators"
|
||||
)
|
||||
report.status_extended = f"Flexible Postgresql server {server.name} from subscription {subscription} has Microsoft Entra ID authentication enabled with {admin_count} {admin_text} configured"
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -1,6 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from azure.mgmt.postgresqlflexibleservers import PostgreSQLManagementClient
|
||||
from azure.mgmt.rdbms.postgresql_flexibleservers import PostgreSQLManagementClient
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.azure.azure_provider import AzureProvider
|
||||
@@ -21,19 +21,9 @@ class PostgreSQL(AzureService):
|
||||
flexible_servers_list = client.servers.list()
|
||||
for postgresql_server in flexible_servers_list:
|
||||
resource_group = self._get_resource_group(postgresql_server.id)
|
||||
# Fetch full server object once to extract multiple properties
|
||||
server_details = client.servers.get(
|
||||
resource_group, postgresql_server.name
|
||||
)
|
||||
require_secure_transport = self._get_require_secure_transport(
|
||||
subscription, resource_group, postgresql_server.name
|
||||
)
|
||||
active_directory_auth = self._extract_active_directory_auth(
|
||||
server_details
|
||||
)
|
||||
entra_id_admins = self._get_entra_id_admins(
|
||||
subscription, resource_group, postgresql_server.name
|
||||
)
|
||||
log_checkpoints = self._get_log_checkpoints(
|
||||
subscription, resource_group, postgresql_server.name
|
||||
)
|
||||
@@ -52,22 +42,22 @@ class PostgreSQL(AzureService):
|
||||
firewall = self._get_firewall(
|
||||
subscription, resource_group, postgresql_server.name
|
||||
)
|
||||
location = server_details.location
|
||||
location = self._get_location(
|
||||
subscription, resource_group, postgresql_server.name
|
||||
)
|
||||
flexible_servers[subscription].append(
|
||||
Server(
|
||||
id=postgresql_server.id,
|
||||
name=postgresql_server.name,
|
||||
resource_group=resource_group,
|
||||
location=location,
|
||||
require_secure_transport=require_secure_transport,
|
||||
active_directory_auth=active_directory_auth,
|
||||
entra_id_admins=entra_id_admins,
|
||||
log_checkpoints=log_checkpoints,
|
||||
log_connections=log_connections,
|
||||
log_disconnections=log_disconnections,
|
||||
connection_throttling=connection_throttling,
|
||||
log_retention_days=log_retention_days,
|
||||
firewall=firewall,
|
||||
location=location,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
@@ -110,47 +100,10 @@ class PostgreSQL(AzureService):
|
||||
)
|
||||
return log_disconnections.value.upper()
|
||||
|
||||
def _extract_active_directory_auth(self, server):
|
||||
"""Extract active directory auth from a server object (no API call)."""
|
||||
try:
|
||||
auth_config = getattr(server, "auth_config", None)
|
||||
active_directory_auth = (
|
||||
getattr(auth_config, "active_directory_auth", None)
|
||||
if auth_config is not None
|
||||
else None
|
||||
)
|
||||
# Normalize enum/string to upper string
|
||||
if hasattr(active_directory_auth, "value"):
|
||||
return str(active_directory_auth.value).upper()
|
||||
return (
|
||||
str(active_directory_auth).upper()
|
||||
if active_directory_auth is not None
|
||||
else None
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error extracting active directory auth: {e}")
|
||||
return None
|
||||
|
||||
def _get_entra_id_admins(self, subscription, resource_group_name, server_name):
|
||||
def _get_location(self, subscription, resouce_group_name, server_name):
|
||||
client = self.clients[subscription]
|
||||
try:
|
||||
admins = client.administrators.list_by_server(
|
||||
resource_group_name, server_name
|
||||
)
|
||||
admin_list = []
|
||||
for admin in admins:
|
||||
admin_list.append(
|
||||
EntraIdAdmin(
|
||||
object_id=admin.object_id,
|
||||
principal_name=admin.principal_name,
|
||||
principal_type=admin.principal_type,
|
||||
tenant_id=admin.tenant_id,
|
||||
)
|
||||
)
|
||||
return admin_list
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Entra ID admins for {server_name}: {e}")
|
||||
return []
|
||||
location = client.servers.get(resouce_group_name, server_name).location
|
||||
return location
|
||||
|
||||
def _get_connection_throttling(self, subscription, resouce_group_name, server_name):
|
||||
client = self.clients[subscription]
|
||||
@@ -194,26 +147,16 @@ class Firewall:
|
||||
end_ip: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class EntraIdAdmin:
|
||||
object_id: str
|
||||
principal_name: str
|
||||
principal_type: str
|
||||
tenant_id: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Server:
|
||||
id: str
|
||||
name: str
|
||||
resource_group: str
|
||||
location: str
|
||||
require_secure_transport: str
|
||||
active_directory_auth: str
|
||||
entra_id_admins: list[EntraIdAdmin]
|
||||
log_checkpoints: str
|
||||
log_connections: str
|
||||
log_disconnections: str
|
||||
connection_throttling: str
|
||||
log_retention_days: str
|
||||
firewall: list[Firewall]
|
||||
location: str
|
||||
@@ -141,12 +141,10 @@ class Storage(AzureService):
|
||||
container_delete_retention_policy,
|
||||
"enabled",
|
||||
False,
|
||||
)
|
||||
or False,
|
||||
),
|
||||
days=getattr(
|
||||
container_delete_retention_policy, "days", 0
|
||||
)
|
||||
or 0,
|
||||
),
|
||||
),
|
||||
versioning_enabled=versioning_enabled,
|
||||
)
|
||||
@@ -222,14 +220,12 @@ class Storage(AzureService):
|
||||
share_delete_retention_policy,
|
||||
"enabled",
|
||||
False,
|
||||
)
|
||||
or False,
|
||||
),
|
||||
days=getattr(
|
||||
share_delete_retention_policy,
|
||||
"days",
|
||||
0,
|
||||
)
|
||||
or 0,
|
||||
),
|
||||
),
|
||||
smb_protocol_settings=SMBProtocolSettings(
|
||||
channel_encryption=(
|
||||
@@ -245,11 +241,6 @@ class Storage(AzureService):
|
||||
),
|
||||
)
|
||||
except Exception as error:
|
||||
if "File is not supported for the account." in str(error).strip():
|
||||
logger.warning(
|
||||
f"Subscription name: {subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
continue
|
||||
logger.error(
|
||||
f"Subscription name: {subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import List, Optional
|
||||
|
||||
@@ -293,14 +294,16 @@ class VirtualMachines(AzureService):
|
||||
return vm_instance_ids
|
||||
|
||||
|
||||
class UefiSettings(BaseModel):
|
||||
@dataclass
|
||||
class UefiSettings:
|
||||
secure_boot_enabled: bool
|
||||
v_tpm_enabled: bool
|
||||
|
||||
|
||||
class SecurityProfile(BaseModel):
|
||||
security_type: Optional[str] = None
|
||||
uefi_settings: Optional[UefiSettings] = None
|
||||
@dataclass
|
||||
class SecurityProfile:
|
||||
security_type: str
|
||||
uefi_settings: Optional[UefiSettings]
|
||||
|
||||
|
||||
class OperatingSystemType(Enum):
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
from importlib import import_module
|
||||
from typing import Optional, Sequence
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.common.provider import Provider, providers_path
|
||||
@@ -54,19 +53,3 @@ def validate_provider_arguments(arguments: Namespace) -> tuple[bool, str]:
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def validate_asff_usage(
|
||||
provider: Optional[str], output_formats: Optional[Sequence[str]]
|
||||
) -> tuple[bool, str]:
|
||||
"""Ensure json-asff output is only requested for the AWS provider."""
|
||||
if not output_formats or "json-asff" not in output_formats:
|
||||
return (True, "")
|
||||
|
||||
if provider == "aws":
|
||||
return (True, "")
|
||||
|
||||
return (
|
||||
False,
|
||||
f"json-asff output format is only available for the aws provider, but {provider} was selected",
|
||||
)
|
||||
@@ -1,36 +1,31 @@
|
||||
{
|
||||
"Provider": "kubernetes",
|
||||
"CheckID": "etcd_client_cert_auth",
|
||||
"CheckTitle": "Etcd pod has client certificate authentication enabled (--client-cert-auth=true)",
|
||||
"CheckTitle": "Ensure that the --client-cert-auth argument is set to true for etcd",
|
||||
"CheckType": [],
|
||||
"ServiceName": "etcd",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Pod",
|
||||
"Description": "**Etcd** is configured to require **TLS client certificate authentication** when the etcd container includes `--client-cert-auth`, so client access is validated with trusted certificates.",
|
||||
"Risk": "Without **mTLS client auth**, any reachable client can query or mutate etcd:\n- Confidentiality: exposure of Secrets and cluster metadata\n- Integrity: tampering with RBAC, pods, and configs\n- Availability: destructive writes can disrupt the control plane",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://etcd.io/docs/latest/op-guide/security/",
|
||||
"https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/"
|
||||
],
|
||||
"ResourceType": "EtcdService",
|
||||
"Description": "This check ensures that client authentication is enabled for the etcd service, which is a key-value store used by Kubernetes for persistent storage of all REST API objects. Enabling client authentication helps in securing access to etcd.",
|
||||
"Risk": "If --client-cert-auth is not set to true, etcd service may be accessible by unauthenticated clients, posing a significant security risk.",
|
||||
"RelatedUrl": "https://etcd.io/docs/latest/op-guide/security/",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. SSH to the control plane node that runs etcd\n2. Edit the static pod manifest: /etc/kubernetes/manifests/etcd.yaml\n3. Under spec.containers[0].command (or args), add:\n ```\n - --client-cert-auth=true # Critical: enables client certificate authentication\n ```\n4. Save the file; kubelet will restart the etcd pod automatically\n5. Repeat on each control-plane node hosting an etcd pod",
|
||||
"Terraform": "```hcl\n# Enable client certificate authentication on etcd\nresource \"kubernetes_pod\" \"<example_resource_name>\" {\n metadata {\n name = \"<example_resource_name>\"\n namespace = \"kube-system\"\n }\n spec {\n container {\n name = \"etcd\"\n image = \"gcr.io/etcd-development/etcd:v3.5.13\"\n command = [\n \"etcd\",\n \"--client-cert-auth=true\" # Critical: enables client cert auth to pass the check\n ]\n }\n }\n}\n```"
|
||||
"CLI": "--client-cert-auth=true",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/kubernetes/kubernetes-policy-index/ensure-that-the-client-cert-auth-argument-is-set-to-true",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce **mutual TLS** for etcd clients by requiring validated certificates (`--client-cert-auth=true`) issued by a trusted CA.\n\nRestrict network access to etcd to API servers, rotate keys regularly, and apply **least privilege** and **separation of duties** for certificate management.",
|
||||
"Url": "https://hub.prowler.com/check/etcd_client_cert_auth"
|
||||
"Text": "Enable client certificate authentication for the etcd service for improved security.",
|
||||
"Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#limiting-access-of-etcd-clusters"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"cluster-security",
|
||||
"identity-access",
|
||||
"encryption"
|
||||
"encryption",
|
||||
"trustboundaries"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,36 +1,31 @@
|
||||
{
|
||||
"Provider": "kubernetes",
|
||||
"CheckID": "etcd_no_auto_tls",
|
||||
"CheckTitle": "Etcd pod has --auto-tls disabled",
|
||||
"CheckTitle": "Ensure that the --auto-tls argument is not set to true for etcd",
|
||||
"CheckType": [],
|
||||
"ServiceName": "etcd",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Pod",
|
||||
"Description": "**Etcd** configuration is reviewed for the `--auto-tls` option, which enables automatically generated self-signed certificates for client TLS.\n\nPresence of this flag indicates self-signed TLS is used; absence indicates client TLS relies on externally managed certificates.",
|
||||
"Risk": "Using **self-signed auto TLS** weakens identity assurance, enabling spoofed endpoints and **man-in-the-middle** on etcd client traffic. Attackers could read or alter Kubernetes state in etcd, impacting **confidentiality** and **integrity**, and facilitating control-plane takeover or data exfiltration.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/",
|
||||
"https://etcd.io/docs/latest/op-guide/security/",
|
||||
"https://etcd.io/docs/v3.2/op-guide/security/"
|
||||
],
|
||||
"ResourceType": "EtcdService",
|
||||
"Description": "This check ensures that etcd does not use self-signed certificates for TLS, which are less secure than certificates from a trusted authority. Avoiding self-signed certificates enhances the security of etcd.",
|
||||
"Risk": "Using --auto-tls=true may result in the use of self-signed certificates, reducing the overall security of the etcd service.",
|
||||
"RelatedUrl": "https://etcd.io/docs/latest/op-guide/security/",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. SSH to the control plane node running etcd\n2. Edit the static Pod manifest: sudo vi /etc/kubernetes/manifests/etcd.yaml\n3. In containers -> command or args, remove any occurrence of --auto-tls or --auto-tls=true (do not set it to false)\n4. Save and exit; kubelet will recreate the etcd pod automatically\n5. Verify the flag is absent: kubectl -n kube-system get pod -l component=etcd -o yaml | grep -q \"auto-tls\" || echo \"PASS: --auto-tls not set\"",
|
||||
"CLI": "--auto-tls=false",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/kubernetes/kubernetes-policy-index/ensure-that-the-auto-tls-argument-is-not-set-to-true",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable `--auto-tls` and use **CA-signed certificates** with **mutual TLS** for etcd clients. Apply managed PKI to enforce trusted CAs, rotate and revoke keys, and prefer modern TLS versions and strong cipher suites. Monitor certificate expiry and limit access per **least privilege** for **defense in depth**.",
|
||||
"Url": "https://hub.prowler.com/check/etcd_no_auto_tls"
|
||||
"Text": "Ensure etcd is not using self-signed certificates for TLS.",
|
||||
"Url": "https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption",
|
||||
"cluster-security"
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,36 +1,31 @@
|
||||
{
|
||||
"Provider": "kubernetes",
|
||||
"CheckID": "etcd_no_peer_auto_tls",
|
||||
"CheckTitle": "Etcd pod does not use automatically generated self-signed certificates for peer TLS connections",
|
||||
"CheckTitle": "Ensure that the --peer-auto-tls argument is not set to true for etcd",
|
||||
"CheckType": [],
|
||||
"ServiceName": "etcd",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Pod",
|
||||
"Description": "**Etcd peer TLS** configuration is evaluated by checking etcd containers for the `--peer-auto-tls` flag. Presence of `--peer-auto-tls` indicates peers use automatically generated self-signed certificates for inter-peer connections.",
|
||||
"Risk": "With `--peer-auto-tls`, traffic is encrypted but peer identity isn't verified, enabling:\n- MITM on peer links\n- Rogue member joins to read/modify data\n- Quorum disruption\n\nThis degrades **confidentiality**, **integrity**, and **availability** of control-plane state replicated in etcd.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://etcd.io/docs/latest/op-guide/security/",
|
||||
"https://etcd.io/docs/v3.4/op-guide/security/",
|
||||
"https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/"
|
||||
],
|
||||
"ResourceType": "EtcdService",
|
||||
"Description": "This check ensures that etcd is not configured to use automatically generated self-signed certificates for TLS connections between peers. Using self-signed certificates for peer authentication is discouraged in a production environment.",
|
||||
"Risk": "Using self-signed certificates can lead to insecure communications between etcd peers, compromising data security.",
|
||||
"RelatedUrl": "https://etcd.io/docs/latest/op-guide/security/",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. SSH to the control-plane node running etcd\n2. Open /etc/kubernetes/manifests/etcd.yaml\n3. In the etcd container args/command, remove any entry that starts with --peer-auto-tls\n4. Save the file; the kubelet will restart etcd automatically",
|
||||
"CLI": "--peer-auto-tls=false",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/kubernetes/kubernetes-policy-index/ensure-that-the-peer-auto-tls-argument-is-not-set-to-true",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Disable `--peer-auto-tls` and use **mTLS** with a trusted CA issuing unique per-member peer certificates. Enforce SAN validation and, *where supported*, peer certificate authentication. Apply **least privilege**, separate CAs for peers/clients, rotate keys, and monitor certificate expiry and peer membership.",
|
||||
"Url": "https://hub.prowler.com/check/etcd_no_peer_auto_tls"
|
||||
"Text": "Ensure etcd is not using automatically generated self-signed certificates for peer TLS connections.",
|
||||
"Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption",
|
||||
"cluster-security"
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,36 +1,31 @@
|
||||
{
|
||||
"Provider": "kubernetes",
|
||||
"CheckID": "etcd_peer_client_cert_auth",
|
||||
"CheckTitle": "Etcd pod has peer client certificate authentication enabled",
|
||||
"CheckTitle": "Ensure that the --peer-client-cert-auth argument is set to true for etcd",
|
||||
"CheckType": [],
|
||||
"ServiceName": "etcd",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Pod",
|
||||
"Description": "**Etcd** requires **peer client certificate authentication** for inter-member traffic via `--peer-client-cert-auth=true` set in the etcd container command",
|
||||
"Risk": "Without peer authentication, a rogue host can impersonate a member, eavesdrop on or alter Raft traffic, inject state, and disrupt elections-compromising **confidentiality** (state leakage), **integrity** (malicious writes), and **availability** (cluster instability/outage).",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://etcd.io/docs/latest/op-guide/security/",
|
||||
"https://etcd.io/docs/v3.6/op-guide/configuration/",
|
||||
"https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#limiting-access-of-etcd-clusters"
|
||||
],
|
||||
"ResourceType": "EtcdService",
|
||||
"Description": "This check ensures that etcd is configured for peer authentication by verifying that the --peer-client-cert-auth argument is set to true. This configuration is crucial to ensure that etcd peers in the cluster are authenticated and secure.",
|
||||
"Risk": "Failing to configure peer client authentication can lead to unauthorized access to the etcd cluster, compromising sensitive data.",
|
||||
"RelatedUrl": "https://etcd.io/docs/latest/op-guide/security/",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. SSH to the control-plane node\n2. Edit the etcd static Pod manifest: /etc/kubernetes/manifests/etcd.yaml\n3. In spec.containers[0].command, add this entry:\n - --peer-client-cert-auth=true\n (Critical: enables peer client certificate authentication)\n4. Save the file; the kubelet will automatically restart the etcd Pod\n5. Verify the Pod's container command includes --peer-client-cert-auth=true",
|
||||
"Terraform": "```hcl\nresource \"kubernetes_pod\" \"<example_resource_name>\" {\n metadata {\n name = \"<example_resource_name>\"\n namespace = \"kube-system\"\n }\n spec {\n container {\n name = \"etcd\"\n image = \"registry.k8s.io/etcd:3.5.12-0\"\n command = [\n \"etcd\",\n \"--peer-client-cert-auth=true\" # Critical: enables peer client certificate authentication for peer traffic\n ]\n }\n }\n}\n```"
|
||||
"CLI": "--peer-client-cert-auth=true",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/kubernetes/kubernetes-policy-index/ensure-that-the-peer-client-cert-auth-argument-is-set-to-true",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce **mTLS** for etcd peers with client certificate auth. Use a dedicated CA, validate SANs, and apply **least privilege** to issued certs. Rotate and revoke certificates regularly, restrict network access to peer ports, and avoid auto-generated self-signed peer TLS to maintain strong identity assurance.",
|
||||
"Url": "https://hub.prowler.com/check/etcd_peer_client_cert_auth"
|
||||
"Text": "Ensure etcd is configured for peer client certificate authentication.",
|
||||
"Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#limiting-access-of-etcd-clusters"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"cluster-security",
|
||||
"identity-access"
|
||||
"encryption",
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
{
|
||||
"Provider": "kubernetes",
|
||||
"CheckID": "etcd_peer_tls_config",
|
||||
"CheckTitle": "Etcd pod uses TLS for peer connections",
|
||||
"CheckTitle": "Ensure that the --peer-cert-file and --peer-key-file arguments are set as appropriate for etcd",
|
||||
"CheckType": [],
|
||||
"ServiceName": "etcd",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Pod",
|
||||
"Description": "**Etcd peer communication** is treated as secure when **TLS** is configured with a peer certificate and key (e.g., `--peer-cert-file` and `--peer-key-file`). The assessment inspects etcd containers for these options to determine whether server-to-server traffic is encrypted and authenticated.",
|
||||
"Risk": "Without **TLS** on peer links, attackers can intercept or alter Raft traffic, enabling node impersonation and **consensus manipulation**. This endangers **confidentiality** (exposed cluster state), **integrity** (tampered writes), and **availability** (quorum disruption), cascading into control-plane instability.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://etcd.io/docs/latest/op-guide/security/",
|
||||
"https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#securing-communication"
|
||||
],
|
||||
"ResourceType": "EtcdService",
|
||||
"Description": "This check ensures that etcd is configured to use TLS encryption for peer connections, which is crucial for securing sensitive data stored in etcd. It verifies the presence of peer certificate and key file arguments in etcd configuration.",
|
||||
"Risk": "Not configuring TLS for peer connections in etcd can lead to potential data breaches and unauthorized access.",
|
||||
"RelatedUrl": "https://etcd.io/docs/latest/op-guide/security/",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. SSH to the control plane node running etcd\n2. Open /etc/kubernetes/manifests/etcd.yaml\n3. Under spec.containers[0].command add:\n - --peer-cert-file=</path/to/peer-cert-file>\n - --peer-key-file=</path/to/peer-key-file>\n4. Save the file; kubelet will restart the etcd Pod automatically\n5. Verify the etcd container command includes both flags",
|
||||
"Terraform": "```hcl\nresource \"kubernetes_pod\" \"<example_resource_name>\" {\n metadata {\n name = \"<example_resource_name>\"\n }\n spec {\n container {\n name = \"etcd\"\n image = \"quay.io/coreos/etcd:latest\"\n command = [\n \"etcd\",\n \"--peer-cert-file=</path/to/peer-cert-file>\", # Critical: enables TLS for peer connections\n \"--peer-key-file=</path/to/peer-key-file>\" # Critical: key for the peer TLS cert\n ]\n }\n }\n}\n```"
|
||||
"CLI": "--peer-client-file=</path/to/peer-cert-file> --peer-key-file=</path/to/peer-key-file>",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/kubernetes/kubernetes-policy-index/ensure-that-the-peer-cert-file-and-peer-key-file-arguments-are-set-as-appropriate",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce **TLS** for etcd peer communication with unique certificates per member and mutual authentication. Apply strong cipher suites and modern protocol versions, rotate keys, and separate CAs for peers and clients. Limit network access to peer ports to trusted nodes, following **least privilege** and **defense in depth**.",
|
||||
"Url": "https://hub.prowler.com/check/etcd_peer_tls_config"
|
||||
"Text": "Secure etcd peer connections with TLS encryption.",
|
||||
"Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#securing-communication"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption",
|
||||
"cluster-security"
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
{
|
||||
"Provider": "kubernetes",
|
||||
"CheckID": "etcd_tls_encryption",
|
||||
"CheckTitle": "Etcd pod has TLS encryption configured",
|
||||
"CheckTitle": "Ensure that the --cert-file and --key-file arguments are set as appropriate for etcd",
|
||||
"CheckType": [],
|
||||
"ServiceName": "etcd",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Pod",
|
||||
"Description": "**Etcd pods** are assessed for **TLS-enabled client communication**, indicated by `--cert-file` and `--key-file` in container arguments, showing that Kubernetes API state traffic is encrypted in transit.",
|
||||
"Risk": "Without **TLS**, etcd traffic is exposed on the network, weakening CIA:\n- Confidentiality: leakage of **secrets** and cluster state\n- Integrity: **MITM** can alter configs, roles, and objects\n- Availability: control-plane instability from tampered responses",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/",
|
||||
"https://etcd.io/docs/latest/op-guide/security/"
|
||||
],
|
||||
"ResourceType": "Etcd",
|
||||
"Description": "This check verifies that the etcd service in a Kubernetes cluster is configured with appropriate TLS encryption settings. etcd, being a key value store for all Kubernetes REST API objects, should have its communication encrypted to protect these sensitive objects in transit.",
|
||||
"Risk": "Without proper TLS configuration, data stored in etcd can be susceptible to interception and unauthorized access, posing a significant security risk to the entire Kubernetes cluster.",
|
||||
"RelatedUrl": "https://etcd.io/docs/latest/op-guide/security/",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. SSH to the control-plane node\n2. Open /etc/kubernetes/manifests/etcd.yaml\n3. In spec.containers[0].command add:\n - --cert-file=/etc/kubernetes/pki/etcd/server.crt\n - --key-file=/etc/kubernetes/pki/etcd/server.key\n4. Save the file; kubelet will automatically restart the etcd Pod\n5. Confirm the etcd container command now includes both --cert-file and --key-file",
|
||||
"CLI": "--cert-file=/path/to/cert-file --key-file=/path/to/key-file",
|
||||
"NativeIaC": "https://docs.prowler.com/checks/kubernetes/kubernetes-policy-index/ensure-that-the-cert-file-and-key-file-arguments-are-set-as-appropriate",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enforce **mTLS** for etcd client and peer traffic and disable plaintext listeners. Restrict access to etcd to control-plane components via tight network policies and firewalls. Use strong TLS versions/ciphers, rotate certificates, and safeguard keys, applying **least privilege** and **defense in depth**.",
|
||||
"Url": "https://hub.prowler.com/check/etcd_tls_encryption"
|
||||
"Text": "Ensure that the etcd service is configured with TLS encryption for secure communication. The --cert-file and --key-file arguments should point to a valid TLS certificate and key.",
|
||||
"Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption",
|
||||
"cluster-security"
|
||||
"encryption"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
{
|
||||
"Provider": "kubernetes",
|
||||
"CheckID": "etcd_unique_ca",
|
||||
"CheckTitle": "Etcd pod uses a unique Certificate Authority distinct from the Kubernetes API server CA",
|
||||
"CheckTitle": "Ensure that a unique Certificate Authority is used for etcd",
|
||||
"CheckType": [],
|
||||
"ServiceName": "etcd",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "high",
|
||||
"ResourceType": "Pod",
|
||||
"Description": "**Etcd** configuration is assessed to ensure it trusts a **unique Certificate Authority** via `--trusted-ca-file`, distinct from the API server's `--client-ca-file`. If the same CA file is used, etcd shares the cluster CA; differing files imply separation, though CA content should still be verified.",
|
||||
"Risk": "Using the Kubernetes CA for etcd allows any cert signed by that CA to authenticate to the datastore. Theft or mis-issuance enables unauthorized reads/writes, causing secret exposure (confidentiality), state tampering (integrity), and potential control-plane disruption (availability).",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://etcd.io/docs/latest/op-guide/security/",
|
||||
"https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#limiting-access-of-etcd-clusters"
|
||||
],
|
||||
"ResourceType": "EtcdService",
|
||||
"Description": "This check ensures that etcd uses a unique Certificate Authority (CA) separate from the one used for the overall Kubernetes cluster. This practice enhances the security by restricting access to the etcd database only to clients and peers with certificates issued by the dedicated etcd CA.",
|
||||
"Risk": "Using the same CA for etcd and the Kubernetes cluster can expose etcd to unauthorized access if any certificate issued by the Kubernetes CA is compromised.",
|
||||
"RelatedUrl": "https://etcd.io/docs/latest/op-guide/security/",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"CLI": "--trusted-ca-file=</path/to/etcd-ca-file>",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. SSH to a control-plane node that runs etcd\n2. Open the API server manifest: sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml and note the value of --client-ca-file=<APISERVER_CA_PATH>\n3. Ensure an etcd-specific CA file exists at a different path (for example: /etc/kubernetes/pki/etcd/ca.crt) and is readable by the etcd container\n4. Edit the etcd manifest: sudo vi /etc/kubernetes/manifests/etcd.yaml\n - In the etcd container command/args, add or update: --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt (this path must NOT equal <APISERVER_CA_PATH>)\n - Save the file; the kubelet will restart the etcd pod automatically\n5. Verify the change: kubectl -n kube-system get pods -o wide | grep etcd, then describe the etcd pod and confirm --trusted-ca-file points to a different path than the API server --client-ca-file",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Adopt a **separate PKI** for etcd: issue client and peer certs from an etcd-only CA and trust only that CA. Enforce mTLS (`--client-cert-auth`, `--peer-client-cert-auth`), avoid `--auto-tls`, rotate keys independently, and apply **least privilege** to CA issuance with regular certificate audits.",
|
||||
"Url": "https://hub.prowler.com/check/etcd_unique_ca"
|
||||
"Text": "Ensure etcd uses a unique CA separate from the Kubernetes cluster CA.",
|
||||
"Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#limiting-access-of-etcd-clusters"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption",
|
||||
"cluster-security",
|
||||
"trust-boundaries"
|
||||
"encryption"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
|
||||
@@ -26,7 +26,6 @@ dependencies = [
|
||||
"azure-mgmt-monitor==6.0.2",
|
||||
"azure-mgmt-network==28.1.0",
|
||||
"azure-mgmt-rdbms==10.1.0",
|
||||
"azure-mgmt-postgresqlflexibleservers==1.1.0",
|
||||
"azure-mgmt-recoveryservices==3.1.0",
|
||||
"azure-mgmt-recoveryservicesbackup==9.2.0",
|
||||
"azure-mgmt-resource==23.3.0",
|
||||
@@ -84,7 +83,7 @@ prowler = "prowler.__main__:prowler"
|
||||
|
||||
[project.urls]
|
||||
"Changelog" = "https://github.com/prowler-cloud/prowler/releases"
|
||||
"Documentation" = "https://docs.prowler.com"
|
||||
"Documentation" = "https://docs.prowler.cloud"
|
||||
"Homepage" = "https://github.com/prowler-cloud/prowler"
|
||||
"Issue tracker" = "https://github.com/prowler-cloud/prowler/issues"
|
||||
|
||||
|
||||
@@ -1235,23 +1235,6 @@ class Test_Parser:
|
||||
== f"{prowler_default_usage_error}\nprowler: error: unrecognized arguments: --subscription-ids\n"
|
||||
)
|
||||
|
||||
def test_parser_non_aws_with_json_asff_output(self, capsys):
|
||||
command = [
|
||||
prowler_command,
|
||||
"azure",
|
||||
"--sp-env-auth",
|
||||
"--output-formats",
|
||||
"json-asff",
|
||||
]
|
||||
with pytest.raises(SystemExit) as wrapped_exit:
|
||||
_ = self.parser.parse(command)
|
||||
assert wrapped_exit.type == SystemExit
|
||||
assert wrapped_exit.value.code == 2
|
||||
assert (
|
||||
capsys.readouterr().err
|
||||
== f"{prowler_default_usage_error}\nprowler: error: json-asff output format is only available for the aws provider, but azure was selected\n"
|
||||
)
|
||||
|
||||
def test_parser_gcp_auth_credentials_file(self):
|
||||
argument = "--credentials-file"
|
||||
file = "test.json"
|
||||
|
||||
@@ -133,7 +133,7 @@ class TestSecurityHub:
|
||||
(
|
||||
"root",
|
||||
WARNING,
|
||||
f"Security Hub is enabled in {AWS_REGION_EU_WEST_1} but Prowler integration does not accept findings. More info: https://docs.prowler.com/user-guide/providers/aws/securityhub#aws-security-hub-integration-with-prowler",
|
||||
f"Security Hub is enabled in {AWS_REGION_EU_WEST_1} but Prowler integration does not accept findings. More info: https://docs.prowler.cloud/en/latest/tutorials/aws/securityhub/",
|
||||
)
|
||||
]
|
||||
|
||||
@@ -1376,7 +1376,7 @@ class TestSecurityHub:
|
||||
(
|
||||
"root",
|
||||
WARNING,
|
||||
f"Security Hub is enabled in {AWS_REGION_EU_WEST_1} but Prowler integration does not accept findings. More info: https://docs.prowler.com/user-guide/providers/aws/securityhub#aws-security-hub-integration-with-prowler",
|
||||
f"Security Hub is enabled in {AWS_REGION_EU_WEST_1} but Prowler integration does not accept findings. More info: https://docs.prowler.cloud/en/latest/tutorials/aws/securityhub/",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -189,7 +189,6 @@ class Test_ec2_instance_uses_single_eni:
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws
|
||||
@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call_v2)
|
||||
def test_ec2_instance_no_eni(self):
|
||||
|
||||
@@ -223,7 +222,6 @@ class Test_ec2_instance_uses_single_eni:
|
||||
== "EC2 Instance i-0123456789abcdef0 has no network interfaces attached."
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
||||
def test_instance_multiple_enis(self):
|
||||
|
||||
@@ -297,7 +295,6 @@ class Test_ec2_instance_uses_single_eni:
|
||||
== f"EC2 Instance {instance.id} uses only one ENI: ( Interfaces: ['{network_interface.id}'] )."
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call_v3)
|
||||
def test_instance_one_efa_multiple_trunks(self):
|
||||
|
||||
|
||||
@@ -53,83 +53,3 @@ class Test_CosmosDB_Service:
|
||||
is None
|
||||
)
|
||||
assert account.accounts[AZURE_SUBSCRIPTION_ID][0].disable_local_auth is None
|
||||
|
||||
|
||||
def mock_cosmosdb_get_accounts_with_none(_):
|
||||
"""Mock CosmosDB accounts with None private_endpoint_connections"""
|
||||
from prowler.providers.azure.services.cosmosdb.cosmosdb_service import (
|
||||
PrivateEndpointConnection,
|
||||
)
|
||||
|
||||
return {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id="/subscriptions/test/account1",
|
||||
name="cosmosdb-none-pec",
|
||||
kind="GlobalDocumentDB",
|
||||
location="eastus",
|
||||
type="Microsoft.DocumentDB/databaseAccounts",
|
||||
tags={},
|
||||
is_virtual_network_filter_enabled=False,
|
||||
disable_local_auth=False,
|
||||
private_endpoint_connections=[], # Empty list from getattr default
|
||||
),
|
||||
Account(
|
||||
id="/subscriptions/test/account2",
|
||||
name="cosmosdb-with-pec",
|
||||
kind="MongoDB",
|
||||
location="westus",
|
||||
type="Microsoft.DocumentDB/databaseAccounts",
|
||||
tags={"env": "test"},
|
||||
is_virtual_network_filter_enabled=True,
|
||||
disable_local_auth=True,
|
||||
private_endpoint_connections=[
|
||||
PrivateEndpointConnection(
|
||||
id="/subscriptions/test/pec1",
|
||||
name="pec-1",
|
||||
type="Microsoft.Network/privateEndpoints",
|
||||
)
|
||||
],
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.cosmosdb.cosmosdb_service.CosmosDB._get_accounts",
|
||||
new=mock_cosmosdb_get_accounts_with_none,
|
||||
)
|
||||
class Test_CosmosDB_Service_None_Handling:
|
||||
"""Test CosmosDB service handling of None values"""
|
||||
|
||||
def test_account_with_none_private_endpoint_connections(self):
|
||||
"""Test that CosmosDB handles None private_endpoint_connections gracefully"""
|
||||
cosmosdb = CosmosDB(set_mocked_azure_provider())
|
||||
|
||||
# Find account with no connections
|
||||
account = next(
|
||||
acc
|
||||
for acc in cosmosdb.accounts[AZURE_SUBSCRIPTION_ID]
|
||||
if acc.name == "cosmosdb-none-pec"
|
||||
)
|
||||
assert account.private_endpoint_connections == []
|
||||
assert account.disable_local_auth is False
|
||||
|
||||
def test_account_with_valid_private_endpoint_connections(self):
|
||||
"""Test that CosmosDB handles valid private_endpoint_connections"""
|
||||
cosmosdb = CosmosDB(set_mocked_azure_provider())
|
||||
|
||||
# Find account with connections
|
||||
account = next(
|
||||
acc
|
||||
for acc in cosmosdb.accounts[AZURE_SUBSCRIPTION_ID]
|
||||
if acc.name == "cosmosdb-with-pec"
|
||||
)
|
||||
assert len(account.private_endpoint_connections) == 1
|
||||
assert account.private_endpoint_connections[0].id == "/subscriptions/test/pec1"
|
||||
assert account.private_endpoint_connections[0].name == "pec-1"
|
||||
assert (
|
||||
account.private_endpoint_connections[0].type
|
||||
== "Microsoft.Network/privateEndpoints"
|
||||
)
|
||||
assert account.disable_local_auth is True
|
||||
@@ -283,77 +283,3 @@ class Test_Defender_Service:
|
||||
assert policy1.name == "JITPolicy1"
|
||||
assert policy1.location == "eastus"
|
||||
assert set(policy1.vm_ids) == {"vm-1", "vm-2"}
|
||||
|
||||
|
||||
def mock_defender_get_assessments_with_none(_):
|
||||
"""Mock Defender assessments with None and valid statuses"""
|
||||
return {
|
||||
AZURE_SUBSCRIPTION_ID: {
|
||||
"Assessment None": Assesment(
|
||||
resource_id="/subscriptions/test/assessment1",
|
||||
resource_name="assessment-none",
|
||||
status=None, # None status
|
||||
),
|
||||
"Assessment Healthy": Assesment(
|
||||
resource_id="/subscriptions/test/assessment2",
|
||||
resource_name="assessment-healthy",
|
||||
status="Healthy",
|
||||
),
|
||||
"Assessment Unhealthy": Assesment(
|
||||
resource_id="/subscriptions/test/assessment3",
|
||||
resource_name="assessment-unhealthy",
|
||||
status="Unhealthy",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@patch(
|
||||
"prowler.providers.azure.services.defender.defender_service.Defender._get_assessments",
|
||||
new=mock_defender_get_assessments_with_none,
|
||||
)
|
||||
class Test_Defender_Service_Assessments_None_Handling:
|
||||
"""Test Defender service handling of None values in assessments"""
|
||||
|
||||
def test_assessment_with_none_status(self):
|
||||
"""Test that Defender handles assessments with None status gracefully"""
|
||||
defender = Defender(set_mocked_azure_provider())
|
||||
|
||||
# Check assessment with None status
|
||||
assessment = defender.assessments[AZURE_SUBSCRIPTION_ID]["Assessment None"]
|
||||
assert assessment.resource_id == "/subscriptions/test/assessment1"
|
||||
assert assessment.resource_name == "assessment-none"
|
||||
assert assessment.status is None
|
||||
|
||||
def test_assessment_with_valid_status(self):
|
||||
"""Test that Defender handles assessments with valid status"""
|
||||
defender = Defender(set_mocked_azure_provider())
|
||||
|
||||
# Check assessment with Healthy status
|
||||
assessment = defender.assessments[AZURE_SUBSCRIPTION_ID]["Assessment Healthy"]
|
||||
assert assessment.resource_id == "/subscriptions/test/assessment2"
|
||||
assert assessment.resource_name == "assessment-healthy"
|
||||
assert assessment.status == "Healthy"
|
||||
|
||||
def test_assessment_with_multiple_mixed_statuses(self):
|
||||
"""Test that Defender handles mix of None and valid statuses"""
|
||||
defender = Defender(set_mocked_azure_provider())
|
||||
|
||||
# Should have all 3 assessments
|
||||
assert len(defender.assessments[AZURE_SUBSCRIPTION_ID]) == 3
|
||||
|
||||
# Check None status
|
||||
assessment_none = defender.assessments[AZURE_SUBSCRIPTION_ID]["Assessment None"]
|
||||
assert assessment_none.status is None
|
||||
|
||||
# Check Healthy status
|
||||
assessment_healthy = defender.assessments[AZURE_SUBSCRIPTION_ID][
|
||||
"Assessment Healthy"
|
||||
]
|
||||
assert assessment_healthy.status == "Healthy"
|
||||
|
||||
# Check Unhealthy status
|
||||
assessment_unhealthy = defender.assessments[AZURE_SUBSCRIPTION_ID][
|
||||
"Assessment Unhealthy"
|
||||
]
|
||||
assert assessment_unhealthy.status == "Unhealthy"
|
||||
@@ -50,16 +50,14 @@ class Test_postgresql_flexible_server_allow_access_services_disabled:
|
||||
id=postgresql_server_id,
|
||||
name=postgresql_server_name,
|
||||
resource_group="resource_group",
|
||||
location="location",
|
||||
require_secure_transport="OFF",
|
||||
active_directory_auth=None,
|
||||
entra_id_admins=[],
|
||||
log_checkpoints="OFF",
|
||||
log_connections="OFF",
|
||||
log_disconnections="OFF",
|
||||
connection_throttling="OFF",
|
||||
log_retention_days="3",
|
||||
firewall=[firewall],
|
||||
location="location",
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -107,16 +105,14 @@ class Test_postgresql_flexible_server_allow_access_services_disabled:
|
||||
id=postgresql_server_id,
|
||||
name=postgresql_server_name,
|
||||
resource_group="resource_group",
|
||||
location="location",
|
||||
require_secure_transport="OFF",
|
||||
active_directory_auth=None,
|
||||
entra_id_admins=[],
|
||||
log_checkpoints="OFF",
|
||||
log_connections="OFF",
|
||||
log_disconnections="OFF",
|
||||
connection_throttling="OFF",
|
||||
log_retention_days="3",
|
||||
firewall=[firewall],
|
||||
location="location",
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -41,16 +41,14 @@ class Test_postgresql_flexible_server_connection_throttling_on:
|
||||
id=postgresql_server_id,
|
||||
name=postgresql_server_name,
|
||||
resource_group="resource_group",
|
||||
location="location",
|
||||
require_secure_transport="OFF",
|
||||
active_directory_auth=None,
|
||||
entra_id_admins=[],
|
||||
log_checkpoints="OFF",
|
||||
log_connections="OFF",
|
||||
log_disconnections="OFF",
|
||||
connection_throttling="OFF",
|
||||
log_retention_days="3",
|
||||
firewall=None,
|
||||
location="location",
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -92,16 +90,14 @@ class Test_postgresql_flexible_server_connection_throttling_on:
|
||||
id=postgresql_server_id,
|
||||
name=postgresql_server_name,
|
||||
resource_group="resource_group",
|
||||
location="location",
|
||||
require_secure_transport="OFF",
|
||||
active_directory_auth=None,
|
||||
entra_id_admins=[],
|
||||
log_checkpoints="ON",
|
||||
log_connections="ON",
|
||||
log_disconnections="ON",
|
||||
connection_throttling="ON",
|
||||
log_retention_days="3",
|
||||
firewall=None,
|
||||
location="location",
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||