diff --git a/argocd-apps/homelab.yaml b/argocd-apps/homelab.yaml index bd8de40..a585844 100644 --- a/argocd-apps/homelab.yaml +++ b/argocd-apps/homelab.yaml @@ -877,4 +877,26 @@ spec: syncOptions: - CreateNamespace=true - PruneLast=true +--- +# Webserver +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: webserver + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: homelab + source: + repoURL: https://gitea.dooplex.hu/admin/homelab-manifests.git + targetRevision: main + path: web-system + destination: + server: https://kubernetes.default.svc + namespace: web-system + syncPolicy: + syncOptions: + - CreateNamespace=true + - PruneLast=true --- \ No newline at end of file diff --git a/web-system/web.yaml b/web-system/web.yaml new file mode 100644 index 0000000..a229b4f --- /dev/null +++ b/web-system/web.yaml @@ -0,0 +1,495 @@ +--- +# FileBrowser - Web-based file manager +# https://filebrowser.org/ +# +# Features: +# - Web-based file browser and manager +# - File upload/download via web UI +# - File preview (images, text, markdown, video) +# - Built-in text editor +# - Share links for files +# - Very lightweight (~15MB RAM) +# - Authentik SSO integration (proxy auth) +# +# Authentication: Via Authentik (no double login!) +# FileBrowser trusts the X-authentik-username header from the proxy. +# +# Authentik Setup: +# 1. Create a Proxy Provider in Authentik: +# - Name: FileBrowser +# - Authorization flow: default-provider-authorization-implicit-consent +# - Type: Forward auth (single application) +# - External host: https://webadmin.dooplex.hu +# +# 2. Create an Application: +# - Name: FileBrowser +# - Slug: filebrowser +# - Provider: FileBrowser (the one you just created) +# +# 3. Create an Outpost (or add to existing): +# - Name: filebrowser-outpost +# - Type: Proxy +# - Integration: Kubernetes (auth-system namespace) +# - Applications: FileBrowser +# +# 4. Authentik will auto-create: +# - Deployment: ak-outpost-filebrowser-outpost in auth-system +# - Service: ak-outpost-filebrowser-outpost in auth-system +# - Ingress for /outpost.goauthentik.io path on webadmin.dooplex.hu +# +# Note: The outpost service name in auth-url must match! +# If you name outpost differently, update the annotation: +# http://ak-outpost-.auth-system.svc.cluster.local:9000/... +# +# Access: +# - Admin UI: https://webadmin.dooplex.hu (Authentik login) +# - Public files: https://web.dooplex.hu (direct file access, no auth) +# +# Usage for static HTML hosting: +# 1. Login to webadmin.dooplex.hu (via Authentik) +# 2. Upload HTML files and assets to /public folder +# 3. Access directly via web.dooplex.hu/filename or web.dooplex.hu/folder +# +# Clean URL examples: +# - /public/fizetes.html → web.dooplex.hu/fizetes +# - /public/fizetes/index.html → web.dooplex.hu/fizetes +# - /public/about/index.html → web.dooplex.hu/about +# +# Note: First user to login via Authentik becomes admin in FileBrowser. +# Additional users logging in will be created automatically. +# +apiVersion: v1 +kind: Namespace +metadata: + name: web-system + labels: + app.kubernetes.io/name: web-system +--- +# PVC for files +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: filebrowser-data + namespace: web-system + labels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser + recurring-job-group.longhorn.io/backup: enabled +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 5Gi +--- +# PVC for database and config +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: filebrowser-config + namespace: web-system + labels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 100Mi +--- +# ConfigMap for FileBrowser settings +apiVersion: v1 +kind: ConfigMap +metadata: + name: filebrowser-config + namespace: web-system + labels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser +data: + .filebrowser.json: | + { + "port": 80, + "baseURL": "", + "address": "", + "log": "stdout", + "database": "/config/filebrowser.db", + "root": "/srv", + "auth": { + "method": "proxy", + "header": "X-authentik-username" + } + } +--- +# FileBrowser Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: filebrowser + namespace: web-system + labels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser + template: + metadata: + labels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + containers: + - name: filebrowser + image: filebrowser/filebrowser:v2.53.1 + ports: + - containerPort: 80 + name: http + protocol: TCP + env: + - name: TZ + value: "Europe/Budapest" + volumeMounts: + - name: data + mountPath: /srv + - name: config + mountPath: /config + - name: settings + mountPath: /.filebrowser.json + subPath: .filebrowser.json + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 500m + memory: 128Mi + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + volumes: + - name: data + persistentVolumeClaim: + claimName: filebrowser-data + - name: config + persistentVolumeClaim: + claimName: filebrowser-config + - name: settings + configMap: + name: filebrowser-config +--- +# Service for FileBrowser +apiVersion: v1 +kind: Service +metadata: + name: filebrowser + namespace: web-system + labels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser +--- +# Ingress for FileBrowser admin UI (Authentik protected) +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: filebrowser + namespace: web-system + labels: + app.kubernetes.io/instance: filebrowser + app.kubernetes.io/name: filebrowser + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + external-dns.alpha.kubernetes.io/hostname: webadmin.dooplex.hu + nginx.ingress.kubernetes.io/proxy-body-size: "1024m" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "300" + nginx.ingress.kubernetes.io/proxy-send-timeout: "300" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + # Authentik forward auth - update outpost name after creating in Authentik! + nginx.ingress.kubernetes.io/auth-url: http://ak-outpost-filebrowser-outpost.auth-system.svc.cluster.local:9000/outpost.goauthentik.io/auth/nginx + nginx.ingress.kubernetes.io/auth-signin: https://webadmin.dooplex.hu/outpost.goauthentik.io/start?rd=$escaped_request_uri + nginx.ingress.kubernetes.io/auth-response-headers: X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid + nginx.ingress.kubernetes.io/auth-snippet: | + proxy_set_header X-Forwarded-Host $http_host; +spec: + ingressClassName: nginx-internal + tls: + - hosts: + - webadmin.dooplex.hu + secretName: filebrowser-tls + rules: + - host: webadmin.dooplex.hu + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: filebrowser + port: + name: http + - host: webadmin.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: filebrowser + port: + name: http +--- +# ============================================ +# NGINX Static File Server (Public Access) +# ============================================ +# This serves files from /public folder without authentication +# Access: https://web.dooplex.hu/filename or https://web.dooplex.hu/folder +# +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-server + namespace: web-system + labels: + app.kubernetes.io/instance: static-server + app.kubernetes.io/name: static-server +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: static-server + app.kubernetes.io/name: static-server + template: + metadata: + labels: + app.kubernetes.io/instance: static-server + app.kubernetes.io/name: static-server + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + # Create public directory if it doesn't exist + - name: init-public-dir + image: busybox:1.36 + command: ['sh', '-c', 'mkdir -p /srv/public && chmod 755 /srv/public'] + volumeMounts: + - name: data + mountPath: /srv + securityContext: + runAsUser: 0 + containers: + - name: nginx + image: nginx:1.27-alpine + ports: + - containerPort: 8080 + name: http + protocol: TCP + volumeMounts: + - name: data + mountPath: /usr/share/nginx/html + subPath: public + readOnly: true + - name: nginx-config + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + resources: + requests: + cpu: 5m + memory: 16Mi + limits: + cpu: 100m + memory: 64Mi + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 10 + volumes: + - name: data + persistentVolumeClaim: + claimName: filebrowser-data + - name: nginx-config + configMap: + name: static-server-config +--- +# ConfigMap for nginx static server +apiVersion: v1 +kind: ConfigMap +metadata: + name: static-server-config + namespace: web-system + labels: + app.kubernetes.io/instance: static-server + app.kubernetes.io/name: static-server +data: + nginx.conf: | + user nginx; + worker_processes auto; + error_log /var/log/nginx/error.log warn; + pid /tmp/nginx.pid; + + events { + worker_connections 1024; + } + + http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + keepalive_timeout 65; + + # Temp paths for non-root user + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + server { + listen 8080; + server_name _; + root /usr/share/nginx/html; + index index.html index.htm; + + # Health check endpoint + location /health { + access_log off; + return 200 "OK\n"; + add_header Content-Type text/plain; + } + + # Serve static files + location / { + try_files $uri $uri/ $uri.html =404; + + # Directory listing disabled for security + # Uncomment below if you want to browse directories + # autoindex on; + # autoindex_exact_size off; + # autoindex_localtime on; + + # Cache static assets + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ { + expires 7d; + add_header Cache-Control "public, immutable"; + } + } + + # Security headers + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options SAMEORIGIN; + } + } +--- +# Service for static server +apiVersion: v1 +kind: Service +metadata: + name: static-server + namespace: web-system + labels: + app.kubernetes.io/instance: static-server + app.kubernetes.io/name: static-server +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app.kubernetes.io/instance: static-server + app.kubernetes.io/name: static-server +--- +# Ingress for public static files +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: static-server + namespace: web-system + labels: + app.kubernetes.io/instance: static-server + app.kubernetes.io/name: static-server + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + external-dns.alpha.kubernetes.io/hostname: web.dooplex.hu +spec: + ingressClassName: nginx-internal + tls: + - hosts: + - web.dooplex.hu + secretName: static-server-tls + rules: + - host: web.dooplex.hu + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: static-server + port: + name: http + - host: web.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: static-server + port: + name: http \ No newline at end of file