Prowler 보안 점검 환경 Docker Compose 구축기




클라우드 보안 점검 도구 Prowler는 AWS, Azure, GCP, Kubernetes 환경에서 취약점 및 보안 설정을 손쉽게 점검할 수 있는 강력한 오픈소스 툴입니다. 이번 글에서는 prowlercloud/prowler-apiprowlercloud/prowler-ui 이미지를 활용해, 도커 컴포즈 기반으로 Prowler 보안 점검 웹 UI 환경을 구축하는 과정을 공유합니다. 실제로 설정하며 겪었던 시행착오와 해결 방법도 정리해두었으니 참고하시기 바랍니다.


1. 왜 Prowler인가?

기업이 클라우드 환경을 운영하다 보면 보안 규정 준수와 취약점 관리가 필수적입니다. 그러나 이를 수동으로 점검하기는 매우 어렵습니다. Prowler는 다음과 같은 장점을 제공합니다:

  • 멀티 클라우드 지원: AWS, GCP, Azure, Kubernetes 점검 가능
  • 규정 준수: CIS, HIPAA, ISO27001, GDPR 등 주요 규정에 맞춘 체크리스트 제공
  • 확장성: CI/CD 파이프라인 및 보안 대시보드 연동 가능
  • 오픈소스: 무료로 사용 가능하며, 도커 기반 자체 구축이 용이

2. 기본 구조

Prowler API와 UI는 별도의 컨테이너로 구동됩니다. 추가로 Postgres 데이터베이스, Valkey(Redis 포크), 그리고 Worker/Beat(비동기 작업 실행 및 스케줄링)를 함께 구성하여 완전한 환경을 만듭니다.

구성 요소는 다음과 같습니다:

  • api: Django 기반 백엔드 API
  • ui: Next.js 기반 웹 프론트엔드
  • postgres: 결과 저장용 DB
  • valkey: Celery 작업 큐
  • worker / worker-beat: 비동기 작업 처리 및 스케줄러

3. Docker Compose 설정

docker-compose.yml

아래는 최종적으로 정상 실행된 docker-compose.yml 예시입니다.

version: "3.9"

services:
  api:
    hostname: "prowler-api"
    image: prowlercloud/prowler-api:${PROWLER_API_VERSION:-stable}
    env_file: .env
    ports:
      - "${DJANGO_PORT:-8080}:${DJANGO_PORT:-8080}"
    volumes:
      - "output:/tmp/prowler_api_output"
    depends_on:
      postgres:
        condition: service_healthy
      valkey:
        condition: service_healthy
    entrypoint:
      - "/home/prowler/docker-entrypoint.sh"
      - "prod"

  ui:
    image: prowlercloud/prowler-ui:${PROWLER_UI_VERSION:-stable}
    env_file: .env
    ports:
      - "${UI_PORT:-3000}:${UI_PORT:-3000}"

  postgres:
    image: postgres:16.3-alpine3.20
    hostname: "postgres-db"
    volumes:
      - ./_data/postgres:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=${POSTGRES_ADMIN_USER}
      - POSTGRES_PASSWORD=${POSTGRES_ADMIN_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
    env_file: .env
    ports:
      - "${POSTGRES_PORT:-5432}:${POSTGRES_PORT:-5432}"
    healthcheck:
      test: ["CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_ADMIN_USER} -d ${POSTGRES_DB}'"]
      interval: 5s
      timeout: 5s
      retries: 5

  valkey:
    image: valkey/valkey:7-alpine3.19
    hostname: "valkey"
    volumes:
      - ./_data/valkey:/data
    env_file: .env
    ports:
      - "${VALKEY_PORT:-6379}:6379"
    healthcheck:
      test: ["CMD-SHELL", "sh -c 'valkey-cli ping'"]
      interval: 10s
      timeout: 5s
      retries: 3

  worker:
    image: prowlercloud/prowler-api:${PROWLER_API_VERSION:-stable}
    env_file: .env
    volumes:
      - "output:/tmp/prowler_api_output"
    depends_on:
      valkey:
        condition: service_healthy
      postgres:
        condition: service_healthy
    entrypoint:
      - "/home/prowler/docker-entrypoint.sh"
      - "worker"

  worker-beat:
    image: prowlercloud/prowler-api:${PROWLER_API_VERSION:-stable}
    env_file: .env
    depends_on:
      valkey:
        condition: service_healthy
      postgres:
        condition: service_healthy
    entrypoint:
      - "/home/prowler/docker-entrypoint.sh"
      - "beat"

volumes:
  output:
    driver: local

.env 파일

Compose에서 사용할 환경 변수는 .env 파일에 정의합니다. 이 파일은 UI와 API에서 공통으로 활용됩니다. 주요 항목은 아래와 같습니다.

#### Important Note ####
# This file is used to store environment variables for the Prowler App.
# For production, it is recommended to use a secure method to store these variables and change the default secret keys.

#### Prowler UI Configuration ####
PROWLER_UI_VERSION="stable"
AUTH_URL=http://localhost:3000
API_BASE_URL=http://prowler-api:8080/api/v1
NEXT_PUBLIC_API_BASE_URL=${API_BASE_URL}
NEXT_PUBLIC_API_DOCS_URL=http://prowler-api:8080/api/v1/docs
AUTH_TRUST_HOST=true
UI_PORT=3000
# Temp URL for feeds need to use actual
RSS_FEED_URL=https://prowler.com/blog/rss
# openssl rand -base64 32
AUTH_SECRET="N/c6mnaS5+SWq81+819OrzQZlmx1Vxtp/orjttJSmw8="
# Google Tag Manager ID
NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID=""


#### Prowler API Configuration ####
PROWLER_API_VERSION="stable"
# PostgreSQL settings
# If running Django and celery on host, use 'localhost', else use 'postgres-db'
POSTGRES_HOST=postgres-db
POSTGRES_PORT=5432
POSTGRES_ADMIN_USER=prowler_admin
POSTGRES_ADMIN_PASSWORD=postgres
POSTGRES_USER=prowler
POSTGRES_PASSWORD=postgres
POSTGRES_DB=prowler_db

# Celery-Prowler task settings
TASK_RETRY_DELAY_SECONDS=0.1
TASK_RETRY_ATTEMPTS=5

# Valkey settings
# If running Valkey and celery on host, use localhost, else use 'valkey'
VALKEY_HOST=valkey
VALKEY_PORT=6379
VALKEY_DB=0

# API scan settings

# The path to the directory where scan output should be stored
DJANGO_TMP_OUTPUT_DIRECTORY="/tmp/prowler_api_output"

# The maximum number of findings to process in a single batch
DJANGO_FINDINGS_BATCH_SIZE=1000

# The AWS access key to be used when uploading scan output to an S3 bucket
# If left empty, default AWS credentials resolution behavior will be used
DJANGO_OUTPUT_S3_AWS_ACCESS_KEY_ID=""

# The AWS secret key to be used when uploading scan output to an S3 bucket
DJANGO_OUTPUT_S3_AWS_SECRET_ACCESS_KEY=""

# An optional AWS session token
DJANGO_OUTPUT_S3_AWS_SESSION_TOKEN=""

# The AWS region where your S3 bucket is located (e.g., "us-east-1")
DJANGO_OUTPUT_S3_AWS_DEFAULT_REGION=""

# The name of the S3 bucket where scan output should be stored
DJANGO_OUTPUT_S3_AWS_OUTPUT_BUCKET=""

# Django settings
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,prowler-api
DJANGO_BIND_ADDRESS=0.0.0.0
DJANGO_PORT=8080
DJANGO_DEBUG=False
DJANGO_SETTINGS_MODULE=config.django.production
# Select one of [ndjson|human_readable]
DJANGO_LOGGING_FORMATTER=human_readable
# Select one of [DEBUG|INFO|WARNING|ERROR|CRITICAL]
# Applies to both Django and Celery Workers
DJANGO_LOGGING_LEVEL=INFO
# Defaults to the maximum available based on CPU cores if not set.
DJANGO_WORKERS=4
# Token lifetime is in minutes
DJANGO_ACCESS_TOKEN_LIFETIME=30
# Token lifetime is in minutes
DJANGO_REFRESH_TOKEN_LIFETIME=1440
DJANGO_CACHE_MAX_AGE=3600
DJANGO_STALE_WHILE_REVALIDATE=60
DJANGO_MANAGE_DB_PARTITIONS=True
# openssl genrsa -out private.pem 2048
DJANGO_TOKEN_SIGNING_KEY="-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDs4e+kt7SnUJek
6V5r9zMGzXCoU5qnChfPiqu+BgANyawz+MyVZPs6RCRfeo6tlCknPQtOziyXYM2I
7X+qckmuzsjqp8+u+o1mw3VvUuJew5k2SQLPYwsiTzuFNVJEOgRo3hywGiGwS2iv
/5nh2QAl7fq2qLqZEXQa5+/xJlQggS1CYxOJgggvLyra50QZlBvPve/AxKJ/EV/Q
irWTZU5lLNI8sH2iZR05vQeBsxZ0dCnGMT+vGl+cGkqrvzQzKsYbDmabMcfTYhYi
78fpv6A4uharJFHayypYBjE39PwhMyyeycrNXlpm1jpq+03HgmDuDMHydk1tNwuT
nEC7m7iNAgMBAAECggEAA2m48nJcJbn9SVi8bclMwKkWmbJErOnyEGEy2sTK3Of+
NWx9BB0FmqAPNxn0ss8K7cANKOhDD7ZLF9E2MO4/HgfoMKtUzHRbM7MWvtEepldi
nnvcUMEgULD8Dk4HnqiIVjt3BdmGiTv46OpBnRWrkSBV56pUL+7msZmMZTjUZvh2
ZWv0+I3gtDIjo2Zo/FiwDV7CfwRjJarRpYUj/0YyuSA4FuOUYl41WAX1I301FKMH
xo3jiAYi1s7IneJ16OtPpOA34Wg5F6ebm/UO0uNe+iD4kCXKaZmxYQPh5tfB0Qa3
qj1T7GNpFNyvtG7VVdauhkb8iu8X/wl6PCwbg0RCKQKBgQD9HfpnpH0lDlHMRw9K
X7Vby/1fSYy1BQtlXFEIPTN/btJ/asGxLmAVwJ2HAPXWlrfSjVAH7CtVmzN7v8oj
HeIHfeSgoWEu1syvnv2AMaYSo03UjFFlfc/GUxF7DUScRIhcJUPCP8jkAROz9nFv
DByNjUL17Q9r43DmDiRsy0IFqQKBgQDvlJ9Uhl+Sp7gRgKYwa/IG0+I4AduAM+Gz
Dxbm52QrMGMTjaJFLmLHBUZ/ot+pge7tZZGws8YR8ufpyMJbMqPjxhIvRRa/p1Tf
E3TQPW93FMsHUvxAgY3MV5MzXFPhlNAKb+akP/RcXUhetGAuZKLubtDCWa55ZQuL
wj2OS+niRQKBgE7K8zUqNi6/22S8xhy/2GPgB1qPObbsABUofK0U6CAGLo6te+gc
6Jo84IyzFtQbDNQFW2Fr+j1m18rw9AqkdcUhQndiZS9AfG07D+zFB86LeWHt4DS4
ymIRX8Kvaak/iDcu/n3Mf0vCrhB6aetImObTj4GgrwlFvtJOmrYnO8EpAoGAIXXP
Xt25gWD9OyyNiVu6HKwA/zN7NYeJcRmdaDhO7B1A6R0x2Zml4AfjlbXoqOLlvLAf
zd79vcoAC82nH1eOPiSOq51plPDI0LMF8IN0CtyTkn1Lj7LIXA6rF1RAvtOqzppc
SvpHpZK9pcRpXnFdtBE0BMDDtl6fYzCIqlP94UUCgYEAnhXbAQMF7LQifEm34Dx8
BizRMOKcqJGPvbO2+Iyt50O5X6onU2ITzSV1QHtOvAazu+B1aG9pEuBFDQ+ASxEu
L9ruJElkOkb/o45TSF6KCsHd55ReTZ8AqnRjf5R+lyzPqTZCXXb8KTcRvWT4zQa3
VxyT2PnaSqEcexWUy4+UXoQ=
-----END PRIVATE KEY-----"
# openssl rsa -in private.pem -pubout -out public.pem
DJANGO_TOKEN_VERIFYING_KEY="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7OHvpLe0p1CXpOlea/cz
Bs1wqFOapwoXz4qrvgYADcmsM/jMlWT7OkQkX3qOrZQpJz0LTs4sl2DNiO1/qnJJ
rs7I6qfPrvqNZsN1b1LiXsOZNkkCz2MLIk87hTVSRDoEaN4csBohsEtor/+Z4dkA
Je36tqi6mRF0Gufv8SZUIIEtQmMTiYIILy8q2udEGZQbz73vwMSifxFf0Iq1k2VO
ZSzSPLB9omUdOb0HgbMWdHQpxjE/rxpfnBpKq780MyrGGw5mmzHH02IWIu/H6b+g
OLoWqyRR2ssqWAYxN/T8ITMsnsnKzV5aZtY6avtNx4Jg7gzB8nZNbTcLk5xAu5u4
jQIDAQAB
-----END PUBLIC KEY-----"
# openssl rand -base64 32
DJANGO_SECRETS_ENCRYPTION_KEY="oE/ltOhp/n1TdbHjVmzcjDPLcLA41CVI/4Rk+UB5ESc="
DJANGO_BROKER_VISIBILITY_TIMEOUT=86400
DJANGO_SENTRY_DSN=

# Sentry settings
SENTRY_ENVIRONMENT=local
SENTRY_RELEASE=local

#### Prowler release version ####
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v5.10.0

# Social login credentials
SOCIAL_GOOGLE_OAUTH_CALLBACK_URL="${AUTH_URL}/api/auth/callback/google"
SOCIAL_GOOGLE_OAUTH_CLIENT_ID=""
SOCIAL_GOOGLE_OAUTH_CLIENT_SECRET=""

SOCIAL_GITHUB_OAUTH_CALLBACK_URL="${AUTH_URL}/api/auth/callback/github"
SOCIAL_GITHUB_OAUTH_CLIENT_ID=""
SOCIAL_GITHUB_OAUTH_CLIENT_SECRET=""

# Single Sign-On (SSO)
SAML_SSO_CALLBACK_URL="${AUTH_URL}/api/auth/callback/saml"

# Lighthouse tracing
LANGSMITH_TRACING=false
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
LANGSMITH_API_KEY=""
LANGCHAIN_PROJECT=""

실제 키 값(DJANGO_TOKEN_SIGNING_KEY, DJANGO_TOKEN_VERIFYING_KEY)은 OpenSSL을 사용해 안전하게 생성하는 것이 권장됩니다.


4. 자주 겪는 오류와 해결법

  1. env_file 포맷 오류
    처음에는 env_file 항목을 path: .env 형식으로 넣어 오류가 발생했습니다. Compose는 단순 문자열만 허용하므로 반드시 아래처럼 작성해야 합니다: env_file: .env
  2. entrypoint 경로 문제
    일부 서비스에서 ../docker-entrypoint.sh로 잘못 지정된 경우 실행이 되지 않았습니다. 컨테이너 내부 경로에 맞게 /home/prowler/docker-entrypoint.sh로 수정해야 정상 동작합니다.
  3. Windows 경로 이슈
    바인드 마운트 경로를 사용할 때 Windows 경로 문제로 실패할 수 있습니다. 가급적 상대경로나 네임드 볼륨을 사용하는 것이 안정적입니다.

5. 실행 및 확인

# 볼륨용 디렉터리 준비
mkdir -p _data/postgres _data/valkey

# 컨테이너 실행
docker compose up -d

# 상태 확인
docker compose ps
docker compose logs -f api

브라우저에서 http://localhost:3000 에 접속하면 Prowler UI를 통해 보안 점검 작업을 수행할 수 있습니다. API 엔드포인트는 http://localhost:8080/api/v1에서 확인 가능합니다.


6. 활용 방안

  • 정기 스케줄링: worker-beat를 이용해 자동 점검
  • 리포트 관리: HTML/JSON/CSV 리포트를 API 출력 디렉토리에 저장 후 분석
  • 대시보드 연동: Security Hub, Splunk, OpenSearch, QuickSight와 연계해 시각화
  • CI/CD 통합: 배포 파이프라인에 포함시켜 보안 게이트 역할 수행

참고 자료

  • https://prowler.com/
  • https://docs.prowler.com/
  • https://github.com/prowler-cloud/prowler

마치며

이번 포스팅에서는 docker-compose.yml.env 설정을 기반으로 Prowler API/UI 환경을 구축하는 방법을 다뤘습니다. 단순 CLI 기반 점검에서 한 단계 나아가, 웹 UI와 백엔드 API를 통해 더욱 직관적이고 효율적인 보안 점검을 수행할 수 있습니다.

실제 운영 환경에서는 반드시 보안 키 관리, IAM 최소 권한 원칙, 데이터 백업을 고려해야 하며, 필요 시 SaaS 형태의 ProwlerPro도 검토해 볼 만합니다.

클라우드 보안 점검 자동화에 관심 있다면 직접 따라 해 보시길 추천드립니다!




댓글 남기기