Skip to content

Commit

Permalink
feat: Add www.ietf.org k8s configuration (#453)
Browse files Browse the repository at this point in the history
* feat: Add www.ietf.org k8s configuration

* fix: Add volume mounts for static and media

* feat: Add persistent volume for the media directory

* fix: k8s deployment files

* fix: remove strategy from wagtail.yaml

* build: Set DJANGO_SETTINGS_MODULE

* chore: Use IETFWWW instead of IETFWEB

* feat: Expose /id

* chore: Expose /charter

* chore: Expose /cr

* build: Use static from the image

* chore: Expose /slides

* chore: Expose /archive/id

* fix: Expose correct dir for /id

* chore: Expose /ietf-ftp

* chore: Expose /rfc

* fix: Add defaults for all os.environ.get()

* fix: Consistent use of character case

* fix: Remove SERVER_MODE

* fix: Change 8 spaces to 4 spaces

* chore: Define fsGroup in securityContext

* feat: Use wagtail 404 for all 404 errors

---------

Co-authored-by: Nicolas Giard <[email protected]>
  • Loading branch information
kesara and NGPixel authored Jun 17, 2024
1 parent ee2a89c commit aee9d29
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 0 deletions.
19 changes: 19 additions & 0 deletions k8s/ietfweb/django-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: django-config
data:
IETFWWW_ADMINS: |-
Robert Sparks <[email protected]>
Kesara Rathnayake <[email protected]>
IETFWWW_ALLOWED_HOSTS: ".ietf.org" # newline-separated list also allowed

IETFWWW_DJANGO_SECRET_KEY: "PDwXboUq!=hPjnrtG2=ge#N$Dwy+wn@uivrugwpic8mxyPfHk" # secret


# IETFWWW_MATOMO_SITE_ID: "1" # must be present to enable Matomo
# IETFWWW_MATOMO_DOMAIN_PATH: "analytics.ietf.org"

# use this to override default - one entry per line
# IETFWWW_CSRF_TRUSTED_ORIGINS: |-
# https://www.staging.ietf.org
13 changes: 13 additions & 0 deletions k8s/ietfweb/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace: ietfwww
namePrefix: ietfwww-
configMapGenerator:
- name: files-cfgmap
files:
- local.py
- supervisord.conf
- nginx-default.conf
- nginx.conf
resources:
- django-config.yaml
- memcached.yaml
- wagtail.yaml
111 changes: 111 additions & 0 deletions k8s/ietfweb/local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright The IETF Trust 2007-2024, All Rights Reserved
# -*- coding: utf-8 -*-

from email.utils import parseaddr
import os

def _multiline_to_list(s):
"""Helper to split at newlines and conver to list"""
return [item.strip() for item in s.split("\n")]


DEFAULT_FROM_EMAIL = "[email protected]"
SERVER_EMAIL = "[email protected]"
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = os.environ.get("IETFWWW_EMAIL_HOST", "localhost")
EMAIL_PORT = int(os.environ.get("IETFWWW_EMAIL_PORT", "2025"))

# Secrets
_SECRET_KEY = os.environ.get("IETFWWW_DJANGO_SECRET_KEY", None)
if _SECRET_KEY is not None:
SECRET_KEY = _SECRET_KEY
else:
raise RuntimeError("IETFWWW_DJANGO_SECRET_KEY must be set")


_CSRF_TRUSTED_ORIGINS_STR = os.environ.get("IETFWWW_CSRF_TRUSTED_ORIGINS", None)
if _CSRF_TRUSTED_ORIGINS_STR is not None:
CSRF_TRUSTED_ORIGINS = _multiline_to_list(_CSRF_TRUSTED_ORIGINS_STR)

FILE_UPLOAD_PERMISSIONS = 0o664
_WAGTAILADMIN_BASE_URL = os.environ.get("WAGTAILADMIN_BASE_URL", None)
if _WAGTAILADMIN_BASE_URL is not None:
WAGTAILADMIN_BASE_URL = _WAGTAILADMIN_BASE_URL
else:
raise RuntimeError("WAGTAILADMIN_BASE_URL must be present")

# Set DEBUG if IETFWWW_DEBUG env var is the word "true"
DEBUG = os.environ.get("IETFWWW_DEBUG", "false").lower() == "true"

# IETFWWW_ALLOWED_HOSTS env var is a comma-separated list of allowed hosts
_ALLOWED_HOSTS_STR = os.environ.get("IETFWWW_ALLOWED_HOSTS", None)
if _ALLOWED_HOSTS_STR is not None:
ALLOWED_HOSTS = _multiline_to_list(_ALLOWED_HOSTS_STR)

DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"HOST": os.environ.get("IETFWWW_DB_HOST", "db"),
"PORT": os.environ.get("IETFWWW_DB_PORT", "5432"),
"NAME": os.environ.get("IETFWWW_DB_NAME", "ietfweb"),
"USER": os.environ.get("IETFWWW_DB_USER", "django"),
"PASSWORD": os.environ.get("IETFWWW_DB_PASS", ""),
"CONN_MAX_AGE": 600, # number of seconds database connections should persist for
},
}

# IETFWWW_ADMINS is a newline-delimited list of addresses parseable by email.utils.parseaddr
_admins_str = os.environ.get("IETFWWW_ADMINS", None)
if _admins_str is not None:
ADMINS = [parseaddr(admin) for admin in _multiline_to_list(_admins_str)]
else:
raise RuntimeError("IETFWWW_ADMINS must be set")

# Leave IETFWWW_MATOMO_SITE_ID unset to disable Matomo reporting
if "IETFWWW_MATOMO_SITE_ID" in os.environ:
MATOMO_DOMAIN_PATH = os.environ.get("IETFWWW_MATOMO_DOMAIN_PATH", "analytics.ietf.org")
MATOMO_SITE_ID = os.environ.get("IETFWWW_MATOMO_SITE_ID", None)
MATOMO_DISABLE_COOKIES = True

# Duplicating production cache from settings.py and using it whether we're in production mode or not
MEMCACHED_HOST = os.environ.get("IETFWWW_MEMCACHED_SERVICE_HOST", "127.0.0.1")
MEMCACHED_PORT = os.environ.get("IETFWWW_MEMCACHED_SERVICE_PORT", "11211")
MEMCACHED_KEY_PREFIX = "ietf"
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": f"{MEMCACHED_HOST}:{MEMCACHED_PORT}",
"KEY_PREFIX": MEMCACHED_KEY_PREFIX,
},
"sessions": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": f"{MEMCACHED_HOST}:{MEMCACHED_PORT}",
"KEY_PREFIX": MEMCACHED_KEY_PREFIX,
},
"dummy": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"},
}

# Logging

LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"mail_admins": {
"level": "ERROR",
"class": "django.utils.log.AdminEmailHandler",
},
},
"loggers": {
"django.request": {
"handlers": ["mail_admins"],
"level": "ERROR",
"propagate": False,
},
"django.security": {
"handlers": ["mail_admins"],
"level": "ERROR",
"propagate": False,
},
},
}
74 changes: 74 additions & 0 deletions k8s/ietfweb/memcached.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: memcached
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app: memcached
template:
metadata:
labels:
app: memcached
spec:
securityContext:
runAsNonRoot: true
containers:
- image: "quay.io/prometheus/memcached-exporter:v0.14.3"
imagePullPolicy: IfNotPresent
name: memcached-exporter
ports:
- name: metrics
containerPort: 9150
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsUser: 65534 # nobody
runAsGroup: 65534 # nobody
- image: "memcached:1.6-alpine"
imagePullPolicy: IfNotPresent
args: ["-m", "1024"]
name: memcached
ports:
- name: memcached
containerPort: 11211
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
# memcached image sets up uid/gid 11211
runAsUser: 11211
runAsGroup: 11211
dnsPolicy: ClusterFirst
restartPolicy: Always
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: memcached
annotations:
k8s.grafana.com/scrape: "true" # this is not a bool
k8s.grafana.com/metrics.portName: "metrics"
spec:
type: ClusterIP
ports:
- port: 11211
targetPort: memcached
protocol: TCP
name: memcached
- port: 9150
targetPort: metrics
protocol: TCP
name: metrics
selector:
app: memcached
119 changes: 119 additions & 0 deletions k8s/ietfweb/nginx-default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
gzip on;
access_log /dev/stdout;
error_log /dev/stdout warn;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $${keepempty}host;
proxy_set_header X-Forwarded-For $${keepempty}proxy_add_x_forwarded_for;
}
location /media/ {
alias /app/media/;

error_page 404 = @error_redirect;
}
location /static/ {
alias /app/static/;

error_page 404 = @error_redirect;
}
location /charter {
alias /a/ietfdata/doc/charter/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

location ~* \.xml$ {
add_header Content-Disposition 'attachment';
}

error_page 404 = @error_redirect;
}
location /cr {
alias /a/ietfdata/doc/conflict-review/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

location ~* \.xml$ {
add_header Content-Disposition 'attachment';
}

error_page 404 = @error_redirect;
}
location /slides {
alias /a/ietfdata/doc/slides/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

location ~* \.xml$ {
add_header Content-Disposition 'attachment';
}

error_page 404 = @error_redirect;
}
location /archive/id {
alias /a/ietfdata/draft/archive/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

location ~* \.xml$ {
add_header Content-Disposition 'attachment';
}

error_page 404 = @error_redirect;
}
location /id {
alias /a/ietfdata/draft/repository;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

location ~* \.xml$ {
add_header Content-Disposition 'attachment';
}

error_page 404 = @error_redirect;
}
location /ietf-ftp {
alias /a/www/ietf-ftp;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

location ~* \.xml$ {
add_header Content-Disposition 'attachment';
}

error_page 404 = @error_redirect;
}
location /rfc {
alias /a/www/ietf-ftp/rfc;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
charset utf-8;

location ~* \.xml$ {
add_header Content-Disposition 'attachment';
}

error_page 404 = @error_redirect;
}
location @error_redirect {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $${keepempty}host;
proxy_set_header X-Forwarded-For $${keepempty}proxy_add_x_forwarded_for;
}
}
53 changes: 53 additions & 0 deletions k8s/ietfweb/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
worker_processes auto;
pid /var/lib/nginx/nginx.pid;
error_log /dev/stdout;
include /etc/nginx/modules-enabled/*.conf;

events {
worker_connections 768;
# multi_accept on;
}

http {

##
# Basic Settings
##

sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;

# server_names_hash_bucket_size 64;
# server_name_in_redirect off;

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

##
# SSL Settings
##

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;

##
# Logging Settings
##

access_log /dev/stdout;

##
# Gzip Settings
##

gzip on;

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Loading

0 comments on commit aee9d29

Please sign in to comment.