hswaw/paperless: initial deployment
Change-Id: Ie6fb0df0bfa047e4fd561c6de8b26ab0fbebbcb8
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1305
Reviewed-by: q3k <q3k@hackerspace.pl>
diff --git a/hswaw/paperless/Dockerfile b/hswaw/paperless/Dockerfile
new file mode 100644
index 0000000..af9a3ee
--- /dev/null
+++ b/hswaw/paperless/Dockerfile
@@ -0,0 +1,15 @@
+# Note: this is required to run the container as non-root.
+
+# Update:
+# export VERSION=1.7.0
+# docker build --build-arg VERSION -t registry.k0.hswaw.net/informatic/paperless-ngx:$VERSION .
+# docker push registry.k0.hswaw.net/informatic/paperless-ngx:$VERSION
+
+ARG VERSION=1.7.0
+FROM ghcr.io/paperless-ngx/paperless-ngx:${VERSION}
+
+# Install polish tesseract training data
+RUN apt-get update && apt-get install -y tesseract-ocr-pol && rm -rf /var/lib/apt/lists/*
+
+# Remove privilege dropping and use paperless user directly everywhere
+RUN sed -i 's/gosu paperless//g' /sbin/docker-entrypoint.sh && sed -i -e 's;user=.*;;g' -e 's;logfile=/var/.*;logfile=/dev/null;g' /etc/supervisord.conf
diff --git a/hswaw/paperless/paperless.libsonnet b/hswaw/paperless/paperless.libsonnet
new file mode 100644
index 0000000..bed51e9
--- /dev/null
+++ b/hswaw/paperless/paperless.libsonnet
@@ -0,0 +1,188 @@
+# kubectl -n paperless create secret generic paperless-proxy --from-literal=cookie_secret=$(pwgen 32 1) --from-literal=oidc_secret=...
+# kubectl -n paperless create secret generic paperless --from-literal=postgres_password=$(pwgen 32 1) --from-literal=redis_password=$(pwgen 32 1) --from-literal=secret_key=$(pwgen 32 1)
+
+# There is no way of handling superusers (Admin panel access) automatically when
+# using OAuth2-Proxy, thus we need to run the following command to mark the
+# first user as such:
+# kubectl -n paperless exec -it deploy/paperless -c paperless -- python ./manage.py shell -c "from django.contrib.auth.models import User; u = User.objects.get_by_natural_key('informatic'); u.is_superuser = True; u.is_staff = True; u.save()"
+
+local kube = import "../../kube/kube.libsonnet";
+local postgres = import "../../kube/postgres.libsonnet";
+local redis = import "../../kube/redis.libsonnet";
+
+{
+ local app = self,
+ local cfg = self.cfg,
+
+ cfg:: {
+ namespace: "paperless",
+ domain: "paperless.hackerspace.pl",
+
+ images: {
+ paperless: "registry.k0.hswaw.net/informatic/paperless-ngx@sha256:78b17e3050f7edea1e8c659c433ebcb6365bb93280a2698e3322075f988b1f41",
+ proxy: "quay.io/oauth2-proxy/oauth2-proxy:v7.2.1",
+ },
+
+ storageClassName: "waw-hdd-redundant-3",
+ },
+
+ ns: kube.Namespace(cfg.namespace),
+
+ redis: redis {
+ cfg+: {
+ namespace: cfg.namespace,
+ storageClassName: cfg.storageClassName,
+ appName: "paperless",
+ image: "redis:6.0",
+ password: { secretKeyRef: { name: "paperless", key: "redis_password" } },
+ },
+ },
+
+ postgres: postgres {
+ cfg+: {
+ namespace: cfg.namespace,
+ appName: "paperless",
+ database: "paperless",
+ username: "paperless",
+
+ password: { secretKeyRef: { name: "paperless", key: "postgres_password" } },
+ storageClassName: cfg.storageClassName,
+ storageSize: "20Gi",
+ },
+ bouncer: {},
+ },
+
+ dataVolume: app.ns.Contain(kube.PersistentVolumeClaim("paperless-data")) {
+ spec+: {
+ storageClassName: cfg.storageClassName,
+ accessModes: [ "ReadWriteOnce" ],
+ resources: {
+ requests: {
+ storage: "100Gi",
+ },
+ },
+ },
+ },
+
+ deploy: app.ns.Contain(kube.Deployment("paperless")) {
+ spec+: {
+ replicas: 1,
+ template+: {
+ spec+: {
+ volumes_: {
+ data: kube.PersistentVolumeClaimVolume(app.dataVolume),
+ },
+
+ securityContext: {
+ runAsUser: 1000,
+ runAsGroup: 1000,
+ fsGroup: 1000,
+ },
+
+ default_container:: "auth",
+ containers_: {
+ auth: kube.Container("authproxy") {
+ image: cfg.images.proxy,
+ ports_: {
+ http: { containerPort: 8001 },
+ },
+
+ env_: {
+ OAUTH2_PROXY_UPSTREAMS: "http://127.0.0.1:8000",
+ OAUTH2_PROXY_HTTP_ADDRESS: "0.0.0.0:8001",
+
+ OAUTH2_PROXY_COOKIE_SECRET: { secretKeyRef: { name: "paperless-proxy", key: "cookie_secret" } },
+
+ OAUTH2_PROXY_PROVIDER: "oidc",
+ OAUTH2_PROXY_OIDC_ISSUER_URL: "https://sso.hackerspace.pl",
+ OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true",
+
+ OAUTH2_PROXY_CLIENT_ID: "b4859334-140b-432a-81f6-8f3e135e021a",
+ OAUTH2_PROXY_CLIENT_SECRET: { secretKeyRef: { name: "paperless-proxy", key: "oidc_secret" } },
+
+ OAUTH2_PROXY_EMAIL_DOMAINS: "*",
+ OAUTH2_PROXY_ALLOWED_GROUPS: "zarzad",
+
+ # Security considerations:
+ #
+ # * OAuth2-Proxy *will* strip X-Forwarded-User
+ # header from requests passed through to
+ # endpoint, preventing authentication bypass
+ #
+ # * OAuth2-Proxy *will not* strip Authorization
+ # header - that can either be a user token,
+ # or a username/password pair. Former can only
+ # be generated by staff/superuser in Admin
+ # panel, and the latter will not work for our
+ # OAuth2 autogenerated users since these do
+ # not have any password set
+ OAUTH2_PROXY_SKIP_AUTH_ROUTES: "^/api/.*",
+ },
+ },
+
+ paperless: kube.Container("paperless") {
+ image: cfg.images.paperless,
+ resources: {
+ requests: { cpu: "500m", memory: "1024M" },
+ limits: { cpu: "4", memory: "6144M" },
+ },
+ env_: {
+ PAPERLESS_PORT: "8000",
+
+ PAPERLESS_SECRET_KEY: { secretKeyRef: { name: "paperless", key: "secret_key" } },
+
+ A_REDIS_PASSWORD: app.redis.cfg.password,
+ PAPERLESS_REDIS: "redis://:$(A_REDIS_PASSWORD)@redis:6379",
+
+ PAPERLESS_DBHOST: "postgres",
+ PAPERLESS_DBNAME: app.postgres.cfg.database,
+ PAPERLESS_DBUSER: app.postgres.cfg.username,
+ PAPERLESS_DBPASS: app.postgres.cfg.password,
+
+ PAPERLESS_ENABLE_HTTP_REMOTE_USER: "true",
+ PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME: "HTTP_X_FORWARDED_USER",
+
+ PAPERLESS_OCR_LANGUAGE: "pol",
+ PAPERLESS_OCR_MODE: "force",
+ PAPERLESS_DATE_ORDER: "YMD",
+ },
+
+ volumeMounts: [
+ { name: "data", mountPath: "/usr/src/paperless/data", subPath: "data" },
+ { name: "data", mountPath: "/usr/src/paperless/media", subPath: "media" },
+ { name: "data", mountPath: "/usr/src/paperless/consume", subPath: "consume" },
+ ],
+ },
+ },
+ },
+ },
+ },
+ },
+
+ service: app.ns.Contain(kube.Service("paperless")) {
+ target_pod:: app.deploy.spec.template,
+ },
+
+ ingress: app.ns.Contain(kube.Ingress("paperless")) {
+ metadata+: {
+ annotations+: {
+ "kubernetes.io/tls-acme": "true",
+ "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
+ "nginx.ingress.kubernetes.io/proxy-body-size": "0",
+ },
+ },
+ spec+: {
+ tls: [ { hosts: [ cfg.domain ], secretName: "paperless-tls" } ],
+ rules: [
+ {
+ host: cfg.domain,
+ http: {
+ paths: [
+ { path: "/", backend: app.service.name_port },
+ ],
+ },
+ },
+ ],
+ },
+ }
+}
diff --git a/hswaw/paperless/prod.jsonnet b/hswaw/paperless/prod.jsonnet
new file mode 100644
index 0000000..8450836
--- /dev/null
+++ b/hswaw/paperless/prod.jsonnet
@@ -0,0 +1,5 @@
+local paperless = import "./paperless.libsonnet";
+
+{
+ prod: paperless {},
+}