diff --git a/apps/fc-landing/fc-landing.yaml b/apps/fc-landing/fc-landing.yaml new file mode 100644 index 0000000..d606df3 --- /dev/null +++ b/apps/fc-landing/fc-landing.yaml @@ -0,0 +1,248 @@ +# FlowerCore Landing Page +# Blue Jay Lab branded landing page +# ArgoCD managed - BlueJay Lab +--- +# fc-system namespace is shared; don't overwrite if it exists +apiVersion: v1 +kind: Namespace +metadata: + name: fc-system + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +# Landing page HTML +apiVersion: v1 +kind: ConfigMap +metadata: + name: fc-landing-html + namespace: fc-system +data: + index.html: | + + + + + + FlowerCore - Blue Jay Lab + + + +
+ +

FlowerCore

+

Blue Jay Lab

+
+
+ +

Gitea

+

Git repositories

+
+ +

ArgoCD

+

GitOps deployments

+
+ +

Zabbix

+

Monitoring

+
+ +

Guacamole

+

Remote desktop

+
+ +

Element

+

Matrix chat

+
+ +

Mail

+

Snappymail webmail

+
+ +

Intranet

+

Lab portal

+
+ +

PKI

+

Certificates

+
+
+ + + +--- +# nginx configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: fc-landing-nginx-conf + namespace: fc-system +data: + default.conf: | + server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + + location /healthz { + access_log off; + return 200 "ok"; + add_header Content-Type text/plain; + } + } +--- +# Landing Page Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fc-landing + namespace: fc-system + labels: + app: fc-landing +spec: + replicas: 1 + selector: + matchLabels: + app: fc-landing + template: + metadata: + labels: + app: fc-landing + spec: + containers: + - name: nginx + image: nginx:alpine + ports: + - containerPort: 80 + name: http + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/conf.d/default.conf + subPath: default.conf + - name: html + mountPath: /usr/share/nginx/html + resources: + requests: + memory: 16Mi + cpu: 5m + limits: + memory: 64Mi + cpu: 50m + livenessProbe: + httpGet: + path: /healthz + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /healthz + port: 80 + initialDelaySeconds: 3 + periodSeconds: 5 + volumes: + - name: nginx-conf + configMap: + name: fc-landing-nginx-conf + - name: html + configMap: + name: fc-landing-html +--- +apiVersion: v1 +kind: Service +metadata: + name: fc-landing + namespace: fc-system +spec: + selector: + app: fc-landing + ports: + - port: 80 + targetPort: 80 + name: http +--- +# Traefik IngressRoute (internal only, no public cert needed) +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: fc-landing + namespace: fc-system +spec: + entryPoints: + - websecure + routes: + - match: Host(`flowercore.iamworkin.lan`) + kind: Rule + services: + - name: fc-landing + port: 80 + tls: {} diff --git a/apps/guacamole/guacamole.yaml b/apps/guacamole/guacamole.yaml new file mode 100644 index 0000000..3771b78 --- /dev/null +++ b/apps/guacamole/guacamole.yaml @@ -0,0 +1,326 @@ +# Apache Guacamole - Remote Desktop Gateway +# MySQL 8 + guacd + guacamole web +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: guacamole + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +apiVersion: v1 +kind: Secret +metadata: + name: guac-db-secret + namespace: guacamole +type: Opaque +stringData: + MYSQL_ROOT_PASSWORD: BlueJay-Guac-DB-2026 + MYSQL_DATABASE: guacamole_db + MYSQL_USER: guacamole + MYSQL_PASSWORD: BlueJay-Guac-DB-2026 +--- +# MySQL 8 StatefulSet +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: guac-mysql + namespace: guacamole + labels: + app: guac-mysql +spec: + serviceName: guac-mysql + replicas: 1 + selector: + matchLabels: + app: guac-mysql + template: + metadata: + labels: + app: guac-mysql + spec: + containers: + - name: mysql + image: mysql:8.0 + ports: + - containerPort: 3306 + name: mysql + envFrom: + - secretRef: + name: guac-db-secret + volumeMounts: + - name: guac-mysql-data + mountPath: /var/lib/mysql + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 1Gi + cpu: 500m + livenessProbe: + exec: + command: + - mysqladmin + - ping + - -h + - localhost + initialDelaySeconds: 60 + periodSeconds: 10 + readinessProbe: + exec: + command: + - mysqladmin + - ping + - -h + - localhost + initialDelaySeconds: 30 + periodSeconds: 5 + volumeClaimTemplates: + - metadata: + name: guac-mysql-data + spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 5Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: guac-mysql + namespace: guacamole +spec: + selector: + app: guac-mysql + ports: + - port: 3306 + targetPort: 3306 + name: mysql + clusterIP: None +--- +# DB schema init Job +# Generates the MySQL schema and pipes it into the database +apiVersion: batch/v1 +kind: Job +metadata: + name: guacamole-initdb + namespace: guacamole + annotations: + argocd.argoproj.io/hook: PostSync + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation +spec: + ttlSecondsAfterFinished: 300 + template: + spec: + restartPolicy: OnFailure + initContainers: + - name: wait-for-mysql + image: mysql:8.0 + command: + - sh + - -c + - | + until mysqladmin ping -h guac-mysql --silent; do + echo "Waiting for MySQL..." + sleep 5 + done + containers: + - name: initdb + image: guacamole/guacamole:latest + command: + - sh + - -c + - | + # Generate schema SQL + /opt/guacamole/bin/initdb.sh --mysql > /tmp/initdb.sql + # Apply schema (ignore errors if tables already exist) + mysql -h guac-mysql -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < /tmp/initdb.sql || true + env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: guac-db-secret + key: MYSQL_ROOT_PASSWORD + - name: MYSQL_DATABASE + valueFrom: + secretKeyRef: + name: guac-db-secret + key: MYSQL_DATABASE +--- +# guacd (Guacamole daemon) +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guacd + namespace: guacamole + labels: + app: guacd +spec: + replicas: 1 + selector: + matchLabels: + app: guacd + template: + metadata: + labels: + app: guacd + spec: + containers: + - name: guacd + image: guacamole/guacd:latest + ports: + - containerPort: 4822 + name: guacd + resources: + requests: + memory: 128Mi + cpu: 100m + limits: + memory: 512Mi + cpu: 500m + livenessProbe: + tcpSocket: + port: 4822 + initialDelaySeconds: 15 + periodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + name: guacd + namespace: guacamole +spec: + selector: + app: guacd + ports: + - port: 4822 + targetPort: 4822 + name: guacd +--- +# Guacamole Web Application +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guacamole + namespace: guacamole + labels: + app: guacamole +spec: + replicas: 1 + selector: + matchLabels: + app: guacamole + template: + metadata: + labels: + app: guacamole + spec: + containers: + - name: guacamole + image: guacamole/guacamole:latest + ports: + - containerPort: 8080 + name: http + env: + - name: GUACD_HOSTNAME + value: guacd + - name: GUACD_PORT + value: "4822" + - name: MYSQL_HOSTNAME + value: guac-mysql + - name: MYSQL_PORT + value: "3306" + - name: MYSQL_DATABASE + valueFrom: + secretKeyRef: + name: guac-db-secret + key: MYSQL_DATABASE + - name: MYSQL_USER + valueFrom: + secretKeyRef: + name: guac-db-secret + key: MYSQL_USER + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: guac-db-secret + key: MYSQL_PASSWORD + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 1Gi + cpu: 500m + livenessProbe: + httpGet: + path: /guacamole/ + port: 8080 + initialDelaySeconds: 120 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /guacamole/ + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: guacamole + namespace: guacamole +spec: + selector: + app: guacamole + ports: + - port: 8080 + targetPort: 8080 + name: http +--- +# Traefik addPrefix middleware +# External URL guac.iamworkin.lan/ gets prefix /guacamole added +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: guac-add-prefix + namespace: guacamole +spec: + addPrefix: + prefix: /guacamole +--- +# TLS Certificate via cert-manager +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: guacamole-tls + namespace: guacamole +spec: + secretName: guacamole-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - guac.iamworkin.lan +--- +# Traefik IngressRoute +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: guacamole + namespace: guacamole +spec: + entryPoints: + - websecure + routes: + - match: Host(`guac.iamworkin.lan`) + kind: Rule + middlewares: + - name: guac-add-prefix + services: + - name: guacamole + port: 8080 + tls: + secretName: guacamole-tls diff --git a/apps/intranet/intranet.yaml b/apps/intranet/intranet.yaml new file mode 100644 index 0000000..ae7d5cd --- /dev/null +++ b/apps/intranet/intranet.yaml @@ -0,0 +1,205 @@ +# Lab Intranet - Static site served by nginx +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: intranet + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +# nginx configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: intranet-nginx-conf + namespace: intranet +data: + default.conf: | + server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + + location /healthz { + access_log off; + return 200 "ok"; + add_header Content-Type text/plain; + } + } +--- +# Placeholder HTML content +apiVersion: v1 +kind: ConfigMap +metadata: + name: intranet-html + namespace: intranet +data: + index.html: | + + + + + + BlueJay Lab - Intranet + + + +
+ +

BlueJay Lab

+

Intranet Portal

+
+

Intranet content coming soon.

+

Replace this ConfigMap with lab-intranet.html content.

+
+
+ + +--- +# nginx Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: intranet + namespace: intranet + labels: + app: intranet +spec: + replicas: 1 + selector: + matchLabels: + app: intranet + template: + metadata: + labels: + app: intranet + spec: + containers: + - name: nginx + image: nginx:alpine + ports: + - containerPort: 80 + name: http + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/conf.d/default.conf + subPath: default.conf + - name: html + mountPath: /usr/share/nginx/html + resources: + requests: + memory: 16Mi + cpu: 5m + limits: + memory: 64Mi + cpu: 50m + livenessProbe: + httpGet: + path: /healthz + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /healthz + port: 80 + initialDelaySeconds: 3 + periodSeconds: 5 + volumes: + - name: nginx-conf + configMap: + name: intranet-nginx-conf + - name: html + configMap: + name: intranet-html +--- +apiVersion: v1 +kind: Service +metadata: + name: intranet + namespace: intranet +spec: + selector: + app: intranet + ports: + - port: 80 + targetPort: 80 + name: http +--- +# TLS Certificate via cert-manager +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: intranet-tls + namespace: intranet +spec: + secretName: intranet-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - intranet.iamworkin.lan +--- +# Traefik IngressRoute +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: intranet + namespace: intranet +spec: + entryPoints: + - websecure + routes: + - match: Host(`intranet.iamworkin.lan`) + kind: Rule + services: + - name: intranet + port: 80 + tls: + secretName: intranet-tls diff --git a/apps/irc/irc.yaml b/apps/irc/irc.yaml new file mode 100644 index 0000000..dab1b92 --- /dev/null +++ b/apps/irc/irc.yaml @@ -0,0 +1,184 @@ +# UnrealIRCd + Anope IRC Services +# PLACEHOLDER - UnrealIRCd needs config files mounted before running +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: irc + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +# UnrealIRCd PVC +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: unrealircd-data + namespace: irc +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi +--- +# Anope PVC +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: anope-data + namespace: irc +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi +--- +# UnrealIRCd Deployment +# NOTE: This is a placeholder. UnrealIRCd requires configuration files +# (unrealircd.conf, TLS certs, etc.) to be present in /data before starting. +# Mount config via ConfigMap/Secret or init container before enabling. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: unrealircd + namespace: irc + labels: + app: unrealircd +spec: + replicas: 1 + selector: + matchLabels: + app: unrealircd + template: + metadata: + labels: + app: unrealircd + spec: + containers: + - name: unrealircd + image: ghcr.io/unrealircd/unrealircd:latest + ports: + - containerPort: 6667 + name: irc-plain + - containerPort: 6697 + name: irc-tls + - containerPort: 8067 + name: services-link + volumeMounts: + - name: unrealircd-data + mountPath: /data + resources: + requests: + memory: 64Mi + cpu: 50m + limits: + memory: 256Mi + cpu: 250m + volumes: + - name: unrealircd-data + persistentVolumeClaim: + claimName: unrealircd-data +--- +# Anope IRC Services Deployment +# NOTE: Placeholder. Anope requires services.conf with link block +# matching UnrealIRCd's link configuration. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: anope + namespace: irc + labels: + app: anope +spec: + replicas: 1 + selector: + matchLabels: + app: anope + template: + metadata: + labels: + app: anope + spec: + containers: + - name: anope + image: anope/anope:latest + volumeMounts: + - name: anope-data + mountPath: /data + resources: + requests: + memory: 64Mi + cpu: 25m + limits: + memory: 128Mi + cpu: 100m + volumes: + - name: anope-data + persistentVolumeClaim: + claimName: anope-data +--- +# UnrealIRCd Service (ClusterIP for internal + Traefik TCP routing) +apiVersion: v1 +kind: Service +metadata: + name: unrealircd + namespace: irc +spec: + selector: + app: unrealircd + ports: + - port: 6667 + targetPort: 6667 + name: irc-plain + - port: 6697 + targetPort: 6697 + name: irc-tls + - port: 8067 + targetPort: 8067 + name: services-link +--- +# Anope Service +apiVersion: v1 +kind: Service +metadata: + name: anope + namespace: irc +spec: + selector: + app: anope + ports: + - port: 8067 + targetPort: 8067 + name: services-link +--- +# Traefik IngressRouteTCP - IRC plain (6667) +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: irc-plain + namespace: irc +spec: + entryPoints: + - irc + routes: + - match: HostSNI(`*`) + services: + - name: unrealircd + port: 6667 +--- +# Traefik IngressRouteTCP - IRC TLS passthrough (6697) +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: irc-tls + namespace: irc +spec: + entryPoints: + - ircs + routes: + - match: HostSNI(`*`) + services: + - name: unrealircd + port: 6697 + tls: + passthrough: true diff --git a/apps/mail/mail.yaml b/apps/mail/mail.yaml new file mode 100644 index 0000000..e0305a7 --- /dev/null +++ b/apps/mail/mail.yaml @@ -0,0 +1,203 @@ +# docker-mailserver - Postfix + Dovecot + rspamd +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: mail + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +# Mail data PVC +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mail-data + namespace: mail +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 5Gi +--- +# Mail state PVC +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mail-state + namespace: mail +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi +--- +# docker-mailserver Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mailserver + namespace: mail + labels: + app: mailserver +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: mailserver + template: + metadata: + labels: + app: mailserver + spec: + hostname: mail + containers: + - name: mailserver + image: docker.io/mailserver/docker-mailserver:latest + ports: + - containerPort: 25 + name: smtp + - containerPort: 465 + name: smtps + - containerPort: 587 + name: submission + - containerPort: 143 + name: imap + - containerPort: 993 + name: imaps + env: + - name: ENABLE_SPAMASSASSIN + value: "1" + - name: ENABLE_CLAMAV + value: "0" + - name: ENABLE_RSPAMD + value: "1" + - name: TZ + value: America/Chicago + - name: POSTMASTER_ADDRESS + value: postmaster@iamwork.in + - name: OVERRIDE_HOSTNAME + value: mail.iamwork.in + - name: ENABLE_FAIL2BAN + value: "0" + - name: ENABLE_POSTGREY + value: "0" + - name: ONE_DIR + value: "1" + - name: PERMIT_DOCKER + value: network + - name: SSL_TYPE + value: manual + - name: SSL_CERT_PATH + value: /etc/ssl/mail/tls.crt + - name: SSL_KEY_PATH + value: /etc/ssl/mail/tls.key + volumeMounts: + - name: mail-data + mountPath: /var/mail + - name: mail-state + mountPath: /var/mail-state + - name: mail-tls + mountPath: /etc/ssl/mail + readOnly: true + resources: + requests: + memory: 512Mi + cpu: 200m + limits: + memory: 2Gi + cpu: "1" + securityContext: + capabilities: + add: + - NET_ADMIN + - SYS_PTRACE + volumes: + - name: mail-data + persistentVolumeClaim: + claimName: mail-data + - name: mail-state + persistentVolumeClaim: + claimName: mail-state + - name: mail-tls + secret: + secretName: mail-tls +--- +# SMTP LoadBalancer Service (external) +apiVersion: v1 +kind: Service +metadata: + name: mail-smtp + namespace: mail + annotations: + metallb.universe.tf/loadBalancerIPs: 10.0.56.202 +spec: + type: LoadBalancer + selector: + app: mailserver + ports: + - port: 25 + targetPort: 25 + name: smtp + protocol: TCP + - port: 465 + targetPort: 465 + name: smtps + protocol: TCP + - port: 587 + targetPort: 587 + name: submission + protocol: TCP +--- +# IMAP ClusterIP Service (internal) +apiVersion: v1 +kind: Service +metadata: + name: mail-imap + namespace: mail +spec: + selector: + app: mailserver + ports: + - port: 143 + targetPort: 143 + name: imap + - port: 993 + targetPort: 993 + name: imaps +--- +# TLS Certificate via cert-manager +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: mail-tls + namespace: mail +spec: + secretName: mail-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - mail.iamworkin.lan +--- +# Traefik IngressRoute - Webmail placeholder +# Snappymail will need a separate deployment; this routes to the +# mail server's HTTP port if available, or to a future webmail deployment +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: mail-webmail + namespace: mail +spec: + entryPoints: + - websecure + routes: + - match: Host(`mail.iamworkin.lan`) + kind: Rule + services: + - name: mail-imap + port: 993 + tls: + secretName: mail-tls diff --git a/apps/matrix/matrix.yaml b/apps/matrix/matrix.yaml new file mode 100644 index 0000000..0d8e195 --- /dev/null +++ b/apps/matrix/matrix.yaml @@ -0,0 +1,354 @@ +# Matrix Synapse + Element Web +# PostgreSQL 16 + Synapse homeserver + Element Web client +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: matrix + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +apiVersion: v1 +kind: Secret +metadata: + name: matrix-db-secret + namespace: matrix +type: Opaque +stringData: + POSTGRES_USER: synapse + POSTGRES_PASSWORD: BlueJay-Matrix-DB-2026 + POSTGRES_DB: synapse + POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C" +--- +# PostgreSQL 16 StatefulSet +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: matrix-postgres + namespace: matrix + labels: + app: matrix-postgres +spec: + serviceName: matrix-postgres + replicas: 1 + selector: + matchLabels: + app: matrix-postgres + template: + metadata: + labels: + app: matrix-postgres + spec: + containers: + - name: postgres + image: postgres:16-alpine + ports: + - containerPort: 5432 + name: postgres + envFrom: + - secretRef: + name: matrix-db-secret + volumeMounts: + - name: matrix-postgres-data + mountPath: /var/lib/postgresql/data + subPath: pgdata + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 1Gi + cpu: 500m + livenessProbe: + exec: + command: + - pg_isready + - -U + - synapse + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + exec: + command: + - pg_isready + - -U + - synapse + initialDelaySeconds: 5 + periodSeconds: 5 + volumeClaimTemplates: + - metadata: + name: matrix-postgres-data + spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 5Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: matrix-postgres + namespace: matrix +spec: + selector: + app: matrix-postgres + ports: + - port: 5432 + targetPort: 5432 + name: postgres + clusterIP: None +--- +# Synapse Data PVC +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: synapse-data + namespace: matrix +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 2Gi +--- +# Synapse Homeserver Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: synapse + namespace: matrix + labels: + app: synapse +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: synapse + template: + metadata: + labels: + app: synapse + spec: + containers: + - name: synapse + image: matrixdotorg/synapse:latest + ports: + - containerPort: 8008 + name: http + env: + - name: SYNAPSE_SERVER_NAME + value: iamworkin.lan + - name: SYNAPSE_REPORT_STATS + value: "no" + - name: SYNAPSE_CONFIG_DIR + value: /data + - name: SYNAPSE_DATA_DIR + value: /data + - name: POSTGRES_HOST + value: matrix-postgres + - name: POSTGRES_PORT + value: "5432" + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: matrix-db-secret + key: POSTGRES_DB + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: matrix-db-secret + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: matrix-db-secret + key: POSTGRES_PASSWORD + volumeMounts: + - name: synapse-data + mountPath: /data + resources: + requests: + memory: 512Mi + cpu: 200m + limits: + memory: 2Gi + cpu: "1" + livenessProbe: + httpGet: + path: /health + port: 8008 + initialDelaySeconds: 60 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 8008 + initialDelaySeconds: 30 + periodSeconds: 5 + volumes: + - name: synapse-data + persistentVolumeClaim: + claimName: synapse-data +--- +apiVersion: v1 +kind: Service +metadata: + name: synapse + namespace: matrix +spec: + selector: + app: synapse + ports: + - port: 8008 + targetPort: 8008 + name: http +--- +# Element Web ConfigMap +apiVersion: v1 +kind: ConfigMap +metadata: + name: element-web-config + namespace: matrix +data: + config.json: | + { + "default_server_config": { + "m.homeserver": { + "base_url": "https://matrix.iamworkin.lan", + "server_name": "iamworkin.lan" + } + }, + "brand": "BlueJay Chat", + "disable_guests": true, + "disable_3pid_login": true + } +--- +# Element Web Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: element-web + namespace: matrix + labels: + app: element-web +spec: + replicas: 1 + selector: + matchLabels: + app: element-web + template: + metadata: + labels: + app: element-web + spec: + containers: + - name: element-web + image: vectorim/element-web:latest + ports: + - containerPort: 80 + name: http + volumeMounts: + - name: element-config + mountPath: /app/config.json + subPath: config.json + resources: + requests: + memory: 32Mi + cpu: 10m + limits: + memory: 128Mi + cpu: 100m + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: element-config + configMap: + name: element-web-config +--- +apiVersion: v1 +kind: Service +metadata: + name: element-web + namespace: matrix +spec: + selector: + app: element-web + ports: + - port: 80 + targetPort: 80 + name: http +--- +# TLS Certificates via cert-manager +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: matrix-tls + namespace: matrix +spec: + secretName: matrix-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - matrix.iamworkin.lan +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: element-tls + namespace: matrix +spec: + secretName: element-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - element.iamworkin.lan +--- +# Traefik IngressRoute - Synapse +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: synapse + namespace: matrix +spec: + entryPoints: + - websecure + routes: + - match: Host(`matrix.iamworkin.lan`) + kind: Rule + services: + - name: synapse + port: 8008 + tls: + secretName: matrix-tls +--- +# Traefik IngressRoute - Element Web +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: element-web + namespace: matrix +spec: + entryPoints: + - websecure + routes: + - match: Host(`element.iamworkin.lan`) + kind: Rule + services: + - name: element-web + port: 80 + tls: + secretName: element-tls diff --git a/apps/pki-web/pki-web.yaml b/apps/pki-web/pki-web.yaml new file mode 100644 index 0000000..907283f --- /dev/null +++ b/apps/pki-web/pki-web.yaml @@ -0,0 +1,220 @@ +# PKI Certificate Web Interface +# Placeholder nginx serving step-ca certificate info +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: pki + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +# PKI Web HTML placeholder +apiVersion: v1 +kind: ConfigMap +metadata: + name: pki-web-html + namespace: pki +data: + index.html: | + + + + + + BlueJay Lab - PKI Portal + + + +
+

BlueJay PKI Portal

+

IAmWorkin ACME Certificate Authority

+
+

Internal CA

+

ClusterIssuer: step-ca-acme

+

Domain: *.iamworkin.lan

+

Validity: 30 days, auto-renewed by cert-manager

+
+
+

Cloudflare Origin Certs

+

*.flowercore.io and *.iamwork.in

+

15-year RSA certificates for public domains

+
+
+

Download Root CA

+

Install the IAmWorkin Root CA certificate to trust internal services.

+

Root CA download will be available here.

+
+
+ + +--- +# nginx configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: pki-web-nginx-conf + namespace: pki +data: + default.conf: | + server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + + location /healthz { + access_log off; + return 200 "ok"; + add_header Content-Type text/plain; + } + } +--- +# PKI Web Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pki-web + namespace: pki + labels: + app: pki-web +spec: + replicas: 1 + selector: + matchLabels: + app: pki-web + template: + metadata: + labels: + app: pki-web + spec: + containers: + - name: nginx + image: nginx:alpine + ports: + - containerPort: 80 + name: http + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/conf.d/default.conf + subPath: default.conf + - name: html + mountPath: /usr/share/nginx/html + resources: + requests: + memory: 16Mi + cpu: 5m + limits: + memory: 64Mi + cpu: 50m + livenessProbe: + httpGet: + path: /healthz + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /healthz + port: 80 + initialDelaySeconds: 3 + periodSeconds: 5 + volumes: + - name: nginx-conf + configMap: + name: pki-web-nginx-conf + - name: html + configMap: + name: pki-web-html +--- +apiVersion: v1 +kind: Service +metadata: + name: pki-web + namespace: pki +spec: + selector: + app: pki-web + ports: + - port: 80 + targetPort: 80 + name: http +--- +# TLS Certificate via cert-manager +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: pki-tls + namespace: pki +spec: + secretName: pki-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - pki.iamworkin.lan +--- +# Traefik IngressRoute +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: pki-web + namespace: pki +spec: + entryPoints: + - websecure + routes: + - match: Host(`pki.iamworkin.lan`) + kind: Rule + services: + - name: pki-web + port: 80 + tls: + secretName: pki-tls diff --git a/apps/teamspeak/teamspeak.yaml b/apps/teamspeak/teamspeak.yaml new file mode 100644 index 0000000..7dcdd38 --- /dev/null +++ b/apps/teamspeak/teamspeak.yaml @@ -0,0 +1,108 @@ +# TeamSpeak 3 Server +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: teamspeak + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +# TeamSpeak data PVC +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: teamspeak-data + namespace: teamspeak +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi +--- +# TeamSpeak 3 Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: teamspeak + namespace: teamspeak + labels: + app: teamspeak +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: teamspeak + template: + metadata: + labels: + app: teamspeak + spec: + containers: + - name: teamspeak + image: teamspeak:latest + ports: + - containerPort: 9987 + name: voice + protocol: UDP + - containerPort: 30033 + name: filetransfer + protocol: TCP + - containerPort: 10011 + name: serverquery + protocol: TCP + env: + - name: TS3SERVER_LICENSE + value: accept + volumeMounts: + - name: teamspeak-data + mountPath: /var/ts3server + resources: + requests: + memory: 128Mi + cpu: 50m + limits: + memory: 512Mi + cpu: 500m + readinessProbe: + tcpSocket: + port: 10011 + initialDelaySeconds: 30 + periodSeconds: 10 + livenessProbe: + tcpSocket: + port: 10011 + initialDelaySeconds: 60 + periodSeconds: 15 + volumes: + - name: teamspeak-data + persistentVolumeClaim: + claimName: teamspeak-data +--- +# TeamSpeak LoadBalancer Service +apiVersion: v1 +kind: Service +metadata: + name: teamspeak + namespace: teamspeak + annotations: + metallb.universe.tf/loadBalancerIPs: 10.0.56.205 +spec: + type: LoadBalancer + selector: + app: teamspeak + ports: + - port: 9987 + targetPort: 9987 + name: voice + protocol: UDP + - port: 30033 + targetPort: 30033 + name: filetransfer + protocol: TCP + - port: 10011 + targetPort: 10011 + name: serverquery + protocol: TCP diff --git a/apps/zabbix/zabbix.yaml b/apps/zabbix/zabbix.yaml new file mode 100644 index 0000000..f394b09 --- /dev/null +++ b/apps/zabbix/zabbix.yaml @@ -0,0 +1,320 @@ +# Zabbix 7.2 Monitoring Stack +# PostgreSQL 16 + Zabbix Server + Zabbix Web (nginx) +# ArgoCD managed - BlueJay Lab +--- +apiVersion: v1 +kind: Namespace +metadata: + name: zabbix + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +apiVersion: v1 +kind: Secret +metadata: + name: zabbix-db-secret + namespace: zabbix +type: Opaque +stringData: + POSTGRES_USER: zabbix + POSTGRES_PASSWORD: BlueJay-ZabbixDB-2026 + POSTGRES_DB: zabbix +--- +apiVersion: v1 +kind: Secret +metadata: + name: zabbix-admin-secret + namespace: zabbix +type: Opaque +stringData: + ZBX_ADMIN_PASSWORD: BlueJay-NOC-2026 +--- +# PostgreSQL 16 StatefulSet +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: zabbix-postgres + namespace: zabbix + labels: + app: zabbix-postgres +spec: + serviceName: zabbix-postgres + replicas: 1 + selector: + matchLabels: + app: zabbix-postgres + template: + metadata: + labels: + app: zabbix-postgres + spec: + containers: + - name: postgres + image: postgres:16-alpine + ports: + - containerPort: 5432 + name: postgres + envFrom: + - secretRef: + name: zabbix-db-secret + volumeMounts: + - name: zabbix-postgres-data + mountPath: /var/lib/postgresql/data + subPath: pgdata + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 512Mi + cpu: 500m + livenessProbe: + exec: + command: + - pg_isready + - -U + - zabbix + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + exec: + command: + - pg_isready + - -U + - zabbix + initialDelaySeconds: 5 + periodSeconds: 5 + volumeClaimTemplates: + - metadata: + name: zabbix-postgres-data + spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 10Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: zabbix-postgres + namespace: zabbix +spec: + selector: + app: zabbix-postgres + ports: + - port: 5432 + targetPort: 5432 + name: postgres + clusterIP: None +--- +# Zabbix Server +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zabbix-server + namespace: zabbix + labels: + app: zabbix-server +spec: + replicas: 1 + selector: + matchLabels: + app: zabbix-server + template: + metadata: + labels: + app: zabbix-server + spec: + containers: + - name: zabbix-server + image: zabbix/zabbix-server-pgsql:7.2-alpine-latest + ports: + - containerPort: 10051 + name: trapper + env: + - name: DB_SERVER_HOST + value: zabbix-postgres + - name: DB_SERVER_PORT + value: "5432" + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: zabbix-db-secret + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: zabbix-db-secret + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: zabbix-db-secret + key: POSTGRES_DB + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 1Gi + cpu: "1" + livenessProbe: + tcpSocket: + port: 10051 + initialDelaySeconds: 60 + periodSeconds: 10 + readinessProbe: + tcpSocket: + port: 10051 + initialDelaySeconds: 30 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: zabbix-server + namespace: zabbix +spec: + selector: + app: zabbix-server + ports: + - port: 10051 + targetPort: 10051 + name: trapper +--- +apiVersion: v1 +kind: Service +metadata: + name: zabbix-trapper + namespace: zabbix + annotations: + metallb.universe.tf/loadBalancerIPs: 10.0.56.203 +spec: + type: LoadBalancer + selector: + app: zabbix-server + ports: + - port: 10051 + targetPort: 10051 + name: trapper + protocol: TCP +--- +# Zabbix Web (nginx + PostgreSQL) +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zabbix-web + namespace: zabbix + labels: + app: zabbix-web +spec: + replicas: 1 + selector: + matchLabels: + app: zabbix-web + template: + metadata: + labels: + app: zabbix-web + spec: + containers: + - name: zabbix-web + image: zabbix/zabbix-web-nginx-pgsql:7.2-alpine-latest + ports: + - containerPort: 8080 + name: http + env: + - name: ZBX_SERVER_HOST + value: zabbix-server + - name: ZBX_SERVER_NAME + value: "BlueJay NOC" + - name: PHP_TZ + value: America/Chicago + - name: DB_SERVER_HOST + value: zabbix-postgres + - name: DB_SERVER_PORT + value: "5432" + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: zabbix-db-secret + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: zabbix-db-secret + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: zabbix-db-secret + key: POSTGRES_DB + - name: ZBX_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: zabbix-admin-secret + key: ZBX_ADMIN_PASSWORD + resources: + requests: + memory: 128Mi + cpu: 50m + limits: + memory: 512Mi + cpu: 500m + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: zabbix-web + namespace: zabbix +spec: + selector: + app: zabbix-web + ports: + - port: 8080 + targetPort: 8080 + name: http +--- +# TLS Certificate via cert-manager +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: zabbix-tls + namespace: zabbix +spec: + secretName: zabbix-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - zabbix.iamworkin.lan +--- +# Traefik IngressRoute +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: zabbix-web + namespace: zabbix +spec: + entryPoints: + - websecure + routes: + - match: Host(`zabbix.iamworkin.lan`) + kind: Rule + services: + - name: zabbix-web + port: 8080 + tls: + secretName: zabbix-tls