Skip to content

Commit

Permalink
Axes / Nginx / Gunicorn (#43)
Browse files Browse the repository at this point in the history
* axes behing reverse proxy works now, but needs [pull request #1521](wger-project/wger#1521) merged
* gunicorn: uses gthread with 4 workers and 3 threads as a default
* nginx: logging shows X_FORWARDED_FOR
* nginx: proxy tuning
* nginx: set timezone
* add example values.yaml for a productive environment
  • Loading branch information
bbkz committed Dec 15, 2023
1 parent 7ef5c1d commit 42e0d94
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 12 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,11 @@ Celery requires persistent volumes.
| Name | Description | Type | Default Value |
|------|-------------|------|---------------|
| `app.axes.enabled` | Enable [axes](https://django-axes.readthedocs.io/en/latest/index.html) Bruteforce protection | Boolean | `false` |
| `app.axes.lockoutParameters` | List (comma separated string) | `"ip_address"` |
| `app.axes.failureLimit` | Limit of failed auth | String | `10` |
| `app.axes.cooloffTime` | in Minutes | String | `30` |
| `app.axes.ipwareProxyCount` | Count of proxies | String | `null` |
| `app.axes.ipwareMetaPrecedenceOrder` | Proxy header magnitude | String | `"['HTTP_X_FORWARDED_FOR','REMOTE_ADDR',]"` |
| `app.axes.ipwareProxyCount` | Count of proxies | String | `0` |
| `app.axes.ipwareMetaPrecedenceOrder` | Proxy header magnitude | List (comma separated string) | `"X_FORWARDED_FOR,REMOTE_ADDR"` |


### Nginx
Expand Down Expand Up @@ -270,12 +271,19 @@ kubectl -n wger get secret flower -o jsonpath='{.data.password}' | base64 -d

## Axes

Bruteforce protection. Depending on your setup, you may need to configure axes to your proxy setup otherwise it will block the IP of the reverse proxy.
Bruteforce protection. Depending on your setup, you may need to configure axes to your proxy setup otherwise it will block by default the IP which can be your reverse proxy.

* https://django-axes.readthedocs.io/en/latest/4_configuration.html#configuring-reverse-proxies
* https://django-axes.readthedocs.io/en/latest/5_customization.html#customizing-lockout-parameters

**-> The axes setup can't yet be configured to do so.**

```bash
python3 manage.py axes_reset
python3 manage.py axes_reset_ip [IP]
python3 manage.py axes_reset_username [USERNAME]
```


## Upgrading

Expand Down
3 changes: 2 additions & 1 deletion charts/wger/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
---
apiVersion: v2
version: 0.1.9
version: 0.1.10-rc.11
appVersion: latest
name: wger
description: A Helm chart for Wger installation on Kubernetes
type: application
maintainers:
- name: rolandgeider
email: roland@geider.net
- name: bbkz
dependencies:
- name: postgres
condition: postgres.enabled
Expand Down
12 changes: 9 additions & 3 deletions charts/wger/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,17 @@ environment:
{{- else }}
value: "False"
{{- end }}
- name: AXES_LOCKOUT_PARAMETERS
value: {{ .Values.app.axes.lockoutParameters | default "ip_address" | quote }}
- name: AXES_FAILURE_LIMIT
value: {{ .Values.app.axes.failureLimit | default "10" | quote }}
- name: AXES_COOLOFF_TIME
value: {{ .Values.app.axes.cooloffTime | default "30" | quote }}
- name: AXES_IPWARE_PROXY_COUNT
value: {{ .Values.app.axes.ipwareProxyCount | default "None" | quote }}
value: {{ .Values.app.axes.ipwareProxyCount | default "0" }}
# @todo bad default, use the default from axes REMOTE_ADDR only
- name: AXES_IPWARE_META_PRECEDENCE_ORDER
value: {{ .Values.app.axes.ipwareMetaPrecedenceOrder | default "['HTTP_X_FORWARDED_FOR','REMOTE_ADDR',]" | quote }}
value: {{ .Values.app.axes.ipwareMetaPrecedenceOrder | default "X_FORWARDED_FOR,REMOTE_ADDR" | quote }}
- name: AXES_HANDLER
value: "axes.handlers.cache.AxesCacheHandler"
# jwt auth
Expand All @@ -91,8 +94,11 @@ environment:
{{- if .Values.app.nginx.enabled }}
- name: WGER_USE_GUNICORN
value: "True"
# workers (2x CPU Cores +1), rpi4 works well with 2 worker / 2 threads / 1 pod
# forward-allow-ips="*" for image serving https url
# accesslog: remote ip - client ip - x-real-ip - x-forward-for -
- name: GUNICORN_CMD_ARGS
value: "--timeout 240 --workers=2 --access-logformat '%({x-forwarded-for}i)s %(l)s %(u)s %(t)s \"%(r)s\" %(s)s %(b)s \"%(f)s\" \"%(a)s\"' --access-logfile - --error-logfile -"
value: "--timeout 240 --workers 4 --worker-class gthread --threads 3 --forwarded-allow-ips * --proxy-protocol True --access-logformat='%(h)s %(l)s %({client-ip}i)s %(l)s %({x-real-ip}i)s %(l)s %({x-forwarded-for}i)s %(l)s %(t)s \"%(r)s\" %(s)s %(b)s \"%(f)s\" \"%(a)s\"' --access-logfile - --error-logfile -"
{{- end }}
- name: EXERCISE_CACHE_TTL
value: "18000"
Expand Down
34 changes: 31 additions & 3 deletions charts/wger/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,31 @@ metadata:
name: {{ .Release.Name }}-nginx-configmap
data:
wger-app.conf: |
# custom access log configuration
log_format custom '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# proxy
upstream app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response <- according to gunicorn doc
server localhost:8000 fail_timeout=0;
zone upstreams 64K;
keepalive 2;
}
# webserver
server {
listen 8080;
client_max_body_size 4G;
# set the correct host(s) for your site
server_name {{ join " " .Values.ingress.url }};
access_log /var/log/nginx/access.log combined;
access_log /var/log/nginx/access.log custom;
error_log /var/log/nginx/error.log warn;
keepalive_timeout 5;
# path for static files (only needed for serving "local" static and media files)
root /var/www/html/;
Expand All @@ -30,14 +39,33 @@ data:
}
location @proxy_to_app {
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
{{- if .Values.ingress.tls }}
proxy_set_header X-Forwarded-Proto https;
{{- else }}
proxy_set_header X-Forwarded-Proto $scheme;
{{- end }}
proxy_set_header Host $http_host;
# keep alive
proxy_set_header "Connection" "";
# https://www.getpagespeed.com/server-setup/nginx/tuning-proxy_buffer_size-in-nginx
proxy_buffer_size 32k;
proxy_busy_buffers_size 40k; # proxy_buffer_size + 2 small buffers of 4k
proxy_buffers 64 4k;
proxy_max_temp_file_size 0;
# send 500 when timeout
proxy_next_upstream error timeout http_500;
proxy_redirect off;
proxy_pass http://app_server;
}
# error handling
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/html/;
Expand Down
3 changes: 3 additions & 0 deletions charts/wger/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ spec:
- name: nginx
image: {{ .Values.app.nginx.image }}
imagePullPolicy: {{ .Values.app.nginx.imagePullPolicy }}
env:
- name: TZ
value: {{ .Values.app.timezone | default "UTC" | quote }}
ports:
- containerPort: 8080
protocol: TCP
Expand Down
5 changes: 3 additions & 2 deletions charts/wger/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ app:
# https://django-axes.readthedocs.io/en/latest/index.html
axes:
enabled: false
lockoutParameters: "ip_address"
failureLimit: 10
# in minutes
cooloffTime: 30
# number of reverse proxies involved
ipwareProxyCount: null
ipwareProxyCount: 0
# order of magnitude from last proxy for the real ip
ipwareMetaPrecedenceOrder: "['HTTP_X_FORWARDED_FOR','REMOTE_ADDR',]"
ipwareMetaPrecedenceOrder: "X_FORWARDED_FOR,REMOTE_ADDR"

#
# celery synchronisation
Expand Down
90 changes: 90 additions & 0 deletions example/prod_values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
#
# The following example uses:
#
# * uses manually created storages (pv & pvc)
# * nginx as reverse proxy
# * enables celery for exercise syncs
# * enables email
# * enables axes with reverse proxy configuration listening for X_FORWARDED_FOR header
# * modifies the gunicorn start parameters
# * enables a ingress assuming cert-manager is setup with a let's encrypt issuer and traefik
#
# Have a look at the packaged values.yaml for defaults and more settings:
# * https://github.com/wger-project/helm-charts/blob/master/charts/wger/values.yaml
#
# App settings
app:
timezone: "Etc/Zulu"
global:
replicas: 1
# image:
# PullPolicy: IfNotPresent
nginx:
enabled: true
axes:
enabled: true
failureLimit: 10
# in minutes
cooloffTime: 30
# number of reverse proxies involved
ipwareProxyCount: 1
# order of magnitude from last proxy for the real ip
ipwareMetaPrecedenceOrder: "X_FORWARDED_FOR,REMOTE_ADDR"
persistence:
enabled: true
existingClaim:
enabled: true
media: wger-media
static: wger-static
celeryBeat: wger-celery-beat
environment:
- name: ENABLE_EMAIL
value: "True"
- name: EMAIL_HOST
value: "mail.example.com"
- name: EMAIL_HOST_USER
value: "fit@example.com"
- name: EMAIL_HOST_PASSWORD
value: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
- name: FROM_EMAIL
value: "fit@example.com"
- name: GUNICORN_CMD_ARGS
value: "--timeout 240 --workers 2 --worker-class gthread --threads 3 --forwarded-allow-ips * --proxy-protocol True --access-logformat='%(h)s %(l)s %({client-ip}i)s %(l)s %({x-real-ip}i)s %(l)s %({x-forwarded-for}i)s %(l)s %(t)s \"%(r)s\" %(s)s %(b)s \"%(f)s\" \"%(a)s\"' --access-logfile - --error-logfile -"

celery:
enabled: true

ingress:
enabled: true
url: fit.example.com
tls: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: traefik

postgres:
enabled: true
settings:
superuser: postgres
superuserPassword: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
userDatabase:
name: wger
user: wger
password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
service:
port: 5432
storage:
persistentVolumeClaimName: wger-db
className: "csi-cephfs-sc"

redis:
enabled: true
auth:
enabled: false
password: wger
storage:
persistentVolumeClaimName: wger-redis
className: "csi-cephfs-sc"
service:
serverPort: 6379

0 comments on commit 42e0d94

Please sign in to comment.