Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated deploy preview workflow and docker files accordingly (test Nginx config) #4045

Open
wants to merge 8 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
11 changes: 4 additions & 7 deletions .github/workflows/deploy-previews.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1180,8 +1180,8 @@ jobs:

- name: Build and Push Container
run: |
cd src/website/
docker build --tag ${{ env.REGISTRY_URL }}/${{ env.PROJECT_ID }}/pr-previews/website-pr-previews:${{ github.sha }} ./
cd website/
docker build --tag ${{ env.REGISTRY_URL }}/${{ env.PROJECT_ID }}/pr-previews/website-pr-previews:${{ github.sha }} .
docker push ${{ env.REGISTRY_URL }}/${{ env.PROJECT_ID }}/pr-previews/website-pr-previews:${{ github.sha }}

- name: Deploy to Cloud Run
Expand All @@ -1192,11 +1192,8 @@ jobs:
--timeout=60 \
--concurrency=10 \
--image=${{ env.REGISTRY_URL }}/${{ env.PROJECT_ID }}/pr-previews/website-pr-previews:${{ github.sha }} \
--port=8000 \
--cpu=1000m \
--memory=1024Mi \
--update-secrets=/etc/env/.env=sta-env-website-backend:latest,/etc/config/google_application_credentials.json=sta-key-analytics-service-account:latest \
--command="/bin/sh","-c","cat /etc/env/.env >> /app/.env; /app/entrypoint.sh" \
--allow-unauthenticated

- name: Get preview service url
Expand All @@ -1221,5 +1218,5 @@ jobs:
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'website changes in this PR available for preview [here](${{ needs.website.outputs.url }})'
})
body: 'Website changes in this PR are available for preview [here](${{ needs.website.outputs.url }})'
})
40 changes: 29 additions & 11 deletions src/website/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,48 @@
FROM python:3.11-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Set the working directory inside the container
# Set work directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
libpq-dev \
&& apt-get clean
nginx \
supervisor \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Copy requirements file and install dependencies
COPY requirements.txt ./
# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the backend code into the container
# Copy project code
COPY . .

# Expose the port the Django app will run on
EXPOSE 8000
# Remove default Nginx configuration if it exists
RUN rm -f /etc/nginx/conf.d/default.conf

# Add execution permissions to entrypoint.sh
# Copy custom Nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Ensure staticfiles directory exists for Nginx
RUN mkdir -p /usr/share/nginx/html/static/

# Copy Supervisor configuration
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# Make entrypoint.sh executable
RUN chmod +x /app/entrypoint.sh

# Set the entrypoint for the container
# Set the PORT environment variable (Cloud Run uses 8080 by default)
ENV PORT=8080

# Expose the port
EXPOSE 8080

# Start the entrypoint script
ENTRYPOINT ["/app/entrypoint.sh"]
9 changes: 3 additions & 6 deletions src/website/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
#!/bin/sh

# Exit immediately if a command exits with a non-zero status
# Exit on error
set -e

# Run Django migrations
echo "Running migrations..."
python manage.py migrate --noinput

# Collect static files (ensure the static files directory exists)
echo "Collecting static files..."
python manage.py collectstatic --noinput

# Start Gunicorn server to serve the Django application
echo "Starting Gunicorn server..."
exec gunicorn core.wsgi:application --bind 0.0.0.0:8000 --timeout 600 --workers 3 --log-level info
echo "Starting Supervisor (which runs Nginx and Gunicorn)..."
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
51 changes: 51 additions & 0 deletions src/website/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

# Set client body size limit to 10MB
client_max_body_size 10M;

upstream django_app {
# Gunicorn is running on 127.0.0.1:8000 inside the container
server 127.0.0.1:8000;
}

server {
listen ${PORT};
server_name _;

# Serve static files
location /static/ {
alias /usr/share/nginx/html/static/;
expires 1y;
access_log off;
add_header Cache-Control "public";
}

# Proxy all other requests to Gunicorn
location / {
proxy_pass http://django_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Comment on lines +43 to +49
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add security headers and rate limiting

The proxy configuration looks good, but missing important security headers and rate limiting.

Add these security enhancements:

# Add before the location blocks
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

# Add inside the location / block
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
limit_req zone=one burst=5 nodelay;

}
}
17 changes: 17 additions & 0 deletions src/website/supervisord.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[supervisord]
nodaemon=true

[program:gunicorn]
command=gunicorn core.wsgi:application --bind 127.0.0.1:8000 --timeout 600 --workers 3 --log-level info
directory=/app
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/gunicorn.log
stderr_logfile=/var/log/supervisor/gunicorn_err.log

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx_err.log
Loading