diff --git a/README.md b/README.md index a4b6c44..a254385 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ The repository is split into three top-level areas. Within each, there is one di - **React-Resume**: Resume builder with MinIO and PostgreSQL - **Passbolt**: Team password manager - **Wiki.js**: Modern documentation platform +- **Outline**: Team knowledge base and wiki (PostgreSQL, Redis/Valkey, MinIO S3, Authentik SSO) - **Zipline**: File sharing and screenshot service - **Gotify**: Push notification server with REST API and WebSocket support - **Ntfy**: Push notification service diff --git a/applications/infisical/Infisical-secrets/outline-infisicalsecret.yaml b/applications/infisical/Infisical-secrets/outline-infisicalsecret.yaml new file mode 100644 index 0000000..539b1fe --- /dev/null +++ b/applications/infisical/Infisical-secrets/outline-infisicalsecret.yaml @@ -0,0 +1,22 @@ +apiVersion: secrets.infisical.com/v1alpha1 +kind: InfisicalSecret +metadata: + name: outline-secrets + namespace: infisical +spec: + hostAPI: https://infisical.jsme.be + resyncInterval: 30 + authentication: + universalAuth: + secretsScope: + projectSlug: "kubernetes" + envSlug: "prod" + secretsPath: "/outline" + recursive: true + credentialsRef: + secretName: infisical-universal-auth + secretNamespace: infisical + managedSecretReference: + secretName: outline-secrets + secretNamespace: outline + secretType: Opaque diff --git a/applications/outline/outline/outline-deployment.yaml b/applications/outline/outline/outline-deployment.yaml new file mode 100644 index 0000000..efeb081 --- /dev/null +++ b/applications/outline/outline/outline-deployment.yaml @@ -0,0 +1,118 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: outline + name: outline +spec: + replicas: 1 + selector: + matchLabels: + app: outline + template: + metadata: + labels: + app: outline + spec: + restartPolicy: Always + terminationGracePeriodSeconds: 60 + containers: + - name: outline + image: docker.getoutline.com/outlinewiki/outline:1.8.1 + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "2Gi" + cpu: "2000m" + ports: + - containerPort: 3000 + env: + - name: TZ + value: "Europe/Brussels" + - name: NODE_ENV + value: "production" + - name: URL + value: "https://outline.jsme.be" + - name: PORT + value: "3000" + # TLS is terminated at the gateway and X-Forwarded-Proto is set to + # https on the HTTPRoute, so Outline must not force an https redirect. + - name: FORCE_HTTPS + value: "false" + + # --- Database (PostgreSQL) --- + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: outline-secrets + key: database_url + # In-cluster Postgres has no TLS; disable SSL on the connection. + - name: PGSSLMODE + value: "disable" + + # --- Cache / queue (Redis-compatible Valkey) --- + - name: REDIS_URL + value: "redis://outline-redis.outline.svc.cluster.local:6379" + + # --- Secrets --- + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: outline-secrets + key: secret_key + - name: UTILS_SECRET + valueFrom: + secretKeyRef: + name: outline-secrets + key: utils_secret + + # --- File storage (MinIO / S3) --- + - name: FILE_STORAGE + value: "s3" + - name: AWS_REGION + value: "us-east-1" + - name: AWS_S3_UPLOAD_BUCKET_URL + value: "https://s3.jsme.be" + - name: AWS_S3_UPLOAD_BUCKET_NAME + value: "outline" + - name: AWS_S3_FORCE_PATH_STYLE + value: "true" + - name: AWS_S3_ACL + value: "private" + - name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: outline-secrets + key: s3_access_key + - name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: outline-secrets + key: s3_secret_key + + # --- Authentication (Authentik OIDC) --- + - name: OIDC_DISPLAY_NAME + value: "Authentik" + - name: OIDC_AUTH_URI + value: "https://auth.jsme.be/application/o/authorize/" + - name: OIDC_TOKEN_URI + value: "https://auth.jsme.be/application/o/token/" + - name: OIDC_USERINFO_URI + value: "https://auth.jsme.be/application/o/userinfo/" + - name: OIDC_LOGOUT_URI + value: "https://auth.jsme.be/application/o/outline/end-session/" + - name: OIDC_USERNAME_CLAIM + value: "preferred_username" + - name: OIDC_SCOPES + value: "openid profile email" + - name: OIDC_CLIENT_ID + valueFrom: + secretKeyRef: + name: outline-secrets + key: oidc_client_id + - name: OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: outline-secrets + key: oidc_client_secret diff --git a/applications/outline/outline/outline-httproute.yaml b/applications/outline/outline/outline-httproute.yaml new file mode 100644 index 0000000..cbaf0ec --- /dev/null +++ b/applications/outline/outline/outline-httproute.yaml @@ -0,0 +1,51 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: outline-route + namespace: outline +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: gateway-internal + namespace: envoy-gateway + sectionName: outline + - group: gateway.networking.k8s.io + kind: Gateway + name: gateway-public + namespace: envoy-gateway + sectionName: outline + hostnames: + - "outline.jsme.be" + rules: + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: X-Forwarded-Host + value: "outline.jsme.be" + - name: X-Forwarded-Proto + value: https + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: Strict-Transport-Security + value: "max-age=31536000; includeSubDomains" + - name: X-Content-Type-Options + value: nosniff + - name: X-Frame-Options + value: SAMEORIGIN + - name: Referrer-Policy + value: strict-origin-when-cross-origin + - name: Permissions-Policy + value: "camera=(), microphone=(), geolocation=(), payment=()" + backendRefs: + - name: outline + port: 3000 + kind: Service + group: "" + weight: 1 diff --git a/applications/outline/outline/outline-service.yaml b/applications/outline/outline/outline-service.yaml new file mode 100644 index 0000000..34b3759 --- /dev/null +++ b/applications/outline/outline/outline-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + namespace: outline + name: outline +spec: + selector: + app: outline + ports: + - protocol: TCP + port: 3000 + targetPort: 3000 + type: ClusterIP diff --git a/applications/outline/postgres/outline-postgres-persistentvolumeclaim.yaml b/applications/outline/postgres/outline-postgres-persistentvolumeclaim.yaml new file mode 100644 index 0000000..8b9afc2 --- /dev/null +++ b/applications/outline/postgres/outline-postgres-persistentvolumeclaim.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: outline + name: outline-postgres-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn-static + resources: + requests: + storage: 5Gi diff --git a/applications/outline/postgres/outline-postgres-service.yaml b/applications/outline/postgres/outline-postgres-service.yaml new file mode 100644 index 0000000..5fec60f --- /dev/null +++ b/applications/outline/postgres/outline-postgres-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: outline-postgres + namespace: outline +spec: + selector: + app: outline-postgres + type: ClusterIP + ports: + - name: outline-postgres-port + protocol: TCP + port: 5432 + targetPort: 5432 diff --git a/applications/outline/postgres/outline-postgres-statefulset.yaml b/applications/outline/postgres/outline-postgres-statefulset.yaml new file mode 100644 index 0000000..4d27096 --- /dev/null +++ b/applications/outline/postgres/outline-postgres-statefulset.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + namespace: outline + name: outline-postgres +spec: + replicas: 1 + serviceName: outline-postgres + selector: + matchLabels: + app: outline-postgres + template: + metadata: + labels: + app: outline-postgres + spec: + restartPolicy: Always + terminationGracePeriodSeconds: 60 + containers: + - name: outline-postgres + image: postgres:18 + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "500m" + ports: + - containerPort: 5432 + volumeMounts: + - name: outline-postgres-data + mountPath: /var/lib/postgresql/data + env: + - name: PGDATA + value: "/var/lib/postgresql/data/pgdata" + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: outline-secrets + key: db_user + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: outline-secrets + key: db_password + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: outline-secrets + key: db_name + volumes: + - name: outline-postgres-data + persistentVolumeClaim: + claimName: outline-postgres-pvc diff --git a/applications/outline/redis/outline-redis-deployment.yaml b/applications/outline/redis/outline-redis-deployment.yaml new file mode 100644 index 0000000..63db0d2 --- /dev/null +++ b/applications/outline/redis/outline-redis-deployment.yaml @@ -0,0 +1,28 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: outline + name: outline-redis +spec: + replicas: 1 + selector: + matchLabels: + app: outline-redis + template: + metadata: + labels: + app: outline-redis + spec: + restartPolicy: Always + containers: + - name: outline-redis + image: valkey/valkey:9 + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "200m" + ports: + - containerPort: 6379 diff --git a/applications/outline/redis/outline-redis-service.yaml b/applications/outline/redis/outline-redis-service.yaml new file mode 100644 index 0000000..0267b61 --- /dev/null +++ b/applications/outline/redis/outline-redis-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: outline-redis + namespace: outline +spec: + selector: + app: outline-redis + type: ClusterIP + ports: + - name: outline-redis-port + protocol: TCP + port: 6379 + targetPort: 6379 diff --git a/infra/argocd/applications/outline-application.yaml b/infra/argocd/applications/outline-application.yaml new file mode 100644 index 0000000..1afc2ea --- /dev/null +++ b/infra/argocd/applications/outline-application.yaml @@ -0,0 +1,24 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: outline + namespace: argocd +spec: + project: default + source: + repoURL: https://gitea.jsme.be/Jeffrey/Kubernetes-Manifests.git + path: applications/outline + targetRevision: HEAD + directory: + recurse: true + destination: + server: https://kubernetes.default.svc + namespace: outline + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true + - PrunePropagationPolicy=foreground diff --git a/infra/envoy-gateway/kustomization.yaml b/infra/envoy-gateway/kustomization.yaml index bd2d013..2146962 100644 --- a/infra/envoy-gateway/kustomization.yaml +++ b/infra/envoy-gateway/kustomization.yaml @@ -48,6 +48,7 @@ resources: - affine/affine-certificate.yaml - pelican/pelican-certificate.yaml - pelican/pelican-wings-certificate.yaml + - outline/outline-certificate.yaml patches: # Internal gateway patches @@ -273,6 +274,12 @@ patches: kind: Gateway name: gateway-internal path: pelican/pelican-wings-gateway-patch.yaml + - target: + group: gateway.networking.k8s.io + version: v1 + kind: Gateway + name: gateway-internal + path: outline/outline-gateway-patch.yaml # Public gateway patches - target: @@ -347,6 +354,12 @@ patches: kind: Gateway name: gateway-public path: gotify/gotify-gateway-public-patch.yaml + - target: + group: gateway.networking.k8s.io + version: v1 + kind: Gateway + name: gateway-public + path: outline/outline-gateway-public-patch.yaml # Uncomment to expose Grafana publicly — ensure Grafana auth is properly configured first # - target: # group: gateway.networking.k8s.io diff --git a/infra/envoy-gateway/outline/outline-certificate.yaml b/infra/envoy-gateway/outline/outline-certificate.yaml new file mode 100644 index 0000000..bdff7a7 --- /dev/null +++ b/infra/envoy-gateway/outline/outline-certificate.yaml @@ -0,0 +1,14 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: outline-tls + namespace: envoy-gateway +spec: + secretName: outline-tls + privateKey: + rotationPolicy: Always + issuerRef: + name: azure-dns + kind: ClusterIssuer + dnsNames: + - outline.jsme.be diff --git a/infra/envoy-gateway/outline/outline-gateway-patch.yaml b/infra/envoy-gateway/outline/outline-gateway-patch.yaml new file mode 100644 index 0000000..20d5bf4 --- /dev/null +++ b/infra/envoy-gateway/outline/outline-gateway-patch.yaml @@ -0,0 +1,19 @@ +- op: add + path: /spec/listeners/- + value: + name: outline + protocol: HTTPS + port: 443 + hostname: "outline.jsme.be" + allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + kubernetes.io/metadata.name: outline + tls: + mode: Terminate + certificateRefs: + - kind: Secret + name: outline-tls + namespace: envoy-gateway diff --git a/infra/envoy-gateway/outline/outline-gateway-public-patch.yaml b/infra/envoy-gateway/outline/outline-gateway-public-patch.yaml new file mode 100644 index 0000000..20d5bf4 --- /dev/null +++ b/infra/envoy-gateway/outline/outline-gateway-public-patch.yaml @@ -0,0 +1,19 @@ +- op: add + path: /spec/listeners/- + value: + name: outline + protocol: HTTPS + port: 443 + hostname: "outline.jsme.be" + allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + kubernetes.io/metadata.name: outline + tls: + mode: Terminate + certificateRefs: + - kind: Secret + name: outline-tls + namespace: envoy-gateway