Skip to content
Merged

cd #19

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions .github/workflows/cd-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: CD-PROD

on:
workflow_run:
workflows: ["CI-PROD"]
types: [completed]
workflow_dispatch:

concurrency:
group: cd-prod
cancel-in-progress: false

permissions:
contents: read
id-token: write

env:
AWS_REGION: ${{ vars.AWS_REGION }}
ECR_REGISTRY: ${{ vars.ECR_REGISTRY }}
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
ECS_CLUSTER: ${{ vars.ECS_CLUSTER }}
ECS_SERVICE: ${{ vars.ECS_SERVICE }}
TASK_FAMILY: ${{ vars.TASK_FAMILY }}
CONTAINER_NAME: ${{ vars.CONTAINER_NAME }}

jobs:
deploy:
if: >
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
Comment on lines +28 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

fd -type f -name "*.yml" -o -name "*.yaml" | grep -E "(workflow|github)" | head -20

Repository: Miche-Let/api-gateway

Length of output: 236


🏁 Script executed:

ls -la .github/workflows/ 2>/dev/null || echo "workflows directory not found"

Repository: Miche-Let/api-gateway

Length of output: 491


🏁 Script executed:

cat .github/workflows/cd-prod.yml

Repository: Miche-Let/api-gateway

Length of output: 4100


🏁 Script executed:

cat .github/workflows/ci-prod.yml 2>/dev/null || cat .github/workflows/ci.yml 2>/dev/null || fd -type f -name "*ci*" .github/workflows/

Repository: Miche-Let/api-gateway

Length of output: 1571


PR 성곡 후에도 배포 작이 μ‹€ν–‰λ˜μ–΄ 이미지 κ²€μ¦μ—μ„œ μ‹€νŒ¨ν•©λ‹ˆλ‹€.

CI-PRODλŠ” pull_request와 push 이벀트 λͺ¨λ‘μ—μ„œ μ‹€ν–‰λ˜μ§€λ§Œ, μ—¬κΈ°μ„œλŠ” 성곡 μ—¬λΆ€λ§Œ ν™•μΈν•˜λ―€λ‘œ μ„±κ³΅ν•œ PR λΉŒλ“œλ„ CD μ›Œν¬ν”Œλ‘œμš°λ₯Ό νŠΈλ¦¬κ±°ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 이미지 ν‘Έμ‹œλŠ” if: github.event_name == 'push' 쑰건으둜 push μ΄λ²€νŠΈμ—μ„œλ§Œ μ‹€ν–‰λ˜λ―€λ‘œ, PRμ—μ„œ 트리거된 싀행은 ECR에 이미지λ₯Ό ν‘Έμ‹œν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이둜 인해 line 52의 "Verify image exists in ECR" λ‹¨κ³„μ—μ„œ 맀번 μ‹€νŒ¨ν•©λ‹ˆλ‹€. workflow_run 트리거λ₯Ό push μ΄λ²€νŠΈμ™€ main 브랜치둜 μ œν•œν•΄μ•Ό ν•©λ‹ˆλ‹€.

μ œμ•ˆ μˆ˜μ •
    if: >
      github.event_name == 'workflow_dispatch' ||
-     (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
+     (github.event_name == 'workflow_run' &&
+      github.event.workflow_run.conclusion == 'success' &&
+      github.event.workflow_run.event == 'push' &&
+      github.event.workflow_run.head_branch == 'main')
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if: >
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
if: >
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main')
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cd-prod.yml around lines 28 - 30, The workflow currently
allows workflow_run triggers from any successful run which causes PR-built runs
to trigger CD but not push image steps; update the top-level if condition that
currently checks "github.event_name == 'workflow_dispatch' || (github.event_name
== 'workflow_run' && github.event.workflow_run.conclusion == 'success')" to also
require the workflow_run originated from a push to main (e.g., add checks like
"github.event_name == 'workflow_dispatch' || (github.event_name ==
'workflow_run' && github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main')") so only successful
push-to-main workflow_runs trigger the CD flow and the "Verify image exists in
ECR" step will only run when the image was actually pushed.

runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}

- name: Resolve image tag
id: tag
run: |
if [ "${{ github.event_name }}" = "workflow_run" ]; then
IMAGE_TAG="${{ github.event.workflow_run.head_sha }}"
else
IMAGE_TAG="${{ github.sha }}"
fi
IMAGE_TAG="${IMAGE_TAG:0:7}"
echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"

- name: Verify image exists in ECR
run: |
aws ecr describe-images \
--repository-name "${ECR_REPOSITORY}" \
--image-ids imageTag="${{ steps.tag.outputs.image_tag }}" \
> /dev/null

- name: Get current task definition
run: |
aws ecs describe-task-definition \
--task-definition "${TASK_FAMILY}" \
--query 'taskDefinition' > taskdef.json

- name: Render new task definition
env:
IMAGE_URI: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.image_tag }}
run: |
jq \
--arg CN "${CONTAINER_NAME}" \
--arg IMG "${IMAGE_URI}" \
--arg SPRING_PROFILES_ACTIVE "prod" \
--arg SERVER_PORT "19000" \
--arg EUREKA_ENABLED "${{ vars.EUREKA_ENABLED }}" \
--arg EUREKA_HOST "${{ vars.EUREKA_HOST }}" \
--arg EUREKA_PORT "${{ vars.EUREKA_PORT }}" \
--arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \
'
.containerDefinitions |= map(
if .name == $CN then
.image = $IMG
| .environment = [
{"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE},
{"name":"SERVER_PORT","value":$SERVER_PORT},
{"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED},
{"name":"EUREKA_HOST","value":$EUREKA_HOST},
{"name":"EUREKA_PORT","value":$EUREKA_PORT},
{"name":"JWT_SECRET","value":$JWT_SECRET}
Comment on lines +77 to +88

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | πŸ—οΈ Heavy lift

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the file
fd -type f "cd-prod.yml" .github/workflows/

Repository: Miche-Let/api-gateway

Length of output: 236


🏁 Script executed:

# Once found, examine lines 77-88 and surrounding context
sed -n '70,95p' .github/workflows/cd-prod.yml | cat -n

Repository: Miche-Let/api-gateway

Length of output: 1432


🏁 Script executed:

# Check if JWT_SECRET is used elsewhere in the workflow
rg "JWT_SECRET" .github/workflows/cd-prod.yml -n -B2 -A2

Repository: Miche-Let/api-gateway

Length of output: 562


🏁 Script executed:

# Check the full workflow file to understand the context better
wc -l .github/workflows/cd-prod.yml

Repository: Miche-Let/api-gateway

Length of output: 99


🏁 Script executed:

# Look at the complete jq command and the task definition registration
sed -n '60,120p' .github/workflows/cd-prod.yml | cat -n

Repository: Miche-Let/api-gateway

Length of output: 2881


🏁 Script executed:

# Check if there are any other references to task definitions or secrets
rg "taskDefinition|secrets|Secrets Manager|SSM Parameter" .github/workflows/cd-prod.yml -n

Repository: Miche-Let/api-gateway

Length of output: 359


🏁 Script executed:

# Check if JWT_SECRET_ARN or similar variables are defined elsewhere
rg "JWT_SECRET|SECRET" .github/workflows/ -n --max-count 20

Repository: Miche-Let/api-gateway

Length of output: 252


🌐 Web query:

ECS task definition secrets valueFrom best practices AWS

πŸ’‘ Result:

To securely manage secrets in AWS ECS using the valueFrom parameter, follow these best practices: 1. Use Managed Services: Store sensitive data in AWS Secrets Manager or AWS Systems Manager (SSM) Parameter Store [1][2]. Both services use AWS KMS for encryption at rest [1][3]. - Use Secrets Manager for secrets requiring automatic rotation, cross-account access, or multi-Region replication [1][3]. - Use SSM Parameter Store for basic configuration data and secrets that do not require these advanced features, as it is often more cost-effective [1][3]. 2. Use the Secrets Parameter: In your ECS task definition, use the secrets container definition parameter to reference these secrets via their ARN [4][5][6]. This allows the ECS agent to inject the secret value as an environment variable at container startup [7]. 3. Implement Least Privilege IAM: Ensure your task execution role has the minimum necessary permissions to retrieve secrets from the specific Secrets Manager or SSM Parameter Store resources [4][8][2]. 4. Consider Programmatic Retrieval for Dynamic Updates: Injecting secrets as environment variables via the task definition requires a task restart to pick up changes if a secret is rotated [6][7][2]. If your application requires real-time secret updates without restarting the task, modify your application code to retrieve secrets directly from Secrets Manager or SSM Parameter Store at runtime using the AWS SDK [8][2]. 5. Mitigate Environment Variable Exposure: Environment variables can sometimes be exposed in logs or via tools like docker inspect [1]. To further enhance security: - Use IAM roles to restrict access to the task definition and the secrets themselves [1][2]. - If extreme security is required, consider having a sidecar container retrieve the secret and write it to a shared volume, or have the application read directly from the secret store at runtime to avoid environment variables entirely [1][2]. - Ensure your logging configuration does not capture or log environment variables [2]. 6. Use Hierarchical Naming: When using SSM Parameter Store, adopt a consistent naming hierarchy (e.g., /app/env/key) to simplify IAM policy management and improve organization [9].

Citations:


🌐 Web query:

ECS environment vs secrets field security best practices

πŸ’‘ Result:

In Amazon ECS, the primary security best practice for handling sensitive data is to avoid hardcoding secrets in task definitions or Dockerfiles [1][2][3]. Instead, you should use the native secrets integration provided by the ECS task definition, which allows you to reference secrets stored in AWS Secrets Manager or AWS Systems Manager Parameter Store [1][4][5]. Comparison of Approaches: 1. ECS Secrets Field (Recommended): By using the secrets parameter in your container definition, ECS automatically retrieves the secret value from Secrets Manager or Parameter Store at runtime and injects it into the container [6][7]. This method ensures that sensitive values are not stored in plain text within your task definition or version control systems [3]. 2. Environment Variables (Standard): When you use the secrets field, ECS typically exposes these values as environment variables within the container [1][6][7]. While this is the standard and supported way to inject secrets, it carries inherent risks: environment variables can sometimes be exposed through logs, error messages, or by unauthorized processes inspecting the container (e.g., via docker inspect) [1][2]. Advanced Security Best Practices: To mitigate the risks associated with environment variables, consider these more secure patterns: - Direct SDK Retrieval: For highly sensitive applications, configure your application code to use the AWS SDK to fetch secrets directly from Secrets Manager or Parameter Store at runtime [1][2]. This avoids exposing the secret as an environment variable entirely [2]. - Sidecar Pattern: Run a sidecar container that retrieves the secret and writes it to a shared, memory-backed volume (tmpfs) [1][8]. Your main application container can then read the secret from this file, ensuring the secret never exists as an environment variable and resides only in memory [1][8]. - IAM Least Privilege: Regardless of the injection method, always use an IAM task execution role with the minimum necessary permissions to access only the specific secrets required by that task [1][8][2]. - Secret Rotation: Utilize AWS Secrets Manager to enable automatic secret rotation, which limits the window of opportunity if a secret is ever compromised [1][2]. In summary, while using the ECS secrets field is significantly more secure than hardcoding values, the most robust security posture involves either direct SDK retrieval or using a sidecar pattern to keep secrets out of the environment variable space [1][8][2].

Citations:


JWT_SECRETλ₯Ό ν™˜κ²½λ³€μˆ˜λ‘œ 평문 μ €μž₯ν•˜μ§€ λ§ˆμ„Έμš”.

ECS νƒœμŠ€ν¬ μ •μ˜ 리비전에 ν‰λ¬ΈμœΌλ‘œ μ €μž₯λ˜μ–΄ ecs:DescribeTaskDefinition κΆŒν•œλ§ŒμœΌλ‘œλ„ λ…ΈμΆœλ©λ‹ˆλ‹€. AWS λ³΄μ•ˆ λͺ¨λ²” 사둀에 따라 JWT_SECRET을 environment이 μ•„λ‹Œ ECS secrets ν•„λ“œμ˜ valueFrom으둜 Secrets Manager λ˜λŠ” SSM Parameter Storeλ₯Ό μ°Έμ‘°ν•˜λ„λ‘ λ³€κ²½ν•˜μ„Έμš”.

μ œμ•ˆ μˆ˜μ •
-            --arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \
+            --arg JWT_SECRET_ARN "${{ vars.JWT_SECRET_ARN }}" \
             '
             .containerDefinitions |= map(
               if .name == $CN then
                 .image = $IMG
                 | .environment = [
                     {"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE},
                     {"name":"SERVER_PORT","value":$SERVER_PORT},
                     {"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED},
                     {"name":"EUREKA_HOST","value":$EUREKA_HOST},
-                    {"name":"EUREKA_PORT","value":$EUREKA_PORT},
-                    {"name":"JWT_SECRET","value":$JWT_SECRET}
+                    {"name":"EUREKA_PORT","value":$EUREKA_PORT}
                   ]
+                | .secrets = (
+                    ((.secrets // []) | map(select(.name != "JWT_SECRET")))
+                    + [{"name":"JWT_SECRET","valueFrom":$JWT_SECRET_ARN}]
+                  )
               else .
               end
             )
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cd-prod.yml around lines 77 - 88, The JWT_SECRET is being
injected as a plaintext environment variable into the ECS task definition;
change the template to place it in the container's secrets with valueFrom
instead of in .environment. Specifically, remove the
{"name":"JWT_SECRET","value":$JWT_SECRET} entry from the .environment array in
the .containerDefinitions mapping and add a .secrets entry such as
{"name":"JWT_SECRET","valueFrom":$JWT_SECRET_ARN} (or a similarly named template
arg), update the workflow args to pass the secret ARN/SSM path (e.g., replace
--arg JWT_SECRET with --arg JWT_SECRET_ARN or add a new arg) and ensure the
value supplied comes from Secrets Manager or SSM Parameter Store via repository
secrets so the ECS task uses secrets.valueFrom rather than plaintext
environment.

]
Comment on lines +82 to +89

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the workflow file
find . -name "cd-prod.yml" -type f 2>/dev/null

Repository: Miche-Let/api-gateway

Length of output: 97


🏁 Script executed:

# Let's check if the file exists and read the relevant section
if [ -f ".github/workflows/cd-prod.yml" ]; then
  echo "=== Lines 70-100 of cd-prod.yml ===" 
  sed -n '70,100p' ".github/workflows/cd-prod.yml" | cat -n
else
  echo "File not found, checking directory structure..."
  find . -path "*workflows*" -name "*.yml" -o -name "*.yaml" | head -20
fi

Repository: Miche-Let/api-gateway

Length of output: 1663


🏁 Script executed:

# Get the full content of the workflow file to understand the context better
if [ -f ".github/workflows/cd-prod.yml" ]; then
  wc -l ".github/workflows/cd-prod.yml"
  echo "=== Full relevant section ===" 
  cat -n ".github/workflows/cd-prod.yml" | sed -n '75,95p'
fi

Repository: Miche-Let/api-gateway

Length of output: 1225


μ»¨ν…Œμ΄λ„ˆ ν™˜κ²½λ³€μˆ˜λ₯Ό μ™„μ „νžˆ κ΅μ²΄ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

Line 82의 .environment = [...] 할당은 κΈ°μ‘΄ ν™˜κ²½λ³€μˆ˜ λ°°μ—΄ 전체λ₯Ό μƒˆλ‘œμš΄ λ°°μ—΄λ‘œ κ΅μ²΄ν•˜λ―€λ‘œ, task definition에 이미 μ„€μ •λœ λ‹€λ₯Έ λŸ°νƒ€μž„ ν™˜κ²½λ³€μˆ˜κ°€ λͺ¨λ‘ μ†μ‹€λ©λ‹ˆλ‹€. ν•„μš”ν•œ λ³€μˆ˜λ“€λ§Œ κ°±μ‹ /μΆ”κ°€ν•˜λ„λ‘ merge μž‘μ—…μœΌλ‘œ λ³€κ²½ν•΄μ•Ό 배포 μ‹œ μ„€μ • μœ μ‹€μ„ λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ œμ•ˆ μˆ˜μ •
-                | .environment = [
-                    {"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE},
-                    {"name":"SERVER_PORT","value":$SERVER_PORT},
-                    {"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED},
-                    {"name":"EUREKA_HOST","value":$EUREKA_HOST},
-                    {"name":"EUREKA_PORT","value":$EUREKA_PORT},
-                    {"name":"JWT_SECRET","value":$JWT_SECRET}
-                  ]
+                | .environment = (
+                    ((.environment // []) | map({key: .name, value: .value}) | from_entries)
+                    + {
+                        "SPRING_PROFILES_ACTIVE": $SPRING_PROFILES_ACTIVE,
+                        "SERVER_PORT": $SERVER_PORT,
+                        "EUREKA_ENABLED": $EUREKA_ENABLED,
+                        "EUREKA_HOST": $EUREKA_HOST,
+                        "EUREKA_PORT": $EUREKA_PORT,
+                        "JWT_SECRET": $JWT_SECRET
+                      }
+                    | to_entries
+                    | map({"name": .key, "value": .value})
+                  )
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cd-prod.yml around lines 82 - 89, The current assignment
to .environment replaces the entire environment array and wipes out any
pre-existing variables; instead update the code that assigns .environment so it
merges/updates entries rather than overwriting: locate the .environment = [...]
block and change the logic to iterate over the listed names
(SPRING_PROFILES_ACTIVE, SERVER_PORT, EUREKA_ENABLED, EUREKA_HOST, EUREKA_PORT,
JWT_SECRET) and for each, update the existing env entry with the same "name" if
present or append a new entry if not, preserving all other existing environment
variables in the task definition.

else .
end
)
| del(
.taskDefinitionArn, .revision, .status,
.requiresAttributes, .compatibilities,
.registeredAt, .registeredBy
)
' taskdef.json > taskdef.new.json

- name: Register new task definition revision
id: register
run: |
ARN=$(aws ecs register-task-definition \
--cli-input-json file://taskdef.new.json \
--query 'taskDefinition.taskDefinitionArn' \
--output text)
echo "task_def_arn=${ARN}" >> "$GITHUB_OUTPUT"

- name: Deploy to ECS service
run: |
aws ecs update-service \
--cluster "${ECS_CLUSTER}" \
--service "${ECS_SERVICE}" \
--task-definition "${{ steps.register.outputs.task_def_arn }}" \
--force-new-deployment

- name: Wait for stable
run: |
aws ecs wait services-stable \
--cluster "${ECS_CLUSTER}" \
--services "${ECS_SERVICE}"
59 changes: 59 additions & 0 deletions .github/workflows/ci-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# .github/workflows/ci-prod.yml
name: CI-PROD

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read
id-token: write

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Grant execute permission for gradlew
run: chmod +x ./gradlew

- name: Run tests
run: ./gradlew test --no-daemon

- name: Generate image tag
if: github.event_name == 'push'
run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV

- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
Comment on lines +41 to +45

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/ci-prod.yml

Repository: Miche-Let/api-gateway

Length of output: 1984


PR κ²€μ¦μ—μ„œλ„ AWS 인증을 μ‹œλ„ν•©λ‹ˆλ‹€.

Line 41-45의 Configure AWS credentials (OIDC) λ‹¨κ³„λŠ” if 쑰건이 μ—†μ–΄μ„œ pull_requestμ—μ„œλ„ μ‹€ν–‰λ©λ‹ˆλ‹€. ν•˜μ§€λ§Œ 이미지 ν‘Έμ‹œ κ΄€λ ¨ λͺ¨λ“  단계(line 48, 52, 57)λŠ” if: github.event_name == 'push'둜 λ³΄ν˜Έλ˜μ–΄ μžˆμœΌλ―€λ‘œ, AWS 인증도 λ™μΌν•˜κ²Œ λ³΄ν˜Έν•΄μ•Ό ν•©λ‹ˆλ‹€. ν˜„μž¬ μƒνƒœμ—μ„œλŠ” PR 검증 μ‹œ λΆˆν•„μš”ν•˜κ²Œ ν”„λ‘œλ•μ…˜ AWS role/OIDC에 μ ‘κ·Όν•˜λ € μ‹œλ„ν•˜λ©°, PR μ»¨ν…μŠ€νŠΈμ—μ„œ κΆŒν•œμ΄ μ—†μœΌλ©΄ μ‹€νŒ¨ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ œμ•ˆ μˆ˜μ •
       - name: Configure AWS credentials (OIDC)
+        if: github.event_name == 'push'
         uses: aws-actions/configure-aws-credentials@v4
         with:
           role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
           aws-region: ${{ vars.AWS_REGION }}
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- name: Configure AWS credentials (OIDC)
if: github.event_name == 'push'
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci-prod.yml around lines 41 - 45, The "Configure AWS
credentials (OIDC)" job step is currently unconditional and runs for
pull_request events; guard it the same as the image-push steps by adding if:
github.event_name == 'push' to that step so AWS OIDC/role-to-assume is only
configured on push events; update the step that uses
aws-actions/configure-aws-credentials@v4 (the Configure AWS credentials (OIDC)
step) to include the if condition and keep role-to-assume and aws-region inputs
unchanged.


- name: Login to Amazon ECR
if: github.event_name == 'push'
uses: aws-actions/amazon-ecr-login@v2

- name: Build Docker image
if: github.event_name == 'push'
run: |
docker build -t ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} .

- name: Push Docker image to ECR
if: github.event_name == 'push'
run: |
docker push ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Java CI with Gradle

on:
pull_request:
branches: [ "dev", "main" ]
branches: [ "dev"]

jobs:
build:
Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ dependencies {
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

implementation 'org.springframework.boot:spring-boot-starter-actuator'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify duplicate actuator declarations in Gradle dependencies
rg -n "spring-boot-starter-actuator" build.gradle

Repository: Miche-Let/api-gateway

Length of output: 215


spring-boot-starter-actuator μ˜μ‘΄μ„±μ΄ 쀑볡 μ„ μ–Έλ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

Line 41은 Line 25와 λ™μΌν•œ μ˜μ‘΄μ„±μ΄λ―€λ‘œ μ€‘λ³΅μž…λ‹ˆλ‹€. ν•˜λ‚˜λ§Œ μœ μ§€ν•˜μ„Έμš”.

πŸ”§ μ œμ•ˆ μˆ˜μ •
-	implementation 'org.springframework.boot:spring-boot-starter-actuator'
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
implementation 'org.springframework.boot:spring-boot-starter-actuator'
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build.gradle` at line 41, The dependency
'org.springframework.boot:spring-boot-starter-actuator' is declared twice;
remove the duplicate declaration and keep only one entry for implementation
'org.springframework.boot:spring-boot-starter-actuator' (remove either the
occurrence at the location matching the diff showing Line 41 or the earlier
declaration at Line 25) so the build.gradle contains a single actuator
dependency entry.


}

dependencyManagement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, Ga
exchanges-> exchanges.pathMatchers(
"/api/*/auth/login",
"/api/*/auth/reissue",
"/api/*/users/signup"
"/api/*/users/signup", "/actuator/health", "/actuator/info"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify where /actuator/info is publicly exposed or referenced
rg -n "/actuator/info|permitAll\\(|management\\.endpoint\\.info|endpoints\\.web\\.exposure" \
  src/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.java \
  src/main/resources/application-prod.yml \
  src/main/resources/application.yml

Repository: Miche-Let/api-gateway

Length of output: 355


🏁 Script executed:

# SecurityConfig.java 전체 λ‚΄μš© 확인
cat -n src/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.java

Repository: Miche-Let/api-gateway

Length of output: 2979


🏁 Script executed:

# μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„€μ • νŒŒμΌμ—μ„œ actuator κ΄€λ ¨ μ„€μ • 확인
cat src/main/resources/application.yml
cat src/main/resources/application-prod.yml 2>/dev/null || echo "application-prod.yml not found"

Repository: Miche-Let/api-gateway

Length of output: 2715


🏁 Script executed:

# actuator μ—”λ“œν¬μΈνŠΈ κ΄€λ ¨ λ‹€λ₯Έ μ°Έμ‘° 확인
rg -n "actuator" src/main/

Repository: Miche-Let/api-gateway

Length of output: 237


/actuator/infoλŠ” 곡개 ν—ˆμš© λŒ€μƒμ—μ„œ μ œμ™Έν•˜λŠ” 것이 μ•ˆμ „ν•©λ‹ˆλ‹€.

Line 31μ—μ„œ /actuator/infoκΉŒμ§€ permitAll둜 μ—΄λ € μžˆμŠ΅λ‹ˆλ‹€. ν”„λ‘œλ•μ…˜ ν—¬μŠ€μ²΄ν¬ λͺ©μ μ΄λ©΄ /actuator/health만 κ³΅κ°œν•˜λŠ” 편이 μ•ˆμ „ν•©λ‹ˆλ‹€. /actuator/infoλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 이름, 버전, λΉŒλ“œ 정보 λ“± 메타데이터λ₯Ό λ…ΈμΆœν•˜λ―€λ‘œ μ œκ±°ν•˜μ—¬ 정보 곡개 μ΅œμ†Œν™” 원칙을 λ”°λ₯΄λŠ” 것이 ꢌμž₯λ©λ‹ˆλ‹€.

πŸ”§ μ œμ•ˆ μˆ˜μ •
-                                "/api/*/users/signup", "/actuator/health", "/actuator/info"
+                                "/api/*/users/signup", "/actuator/health"
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.java`
at line 31, Remove `/actuator/info` from the list of publicly permitted
endpoints in SecurityConfig so only `/actuator/health` remains exposed; locate
the authorizeRequests/antMatchers(...) call (inside SecurityConfig, e.g., the
configure(HttpSecurity) method or wherever permitAll is invoked) and edit the
antMatchers/permitAll array to omit "/actuator/info", leaving other entries like
"/api/*/users/signup" and "/actuator/health" unchanged.

).permitAll()
.pathMatchers("/api/*/admin/**").hasRole("MASTER")
.anyExchange().authenticated()
Expand Down
30 changes: 30 additions & 0 deletions src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
server:
port: ${SERVER_PORT:19000}

eureka:
client:
enabled: ${EUREKA_ENABLED:true}
service-url:
defaultZone: http://${EUREKA_HOST}:${EUREKA_PORT}/eureka/
instance:
prefer-ip-address: true

spring:
cloud:
inetutils:
ignored-interfaces:
- "lo"
- "docker.*"
- "veth.*"
preferred-networks:
- "10.0"
use-only-site-local-interfaces: true

management:
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
health:
show-details: always
Comment on lines +29 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify risky combination across prod config + security config
rg -n "show-details|/actuator/health|/actuator/info" \
  src/main/resources/application-prod.yml \
  src/main/java/com/michelet/gateway/infrastructure/config/SecurityConfig.java

Repository: Miche-Let/api-gateway

Length of output: 307


곡개 ν—¬μŠ€ μ—”λ“œν¬μΈνŠΈμ™€ show-details: always 쑰합은 λ‚΄λΆ€ 정보 λ…ΈμΆœ λ³΄μ•ˆ μœ„ν—˜μ„ μ΄ˆλž˜ν•©λ‹ˆλ‹€.

SecurityConfigμ—μ„œ /actuator/health와 /actuator/infoκ°€ 인증 없이 κ³΅κ°œλ˜μ–΄ 있고, application-prod.yml의 line 30μ—μ„œ show-details: always둜 μ„€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 이 쑰합은 μΈμ¦λ˜μ§€ μ•Šμ€ μ‚¬μš©μžκ°€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μƒμ„Έν•œ λ‚΄λΆ€ μƒνƒœ 정보(λ°μ΄ν„°λ² μ΄μŠ€, λ””μŠ€ν¬, μΊμ‹œ λ“±)에 μ ‘κ·Όν•  수 있게 ν•˜λ―€λ‘œ 운영 ν™˜κ²½μ—μ„œ μ¦‰μ‹œ μˆ˜μ •μ΄ ν•„μš”ν•©λ‹ˆλ‹€.

πŸ”§ μ œμ•ˆ μˆ˜μ •
 management:
   endpoint:
     health:
-      show-details: always
+      show-details: when-authorized
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
health:
show-details: always
health:
show-details: when-authorized
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/application-prod.yml` around lines 29 - 30, The production
config currently exposes detailed health info (health.show-details: always)
while SecurityConfig leaves /actuator/health and /actuator/info public; change
health.show-details in application-prod.yml from "always" to "when-authorized"
(or "never" if you prefer no details at all) and update SecurityConfig to stop
permitting anonymous access to /actuator/health and /actuator/info (require
authentication or a specific role such as ROLE_ACTUATOR) so that detailed health
payloads are only returned to authorized callers; target the health.show-details
setting and the SecurityConfig class (the permitAll/antMatchers for /actuator/*)
when making these edits.

20 changes: 10 additions & 10 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,54 @@ spring:
webflux:
routes:
- id: user-service
uri: lb://USER-SERVICE
uri: lb://user-service
predicates:
- Path=/api/v1/users/**,/api/v1/admin/users/**,/api/v1/auth/**,/api/v1/admin/auth/**

- id: timeslot-service
uri: lb://TIMESLOT-SERVICE
uri: lb://timeslot-service
predicates:
- Path=/api/v1/restaurants/*/time-slots/**,/api/v1/admin/restaurants/*/time-slots/**

- id: restaurant-service
uri: lb://RESTAURANT-SERVICE
uri: lb://restaurant-service
predicates:
- Path=/api/v1/restaurants/**,/api/v1/admin/restaurants/**

- id: reservation-service
uri: lb://RESERVATION-SERVICE
uri: lb://reservation-service
predicates:
- Path=/api/v1/reservations/**,/api/v1/admin/reservations/**

- id: catalog-service
uri: lb://CATALOG-SERVICE
uri: lb://catalog-service
predicates:
- Path=/api/v1/products/**
- Method=GET

- id: inventory-service
uri: lb://INVENTORY-SERVICE
uri: lb://inventory-service
predicates:
- Path=/api/v1/products/**,/api/v1/admin/products/**
- Method=POST,PUT,PATCH,DELETE

- id: order-service
uri: lb://ORDER-SERVICE
uri: lb://order-service
predicates:
- Path=/api/v1/orders/**,/api/v1/admin/orders/**

- id: waiting-service
uri: lb://WAITING-SERVICE
uri: lb://waiting-service
predicates:
- Path=/api/v1/waitings/**,/api/v1/admin/waitings/**

- id: notification-service
uri: lb://NOTIFICATION-SERVICE
uri: lb://notification-service
predicates:
- Path=/api/v1/notifications/**,/api/v1/admin/notifications/**

- id: payment-service
uri: lb://PAYMENT-SERVICE
uri: lb://payment-service
predicates:
- Path=/api/v1/payments/**,/api/v1/admin/payments/**

Expand Down
Loading