diff --git a/Deployment/Test/Dockerfile.nginx b/Deployment/Test/Dockerfile.nginx new file mode 100644 index 0000000..fa50588 --- /dev/null +++ b/Deployment/Test/Dockerfile.nginx @@ -0,0 +1,4 @@ +FROM nginx:1.23.1-alpine + +RUN rm /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/conf.d \ No newline at end of file diff --git a/Deployment/Test/Dockerfile.python b/Deployment/Test/Dockerfile.python new file mode 100644 index 0000000..ba2025a --- /dev/null +++ b/Deployment/Test/Dockerfile.python @@ -0,0 +1,11 @@ +FROM python:3.10 + +COPY clubhaus/requirements.txt /tmp/requirements.txt +RUN pip3 install -r /tmp/requirements.txt +RUN pip3 install gunicorn~=20.1.0 + +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +ENV DEBUG_MODE=False +WORKDIR /usr/src/app diff --git a/Deployment/Test/docker-compose.yml b/Deployment/Test/docker-compose.yml new file mode 100644 index 0000000..380a802 --- /dev/null +++ b/Deployment/Test/docker-compose.yml @@ -0,0 +1,28 @@ +version: "3" +services: + backend: + container_name: clubhaus_backend + image: icaotix/clubhaus_backend + build: + context: ../../ + dockerfile: Deployment/Test/Dockerfile.python + command: gunicorn clubhaus.wsgi:application --bind 0.0.0.0:8000 + volumes: + - ../../clubhaus:/usr/src/app + environment: + - ALLOWED_HOSTS=internal.security + - CSRF_TRUSTED_ORIGINS=internal.security + - IP_RATE_LIMIT_TIME=1 # rate limit doesn't work in docker networking therefore disable it + + nginx: + container_name: clubhaus_nginx + image: icaotix/clubhaus_nginx + build: + context: . + dockerfile: Dockerfile.nginx + volumes: + - ../../clubhaus:/usr/src/app:ro + ports: + - 1337:80 + depends_on: + - backend diff --git a/Deployment/Test/nginx.conf b/Deployment/Test/nginx.conf new file mode 100644 index 0000000..5bc8185 --- /dev/null +++ b/Deployment/Test/nginx.conf @@ -0,0 +1,34 @@ +upstream clubhaus_website { + server backend:8000; +} + +server { + + listen 80; + + location / { + proxy_pass http://clubhaus_website; + # forwarding the remote ip + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header REMOTE_ADDR $remote_addr; + # setting the origin always to the same value as in the docker compose file + # removes the need to set the host on different dev envs + # NEVER USE IN PRODUCTION + proxy_set_header Origin "http://internal.security"; + proxy_set_header Host "internal.security"; + proxy_redirect off; + # allow uploads up to 20 megabytes (like pictures) + client_max_body_size 20M; + } + + # redirect static files to filesystem + location /static/ { + alias /usr/src/app/static/; + } + + # redirect media files to filesystem (user uploaded files) + location /media/ { + alias /usr/src/app/media/; + } + +} \ No newline at end of file diff --git a/clubhaus/clubhaus/settings.py b/clubhaus/clubhaus/settings.py index 88147fd..c226bfa 100644 --- a/clubhaus/clubhaus/settings.py +++ b/clubhaus/clubhaus/settings.py @@ -9,6 +9,7 @@ https://docs.djangoproject.com/en/4.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.0/ref/settings/ """ +import os from pathlib import Path from django.conf.locale.en import formats as en_formats @@ -20,12 +21,12 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-!@gqe(nmv4ylos+2p14nk+&8h$j7g%=n4sdrwqzvr6!8ee$y9@' +SECRET_KEY = os.getenv("SECRET_KEY", "django-insecure-!@gqe(nmv4ylos+2p14nk+&8h$j7g%=n4sdrwqzvr6!8ee$y9@") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.getenv("DEBUG_MODE", True) -ALLOWED_HOSTS = ["*", "192.168.235.51", "127.0.0.1", "localhost", "192.168.0.52", "192.168.235.35"] +ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "* 192.168.235.51 127.0.0.1 localhost 192.168.0.52 192.168.235.35").split(" ") # Application definition @@ -67,6 +68,9 @@ TEMPLATES = [ }, ] +if csrf_origins := os.getenv("CSRF_TRUSTED_ORIGINS"): + CSRF_TRUSTED_ORIGINS = csrf_origins.split(" ") + WSGI_APPLICATION = 'clubhaus.wsgi.application' # Database @@ -129,4 +133,8 @@ MEDIA_ROOT = BASE_DIR / "media/" DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +# Custom Settings +IP_RATE_LIMIT_TIME = int(os.getenv("IP_RATE_LIMIT_TIME", "3600")) +IP_RATE_LIMIT_COUNT = int(os.getenv("IP_RATE_LIMIT_COUNT", "2")) + # TODO: delete devserver port forward in windows firewall in hammerhead diff --git a/clubhaus/homepage/views.py b/clubhaus/homepage/views.py index eeb4e72..317b905 100644 --- a/clubhaus/homepage/views.py +++ b/clubhaus/homepage/views.py @@ -5,6 +5,7 @@ from django.shortcuts import render from django.urls import reverse from .models import Tobacco, ClubhausEvent, EventDate, EventDateVotes +import clubhaus.settings as django_settings def index(request: HttpRequest) -> django.http.HttpResponse: @@ -42,11 +43,15 @@ def events(request: HttpRequest) -> django.http.HttpResponse: def voting(request: HttpRequest) -> django.http.HttpResponse: request.session.clear_expired() + # Proxy use is forbidden + if request.META.get("X-Forwarded-For"): + return HttpResponseForbidden() + ip = request.META.get("REMOTE_ADDR") cache_key = f"voting_block_{ip}" rate_cache: django.core.cache.BaseCache = cache if ip not in rate_cache: - rate_cache.add(cache_key, 0, 3600) + rate_cache.add(cache_key, 0, django_settings.IP_RATE_LIMIT_TIME) rate_cache.incr(cache_key) if request.method == "POST" and rate_cache.get(cache_key) < 3: