diff --git a/.dockerignore b/.dockerignore index e9ff2bac..8bad9296 100644 --- a/.dockerignore +++ b/.dockerignore @@ -16,12 +16,14 @@ log/* LICENSE *.md -# Skip pipenv/virtualenv related things +# Skip envrc .envrc -.venv # Skip ansible-container stuff ansible* container.yml meta.yml requirements.yml + +# Skip kubernetes and Docker config stuff +deploy diff --git a/deploy/docker/prod/Dockerfile b/deploy/docker/prod/Dockerfile index ea4bdbcc..37a36859 100644 --- a/deploy/docker/prod/Dockerfile +++ b/deploy/docker/prod/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.6.5-alpine +FROM alpine:3.8 ### Very low chance of changing ############################### @@ -7,12 +7,12 @@ ARG APP_USER=atst ARG APP_GROUP=atat ARG APP_DIR=/opt/atat/atst ARG APP_PORT=8000 -ARG SITE_PACKAGES_DIR=/usr/local/lib/python3.6/site-packages +ARG LOCAL_BIN_DIR=/usr/bin +ARG SITE_PACKAGES_DIR=/usr/lib/python3.6/site-packages ENV APP_USER "${APP_USER}" ENV APP_GROUP "${APP_GROUP}" ENV APP_DIR "${APP_DIR}" -ENV SKIP_PIPENV true # Set port to open EXPOSE "${APP_PORT}" @@ -21,13 +21,16 @@ EXPOSE "${APP_PORT}" ENTRYPOINT ["/usr/bin/dumb-init", "--"] # Default command is to launch the server -CMD ["bash", "-c", "${APP_DIR}/script/server"] +CMD ["bash", "-c", "${APP_DIR}/script/uwsgi_server"] ### Items that will change almost every build ############################################# # Copy installed python packages from the tester image COPY --from=atst-tester:latest "${SITE_PACKAGES_DIR}" "${SITE_PACKAGES_DIR}" +# Copy local bin directory (contains python system package wrappers) +COPY --from=atst-tester:latest "${LOCAL_BIN_DIR}" "${LOCAL_BIN_DIR}" + # Copy the app directory contents from the tester image (includes node modules) COPY --from=atst-tester:latest "${APP_DIR}" "${APP_DIR}" diff --git a/deploy/kubernetes/atst-configmap.yml b/deploy/kubernetes/atst-configmap.yml new file mode 100644 index 00000000..a9584fc5 --- /dev/null +++ b/deploy/kubernetes/atst-configmap.yml @@ -0,0 +1,15 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: atst-config + namespace: atat +data: + uwsgi-config: |- + [uwsgi] + callable = app + module = app + socket = /var/run/uwsgi/uwsgi.socket + plugins = python3 + virtualenv = /opt/atat/atst/.venv + chmod-socket = 666 diff --git a/deploy/kubernetes/atst-envvars-configmap.yml b/deploy/kubernetes/atst-envvars-configmap.yml new file mode 100644 index 00000000..e7bfec14 --- /dev/null +++ b/deploy/kubernetes/atst-envvars-configmap.yml @@ -0,0 +1,10 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: atst-envvars + namespace: atat +data: + FLASK_ENV: dev + OVERRIDE_CONFIG_FULLPATH: /opt/atat/atst/atst-overrides.ini + UWSGI_CONFIG_FULLPATH: /opt/atat/atst/uwsgi-config.ini diff --git a/deploy/kubernetes/atst-nginx-configmap.yml b/deploy/kubernetes/atst-nginx-configmap.yml new file mode 100644 index 00000000..6e2b1d69 --- /dev/null +++ b/deploy/kubernetes/atst-nginx-configmap.yml @@ -0,0 +1,79 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: atst-nginx + namespace: atat +data: + nginx-config: |- + server { + server_name www.atat.codes atat.codes; + listen 8442; + listen [::]:8442 ipv6only=on; + if ($http_x_forwarded_proto != 'https') { + return 301 https://$host$request_uri; + } + location /login-redirect { + return 301 https://auth.atat.codes$request_uri; + } + location /login-dev { + try_files $uri @appbasicauth; + } + location / { + try_files $uri @app; + } + location @app { + include uwsgi_params; + uwsgi_pass unix:///var/run/uwsgi/uwsgi.socket; + } + location @appbasicauth { + include uwsgi_params; + uwsgi_pass unix:///var/run/uwsgi/uwsgi.socket; + auth_basic "Developer Access"; + auth_basic_user_file /etc/nginx/.htpasswd; + } + } + server { + server_name auth.atat.codes; + listen 8443 ssl; + listen [::]:8443 ssl ipv6only=on; + # SSL server certificate and private key + ssl_certificate /etc/ssl/private/auth.atat.crt; + ssl_certificate_key /etc/ssl/private/auth.atat.key; + # Set SSL protocols, ciphers, and related options + ssl_protocols TLSv1.3 TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + ssl_prefer_server_ciphers on; + ssl_ecdh_curve secp384r1; + ssl_dhparam /etc/ssl/dhparam.pem; + # SSL session options + ssl_session_timeout 4h; + ssl_session_cache shared:SSL:10m; # 1mb = ~4000 sessions + ssl_session_tickets off; + # OCSP Stapling + ssl_stapling on; + ssl_stapling_verify on; + resolver 8.8.8.8 8.8.4.4; + # Request and validate client certificate + #ssl_verify_client on; + #ssl_verify_depth 10; + #ssl_client_certificate /etc/nginx/ssl/ca/client-ca.pem; + # Guard against HTTPS -> HTTP downgrade + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always"; + location / { + return 301 https://www.atat.codes$request_uri; + } + location /login-redirect { + try_files $uri @app; + } + location @app { + include uwsgi_params; + uwsgi_pass unix:///var/run/uwsgi/uwsgi.socket; + uwsgi_param HTTP_X_SSL_CLIENT_VERIFY $ssl_client_verify; + uwsgi_param HTTP_X_SSL_CLIENT_CERT $ssl_client_raw_cert; + uwsgi_param HTTP_X_SSL_CLIENT_S_DN $ssl_client_s_dn; + uwsgi_param HTTP_X_SSL_CLIENT_S_DN_LEGACY $ssl_client_s_dn_legacy; + uwsgi_param HTTP_X_SSL_CLIENT_I_DN $ssl_client_i_dn; + uwsgi_param HTTP_X_SSL_CLIENT_I_DN_LEGACY $ssl_client_i_dn_legacy; + } + } diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml new file mode 100644 index 00000000..c302f8af --- /dev/null +++ b/deploy/kubernetes/atst.yml @@ -0,0 +1,165 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: atat +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: atst + name: atst + namespace: atat +spec: + replicas: 1 + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: atst + spec: + securityContext: + fsGroup: 101 + containers: + - name: atst + image: registry.atat.codes:443/atst-prod:e9b6f76 + envFrom: + - configMapRef: + name: atst-envvars + volumeMounts: + - name: atst-config + mountPath: "/opt/atat/atst/atst-overrides.ini" + subPath: atst-overrides.ini + - name: uwsgi-config + mountPath: "/opt/atat/atst/uwsgi-config.ini" + subPath: uwsgi-config.ini + - name: uwsgi-socket-dir + mountPath: "/var/run/uwsgi" + - name: atst-nginx + image: nginx:alpine + ports: + - containerPort: 8442 + name: http + - containerPort: 8443 + name: https + volumeMounts: + - name: nginx-auth-tls + mountPath: "/etc/ssl/private" + - name: nginx-config + mountPath: "/etc/nginx/conf.d/atst.conf" + subPath: atst.conf + - name: nginx-dhparam + mountPath: "/etc/ssl/dhparam.pem" + subPath: dhparam.pem + - name: nginx-htpasswd + mountPath: "/etc/nginx/.htpasswd" + subPath: .htpasswd + - name: uwsgi-socket-dir + mountPath: "/var/run/uwsgi" + imagePullSecrets: + - name: regcred + volumes: + - name: atst-config + secret: + secretName: atst-config-ini + items: + - key: atst-overrides.ini + path: atst-overrides.ini + mode: 0644 + - name: nginx-auth-tls + secret: + secretName: auth-atst-ingress-tls + items: + - key: tls.crt + path: auth.atat.crt + mode: 0644 + - key: tls.key + path: auth.atat.key + mode: 0640 + - name: nginx-config + configMap: + name: atst-nginx + items: + - key: nginx-config + path: atst.conf + - name: nginx-dhparam + secret: + secretName: dhparam-4096 + items: + - key: dhparam.pem + path: dhparam.pem + mode: 0640 + - name: nginx-htpasswd + secret: + secretName: atst-nginx-htpasswd + items: + - key: htpasswd + path: .htpasswd + mode: 0640 + - name: uwsgi-config + configMap: + name: atst-config + items: + - key: uwsgi-config + path: uwsgi-config.ini + mode: 0644 + - name: uwsgi-socket-dir + emptyDir: + medium: Memory +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: atst + name: atst + namespace: atat +spec: + ports: + - name: http + port: 80 + targetPort: 8442 + selector: + app: atst +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: atst + name: atst-auth + namespace: atat +spec: + type: NodePort + ports: + - name: https + protocol: TCP + nodePort: 32751 + port: 8443 + selector: + app: atst +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: atst + namespace: atat + annotations: + kubernetes.io/tls-acme: "true" + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/proxy-body-size: 10m +spec: + tls: + - hosts: + - www.atat.codes + secretName: atst-ingress-tls + rules: + - host: www.atat.codes + http: + paths: + - path: / + backend: + serviceName: atst + servicePort: 80 diff --git a/deploy/kubernetes/set_atstconfig_secret.sh b/deploy/kubernetes/set_atstconfig_secret.sh new file mode 100755 index 00000000..0dcb90b0 --- /dev/null +++ b/deploy/kubernetes/set_atstconfig_secret.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +kubectl -n atat delete secret atst-config-ini +kubectl -n atat create secret generic atst-config-ini --from-file="${1}" diff --git a/deploy/kubernetes/set_dhparam_secret.sh b/deploy/kubernetes/set_dhparam_secret.sh new file mode 100755 index 00000000..dfc9401a --- /dev/null +++ b/deploy/kubernetes/set_dhparam_secret.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +kubectl -n atat delete secret dhparam-4096 +kubectl -n atat create secret generic dhparam-4096 --from-file="${1}" diff --git a/deploy/kubernetes/set_htpasswd_secret.sh b/deploy/kubernetes/set_htpasswd_secret.sh new file mode 100755 index 00000000..540048ca --- /dev/null +++ b/deploy/kubernetes/set_htpasswd_secret.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +kubectl -n atat delete secret atst-nginx-htpasswd +kubectl -n atat create secret generic atst-nginx-htpasswd --from-file="${1}" diff --git a/script/alpine_setup b/script/alpine_setup index 28f836c2..b9eeb9a7 100755 --- a/script/alpine_setup +++ b/script/alpine_setup @@ -9,5 +9,8 @@ source "$(dirname "${0}")"/../script/include/global_header.inc.sh APP_USER="atst" APP_UID="8010" +# Add additional packages required by app dependencies +ADDITIONAL_PACKAGES="postgresql-libs python3 uwsgi uwsgi-python3" + # Run the shared alpine setup script source ./script/include/run_alpine_setup diff --git a/script/include b/script/include index 8cf96c97..c44ca507 160000 --- a/script/include +++ b/script/include @@ -1 +1 @@ -Subproject commit 8cf96c9776e7fd73c11d57160d26fc1715bf00da +Subproject commit c44ca5070da78fd522a2e485aaa225cc638e11d3 diff --git a/script/setup b/script/setup index 23a2fce3..f3b5b616 100755 --- a/script/setup +++ b/script/setup @@ -16,3 +16,5 @@ source ./script/include/run_setup # Fetch and import the PE numbers run_command "python script/ingest_pe_numbers.py" + +yarn build diff --git a/script/uwsgi_server b/script/uwsgi_server new file mode 100755 index 00000000..e9633892 --- /dev/null +++ b/script/uwsgi_server @@ -0,0 +1,8 @@ +#!/bin/bash + +# script/uwsgi_server: Launch the UWSGI server + +source "$(dirname "${0}")"/../script/include/global_header.inc.sh + +# Launch UWSGI +run_command "uwsgi --ini ${UWSGI_CONFIG_FULLPATH}"