deploy-stack 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/init.js +5 -0
- package/templates/README.md +12 -1
- package/templates/docker/nextjs.Dockerfile +11 -4
- package/templates/docker/node.Dockerfile +6 -8
- package/templates/docker/python.Dockerfile +2 -4
- package/templates/docker/static.Dockerfile +1 -14
- package/templates/github/deploy.yml +56 -1
- package/templates/terraform/cloudfront.tf +1 -0
- package/templates/terraform/main.tf +8 -0
- package/templates/terraform/network.tf +3 -0
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -261,6 +261,11 @@ export async function mainStack() {
|
|
|
261
261
|
outro(`
|
|
262
262
|
${color.green('✅ Project provisioned successfully!')}
|
|
263
263
|
|
|
264
|
+
${color.blue('🛡️ DevSecOps Enabled:')}
|
|
265
|
+
Automated Trivy vulnerability scanning for your Docker container
|
|
266
|
+
and Terraform has been added to your CI/CD pipeline.
|
|
267
|
+
Check the 'Summary' page of your GitHub Actions runs for reports.
|
|
268
|
+
|
|
264
269
|
${frameworkWarnings}
|
|
265
270
|
|
|
266
271
|
Next steps:
|
package/templates/README.md
CHANGED
|
@@ -96,4 +96,15 @@ Make sure your app is configured correctly:
|
|
|
96
96
|
|
|
97
97
|
If you are deploying a static site, your application is served via a highly optimized, unprivileged Nginx container.
|
|
98
98
|
* **Zero-Config Build:** The CLI automatically detected your framework's output folder (`dist`, `build`, etc.) and pre-configured your Dockerfile.
|
|
99
|
-
* **Health Checks:** You do not need to configure a custom `/health` route. Nginx will automatically return a `200 OK` when AWS pings the root `/` index page.
|
|
99
|
+
* **Health Checks:** You do not need to configure a custom `/health` route. Nginx will automatically return a `200 OK` when AWS pings the root `/` index page.
|
|
100
|
+
|
|
101
|
+
## 🛡️ Security Scanning
|
|
102
|
+
|
|
103
|
+
This pipeline includes automated DevSecOps guardrails using [Trivy](https://trivy.dev/).
|
|
104
|
+
Every time you push code, the pipeline will scan both your Docker container and your
|
|
105
|
+
Terraform configurations for vulnerabilities and misconfigurations.
|
|
106
|
+
|
|
107
|
+
To view the security reports:
|
|
108
|
+
1. Navigate to the **Actions** tab in GitHub.
|
|
109
|
+
2. Click on the latest deployment run.
|
|
110
|
+
3. Scroll down the **Summary** page to view the generated vulnerability tables.
|
|
@@ -21,10 +21,17 @@ ENV NODE_ENV=production
|
|
|
21
21
|
ENV PORT={{PORT}}
|
|
22
22
|
ENV HOSTNAME="0.0.0.0"
|
|
23
23
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
# Create an unprivileged user and group
|
|
25
|
+
RUN addgroup -g 1001 -S nodejs && \
|
|
26
|
+
adduser -S nextjs -u 1001 -G nodejs
|
|
27
|
+
|
|
28
|
+
# Copy the standalone output and assign ownership to the unprivileged user
|
|
29
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
30
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
31
|
+
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
|
32
|
+
|
|
33
|
+
# Switch to the unprivileged user before executing
|
|
34
|
+
USER nextjs
|
|
28
35
|
|
|
29
36
|
EXPOSE {{PORT}}
|
|
30
37
|
|
|
@@ -5,16 +5,14 @@ ENV NODE_ENV=production
|
|
|
5
5
|
|
|
6
6
|
WORKDIR /app
|
|
7
7
|
|
|
8
|
-
# 2. Copy
|
|
9
|
-
COPY package*.json ./
|
|
8
|
+
# 2. Copy dependency manifests with non-root ownership
|
|
9
|
+
COPY --chown=node:node package*.json ./
|
|
10
|
+
RUN npm ci --omit=dev
|
|
10
11
|
|
|
11
|
-
# 3.
|
|
12
|
-
|
|
12
|
+
# 3. Copy application code with non-root ownership
|
|
13
|
+
COPY --chown=node:node . .
|
|
13
14
|
|
|
14
|
-
# 4.
|
|
15
|
-
COPY . .
|
|
16
|
-
|
|
17
|
-
# 5. DevSecOps best practice: do not run the container as root
|
|
15
|
+
# 4. DevSecOps best practice: do not run the container as root
|
|
18
16
|
USER node
|
|
19
17
|
|
|
20
18
|
EXPOSE {{PORT}}
|
|
@@ -13,10 +13,8 @@ RUN adduser --disabled-password --gecos '' appuser
|
|
|
13
13
|
COPY requirements.txt .
|
|
14
14
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# Secure file permissions
|
|
19
|
-
RUN chown -R appuser:appuser /app
|
|
16
|
+
# Copy with ownership
|
|
17
|
+
COPY --chown=appuser:appuser . .
|
|
20
18
|
|
|
21
19
|
# Drop root privileges
|
|
22
20
|
USER appuser
|
|
@@ -11,7 +11,7 @@ COPY . .
|
|
|
11
11
|
RUN npm run build
|
|
12
12
|
|
|
13
13
|
# STAGE 2: Serve with Hardened Nginx
|
|
14
|
-
FROM nginx:alpine
|
|
14
|
+
FROM nginxinc/nginx-unprivileged:alpine
|
|
15
15
|
|
|
16
16
|
# Adjusts BUILD_DIR to match your framework's output folder
|
|
17
17
|
COPY --from=builder /app/{{BUILD_DIR}} /usr/share/nginx/html
|
|
@@ -28,18 +28,5 @@ RUN echo "server {" > /etc/nginx/conf.d/default.conf && \
|
|
|
28
28
|
echo " }" >> /etc/nginx/conf.d/default.conf && \
|
|
29
29
|
echo "}" >> /etc/nginx/conf.d/default.conf
|
|
30
30
|
|
|
31
|
-
# Silence unprivileged user directive warning in main config
|
|
32
|
-
RUN sed -i 's/^user\s\+nginx;/# user nginx;/' /etc/nginx/nginx.conf
|
|
33
|
-
|
|
34
|
-
# DevSecOps Hardening: Drop root privileges for the Nginx process
|
|
35
|
-
RUN chown -R nginx:nginx /usr/share/nginx/html && \
|
|
36
|
-
chown -R nginx:nginx /var/cache/nginx && \
|
|
37
|
-
chown -R nginx:nginx /var/log/nginx && \
|
|
38
|
-
chown -R nginx:nginx /etc/nginx/conf.d && \
|
|
39
|
-
touch /var/run/nginx.pid && \
|
|
40
|
-
chown -R nginx:nginx /var/run/nginx.pid
|
|
41
|
-
|
|
42
|
-
USER nginx
|
|
43
|
-
|
|
44
31
|
EXPOSE {{PORT}}
|
|
45
32
|
CMD ["nginx", "-g", "daemon off;"]
|
|
@@ -4,6 +4,8 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches:
|
|
6
6
|
- {{DEPLOY_BRANCH}}
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: '0 0 * * 0'
|
|
7
9
|
|
|
8
10
|
env:
|
|
9
11
|
AWS_REGION: {{REGION}}
|
|
@@ -24,6 +26,29 @@ jobs:
|
|
|
24
26
|
- name: Checkout Code
|
|
25
27
|
uses: actions/checkout@v4
|
|
26
28
|
|
|
29
|
+
# --- 1. IaC SECURITY SCAN ---
|
|
30
|
+
- name: Scan Terraform for Misconfigurations
|
|
31
|
+
uses: aquasecurity/trivy-action@master
|
|
32
|
+
with:
|
|
33
|
+
scan-type: 'fs'
|
|
34
|
+
scan-ref: 'terraform/'
|
|
35
|
+
scanners: 'vuln,secret,misconfig'
|
|
36
|
+
format: 'table'
|
|
37
|
+
exit-code: '0' # Informational only, will not break the build
|
|
38
|
+
severity: 'CRITICAL,HIGH'
|
|
39
|
+
output: 'trivy-iac-summary.txt'
|
|
40
|
+
|
|
41
|
+
- name: Publish IaC Summary
|
|
42
|
+
if: always()
|
|
43
|
+
run: |
|
|
44
|
+
if [ -f trivy-iac-summary.txt ]; then
|
|
45
|
+
echo "### 🏗️ Terraform Misconfiguration Scan" >> $GITHUB_STEP_SUMMARY
|
|
46
|
+
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
47
|
+
cat trivy-iac-summary.txt >> $GITHUB_STEP_SUMMARY
|
|
48
|
+
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# --- 2. DEPLOYMENT PREP ---
|
|
27
52
|
- name: Configure AWS Credentials
|
|
28
53
|
uses: aws-actions/configure-aws-credentials@v4
|
|
29
54
|
with:
|
|
@@ -34,13 +59,43 @@ jobs:
|
|
|
34
59
|
id: login-ecr
|
|
35
60
|
uses: aws-actions/amazon-ecr-login@v2
|
|
36
61
|
|
|
37
|
-
|
|
62
|
+
# --- 3. BUILD ---
|
|
63
|
+
- name: Build Docker image
|
|
38
64
|
id: build-image
|
|
39
65
|
env:
|
|
40
66
|
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
|
|
41
67
|
IMAGE_TAG: latest
|
|
42
68
|
run: |
|
|
43
69
|
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
|
|
70
|
+
|
|
71
|
+
# --- 4. CONTAINER SECURITY SCAN ---
|
|
72
|
+
- name: Scan Container for Vulnerabilities (SARIF Output)
|
|
73
|
+
uses: aquasecurity/trivy-action@master
|
|
74
|
+
with:
|
|
75
|
+
image-ref: '${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest'
|
|
76
|
+
format: 'table'
|
|
77
|
+
output: 'trivy-container-summary.txt'
|
|
78
|
+
exit-code: '0' # Informational only, will not break the build
|
|
79
|
+
ignore-unfixed: true
|
|
80
|
+
vuln-type: 'os,library'
|
|
81
|
+
severity: 'CRITICAL,HIGH'
|
|
82
|
+
|
|
83
|
+
- name: Publish Container Summary
|
|
84
|
+
if: always()
|
|
85
|
+
run: |
|
|
86
|
+
if [ -f trivy-container-summary.txt ]; then
|
|
87
|
+
echo "### 🛡️ Container Security Scan Results" >> $GITHUB_STEP_SUMMARY
|
|
88
|
+
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
89
|
+
cat trivy-container-summary.txt >> $GITHUB_STEP_SUMMARY
|
|
90
|
+
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# --- 5. DEPLOY ---
|
|
94
|
+
- name: Push image to Amazon ECR
|
|
95
|
+
env:
|
|
96
|
+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
|
|
97
|
+
IMAGE_TAG: latest
|
|
98
|
+
run: |
|
|
44
99
|
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
|
|
45
100
|
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
|
|
46
101
|
|
|
@@ -8,6 +8,7 @@ data "aws_cloudfront_origin_request_policy" "all_viewer" {
|
|
|
8
8
|
name = "Managed-AllViewerExceptHostHeader"
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
# trivy:ignore:AVD-AWS-0011 - WAF is omitted by default to prevent unexpected monthly costs for users
|
|
11
12
|
resource "aws_cloudfront_distribution" "cdn" {
|
|
12
13
|
enabled = true
|
|
13
14
|
is_ipv6_enabled = true
|
|
@@ -17,8 +17,13 @@ resource "aws_cloudwatch_log_group" "app_logs" {
|
|
|
17
17
|
# --- ECR Repository ---
|
|
18
18
|
resource "aws_ecr_repository" "app" {
|
|
19
19
|
name = "{{PROJECT_NAME}}-repo"
|
|
20
|
+
# trivy:ignore:AVD-AWS-0031 - Mutable tags allow the CI/CD pipeline to reuse the 'latest' tag for simplified deployments
|
|
20
21
|
image_tag_mutability = "MUTABLE"
|
|
21
22
|
force_delete = true
|
|
23
|
+
|
|
24
|
+
image_scanning_configuration {
|
|
25
|
+
scan_on_push = true
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
# --- IAM: Execution Role ---
|
|
@@ -100,11 +105,13 @@ resource "aws_ecs_task_definition" "app" {
|
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
# --- Application Load Balancer ---
|
|
108
|
+
# trivy:ignore:AVD-AWS-0053 - This ALB is intended to be publicly facing behind CloudFront
|
|
103
109
|
resource "aws_lb" "main" {
|
|
104
110
|
name = "{{PROJECT_NAME}}-alb"
|
|
105
111
|
load_balancer_type = "application"
|
|
106
112
|
security_groups = [aws_security_group.alb.id]
|
|
107
113
|
subnets = aws_subnet.public[*].id
|
|
114
|
+
drop_invalid_header_fields = true
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
resource "aws_lb_target_group" "app" {
|
|
@@ -124,6 +131,7 @@ resource "aws_lb_target_group" "app" {
|
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
|
|
134
|
+
# trivy:ignore:AVD-AWS-0054 - CloudFront handles HTTPS edge termination; ALB uses HTTP to avoid complex ACM DNS validation for users
|
|
127
135
|
resource "aws_lb_listener" "http" {
|
|
128
136
|
load_balancer_arn = aws_lb.main.arn
|
|
129
137
|
port = "80"
|
|
@@ -19,6 +19,7 @@ resource "aws_internet_gateway" "main" {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
# Create 2 Public Subnets
|
|
22
|
+
# trivy:ignore:AVD-AWS-0164 - Public subnets are used to avoid the $30/mo cost of a NAT Gateway for outbound container traffic
|
|
22
23
|
resource "aws_subnet" "public" {
|
|
23
24
|
count = 2
|
|
24
25
|
vpc_id = aws_vpc.main.id
|
|
@@ -64,6 +65,7 @@ resource "aws_security_group" "alb" {
|
|
|
64
65
|
from_port = 0
|
|
65
66
|
to_port = 0
|
|
66
67
|
protocol = "-1"
|
|
68
|
+
# trivy:ignore:AVD-AWS-0104 - Allow ALB to route out to standard AWS services
|
|
67
69
|
cidr_blocks = ["0.0.0.0/0"]
|
|
68
70
|
}
|
|
69
71
|
}
|
|
@@ -85,6 +87,7 @@ resource "aws_security_group" "ecs_tasks" {
|
|
|
85
87
|
from_port = 0
|
|
86
88
|
to_port = 0
|
|
87
89
|
protocol = "-1"
|
|
90
|
+
# trivy:ignore:AVD-AWS-0104 - Allow containers to pull images and hit external APIs
|
|
88
91
|
cidr_blocks = ["0.0.0.0/0"]
|
|
89
92
|
}
|
|
90
93
|
}
|