Update telephony-web image to v20260324d, resolve merge conflicts

This commit is contained in:
Andrew M. Stoltz
2026-03-24 15:55:52 -05:00
parent 42d2894ed1
commit f3fde15002
14 changed files with 3333 additions and 3420 deletions

View File

@@ -1,3 +1,3 @@
# bluejay-infra # bluejay-infra
Infrastructure manifests for ArgoCD Infrastructure manifests for ArgoCD

View File

@@ -1,150 +1,150 @@
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: asterisk name: asterisk
namespace: telephony namespace: telephony
labels: labels:
app: asterisk app: asterisk
spec: spec:
replicas: 1 replicas: 1
strategy: strategy:
type: Recreate type: Recreate
selector: selector:
matchLabels: matchLabels:
app: asterisk app: asterisk
template: template:
metadata: metadata:
labels: labels:
app: asterisk app: asterisk
spec: spec:
hostNetwork: true hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet dnsPolicy: ClusterFirstWithHostNet
securityContext: securityContext:
fsGroup: 0 fsGroup: 0
initContainers: initContainers:
- name: install-sounds - name: install-sounds
image: busybox:latest image: busybox:latest
command: command:
- sh - sh
- -c - -c
- | - |
mkdir -p /sounds/en && mkdir -p /sounds/en &&
wget -qO- http://downloads.asterisk.org/pub/telephony/sounds/asterisk-core-sounds-en-ulaw-current.tar.gz | tar xz -C /sounds/en/ && wget -qO- http://downloads.asterisk.org/pub/telephony/sounds/asterisk-core-sounds-en-ulaw-current.tar.gz | tar xz -C /sounds/en/ &&
wget -qO- http://downloads.asterisk.org/pub/telephony/sounds/asterisk-extra-sounds-en-ulaw-current.tar.gz | tar xz -C /sounds/en/ && wget -qO- http://downloads.asterisk.org/pub/telephony/sounds/asterisk-extra-sounds-en-ulaw-current.tar.gz | tar xz -C /sounds/en/ &&
echo "Sound files installed: $(find /sounds/en -type f | wc -l) files" echo "Sound files installed: $(find /sounds/en -type f | wc -l) files"
volumeMounts: volumeMounts:
- name: sounds - name: sounds
mountPath: /sounds/en mountPath: /sounds/en
containers: containers:
- name: asterisk - name: asterisk
image: localhost/andrius/asterisk:latest image: localhost/andrius/asterisk:latest
imagePullPolicy: Never imagePullPolicy: Never
ports: ports:
- name: sip-udp - name: sip-udp
containerPort: 5060 containerPort: 5060
protocol: UDP protocol: UDP
- name: sip-tcp - name: sip-tcp
containerPort: 5060 containerPort: 5060
protocol: TCP protocol: TCP
- name: ari - name: ari
containerPort: 8088 containerPort: 8088
protocol: TCP protocol: TCP
volumeMounts: volumeMounts:
- name: config-modules - name: config-modules
mountPath: /etc/asterisk/modules.conf mountPath: /etc/asterisk/modules.conf
subPath: modules.conf subPath: modules.conf
- name: config-http - name: config-http
mountPath: /etc/asterisk/http.conf mountPath: /etc/asterisk/http.conf
subPath: http.conf subPath: http.conf
- name: config-ari - name: config-ari
mountPath: /etc/asterisk/ari.conf mountPath: /etc/asterisk/ari.conf
subPath: ari.conf subPath: ari.conf
- name: config-manager - name: config-manager
mountPath: /etc/asterisk/manager.conf mountPath: /etc/asterisk/manager.conf
subPath: manager.conf subPath: manager.conf
- name: config-pjsip - name: config-pjsip
mountPath: /etc/asterisk/pjsip.conf mountPath: /etc/asterisk/pjsip.conf
subPath: pjsip.conf subPath: pjsip.conf
- name: config-extensions - name: config-extensions
mountPath: /etc/asterisk/extensions.conf mountPath: /etc/asterisk/extensions.conf
subPath: extensions.conf subPath: extensions.conf
- name: config-rtp - name: config-rtp
mountPath: /etc/asterisk/rtp.conf mountPath: /etc/asterisk/rtp.conf
subPath: rtp.conf subPath: rtp.conf
- name: asterisk-data - name: asterisk-data
mountPath: /var/spool/asterisk mountPath: /var/spool/asterisk
- name: asterisk-logs - name: asterisk-logs
mountPath: /var/log/asterisk mountPath: /var/log/asterisk
- name: sounds - name: sounds
mountPath: /var/lib/asterisk/sounds/en mountPath: /var/lib/asterisk/sounds/en
resources: resources:
requests: requests:
cpu: 100m cpu: 100m
memory: 128Mi memory: 128Mi
limits: limits:
cpu: "1" cpu: "1"
memory: 512Mi memory: 512Mi
livenessProbe: livenessProbe:
tcpSocket: tcpSocket:
port: 8088 port: 8088
initialDelaySeconds: 15 initialDelaySeconds: 15
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /ari/asterisk/info path: /ari/asterisk/info
port: 8088 port: 8088
httpHeaders: httpHeaders:
- name: Authorization - name: Authorization
value: "Basic Zmxvd2VyY29yZTpibHVlamF5LWFzdGVyaXNrLWFyaQ==" value: "Basic Zmxvd2VyY29yZTpibHVlamF5LWFzdGVyaXNrLWFyaQ=="
initialDelaySeconds: 10 initialDelaySeconds: 10
periodSeconds: 5 periodSeconds: 5
volumes: volumes:
- name: config-modules - name: config-modules
configMap: configMap:
name: asterisk-config name: asterisk-config
items: items:
- key: modules.conf - key: modules.conf
path: modules.conf path: modules.conf
- name: config-http - name: config-http
configMap: configMap:
name: asterisk-config name: asterisk-config
items: items:
- key: http.conf - key: http.conf
path: http.conf path: http.conf
- name: config-ari - name: config-ari
configMap: configMap:
name: asterisk-config name: asterisk-config
items: items:
- key: ari.conf - key: ari.conf
path: ari.conf path: ari.conf
- name: config-manager - name: config-manager
configMap: configMap:
name: asterisk-config name: asterisk-config
items: items:
- key: manager.conf - key: manager.conf
path: manager.conf path: manager.conf
- name: config-pjsip - name: config-pjsip
configMap: configMap:
name: asterisk-config name: asterisk-config
items: items:
- key: pjsip.conf - key: pjsip.conf
path: pjsip.conf path: pjsip.conf
- name: config-extensions - name: config-extensions
configMap: configMap:
name: asterisk-config name: asterisk-config
items: items:
- key: extensions.conf - key: extensions.conf
path: extensions.conf path: extensions.conf
- name: config-rtp - name: config-rtp
configMap: configMap:
name: asterisk-config name: asterisk-config
items: items:
- key: rtp.conf - key: rtp.conf
path: rtp.conf path: rtp.conf
- name: asterisk-data - name: asterisk-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: asterisk-data claimName: asterisk-data
- name: asterisk-logs - name: asterisk-logs
emptyDir: {} emptyDir: {}
- name: sounds - name: sounds
emptyDir: {} emptyDir: {}

View File

@@ -1,40 +1,40 @@
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: asterisk-sip name: asterisk-sip
namespace: telephony namespace: telephony
labels: labels:
app: asterisk app: asterisk
annotations: annotations:
metallb.universe.tf/loadBalancerIPs: "10.0.56.207" metallb.universe.tf/loadBalancerIPs: "10.0.56.207"
spec: spec:
type: LoadBalancer type: LoadBalancer
externalTrafficPolicy: Local externalTrafficPolicy: Local
selector: selector:
app: asterisk app: asterisk
ports: ports:
- name: sip-udp - name: sip-udp
port: 5060 port: 5060
targetPort: 5060 targetPort: 5060
protocol: UDP protocol: UDP
- name: sip-tcp - name: sip-tcp
port: 5060 port: 5060
targetPort: 5060 targetPort: 5060
protocol: TCP protocol: TCP
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: asterisk-ari name: asterisk-ari
namespace: telephony namespace: telephony
labels: labels:
app: asterisk app: asterisk
spec: spec:
type: ClusterIP type: ClusterIP
selector: selector:
app: asterisk app: asterisk
ports: ports:
- name: ari - name: ari
port: 8088 port: 8088
targetPort: 8088 targetPort: 8088
protocol: TCP protocol: TCP

View File

@@ -1,320 +1,320 @@
# FlowerCore Landing Page # FlowerCore Landing Page
# Blue Jay Lab branded landing page - PUBLIC facing # Blue Jay Lab branded landing page - PUBLIC facing
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: fc-system name: fc-system
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# Landing page HTML (public-safe - no internal LAN references) # Landing page HTML (public-safe - no internal LAN references)
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: fc-landing-html name: fc-landing-html
namespace: fc-system namespace: fc-system
data: data:
index.html: | index.html: |
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>FlowerCore</title> <title>FlowerCore</title>
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
body { body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #0a1628 0%, #1a2744 50%, #0d1f3c 100%); background: linear-gradient(135deg, #0a1628 0%, #1a2744 50%, #0d1f3c 100%);
color: #e0e8f0; color: #e0e8f0;
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.hero { .hero {
text-align: center; text-align: center;
padding: 3rem; padding: 3rem;
max-width: 800px; max-width: 800px;
} }
.logo { .logo {
font-size: 5rem; font-size: 5rem;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
filter: drop-shadow(0 0 20px rgba(74, 158, 255, 0.3)); filter: drop-shadow(0 0 20px rgba(74, 158, 255, 0.3));
} }
h1 { h1 {
font-size: 3rem; font-size: 3rem;
background: linear-gradient(135deg, #4a9eff, #7ab3ff); background: linear-gradient(135deg, #4a9eff, #7ab3ff);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
background-clip: text; background-clip: text;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.subtitle { .subtitle {
font-size: 1.3rem; font-size: 1.3rem;
color: #7ab3ff; color: #7ab3ff;
font-weight: 300; font-weight: 300;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.description { .description {
font-size: 1rem; font-size: 1rem;
color: #8aa8c4; color: #8aa8c4;
line-height: 1.6; line-height: 1.6;
margin-bottom: 3rem; margin-bottom: 3rem;
max-width: 600px; max-width: 600px;
} }
.services { .services {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem; gap: 1rem;
width: 100%; width: 100%;
max-width: 700px; max-width: 700px;
padding: 0 1rem; padding: 0 1rem;
} }
.service { .service {
background: rgba(74, 158, 255, 0.08); background: rgba(74, 158, 255, 0.08);
border: 1px solid rgba(74, 158, 255, 0.2); border: 1px solid rgba(74, 158, 255, 0.2);
border-radius: 8px; border-radius: 8px;
padding: 1.2rem; padding: 1.2rem;
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
transition: all 0.2s; transition: all 0.2s;
} }
.service:hover { .service:hover {
background: rgba(74, 158, 255, 0.15); background: rgba(74, 158, 255, 0.15);
border-color: rgba(74, 158, 255, 0.5); border-color: rgba(74, 158, 255, 0.5);
transform: translateY(-2px); transform: translateY(-2px);
} }
.service h3 { color: #4a9eff; font-size: 0.95rem; margin-bottom: 0.3rem; } .service h3 { color: #4a9eff; font-size: 0.95rem; margin-bottom: 0.3rem; }
.service p { color: #8aa8c4; font-size: 0.8rem; } .service p { color: #8aa8c4; font-size: 0.8rem; }
.status-bar { .status-bar {
display: flex; display: flex;
gap: 2rem; gap: 2rem;
margin-top: 2rem; margin-top: 2rem;
padding: 1rem 2rem; padding: 1rem 2rem;
background: rgba(74, 158, 255, 0.05); background: rgba(74, 158, 255, 0.05);
border-radius: 8px; border-radius: 8px;
border: 1px solid rgba(74, 158, 255, 0.1); border: 1px solid rgba(74, 158, 255, 0.1);
} }
.status-item { text-align: center; } .status-item { text-align: center; }
.status-item .value { color: #4a9eff; font-size: 1.5rem; font-weight: 700; } .status-item .value { color: #4a9eff; font-size: 1.5rem; font-weight: 700; }
.status-item .label { color: #6a8ca4; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 1px; } .status-item .label { color: #6a8ca4; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 1px; }
.footer { .footer {
margin-top: 3rem; margin-top: 3rem;
color: #4a6580; color: #4a6580;
font-size: 0.8rem; font-size: 0.8rem;
} }
.footer a { color: #4a6580; text-decoration: none; } .footer a { color: #4a6580; text-decoration: none; }
.footer a:hover { color: #7ab3ff; } .footer a:hover { color: #7ab3ff; }
</style> </style>
</head> </head>
<body> <body>
<div class="hero"> <div class="hero">
<div class="logo">&#x1F33B;</div> <div class="logo">&#x1F33B;</div>
<h1>FlowerCore</h1> <h1>FlowerCore</h1>
<p class="subtitle">Blue Jay Lab</p> <p class="subtitle">Blue Jay Lab</p>
<p class="description"> <p class="description">
Multi-tenant service management platform built on .NET 10, Multi-tenant service management platform built on .NET 10,
Kubernetes, and GitOps. Digital signage, telephony IVR, Kubernetes, and GitOps. Digital signage, telephony IVR,
MySQL/PHP hosting, and infrastructure automation. MySQL/PHP hosting, and infrastructure automation.
</p> </p>
</div> </div>
<div class="services"> <div class="services">
<a class="service" href="https://gitea.flowercore.io"> <a class="service" href="https://gitea.flowercore.io">
<h3>Source</h3> <h3>Source</h3>
<p>Gitea repositories</p> <p>Gitea repositories</p>
</a> </a>
<a class="service" href="https://webmail.flowercore.io"> <a class="service" href="https://webmail.flowercore.io">
<h3>Mail</h3> <h3>Mail</h3>
<p>Webmail access</p> <p>Webmail access</p>
</a> </a>
<a class="service" href="https://element.flowercore.io"> <a class="service" href="https://element.flowercore.io">
<h3>Chat</h3> <h3>Chat</h3>
<p>Matrix messaging</p> <p>Matrix messaging</p>
</a> </a>
<a class="service" href="https://github.com/FlowerCoreIO"> <a class="service" href="https://github.com/FlowerCoreIO">
<h3>GitHub</h3> <h3>GitHub</h3>
<p>Open source</p> <p>Open source</p>
</a> </a>
</div> </div>
<div class="status-bar"> <div class="status-bar">
<div class="status-item"> <div class="status-item">
<div class="value">17</div> <div class="value">17</div>
<div class="label">Services</div> <div class="label">Services</div>
</div> </div>
<div class="status-item"> <div class="status-item">
<div class="value">13</div> <div class="value">13</div>
<div class="label">VLANs</div> <div class="label">VLANs</div>
</div> </div>
<div class="status-item"> <div class="status-item">
<div class="value">12k+</div> <div class="value">12k+</div>
<div class="label">Tests</div> <div class="label">Tests</div>
</div> </div>
</div> </div>
<p class="footer"> <p class="footer">
FlowerCore &middot; Bare-metal RKE2 &middot; ArgoCD managed FlowerCore &middot; Bare-metal RKE2 &middot; ArgoCD managed
&middot; <a href="mailto:admin@flowercore.io">Contact</a> &middot; <a href="mailto:admin@flowercore.io">Contact</a>
</p> </p>
</body> </body>
</html> </html>
--- ---
# nginx configuration # nginx configuration
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: fc-landing-nginx-conf name: fc-landing-nginx-conf
namespace: fc-system namespace: fc-system
data: data:
default.conf: | default.conf: |
server { server {
listen 80; listen 80;
server_name _; server_name _;
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
location / { location / {
try_files $uri $uri/ =404; try_files $uri $uri/ =404;
} }
location /healthz { location /healthz {
access_log off; access_log off;
return 200 "ok"; return 200 "ok";
add_header Content-Type text/plain; add_header Content-Type text/plain;
} }
} }
--- ---
# Landing Page Deployment # Landing Page Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: fc-landing name: fc-landing
namespace: fc-system namespace: fc-system
labels: labels:
app: fc-landing app: fc-landing
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: fc-landing app: fc-landing
template: template:
metadata: metadata:
labels: labels:
app: fc-landing app: fc-landing
spec: spec:
containers: containers:
- name: nginx - name: nginx
image: nginx:alpine image: nginx:alpine
ports: ports:
- containerPort: 80 - containerPort: 80
name: http name: http
volumeMounts: volumeMounts:
- name: nginx-conf - name: nginx-conf
mountPath: /etc/nginx/conf.d/default.conf mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf subPath: default.conf
- name: html - name: html
mountPath: /usr/share/nginx/html mountPath: /usr/share/nginx/html
resources: resources:
requests: requests:
memory: 16Mi memory: 16Mi
cpu: 5m cpu: 5m
limits: limits:
memory: 64Mi memory: 64Mi
cpu: 50m cpu: 50m
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /healthz path: /healthz
port: 80 port: 80
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /healthz path: /healthz
port: 80 port: 80
initialDelaySeconds: 3 initialDelaySeconds: 3
periodSeconds: 5 periodSeconds: 5
volumes: volumes:
- name: nginx-conf - name: nginx-conf
configMap: configMap:
name: fc-landing-nginx-conf name: fc-landing-nginx-conf
- name: html - name: html
configMap: configMap:
name: fc-landing-html name: fc-landing-html
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: fc-landing name: fc-landing
namespace: fc-system namespace: fc-system
spec: spec:
selector: selector:
app: fc-landing app: fc-landing
ports: ports:
- port: 80 - port: 80
targetPort: 80 targetPort: 80
name: http name: http
--- ---
# Internal IngressRoute (LAN access) # Internal IngressRoute (LAN access)
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: fc-landing name: fc-landing
namespace: fc-system namespace: fc-system
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`flowercore.iamworkin.lan`) - match: Host(`flowercore.iamworkin.lan`)
kind: Rule kind: Rule
services: services:
- name: fc-landing - name: fc-landing
port: 80 port: 80
tls: {} tls: {}
--- ---
# Public IngressRoute (flowercore.io with Cloudflare origin cert) # Public IngressRoute (flowercore.io with Cloudflare origin cert)
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: fc-landing-public name: fc-landing-public
namespace: fc-system namespace: fc-system
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`flowercore.io`) || Host(`www.flowercore.io`) - match: Host(`flowercore.io`) || Host(`www.flowercore.io`)
kind: Rule kind: Rule
services: services:
- name: fc-landing - name: fc-landing
port: 80 port: 80
tls: tls:
secretName: cf-origin-flowercore-io secretName: cf-origin-flowercore-io
--- ---
# HTTP to HTTPS redirect for public domain # HTTP to HTTPS redirect for public domain
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: fc-landing-public-http name: fc-landing-public-http
namespace: fc-system namespace: fc-system
spec: spec:
entryPoints: entryPoints:
- web - web
routes: routes:
- match: Host(`flowercore.io`) || Host(`www.flowercore.io`) - match: Host(`flowercore.io`) || Host(`www.flowercore.io`)
kind: Rule kind: Rule
services: services:
- name: fc-landing - name: fc-landing
port: 80 port: 80
middlewares: middlewares:
- name: redirect-https - name: redirect-https
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: Middleware kind: Middleware
metadata: metadata:
name: redirect-https name: redirect-https
namespace: fc-system namespace: fc-system
spec: spec:
redirectScheme: redirectScheme:
scheme: https scheme: https
permanent: true permanent: true

View File

@@ -1,20 +1,20 @@
# Gitea Public IngressRoute # Gitea Public IngressRoute
# Routes gitea.flowercore.io to internal Gitea service via Cloudflare origin cert # Routes gitea.flowercore.io to internal Gitea service via Cloudflare origin cert
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: gitea-public name: gitea-public
namespace: gitea namespace: gitea
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`gitea.flowercore.io`) - match: Host(`gitea.flowercore.io`)
kind: Rule kind: Rule
services: services:
- name: gitea-http - name: gitea-http
port: 3000 port: 3000
tls: tls:
secretName: cf-origin-flowercore-io secretName: cf-origin-flowercore-io

View File

@@ -1,426 +1,344 @@
# Apache Guacamole - Blue Jay Remote Access # Apache Guacamole - Remote Desktop Gateway
# FlowerCore Infrastructure Gateway # MySQL 8 + guacd + guacamole web
# MySQL 8 + guacd + guacamole web (Blue Jay branded) # ArgoCD managed - BlueJay Lab
# ArgoCD managed - BlueJay Lab # ALL credentials sourced from 1Password via OnePasswordItem CRD (guacamole-credentials)
# ALL credentials sourced from 1Password via OnePasswordItem CRD (guacamole-credentials) # Fields: username, password, DB-User, DB-Password, DB-Root-Password, DB-Name, URL
# Custom image: fc-guacamole:bluejay (Blue Jay branding + 1Password vault extension) ---
--- apiVersion: v1
apiVersion: v1 kind: Namespace
kind: Namespace metadata:
metadata: name: guacamole
name: guacamole labels:
labels: app.kubernetes.io/part-of: bluejay-infra
app.kubernetes.io/part-of: bluejay-infra ---
--- # MySQL 8 StatefulSet
# MySQL 8 StatefulSet apiVersion: apps/v1
apiVersion: apps/v1 kind: StatefulSet
kind: StatefulSet metadata:
metadata: name: guac-mysql
name: guac-mysql namespace: guacamole
namespace: guacamole labels:
labels: app: guac-mysql
app: guac-mysql spec:
spec: serviceName: guac-mysql
serviceName: guac-mysql replicas: 1
replicas: 1 selector:
selector: matchLabels:
matchLabels: app: guac-mysql
app: guac-mysql template:
template: metadata:
metadata: labels:
labels: app: guac-mysql
app: guac-mysql spec:
spec: containers:
containers: - name: mysql
- name: mysql image: mysql:8.0
image: mysql:8.0 ports:
ports: - containerPort: 3306
- containerPort: 3306 name: mysql
name: mysql env:
env: - name: MYSQL_ROOT_PASSWORD
- name: MYSQL_ROOT_PASSWORD valueFrom:
valueFrom: secretKeyRef:
secretKeyRef: name: guacamole-credentials
name: guacamole-credentials key: DB-Root-Password
key: DB-Root-Password - name: MYSQL_DATABASE
- name: MYSQL_DATABASE valueFrom:
valueFrom: secretKeyRef:
secretKeyRef: name: guacamole-credentials
name: guacamole-credentials key: DB-Name
key: DB-Name - name: MYSQL_USER
- name: MYSQL_USER valueFrom:
valueFrom: secretKeyRef:
secretKeyRef: name: guacamole-credentials
name: guacamole-credentials key: DB-User
key: DB-User - name: MYSQL_PASSWORD
- name: MYSQL_PASSWORD valueFrom:
valueFrom: secretKeyRef:
secretKeyRef: name: guacamole-credentials
name: guacamole-credentials key: DB-Password
key: DB-Password volumeMounts:
volumeMounts: - name: guac-mysql-data
- name: guac-mysql-data mountPath: /var/lib/mysql
mountPath: /var/lib/mysql resources:
resources: requests:
requests: memory: 256Mi
memory: 256Mi cpu: 100m
cpu: 100m limits:
limits: memory: 1Gi
memory: 1Gi cpu: 500m
cpu: 500m livenessProbe:
livenessProbe: exec:
exec: command:
command: - mysqladmin
- mysqladmin - ping
- ping - -h
- -h - localhost
- localhost initialDelaySeconds: 60
initialDelaySeconds: 60 periodSeconds: 10
periodSeconds: 10 readinessProbe:
readinessProbe: exec:
exec: command:
command: - mysqladmin
- mysqladmin - ping
- ping - -h
- -h - localhost
- localhost initialDelaySeconds: 30
initialDelaySeconds: 30 periodSeconds: 5
periodSeconds: 5 volumeClaimTemplates:
volumeClaimTemplates: - metadata:
- metadata: name: guac-mysql-data
name: guac-mysql-data spec:
spec: accessModes: [ReadWriteOnce]
accessModes: [ReadWriteOnce] resources:
resources: requests:
requests: storage: 5Gi
storage: 5Gi ---
--- apiVersion: v1
apiVersion: v1 kind: Service
kind: Service metadata:
metadata: name: guac-mysql
name: guac-mysql namespace: guacamole
namespace: guacamole spec:
spec: selector:
selector: app: guac-mysql
app: guac-mysql ports:
ports: - port: 3306
- port: 3306 targetPort: 3306
targetPort: 3306 name: mysql
name: mysql clusterIP: None
clusterIP: None ---
--- # DB schema init Job
# DB schema init Job # Generates the MySQL schema and pipes it into the database
apiVersion: batch/v1 apiVersion: batch/v1
kind: Job kind: Job
metadata: metadata:
name: guacamole-initdb name: guacamole-initdb
namespace: guacamole namespace: guacamole
annotations: annotations:
argocd.argoproj.io/hook: PostSync argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec: spec:
ttlSecondsAfterFinished: 300 ttlSecondsAfterFinished: 300
template: template:
spec: spec:
restartPolicy: OnFailure restartPolicy: OnFailure
initContainers: initContainers:
- name: wait-for-mysql - name: wait-for-mysql
image: mysql:8.0 image: mysql:8.0
command: command:
- sh - sh
- -c - -c
- | - |
until mysqladmin ping -h guac-mysql --silent; do until mysqladmin ping -h guac-mysql --silent; do
echo "Waiting for MySQL..." echo "Waiting for MySQL..."
sleep 5 sleep 5
done done
containers: containers:
- name: initdb - name: initdb
image: guacamole/guacamole:latest image: guacamole/guacamole:latest
command: command:
- sh - sh
- -c - -c
- | - |
/opt/guacamole/bin/initdb.sh --mysql > /tmp/initdb.sql # Generate schema SQL
mysql -h guac-mysql -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < /tmp/initdb.sql || true /opt/guacamole/bin/initdb.sh --mysql > /tmp/initdb.sql
env: # Apply schema (ignore errors if tables already exist)
- name: MYSQL_ROOT_PASSWORD mysql -h guac-mysql -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < /tmp/initdb.sql || true
valueFrom: env:
secretKeyRef: - name: MYSQL_ROOT_PASSWORD
name: guacamole-credentials valueFrom:
key: DB-Root-Password secretKeyRef:
- name: MYSQL_DATABASE name: guacamole-credentials
valueFrom: key: DB-Root-Password
secretKeyRef: - name: MYSQL_DATABASE
name: guacamole-credentials valueFrom:
key: DB-Name secretKeyRef:
--- name: guacamole-credentials
# guacd (Guacamole daemon) key: DB-Name
apiVersion: apps/v1 ---
kind: Deployment # guacd (Guacamole daemon)
metadata: apiVersion: apps/v1
name: guacd kind: Deployment
namespace: guacamole metadata:
labels: name: guacd
app: guacd namespace: guacamole
spec: labels:
replicas: 1 app: guacd
selector: spec:
matchLabels: replicas: 1
app: guacd selector:
template: matchLabels:
metadata: app: guacd
labels: template:
app: guacd metadata:
spec: labels:
containers: app: guacd
serviceAccountName: guacd-exec spec:
- name: guacd containers:
image: guacamole/guacd:latest - name: guacd
ports: image: guacamole/guacd:latest
- containerPort: 4822 ports:
name: guacd - containerPort: 4822
resources: name: guacd
requests: resources:
memory: 128Mi requests:
cpu: 100m memory: 128Mi
limits: cpu: 100m
memory: 512Mi limits:
cpu: 500m memory: 512Mi
livenessProbe: cpu: 500m
tcpSocket: livenessProbe:
port: 4822 tcpSocket:
initialDelaySeconds: 15 port: 4822
periodSeconds: 10 initialDelaySeconds: 15
--- periodSeconds: 10
apiVersion: v1 ---
kind: Service apiVersion: v1
metadata: kind: Service
name: guacd metadata:
namespace: guacamole name: guacd
spec: namespace: guacamole
selector: spec:
app: guacd selector:
ports: app: guacd
- port: 4822 ports:
targetPort: 4822 - port: 4822
name: guacd targetPort: 4822
--- name: guacd
# Guacamole Properties ConfigMap ---
apiVersion: v1 # Guacamole Web Application
kind: ConfigMap apiVersion: apps/v1
metadata: kind: Deployment
name: guacamole-properties metadata:
namespace: guacamole name: guacamole
labels: namespace: guacamole
app: guacamole labels:
data: app: guacamole
guacamole.properties: | spec:
# Blue Jay Remote Access — Guacamole Configuration replicas: 1
# MySQL/guacd settings provided via env vars — do NOT duplicate here selector:
matchLabels:
# 1Password Vault Integration app: guacamole
1password-connect-url: http://onepassword-connect.onepassword-system.svc.cluster.local:8080 template:
1password-connect-token: placeholder-configure-via-secret metadata:
1password-vault-id: qaphopopkryhbg353ukzhhuqoq labels:
app: guacamole
# Extension Priority spec:
extension-priority: mysql, ban, bluejay, 1password-vault, * containers:
- name: guacamole
# Ban (brute force) image: guacamole/guacamole:latest
ban-max-invalid-attempts: 5 ports:
ban-address-duration: 300000 - containerPort: 8080
ban-max-addresses: 1000 name: http
env:
# TOTP - name: GUACD_HOSTNAME
totp-issuer: Blue Jay Remote Access value: guacd
totp-digits: 6 - name: GUACD_PORT
totp-period: 30 value: "4822"
totp-mode: sha256 - name: MYSQL_HOSTNAME
value: guac-mysql
# Session Recording - name: MYSQL_PORT
recording-search-path: /var/lib/guacamole/recordings value: "3306"
- name: MYSQL_DATABASE
# Logging valueFrom:
log-level: info secretKeyRef:
name: guacamole-credentials
# API Token Expiry key: DB-Name
api-session-timeout: 60 - name: MYSQL_USER
--- valueFrom:
# Guacamole Web Application — Blue Jay branded secretKeyRef:
apiVersion: apps/v1 name: guacamole-credentials
kind: Deployment key: DB-User
metadata: - name: MYSQL_PASSWORD
name: guacamole valueFrom:
namespace: guacamole secretKeyRef:
labels: name: guacamole-credentials
app: guacamole key: DB-Password
spec: resources:
replicas: 1 requests:
selector: memory: 256Mi
matchLabels: cpu: 100m
app: guacamole limits:
template: memory: 1Gi
metadata: cpu: 500m
labels: livenessProbe:
app: guacamole httpGet:
spec: path: /guacamole/
containers: port: 8080
- name: guacamole initialDelaySeconds: 120
image: localhost/fc-guacamole:bluejay periodSeconds: 10
imagePullPolicy: Never readinessProbe:
ports: httpGet:
- containerPort: 8080 path: /guacamole/
name: http port: 8080
env: initialDelaySeconds: 60
- name: GUACD_HOSTNAME periodSeconds: 5
value: guacd ---
- name: GUACD_PORT apiVersion: v1
value: "4822" kind: Service
- name: MYSQL_HOSTNAME metadata:
value: guac-mysql name: guacamole
- name: MYSQL_PORT namespace: guacamole
value: "3306" spec:
- name: MYSQL_DATABASE selector:
valueFrom: app: guacamole
secretKeyRef: ports:
name: guacamole-credentials - port: 8080
key: DB-Name targetPort: 8080
- name: MYSQL_USER name: http
valueFrom: ---
secretKeyRef: # Traefik addPrefix middleware
name: guacamole-credentials # External URL guac.iamworkin.lan/ gets prefix /guacamole added
key: DB-User apiVersion: traefik.io/v1alpha1
- name: MYSQL_PASSWORD kind: Middleware
valueFrom: metadata:
secretKeyRef: name: guac-add-prefix
name: guacamole-credentials namespace: guacamole
key: DB-Password spec:
volumeMounts: addPrefix:
- name: guac-properties prefix: /guacamole
mountPath: /etc/guacamole/guacamole.properties ---
subPath: guacamole.properties # TLS Certificate via cert-manager
resources: apiVersion: cert-manager.io/v1
requests: kind: Certificate
memory: 256Mi metadata:
cpu: 100m name: guacamole-tls
limits: namespace: guacamole
memory: 1Gi spec:
cpu: 500m secretName: guacamole-tls
livenessProbe: issuerRef:
httpGet: name: step-ca-acme
path: /guacamole/ kind: ClusterIssuer
port: 8080 dnsNames:
initialDelaySeconds: 120 - guac.iamworkin.lan
periodSeconds: 10 ---
readinessProbe: # Traefik IngressRoute
httpGet: apiVersion: traefik.io/v1alpha1
path: /guacamole/ kind: IngressRoute
port: 8080 metadata:
initialDelaySeconds: 60 name: guacamole
periodSeconds: 5 namespace: guacamole
volumes: spec:
- name: guac-properties entryPoints:
configMap: - websecure
name: guacamole-properties routes:
--- - match: Host(`guac.iamworkin.lan`)
apiVersion: v1 kind: Rule
kind: Service middlewares:
metadata: - name: guac-add-prefix
name: guacamole services:
namespace: guacamole - name: guacamole
spec: port: 8080
selector: tls:
app: guacamole secretName: guacamole-tls
ports: ---
- port: 8080 # 1Password secret sync — creates guacamole-credentials K8s Secret
targetPort: 8080 # Fields: username, password, DB-User, DB-Password, DB-Root-Password, DB-Name, URL
name: http apiVersion: onepassword.com/v1
--- kind: OnePasswordItem
# Traefik addPrefix middleware metadata:
apiVersion: traefik.io/v1alpha1 name: guacamole-credentials
kind: Middleware namespace: guacamole
metadata: spec:
name: guac-add-prefix itemPath: vaults/IAmWorkin/items/Guacamole
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
---
# 1Password secret sync
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: guacamole-credentials
namespace: guacamole
spec:
itemPath: vaults/IAmWorkin/items/Guacamole
---
# RBAC for guacd K8s exec protocol
apiVersion: v1
kind: ServiceAccount
metadata:
name: guacd-exec
namespace: guacamole
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: guacd-pod-exec
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: guacd-pod-exec
subjects:
- kind: ServiceAccount
name: guacd-exec
namespace: guacamole
roleRef:
kind: ClusterRole
name: guacd-pod-exec
apiGroup: rbac.authorization.k8s.io

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1,259 +1,259 @@
# docker-mailserver - Postfix + Dovecot + rspamd # docker-mailserver - Postfix + Dovecot + rspamd
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
# Credentials: 1Password → OnePasswordItem CRD → K8s Secret # Credentials: 1Password → OnePasswordItem CRD → K8s Secret
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: mail name: mail
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# 1Password → K8s Secret sync for mail credentials # 1Password → K8s Secret sync for mail credentials
apiVersion: onepassword.com/v1 apiVersion: onepassword.com/v1
kind: OnePasswordItem kind: OnePasswordItem
metadata: metadata:
name: mail-credentials name: mail-credentials
namespace: mail namespace: mail
spec: spec:
itemPath: "vaults/IAmWorkin/items/Mail Postmaster" itemPath: "vaults/IAmWorkin/items/Mail Postmaster"
--- ---
# Mail data PVC # Mail data PVC
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: mail-data name: mail-data
namespace: mail namespace: mail
spec: spec:
accessModes: [ReadWriteOnce] accessModes: [ReadWriteOnce]
resources: resources:
requests: requests:
storage: 5Gi storage: 5Gi
--- ---
# Mail state PVC # Mail state PVC
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: mail-state name: mail-state
namespace: mail namespace: mail
spec: spec:
accessModes: [ReadWriteOnce] accessModes: [ReadWriteOnce]
resources: resources:
requests: requests:
storage: 1Gi storage: 1Gi
--- ---
# docker-mailserver Deployment # docker-mailserver Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: mailserver name: mailserver
namespace: mail namespace: mail
labels: labels:
app: mailserver app: mailserver
spec: spec:
replicas: 1 replicas: 1
strategy: strategy:
type: Recreate type: Recreate
selector: selector:
matchLabels: matchLabels:
app: mailserver app: mailserver
template: template:
metadata: metadata:
labels: labels:
app: mailserver app: mailserver
spec: spec:
hostname: mail hostname: mail
initContainers: initContainers:
- name: inject-accounts - name: inject-accounts
image: busybox:1.36 image: busybox:1.36
command: command:
- sh - sh
- -c - -c
- | - |
ADMIN_EMAIL=$(cat /credentials/Admin-Email) ADMIN_EMAIL=$(cat /credentials/Admin-Email)
ADMIN_HASH=$(cat /credentials/Admin-Hash) ADMIN_HASH=$(cat /credentials/Admin-Hash)
NOREPLY_EMAIL=$(cat /credentials/Noreply-Email) NOREPLY_EMAIL=$(cat /credentials/Noreply-Email)
NOREPLY_HASH=$(cat /credentials/Noreply-Hash) NOREPLY_HASH=$(cat /credentials/Noreply-Hash)
echo "${ADMIN_EMAIL}|${ADMIN_HASH}" > /accounts/postfix-accounts.cf echo "${ADMIN_EMAIL}|${ADMIN_HASH}" > /accounts/postfix-accounts.cf
echo "${NOREPLY_EMAIL}|${NOREPLY_HASH}" >> /accounts/postfix-accounts.cf echo "${NOREPLY_EMAIL}|${NOREPLY_HASH}" >> /accounts/postfix-accounts.cf
volumeMounts: volumeMounts:
- name: mail-credentials - name: mail-credentials
mountPath: /credentials mountPath: /credentials
readOnly: true readOnly: true
- name: mail-accounts-generated - name: mail-accounts-generated
mountPath: /accounts mountPath: /accounts
containers: containers:
- name: mailserver - name: mailserver
image: docker.io/mailserver/docker-mailserver:latest image: docker.io/mailserver/docker-mailserver:latest
ports: ports:
- containerPort: 25 - containerPort: 25
name: smtp name: smtp
- containerPort: 465 - containerPort: 465
name: smtps name: smtps
- containerPort: 587 - containerPort: 587
name: submission name: submission
- containerPort: 143 - containerPort: 143
name: imap name: imap
- containerPort: 993 - containerPort: 993
name: imaps name: imaps
env: env:
- name: ENABLE_SPAMASSASSIN - name: ENABLE_SPAMASSASSIN
value: "1" value: "1"
- name: ENABLE_CLAMAV - name: ENABLE_CLAMAV
value: "0" value: "0"
- name: ENABLE_RSPAMD - name: ENABLE_RSPAMD
value: "1" value: "1"
- name: TZ - name: TZ
value: America/Chicago value: America/Chicago
- name: POSTMASTER_ADDRESS - name: POSTMASTER_ADDRESS
value: postmaster@iamwork.in value: postmaster@iamwork.in
- name: OVERRIDE_HOSTNAME - name: OVERRIDE_HOSTNAME
value: mail.iamwork.in value: mail.iamwork.in
- name: ENABLE_FAIL2BAN - name: ENABLE_FAIL2BAN
value: "0" value: "0"
- name: ENABLE_POSTGREY - name: ENABLE_POSTGREY
value: "0" value: "0"
- name: ONE_DIR - name: ONE_DIR
value: "1" value: "1"
- name: PERMIT_DOCKER - name: PERMIT_DOCKER
value: network value: network
- name: SSL_TYPE - name: SSL_TYPE
value: manual value: manual
- name: SSL_CERT_PATH - name: SSL_CERT_PATH
value: /etc/ssl/mail/tls.crt value: /etc/ssl/mail/tls.crt
- name: SSL_KEY_PATH - name: SSL_KEY_PATH
value: /etc/ssl/mail/tls.key value: /etc/ssl/mail/tls.key
- name: ACCOUNT_PROVISIONER - name: ACCOUNT_PROVISIONER
value: FILE value: FILE
volumeMounts: volumeMounts:
- name: mail-data - name: mail-data
mountPath: /var/mail mountPath: /var/mail
- name: mail-state - name: mail-state
mountPath: /var/mail-state mountPath: /var/mail-state
- name: mail-tls - name: mail-tls
mountPath: /etc/ssl/mail mountPath: /etc/ssl/mail
readOnly: true readOnly: true
- name: mail-accounts-generated - name: mail-accounts-generated
mountPath: /tmp/docker-mailserver/postfix-accounts.cf mountPath: /tmp/docker-mailserver/postfix-accounts.cf
subPath: postfix-accounts.cf subPath: postfix-accounts.cf
readOnly: true readOnly: true
resources: resources:
requests: requests:
memory: 512Mi memory: 512Mi
cpu: 200m cpu: 200m
limits: limits:
memory: 2Gi memory: 2Gi
cpu: "1" cpu: "1"
securityContext: securityContext:
capabilities: capabilities:
add: add:
- NET_ADMIN - NET_ADMIN
- SYS_PTRACE - SYS_PTRACE
volumes: volumes:
- name: mail-data - name: mail-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: mail-data claimName: mail-data
- name: mail-state - name: mail-state
persistentVolumeClaim: persistentVolumeClaim:
claimName: mail-state claimName: mail-state
- name: mail-tls - name: mail-tls
secret: secret:
secretName: mail-tls secretName: mail-tls
- name: mail-credentials - name: mail-credentials
secret: secret:
secretName: mail-credentials secretName: mail-credentials
- name: mail-accounts-generated - name: mail-accounts-generated
emptyDir: {} emptyDir: {}
--- ---
# SMTP LoadBalancer Service (external) # SMTP LoadBalancer Service (external)
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: mail-smtp name: mail-smtp
namespace: mail namespace: mail
annotations: annotations:
metallb.universe.tf/loadBalancerIPs: 10.0.56.202 metallb.universe.tf/loadBalancerIPs: 10.0.56.202
spec: spec:
type: LoadBalancer type: LoadBalancer
selector: selector:
app: mailserver app: mailserver
ports: ports:
- port: 25 - port: 25
targetPort: 25 targetPort: 25
name: smtp name: smtp
protocol: TCP protocol: TCP
- port: 465 - port: 465
targetPort: 465 targetPort: 465
name: smtps name: smtps
protocol: TCP protocol: TCP
- port: 587 - port: 587
targetPort: 587 targetPort: 587
name: submission name: submission
protocol: TCP protocol: TCP
--- ---
# IMAP ClusterIP Service (internal) # IMAP ClusterIP Service (internal)
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: mail-imap name: mail-imap
namespace: mail namespace: mail
spec: spec:
selector: selector:
app: mailserver app: mailserver
ports: ports:
- port: 143 - port: 143
targetPort: 143 targetPort: 143
name: imap name: imap
- port: 993 - port: 993
targetPort: 993 targetPort: 993
name: imaps name: imaps
--- ---
# TLS Certificate via cert-manager # TLS Certificate via cert-manager
apiVersion: cert-manager.io/v1 apiVersion: cert-manager.io/v1
kind: Certificate kind: Certificate
metadata: metadata:
name: mail-tls name: mail-tls
namespace: mail namespace: mail
spec: spec:
secretName: mail-tls secretName: mail-tls
issuerRef: issuerRef:
name: step-ca-acme name: step-ca-acme
kind: ClusterIssuer kind: ClusterIssuer
dnsNames: dnsNames:
- mail.iamworkin.lan - mail.iamworkin.lan
--- ---
# Traefik IngressRoute - Webmail placeholder # Traefik IngressRoute - Webmail placeholder
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: mail-webmail name: mail-webmail
namespace: mail namespace: mail
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`mail.iamworkin.lan`) - match: Host(`mail.iamworkin.lan`)
kind: Rule kind: Rule
services: services:
- name: mail-imap - name: mail-imap
port: 993 port: 993
tls: tls:
secretName: mail-tls secretName: mail-tls
--- ---
# Public IngressRoute - Webmail (flowercore.io with Cloudflare origin cert) # Public IngressRoute - Webmail (flowercore.io with Cloudflare origin cert)
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: mail-webmail-public name: mail-webmail-public
namespace: mail namespace: mail
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`webmail.flowercore.io`) - match: Host(`webmail.flowercore.io`)
kind: Rule kind: Rule
services: services:
- name: mail-webmail - name: mail-webmail
port: 8080 port: 8080
tls: tls:
secretName: cf-origin-flowercore-io secretName: cf-origin-flowercore-io

View File

@@ -1,495 +1,495 @@
# Matrix Synapse + Element Web # Matrix Synapse + Element Web
# PostgreSQL 16 + Synapse homeserver + Element Web client # PostgreSQL 16 + Synapse homeserver + Element Web client
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
# DB credentials sourced from 1Password via OnePasswordItem CRD (matrix-credentials) # DB credentials sourced from 1Password via OnePasswordItem CRD (matrix-credentials)
# Synapse homeserver.yaml DB password injected at runtime via init container # Synapse homeserver.yaml DB password injected at runtime via init container
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: matrix name: matrix
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# Synapse homeserver.yaml template ConfigMap # Synapse homeserver.yaml template ConfigMap
# DB password placeholder __DB_PASSWORD__ is replaced at pod startup by init container # DB password placeholder __DB_PASSWORD__ is replaced at pod startup by init container
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: synapse-config name: synapse-config
namespace: matrix namespace: matrix
data: data:
homeserver.yaml.template: | homeserver.yaml.template: |
server_name: "iamworkin.lan" server_name: "iamworkin.lan"
pid_file: /data/homeserver.pid pid_file: /data/homeserver.pid
public_baseurl: "https://matrix.iamworkin.lan/" public_baseurl: "https://matrix.iamworkin.lan/"
listeners: listeners:
- port: 8008 - port: 8008
tls: false tls: false
type: http type: http
x_forwarded: true x_forwarded: true
bind_addresses: ["0.0.0.0"] bind_addresses: ["0.0.0.0"]
resources: resources:
- names: [client, federation] - names: [client, federation]
compress: false compress: false
database: database:
name: psycopg2 name: psycopg2
args: args:
user: __DB_USER__ user: __DB_USER__
password: __DB_PASSWORD__ password: __DB_PASSWORD__
database: synapse database: synapse
host: matrix-postgres host: matrix-postgres
port: 5432 port: 5432
cp_min: 5 cp_min: 5
cp_max: 10 cp_max: 10
log_config: "/config/log.config" log_config: "/config/log.config"
media_store_path: /data/media_store media_store_path: /data/media_store
registration_shared_secret: "a208f2e4b260f6b7d6ff4566df49c56c8b73fa20b911ce4e617b791ee7868adc" registration_shared_secret: "a208f2e4b260f6b7d6ff4566df49c56c8b73fa20b911ce4e617b791ee7868adc"
report_stats: false report_stats: false
macaroon_secret_key: "9964f398e8b48a91469ad419d293c06db4562f49df8cc6e129fb3a801fd9052d" macaroon_secret_key: "9964f398e8b48a91469ad419d293c06db4562f49df8cc6e129fb3a801fd9052d"
form_secret: "7b0a9dbaf9ee94450e0b3271c408dfc4d313a55843ce4eec2ac1bb0315ffeb76" form_secret: "7b0a9dbaf9ee94450e0b3271c408dfc4d313a55843ce4eec2ac1bb0315ffeb76"
signing_key_path: "/data/signing.key" signing_key_path: "/data/signing.key"
trusted_key_servers: trusted_key_servers:
- server_name: "matrix.org" - server_name: "matrix.org"
enable_registration: false enable_registration: false
suppress_key_server_warning: true suppress_key_server_warning: true
log.config: | log.config: |
version: 1 version: 1
formatters: formatters:
precise: precise:
format: "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s" format: "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s"
handlers: handlers:
console: console:
class: logging.StreamHandler class: logging.StreamHandler
formatter: precise formatter: precise
loggers: loggers:
synapse.storage.SQL: synapse.storage.SQL:
level: WARNING level: WARNING
root: root:
level: WARNING level: WARNING
handlers: [console] handlers: [console]
disable_existing_loggers: false disable_existing_loggers: false
--- ---
# PostgreSQL 16 StatefulSet # PostgreSQL 16 StatefulSet
# Credentials from 1Password-synced matrix-credentials secret # Credentials from 1Password-synced matrix-credentials secret
apiVersion: apps/v1 apiVersion: apps/v1
kind: StatefulSet kind: StatefulSet
metadata: metadata:
name: matrix-postgres name: matrix-postgres
namespace: matrix namespace: matrix
labels: labels:
app: matrix-postgres app: matrix-postgres
spec: spec:
serviceName: matrix-postgres serviceName: matrix-postgres
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: matrix-postgres app: matrix-postgres
template: template:
metadata: metadata:
labels: labels:
app: matrix-postgres app: matrix-postgres
spec: spec:
containers: containers:
- name: postgres - name: postgres
image: postgres:16-alpine image: postgres:16-alpine
ports: ports:
- containerPort: 5432 - containerPort: 5432
name: postgres name: postgres
env: env:
- name: POSTGRES_USER - name: POSTGRES_USER
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: matrix-credentials name: matrix-credentials
key: DB-User key: DB-User
- name: POSTGRES_PASSWORD - name: POSTGRES_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: matrix-credentials name: matrix-credentials
key: DB-Password key: DB-Password
- name: POSTGRES_DB - name: POSTGRES_DB
value: synapse value: synapse
- name: POSTGRES_INITDB_ARGS - name: POSTGRES_INITDB_ARGS
value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C" value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
volumeMounts: volumeMounts:
- name: matrix-postgres-data - name: matrix-postgres-data
mountPath: /var/lib/postgresql/data mountPath: /var/lib/postgresql/data
subPath: pgdata subPath: pgdata
resources: resources:
requests: requests:
memory: 256Mi memory: 256Mi
cpu: 100m cpu: 100m
limits: limits:
memory: 1Gi memory: 1Gi
cpu: 500m cpu: 500m
livenessProbe: livenessProbe:
exec: exec:
command: ["pg_isready", "-U", "synapse"] command: ["pg_isready", "-U", "synapse"]
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
exec: exec:
command: ["pg_isready", "-U", "synapse"] command: ["pg_isready", "-U", "synapse"]
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 5 periodSeconds: 5
volumeClaimTemplates: volumeClaimTemplates:
- metadata: - metadata:
name: matrix-postgres-data name: matrix-postgres-data
spec: spec:
accessModes: [ReadWriteOnce] accessModes: [ReadWriteOnce]
resources: resources:
requests: requests:
storage: 5Gi storage: 5Gi
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: matrix-postgres name: matrix-postgres
namespace: matrix namespace: matrix
spec: spec:
selector: selector:
app: matrix-postgres app: matrix-postgres
ports: ports:
- port: 5432 - port: 5432
targetPort: 5432 targetPort: 5432
name: postgres name: postgres
clusterIP: None clusterIP: None
--- ---
# Synapse Data PVC # Synapse Data PVC
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: synapse-data name: synapse-data
namespace: matrix namespace: matrix
spec: spec:
accessModes: [ReadWriteOnce] accessModes: [ReadWriteOnce]
resources: resources:
requests: requests:
storage: 2Gi storage: 2Gi
--- ---
# Synapse Deployment # Synapse Deployment
# Init container injects DB credentials from 1Password secret into homeserver.yaml # Init container injects DB credentials from 1Password secret into homeserver.yaml
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: synapse name: synapse
namespace: matrix namespace: matrix
labels: labels:
app: synapse app: synapse
spec: spec:
replicas: 1 replicas: 1
strategy: strategy:
type: Recreate type: Recreate
selector: selector:
matchLabels: matchLabels:
app: synapse app: synapse
template: template:
metadata: metadata:
labels: labels:
app: synapse app: synapse
spec: spec:
initContainers: initContainers:
- name: generate-signing-key - name: generate-signing-key
image: matrixdotorg/synapse:latest image: matrixdotorg/synapse:latest
securityContext: securityContext:
runAsUser: 0 runAsUser: 0
command: ["sh", "-c"] command: ["sh", "-c"]
args: args:
- | - |
if [ \! -f /data/signing.key ]; then if [ \! -f /data/signing.key ]; then
python -m synapse.app.homeserver --generate-keys --config-path /config-template/homeserver.yaml.template 2>/dev/null || true python -m synapse.app.homeserver --generate-keys --config-path /config-template/homeserver.yaml.template 2>/dev/null || true
# If key generation fails with template, create a minimal config for key gen # If key generation fails with template, create a minimal config for key gen
if [ \! -f /data/signing.key ]; then if [ \! -f /data/signing.key ]; then
echo server_name: iamworkin.lan > /tmp/minimal.yaml echo server_name: iamworkin.lan > /tmp/minimal.yaml
echo signing_key_path: /data/signing.key >> /tmp/minimal.yaml echo signing_key_path: /data/signing.key >> /tmp/minimal.yaml
python -c "from signedjson.key import generate_signing_key, write_signing_keys; import sys; key = generate_signing_key(a_auto); write_signing_keys(open(/data/signing.key,w), [key])" 2>/dev/null || true python -c "from signedjson.key import generate_signing_key, write_signing_keys; import sys; key = generate_signing_key(a_auto); write_signing_keys(open(/data/signing.key,w), [key])" 2>/dev/null || true
fi fi
fi fi
chown 991:991 /data/signing.key 2>/dev/null || true chown 991:991 /data/signing.key 2>/dev/null || true
chmod 644 /data/signing.key 2>/dev/null || true chmod 644 /data/signing.key 2>/dev/null || true
mkdir -p /data/media_store mkdir -p /data/media_store
chown -R 991:991 /data 2>/dev/null || true chown -R 991:991 /data 2>/dev/null || true
volumeMounts: volumeMounts:
- name: synapse-data - name: synapse-data
mountPath: /data mountPath: /data
- name: synapse-config-template - name: synapse-config-template
mountPath: /config-template mountPath: /config-template
- name: inject-credentials - name: inject-credentials
image: busybox:latest image: busybox:latest
command: ["sh", "-c"] command: ["sh", "-c"]
args: args:
- | - |
# Copy template and substitute DB credentials from 1Password secret # Copy template and substitute DB credentials from 1Password secret
cp /config-template/log.config /config/log.config cp /config-template/log.config /config/log.config
sed -e "s/__DB_PASSWORD__/${DB_PASSWORD}/g" \ sed -e "s/__DB_PASSWORD__/${DB_PASSWORD}/g" \
-e "s/__DB_USER__/${DB_USER}/g" \ -e "s/__DB_USER__/${DB_USER}/g" \
/config-template/homeserver.yaml.template > /config/homeserver.yaml /config-template/homeserver.yaml.template > /config/homeserver.yaml
echo "Credentials injected into homeserver.yaml" echo "Credentials injected into homeserver.yaml"
env: env:
- name: DB_PASSWORD - name: DB_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: matrix-credentials name: matrix-credentials
key: DB-Password key: DB-Password
- name: DB_USER - name: DB_USER
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: matrix-credentials name: matrix-credentials
key: DB-User key: DB-User
volumeMounts: volumeMounts:
- name: synapse-config-template - name: synapse-config-template
mountPath: /config-template mountPath: /config-template
- name: synapse-config-rendered - name: synapse-config-rendered
mountPath: /config mountPath: /config
containers: containers:
- name: synapse - name: synapse
image: matrixdotorg/synapse:latest image: matrixdotorg/synapse:latest
ports: ports:
- containerPort: 8008 - containerPort: 8008
name: http name: http
env: env:
- name: SYNAPSE_CONFIG_DIR - name: SYNAPSE_CONFIG_DIR
value: /config value: /config
- name: SYNAPSE_CONFIG_PATH - name: SYNAPSE_CONFIG_PATH
value: /config/homeserver.yaml value: /config/homeserver.yaml
volumeMounts: volumeMounts:
- name: synapse-data - name: synapse-data
mountPath: /data mountPath: /data
- name: synapse-config-rendered - name: synapse-config-rendered
mountPath: /config mountPath: /config
resources: resources:
requests: requests:
memory: 512Mi memory: 512Mi
cpu: 200m cpu: 200m
limits: limits:
memory: 2Gi memory: 2Gi
cpu: "1" cpu: "1"
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /health path: /health
port: 8008 port: 8008
initialDelaySeconds: 60 initialDelaySeconds: 60
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /health path: /health
port: 8008 port: 8008
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 5 periodSeconds: 5
volumes: volumes:
- name: synapse-data - name: synapse-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: synapse-data claimName: synapse-data
- name: synapse-config-template - name: synapse-config-template
configMap: configMap:
name: synapse-config name: synapse-config
- name: synapse-config-rendered - name: synapse-config-rendered
emptyDir: {} emptyDir: {}
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: synapse name: synapse
namespace: matrix namespace: matrix
spec: spec:
selector: selector:
app: synapse app: synapse
ports: ports:
- port: 8008 - port: 8008
targetPort: 8008 targetPort: 8008
name: http name: http
--- ---
# Element Web ConfigMap # Element Web ConfigMap
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: element-web-config name: element-web-config
namespace: matrix namespace: matrix
data: data:
config.json: | config.json: |
{ {
"default_server_config": { "default_server_config": {
"m.homeserver": { "m.homeserver": {
"base_url": "https://matrix.iamworkin.lan", "base_url": "https://matrix.iamworkin.lan",
"server_name": "iamworkin.lan" "server_name": "iamworkin.lan"
} }
}, },
"brand": "BlueJay Chat", "brand": "BlueJay Chat",
"disable_guests": true, "disable_guests": true,
"disable_3pid_login": true "disable_3pid_login": true
} }
--- ---
# Element Web Deployment # Element Web Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: element-web name: element-web
namespace: matrix namespace: matrix
labels: labels:
app: element-web app: element-web
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: element-web app: element-web
template: template:
metadata: metadata:
labels: labels:
app: element-web app: element-web
spec: spec:
enableServiceLinks: false enableServiceLinks: false
containers: containers:
- name: element-web - name: element-web
image: vectorim/element-web:latest image: vectorim/element-web:latest
ports: ports:
- containerPort: 80 - containerPort: 80
name: http name: http
volumeMounts: volumeMounts:
- name: element-config - name: element-config
mountPath: /app/config.json mountPath: /app/config.json
subPath: config.json subPath: config.json
resources: resources:
requests: requests:
memory: 32Mi memory: 32Mi
cpu: 10m cpu: 10m
limits: limits:
memory: 128Mi memory: 128Mi
cpu: 100m cpu: 100m
livenessProbe: livenessProbe:
httpGet: httpGet:
path: / path: /
port: 80 port: 80
initialDelaySeconds: 10 initialDelaySeconds: 10
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
httpGet: httpGet:
path: / path: /
port: 80 port: 80
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 5 periodSeconds: 5
volumes: volumes:
- name: element-config - name: element-config
configMap: configMap:
name: element-web-config name: element-web-config
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: element-web name: element-web
namespace: matrix namespace: matrix
spec: spec:
selector: selector:
app: element-web app: element-web
ports: ports:
- port: 80 - port: 80
targetPort: 80 targetPort: 80
name: http name: http
--- ---
# TLS Certificates via cert-manager # TLS Certificates via cert-manager
apiVersion: cert-manager.io/v1 apiVersion: cert-manager.io/v1
kind: Certificate kind: Certificate
metadata: metadata:
name: matrix-tls name: matrix-tls
namespace: matrix namespace: matrix
spec: spec:
secretName: matrix-tls secretName: matrix-tls
issuerRef: issuerRef:
name: step-ca-acme name: step-ca-acme
kind: ClusterIssuer kind: ClusterIssuer
dnsNames: dnsNames:
- matrix.iamworkin.lan - matrix.iamworkin.lan
--- ---
apiVersion: cert-manager.io/v1 apiVersion: cert-manager.io/v1
kind: Certificate kind: Certificate
metadata: metadata:
name: element-tls name: element-tls
namespace: matrix namespace: matrix
spec: spec:
secretName: element-tls secretName: element-tls
issuerRef: issuerRef:
name: step-ca-acme name: step-ca-acme
kind: ClusterIssuer kind: ClusterIssuer
dnsNames: dnsNames:
- element.iamworkin.lan - element.iamworkin.lan
--- ---
# Traefik IngressRoute - Synapse # Traefik IngressRoute - Synapse
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: synapse name: synapse
namespace: matrix namespace: matrix
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`matrix.iamworkin.lan`) - match: Host(`matrix.iamworkin.lan`)
kind: Rule kind: Rule
services: services:
- name: synapse - name: synapse
port: 8008 port: 8008
tls: tls:
secretName: matrix-tls secretName: matrix-tls
--- ---
# Traefik IngressRoute - Element Web # Traefik IngressRoute - Element Web
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: element-web name: element-web
namespace: matrix namespace: matrix
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`element.iamworkin.lan`) - match: Host(`element.iamworkin.lan`)
kind: Rule kind: Rule
services: services:
- name: element-web - name: element-web
port: 80 port: 80
tls: tls:
secretName: element-tls secretName: element-tls
--- ---
# 1Password secret sync — creates matrix-credentials K8s Secret # 1Password secret sync — creates matrix-credentials K8s Secret
# Fields: DB-User, DB-Password, Registration-Secret, username, password, URL # Fields: DB-User, DB-Password, Registration-Secret, username, password, URL
apiVersion: onepassword.com/v1 apiVersion: onepassword.com/v1
kind: OnePasswordItem kind: OnePasswordItem
metadata: metadata:
name: matrix-credentials name: matrix-credentials
namespace: matrix namespace: matrix
spec: spec:
itemPath: vaults/IAmWorkin/items/Matrix Synapse itemPath: vaults/IAmWorkin/items/Matrix Synapse
--- ---
# Public IngressRoute - Element Web (flowercore.io with Cloudflare origin cert) # Public IngressRoute - Element Web (flowercore.io with Cloudflare origin cert)
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: element-public name: element-public
namespace: matrix namespace: matrix
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`element.flowercore.io`) - match: Host(`element.flowercore.io`)
kind: Rule kind: Rule
services: services:
- name: element-web - name: element-web
port: 80 port: 80
tls: tls:
secretName: cf-origin-flowercore-io secretName: cf-origin-flowercore-io
--- ---
# Public IngressRoute - Synapse (flowercore.io with Cloudflare origin cert) # Public IngressRoute - Synapse (flowercore.io with Cloudflare origin cert)
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: synapse-public name: synapse-public
namespace: matrix namespace: matrix
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`matrix.flowercore.io`) - match: Host(`matrix.flowercore.io`)
kind: Rule kind: Rule
services: services:
- name: synapse - name: synapse
port: 8008 port: 8008
tls: tls:
secretName: cf-origin-flowercore-io secretName: cf-origin-flowercore-io

View File

@@ -1,257 +1,257 @@
# NOC Services - Traefik IngressRoutes for noc1 services # NOC Services - Traefik IngressRoutes for noc1 services
# Proxies internal .iamworkin.lan hostnames to noc1 (10.0.56.10) via # Proxies internal .iamworkin.lan hostnames to noc1 (10.0.56.10) via
# headless Service + manual Endpoints (standard K8s external proxy pattern) # headless Service + manual Endpoints (standard K8s external proxy pattern)
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: noc-proxy name: noc-proxy
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# ============================================================ # ============================================================
# BasicAuth - shared across all NOC proxy IngressRoutes # BasicAuth - shared across all NOC proxy IngressRoutes
# ============================================================ # ============================================================
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: noc-proxy-auth name: noc-proxy-auth
namespace: noc-proxy namespace: noc-proxy
type: Opaque type: Opaque
data: data:
users: YWRtaW46JDJiJDEwJEZjdlVFNWNpNkxvNi5rZ1k5L3hJV2V5M2tvM3VVY1U5YXJaSlQ4N29ZREtCSi5lNkoucXJD users: YWRtaW46JDJiJDEwJEZjdlVFNWNpNkxvNi5rZ1k5L3hJV2V5M2tvM3VVY1U5YXJaSlQ4N29ZREtCSi5lNkoucXJD
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: Middleware kind: Middleware
metadata: metadata:
name: noc-proxy-auth name: noc-proxy-auth
namespace: noc-proxy namespace: noc-proxy
spec: spec:
basicAuth: basicAuth:
secret: noc-proxy-auth secret: noc-proxy-auth
--- ---
# ============================================================ # ============================================================
# Grafana - noc1:3000 # Grafana - noc1:3000
# ============================================================ # ============================================================
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: grafana-external name: grafana-external
namespace: noc-proxy namespace: noc-proxy
spec: spec:
ports: ports:
- port: 3000 - port: 3000
targetPort: 3000 targetPort: 3000
name: http name: http
clusterIP: None clusterIP: None
--- ---
apiVersion: v1 apiVersion: v1
kind: Endpoints kind: Endpoints
metadata: metadata:
name: grafana-external name: grafana-external
namespace: noc-proxy namespace: noc-proxy
subsets: subsets:
- addresses: - addresses:
- ip: 10.0.56.10 - ip: 10.0.56.10
ports: ports:
- port: 3000 - port: 3000
name: http name: http
--- ---
apiVersion: cert-manager.io/v1 apiVersion: cert-manager.io/v1
kind: Certificate kind: Certificate
metadata: metadata:
name: grafana-tls name: grafana-tls
namespace: noc-proxy namespace: noc-proxy
spec: spec:
secretName: grafana-tls secretName: grafana-tls
issuerRef: issuerRef:
name: step-ca-acme name: step-ca-acme
kind: ClusterIssuer kind: ClusterIssuer
dnsNames: dnsNames:
- grafana.iamworkin.lan - grafana.iamworkin.lan
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: grafana name: grafana
namespace: noc-proxy namespace: noc-proxy
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- kind: Rule - kind: Rule
match: Host(`grafana.iamworkin.lan`) match: Host(`grafana.iamworkin.lan`)
middlewares: middlewares:
- name: noc-proxy-auth - name: noc-proxy-auth
services: services:
- name: grafana-external - name: grafana-external
port: 3000 port: 3000
tls: tls:
secretName: grafana-tls secretName: grafana-tls
--- ---
# ============================================================ # ============================================================
# Prometheus - noc1:9091 # Prometheus - noc1:9091
# ============================================================ # ============================================================
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: prometheus-external name: prometheus-external
namespace: noc-proxy namespace: noc-proxy
spec: spec:
ports: ports:
- port: 9091 - port: 9091
targetPort: 9091 targetPort: 9091
name: http name: http
clusterIP: None clusterIP: None
--- ---
apiVersion: v1 apiVersion: v1
kind: Endpoints kind: Endpoints
metadata: metadata:
name: prometheus-external name: prometheus-external
namespace: noc-proxy namespace: noc-proxy
subsets: subsets:
- addresses: - addresses:
- ip: 10.0.56.10 - ip: 10.0.56.10
ports: ports:
- port: 9091 - port: 9091
name: http name: http
--- ---
apiVersion: cert-manager.io/v1 apiVersion: cert-manager.io/v1
kind: Certificate kind: Certificate
metadata: metadata:
name: prometheus-tls name: prometheus-tls
namespace: noc-proxy namespace: noc-proxy
spec: spec:
secretName: prometheus-tls secretName: prometheus-tls
issuerRef: issuerRef:
name: step-ca-acme name: step-ca-acme
kind: ClusterIssuer kind: ClusterIssuer
dnsNames: dnsNames:
- prometheus.iamworkin.lan - prometheus.iamworkin.lan
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: prometheus name: prometheus
namespace: noc-proxy namespace: noc-proxy
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- kind: Rule - kind: Rule
match: Host(`prometheus.iamworkin.lan`) match: Host(`prometheus.iamworkin.lan`)
middlewares: middlewares:
- name: noc-proxy-auth - name: noc-proxy-auth
services: services:
- name: prometheus-external - name: prometheus-external
port: 9091 port: 9091
tls: tls:
secretName: prometheus-tls secretName: prometheus-tls
--- ---
# ============================================================ # ============================================================
# Cockpit - noc1:9090 # Cockpit - noc1:9090
# ============================================================ # ============================================================
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: cockpit-external name: cockpit-external
namespace: noc-proxy namespace: noc-proxy
spec: spec:
ports: ports:
- port: 9090 - port: 9090
targetPort: 9090 targetPort: 9090
name: https name: https
clusterIP: None clusterIP: None
--- ---
apiVersion: v1 apiVersion: v1
kind: Endpoints kind: Endpoints
metadata: metadata:
name: cockpit-external name: cockpit-external
namespace: noc-proxy namespace: noc-proxy
subsets: subsets:
- addresses: - addresses:
- ip: 10.0.56.10 - ip: 10.0.56.10
ports: ports:
- port: 9090 - port: 9090
name: https name: https
--- ---
apiVersion: cert-manager.io/v1 apiVersion: cert-manager.io/v1
kind: Certificate kind: Certificate
metadata: metadata:
name: cockpit-tls name: cockpit-tls
namespace: noc-proxy namespace: noc-proxy
spec: spec:
secretName: cockpit-tls secretName: cockpit-tls
issuerRef: issuerRef:
name: step-ca-acme name: step-ca-acme
kind: ClusterIssuer kind: ClusterIssuer
dnsNames: dnsNames:
- cockpit.iamworkin.lan - cockpit.iamworkin.lan
--- ---
# Cockpit uses self-signed HTTPS on 9090, so we need a ServersTransport # Cockpit uses self-signed HTTPS on 9090, so we need a ServersTransport
# to skip backend TLS verification # to skip backend TLS verification
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: ServersTransport kind: ServersTransport
metadata: metadata:
name: cockpit-transport name: cockpit-transport
namespace: noc-proxy namespace: noc-proxy
spec: spec:
insecureSkipVerify: true insecureSkipVerify: true
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: cockpit name: cockpit
namespace: noc-proxy namespace: noc-proxy
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- kind: Rule - kind: Rule
match: Host(`cockpit.iamworkin.lan`) match: Host(`cockpit.iamworkin.lan`)
middlewares: middlewares:
- name: noc-proxy-auth - name: noc-proxy-auth
services: services:
- name: cockpit-external - name: cockpit-external
port: 9090 port: 9090
serversTransport: cockpit-transport serversTransport: cockpit-transport
tls: tls:
secretName: cockpit-tls secretName: cockpit-tls
--- ---
# NetworkPolicy: allow Traefik ingress, allow egress to noc1 # NetworkPolicy: allow Traefik ingress, allow egress to noc1
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
name: noc-proxy-netpol name: noc-proxy-netpol
namespace: noc-proxy namespace: noc-proxy
spec: spec:
podSelector: {} podSelector: {}
policyTypes: policyTypes:
- Ingress - Ingress
- Egress - Egress
ingress: ingress:
- from: - from:
- namespaceSelector: - namespaceSelector:
matchLabels: matchLabels:
kubernetes.io/metadata.name: traefik-system kubernetes.io/metadata.name: traefik-system
egress: egress:
- to: - to:
- ipBlock: - ipBlock:
cidr: 10.0.56.10/32 cidr: 10.0.56.10/32
ports: ports:
- port: 3000 - port: 3000
protocol: TCP protocol: TCP
- port: 9090 - port: 9090
protocol: TCP protocol: TCP
- port: 9091 - port: 9091
protocol: TCP protocol: TCP
- to: - to:
- namespaceSelector: - namespaceSelector:
matchLabels: matchLabels:
kubernetes.io/metadata.name: kube-system kubernetes.io/metadata.name: kube-system
ports: ports:
- port: 53 - port: 53
protocol: UDP protocol: UDP
- port: 53 - port: 53
protocol: TCP protocol: TCP

View File

@@ -1,145 +1,145 @@
# TeamSpeak 3 Server # TeamSpeak 3 Server
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: teamspeak name: teamspeak
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# 1Password secret sync - TeamSpeak credentials # 1Password secret sync - TeamSpeak credentials
apiVersion: onepassword.com/v1 apiVersion: onepassword.com/v1
kind: OnePasswordItem kind: OnePasswordItem
metadata: metadata:
name: teamspeak-credentials name: teamspeak-credentials
namespace: teamspeak namespace: teamspeak
spec: spec:
itemPath: "vaults/IAmWorkin/items/TeamSpeak 3" itemPath: "vaults/IAmWorkin/items/TeamSpeak 3"
--- ---
# TeamSpeak data PVC # TeamSpeak data PVC
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: teamspeak-data name: teamspeak-data
namespace: teamspeak namespace: teamspeak
spec: spec:
accessModes: [ReadWriteOnce] accessModes: [ReadWriteOnce]
resources: resources:
requests: requests:
storage: 1Gi storage: 1Gi
--- ---
# TeamSpeak license key # TeamSpeak license key
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: teamspeak-license name: teamspeak-license
namespace: teamspeak namespace: teamspeak
type: Opaque type: Opaque
data: data:
licensekey.dat: Y29tcGFueSBuYW1lIDogQW5kcmV3IFN0b2x0egphZGRyZXNzICAgICAgOiA2NjI1IDE2Mm5kIHQKemlwY29kZSAgICAgIDogNTUwNjgKY2l0eSAgICAgICAgIDogTGFrZXZpbGxlCmNvdW50cnkgICAgICA6IFVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYQpwaG9uZSAgICAgICAgOiA5NTI5OTk2NDExCmZheCAgICAgICAgICA6IApzYWxlcyBjb250YWN0OiBBbmRyZXcgU3RvbHR6IChhc3RvbHR6QGlhbXdvcmsuaW4pCnRlY2ggY29udGFjdCA6IEFuZHJldyBTdG9sdHogKGFzdG9sdHpAaWFtd29yay5pbikKCnRzIHZlcnNpb24gICA6IDMKdHlwZSAgICAgICAgIDogQWN0aXZhdGlvbiBMaWNlbnNlCnN0YXJ0IGRhdGUgICA6IFN1biBNYXIgIDggMDA6MDA6MDAgMjAyNgplbmQgZGF0ZSAgICAgOiBNb24gTWFyICA4IDAwOjAwOjAwIDIwMjcKbWF4LiB2aXJ0dWFsIHNlcnZlcnM6IDEKbWF4LiBzbG90cyAgIDogMzIKZGVzY3JpcHRpb24gIDogVGVhbVNwZWFrIDMgQUwKCgo9PWtleTI9PQpDb0VDQ3NnQkFRQ3ZiSEZUUURZL3RlclBlaWxycC9FQ1U5eENINVUzeEM5MmxZVE5hWS8wS1FBSkZ1ZUFhemJzZ0FBQUFDVlVaV0Z0VTNCbFlXc2dVM2x6ZEdWdGN5QkhiV0pJQUFCTnozMW0vRVdKU2w4QjFtcjJ2anZvdjRIL2s2UE9KVGJLb2NzVnoxRTNCUUFZd1c3S1B0R0k4QUFBQUNSVVpXRnRVM0JsWVdzZ2MzbHpkR1Z0Y3lCSGJXSklBQUFXSlVXQkErRjQzTERsMzFxS0NpOWw4WWdpQmhyOXlIWW1rbTFvWVVTMlhnSVl5cFVBR3F2SWdBWUFBQUFBUVc1a2NtVjNJRk4wYjJ4MGVnQVNJTmp2OStDVDQwTTUvQWdlY0lLcW1Gb2hyZnAzd3dSbGVSL25Ia3kvdHRKcEdDQWdBU29PVkdWaGJWTndaV0ZySURNZ1FVd1N0UUVCQUs5c2NWTkFOaisxNnM5NktXdW44UUpUM0VJZmxUZkVMM2FWaE0xcGovUXBBQWtXNTRCck51eUFBQUFBSlZSbFlXMVRjR1ZoYXlCVGVYTjBaVzF6SUVkdFlrZ0FBRTNQZldiOFJZbEtYd0hXYXZhK08raS9nZitUbzg0bE5zcWh5eFhQVVRjRkFCakJic28rMFlqd0FBQUFKRlJsWVcxVGNHVmhheUJ6ZVhOMFpXMXpJRWR0WWtnQUFGTTFQT2YxY0VzclhjckFtMU9wS1RnN2cyaHlVNlROY093TFVJaXJpbnc4QlJqQmJzbyswWWp3R2tBRFpWRFRLVndEQTdqYWVFK0pqRFQ1WUFJc1hScWxpdlpTeTR1aUJSYlB0RUZ3d0VnVXR2RHg4TEJaQ29zOEhPTDI0bllBalZ0UFRCdHJnRWF0NHVBSgo9PWtleTI9PQoKPT1rZXk9PQpWRk16VEdsalpXNXpaV1l3WkFJd1JCL1VCTWQ0MnRnQmg4TkY0dnN4K2lsTmpFZHQzazdxbXZPamRYZUVtUWFjQ0J5S0tnelZ4T2MwMDlYanIrU09BakFjR2oxV1ZwTGpiNGxGYk1DaEpsU1FXZW5zYTJhNmJXK2JoN2lCdzF1Zk5WaXFnTk41YThpN1VFS29sNmhsSmExVFViQkVpTng2T1NjbHBBOENhNDhjVk5zS050N0wvandDaVhuZFBkV3BuUW5CdUlYeXFLUXkxM2ZsNDJWUWo0Rk90V2kzTHRoYkNpODlWWEQwYXRpNTNjQlRTWHN4QXdMUndzZFBqeWFobmFsNStXaiswUFdYOE4ySlEzMFZmalFQVnAyMFk2dmc0K29lcm1vV291QUc5RDFjQzRrQVJoQnlVRmU5VnUvM2VBTVRiNUlHbnllY1k0QUF0RjJmdTR1aG5NYUFYQ3Q3UWNHN1JSVEFLQnNjSVhyVlVLM1NnS3ZLR1p6T2RGeU0= licensekey.dat: Y29tcGFueSBuYW1lIDogQW5kcmV3IFN0b2x0egphZGRyZXNzICAgICAgOiA2NjI1IDE2Mm5kIHQKemlwY29kZSAgICAgIDogNTUwNjgKY2l0eSAgICAgICAgIDogTGFrZXZpbGxlCmNvdW50cnkgICAgICA6IFVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYQpwaG9uZSAgICAgICAgOiA5NTI5OTk2NDExCmZheCAgICAgICAgICA6IApzYWxlcyBjb250YWN0OiBBbmRyZXcgU3RvbHR6IChhc3RvbHR6QGlhbXdvcmsuaW4pCnRlY2ggY29udGFjdCA6IEFuZHJldyBTdG9sdHogKGFzdG9sdHpAaWFtd29yay5pbikKCnRzIHZlcnNpb24gICA6IDMKdHlwZSAgICAgICAgIDogQWN0aXZhdGlvbiBMaWNlbnNlCnN0YXJ0IGRhdGUgICA6IFN1biBNYXIgIDggMDA6MDA6MDAgMjAyNgplbmQgZGF0ZSAgICAgOiBNb24gTWFyICA4IDAwOjAwOjAwIDIwMjcKbWF4LiB2aXJ0dWFsIHNlcnZlcnM6IDEKbWF4LiBzbG90cyAgIDogMzIKZGVzY3JpcHRpb24gIDogVGVhbVNwZWFrIDMgQUwKCgo9PWtleTI9PQpDb0VDQ3NnQkFRQ3ZiSEZUUURZL3RlclBlaWxycC9FQ1U5eENINVUzeEM5MmxZVE5hWS8wS1FBSkZ1ZUFhemJzZ0FBQUFDVlVaV0Z0VTNCbFlXc2dVM2x6ZEdWdGN5QkhiV0pJQUFCTnozMW0vRVdKU2w4QjFtcjJ2anZvdjRIL2s2UE9KVGJLb2NzVnoxRTNCUUFZd1c3S1B0R0k4QUFBQUNSVVpXRnRVM0JsWVdzZ2MzbHpkR1Z0Y3lCSGJXSklBQUFXSlVXQkErRjQzTERsMzFxS0NpOWw4WWdpQmhyOXlIWW1rbTFvWVVTMlhnSVl5cFVBR3F2SWdBWUFBQUFBUVc1a2NtVjNJRk4wYjJ4MGVnQVNJTmp2OStDVDQwTTUvQWdlY0lLcW1Gb2hyZnAzd3dSbGVSL25Ia3kvdHRKcEdDQWdBU29PVkdWaGJWTndaV0ZySURNZ1FVd1N0UUVCQUs5c2NWTkFOaisxNnM5NktXdW44UUpUM0VJZmxUZkVMM2FWaE0xcGovUXBBQWtXNTRCck51eUFBQUFBSlZSbFlXMVRjR1ZoYXlCVGVYTjBaVzF6SUVkdFlrZ0FBRTNQZldiOFJZbEtYd0hXYXZhK08raS9nZitUbzg0bE5zcWh5eFhQVVRjRkFCakJic28rMFlqd0FBQUFKRlJsWVcxVGNHVmhheUJ6ZVhOMFpXMXpJRWR0WWtnQUFGTTFQT2YxY0VzclhjckFtMU9wS1RnN2cyaHlVNlROY093TFVJaXJpbnc4QlJqQmJzbyswWWp3R2tBRFpWRFRLVndEQTdqYWVFK0pqRFQ1WUFJc1hScWxpdlpTeTR1aUJSYlB0RUZ3d0VnVXR2RHg4TEJaQ29zOEhPTDI0bllBalZ0UFRCdHJnRWF0NHVBSgo9PWtleTI9PQoKPT1rZXk9PQpWRk16VEdsalpXNXpaV1l3WkFJd1JCL1VCTWQ0MnRnQmg4TkY0dnN4K2lsTmpFZHQzazdxbXZPamRYZUVtUWFjQ0J5S0tnelZ4T2MwMDlYanIrU09BakFjR2oxV1ZwTGpiNGxGYk1DaEpsU1FXZW5zYTJhNmJXK2JoN2lCdzF1Zk5WaXFnTk41YThpN1VFS29sNmhsSmExVFViQkVpTng2T1NjbHBBOENhNDhjVk5zS050N0wvandDaVhuZFBkV3BuUW5CdUlYeXFLUXkxM2ZsNDJWUWo0Rk90V2kzTHRoYkNpODlWWEQwYXRpNTNjQlRTWHN4QXdMUndzZFBqeWFobmFsNStXaiswUFdYOE4ySlEzMFZmalFQVnAyMFk2dmc0K29lcm1vV291QUc5RDFjQzRrQVJoQnlVRmU5VnUvM2VBTVRiNUlHbnllY1k0QUF0RjJmdTR1aG5NYUFYQ3Q3UWNHN1JSVEFLQnNjSVhyVlVLM1NnS3ZLR1p6T2RGeU0=
--- ---
# TeamSpeak 3 Deployment # TeamSpeak 3 Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: teamspeak name: teamspeak
namespace: teamspeak namespace: teamspeak
labels: labels:
app: teamspeak app: teamspeak
spec: spec:
replicas: 1 replicas: 1
strategy: strategy:
type: Recreate type: Recreate
selector: selector:
matchLabels: matchLabels:
app: teamspeak app: teamspeak
template: template:
metadata: metadata:
labels: labels:
app: teamspeak app: teamspeak
spec: spec:
initContainers: initContainers:
- name: copy-license - name: copy-license
image: busybox:latest image: busybox:latest
command: ['sh', '-c', 'cp /license/licensekey.dat /data/licensekey.dat'] command: ['sh', '-c', 'cp /license/licensekey.dat /data/licensekey.dat']
volumeMounts: volumeMounts:
- name: teamspeak-data - name: teamspeak-data
mountPath: /data mountPath: /data
- name: license - name: license
mountPath: /license mountPath: /license
readOnly: true readOnly: true
containers: containers:
- name: teamspeak - name: teamspeak
image: teamspeak:latest image: teamspeak:latest
ports: ports:
- containerPort: 9987 - containerPort: 9987
name: voice name: voice
protocol: UDP protocol: UDP
- containerPort: 30033 - containerPort: 30033
name: filetransfer name: filetransfer
protocol: TCP protocol: TCP
- containerPort: 10011 - containerPort: 10011
name: serverquery name: serverquery
protocol: TCP protocol: TCP
env: env:
- name: TS3SERVER_LICENSE - name: TS3SERVER_LICENSE
value: accept value: accept
- name: TS3SERVER_SERVERADMIN_PASSWORD - name: TS3SERVER_SERVERADMIN_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: teamspeak-credentials name: teamspeak-credentials
key: ServerQuery-Password key: ServerQuery-Password
volumeMounts: volumeMounts:
- name: teamspeak-data - name: teamspeak-data
mountPath: /var/ts3server mountPath: /var/ts3server
resources: resources:
requests: requests:
memory: 128Mi memory: 128Mi
cpu: 50m cpu: 50m
limits: limits:
memory: 512Mi memory: 512Mi
cpu: 500m cpu: 500m
readinessProbe: readinessProbe:
tcpSocket: tcpSocket:
port: 10011 port: 10011
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 10 periodSeconds: 10
livenessProbe: livenessProbe:
tcpSocket: tcpSocket:
port: 10011 port: 10011
initialDelaySeconds: 60 initialDelaySeconds: 60
periodSeconds: 15 periodSeconds: 15
volumes: volumes:
- name: teamspeak-data - name: teamspeak-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: teamspeak-data claimName: teamspeak-data
- name: license - name: license
secret: secret:
secretName: teamspeak-license secretName: teamspeak-license
--- ---
# TeamSpeak LoadBalancer Service # TeamSpeak LoadBalancer Service
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: teamspeak name: teamspeak
namespace: teamspeak namespace: teamspeak
annotations: annotations:
metallb.universe.tf/loadBalancerIPs: 10.0.56.205 metallb.universe.tf/loadBalancerIPs: 10.0.56.205
spec: spec:
type: LoadBalancer type: LoadBalancer
selector: selector:
app: teamspeak app: teamspeak
ports: ports:
- port: 9987 - port: 9987
targetPort: 9987 targetPort: 9987
name: voice name: voice
protocol: UDP protocol: UDP
- port: 30033 - port: 30033
targetPort: 30033 targetPort: 30033
name: filetransfer name: filetransfer
protocol: TCP protocol: TCP
- port: 10011 - port: 10011
targetPort: 10011 targetPort: 10011
name: serverquery name: serverquery
protocol: TCP protocol: TCP

View File

@@ -1,127 +1,127 @@
# Twilio Voice Bridge - Traefik ingress to edge1 # Twilio Voice Bridge - Traefik ingress to edge1
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
# Routes voice.bluejay.dev (TwiML) and voice-ws.bluejay.dev (WebSocket) # Routes voice.bluejay.dev (TwiML) and voice-ws.bluejay.dev (WebSocket)
# to edge1 Pi5 at 10.0.57.15 (PROD VLAN) # to edge1 Pi5 at 10.0.57.15 (PROD VLAN)
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: voice name: voice
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# Cloudflare origin cert for *.bluejay.dev # Cloudflare origin cert for *.bluejay.dev
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: cf-origin-bluejay-dev name: cf-origin-bluejay-dev
namespace: voice namespace: voice
type: kubernetes.io/tls type: kubernetes.io/tls
data: data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVvakNDQTRxZ0F3SUJBZ0lVTkxnemZ4UVRzMElyWWRaZUZKUGN5TjRyNmFjd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZc3hDekFKQmdOVkJBWVRBbFZUTVJrd0Z3WURWUVFLRXhCRGJHOTFaRVpzWVhKbExDQkpibU11TVRRdwpNZ1lEVlFRTEV5dERiRzkxWkVac1lYSmxJRTl5YVdkcGJpQlRVMHdnUTJWeWRHbG1hV05oZEdVZ1FYVjBhRzl5CmFYUjVNUll3RkFZRFZRUUhFdzFUWVc0Z1JuSmhibU5wYzJOdk1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGgKTUI0WERUSTJNRE14TURBMU1USXdNRm9YRFRReE1ETXdOakExTVRJd01Gb3dZakVaTUJjR0ExVUVDaE1RUTJ4dgpkV1JHYkdGeVpTd2dTVzVqTGpFZE1Cc0dBMVVFQ3hNVVEyeHZkV1JHYkdGeVpTQlBjbWxuYVc0Z1EwRXhKakFrCkJnTlZCQU1USFVOc2IzVmtSbXhoY21VZ1QzSnBaMmx1SUVObGNuUnBabWxqWVhSbE1JSUJJakFOQmdrcWhraUcKOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXlPODBJQ3dPV0RWTEpQNm9RenI3aXVENmtWMGNWZ01VbUx5VApKVnVYUlhZSEY3M2ZrM2pPbytCQVE1M2pmbERHUFVYc0UvNlV6VDRoUDVYTlVLaUNXaitvYy84eE1BSWsxcWwrClZnbFI1MDBUQ0FtZDliazNZVkxiZjBSejVMMUQ0WGJmOEVzamhOUVV2Z3Y0dTZoQzdnRmdrVGplc1dIZjg0K04KNERETDdmTjFQZHR4RVBiVWZrbGN1MUZSdXdlMk9QNkFEMlJvdkphNWZwODRHcVY2TDAzdjY2RjFtMnBST1VmRwpFdWpRNG4zSms2cUx5NHZTTENzOGJlOGRBRW5QcDgyZ2NRZk9mUVlIS2JTUWhiQnMwK01vK3lkTHpHSzFRdklRCnVPcDZRT1BtM0lac09Eb0VCdG5kMTh2amx6Y1JSdE94cjNzaFovdmNWY0o0YUJNZDVRSURBUUFCbzRJQkpEQ0MKQVNBd0RnWURWUjBQQVFIL0JBUURBZ1dnTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGQlFjRApBVEFNQmdOVkhSTUJBZjhFQWpBQU1CMEdBMVVkRGdRV0JCVHgyYmFCUGlvWjZUd2U3THhnaTViUS82cUFkakFmCkJnTlZIU01FR0RBV2dCUWs2Rk5YWFh3MFFJZXA2NVRidXVFV2VQd3BwREJBQmdnckJnRUZCUWNCQVFRME1ESXcKTUFZSUt3WUJCUVVITUFHR0pHaDBkSEE2THk5dlkzTndMbU5zYjNWa1pteGhjbVV1WTI5dEwyOXlhV2RwYmw5agpZVEFsQmdOVkhSRUVIakFjZ2cwcUxtSnNkV1ZxWVhrdVpHVjJnZ3RpYkhWbGFtRjVMbVJsZGpBNEJnTlZIUjhFCk1UQXZNQzJnSzZBcGhpZG9kSFJ3T2k4dlkzSnNMbU5zYjNWa1pteGhjbVV1WTI5dEwyOXlhV2RwYmw5allTNWoKY213d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFDS0ZVRXhYbFB5KzlPRytJc1VWN1NXS09Udkk0b2JrUkd6bwpOckhudWZ3dGtxa0dzUGErUU5LbUk1UVNaYTVMS1YybWZJdWsrNE12U1FvZklmbUFUb1JEWEdQK041aWxEbWRSCk5rS2ttSUpZL242UzM3MGdZN0JQMjFwNjJKUnZkVUU5ZmV5RU1iMUdNbGNINjN6MUQxMzZZOWlvQ1FnYWNFZVUKOFZSVWFPZkJvby9sVzlYbXA1ZDZzcTBic2tybUhRN1ZSTjUxZCtsL0RvY2lkU2xZcHQxbXljSUN3c1F4U0dpMApYN1pMTXhHdCtDVG9jcFRFbkdrQ2t0NnhrKzVJUElXaHYvVnZuTnlQNUwxM0ZRN1d4QzFsaUIwcVdKMkEwcWpoCkR2cmxPNUpsclNWWEtNc0hwSWRFN3pJZ29JUUc2cnU1N1V4UjJyeko0d0VrSXcrd1ZyMD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVvakNDQTRxZ0F3SUJBZ0lVTkxnemZ4UVRzMElyWWRaZUZKUGN5TjRyNmFjd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZc3hDekFKQmdOVkJBWVRBbFZUTVJrd0Z3WURWUVFLRXhCRGJHOTFaRVpzWVhKbExDQkpibU11TVRRdwpNZ1lEVlFRTEV5dERiRzkxWkVac1lYSmxJRTl5YVdkcGJpQlRVMHdnUTJWeWRHbG1hV05oZEdVZ1FYVjBhRzl5CmFYUjVNUll3RkFZRFZRUUhFdzFUWVc0Z1JuSmhibU5wYzJOdk1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGgKTUI0WERUSTJNRE14TURBMU1USXdNRm9YRFRReE1ETXdOakExTVRJd01Gb3dZakVaTUJjR0ExVUVDaE1RUTJ4dgpkV1JHYkdGeVpTd2dTVzVqTGpFZE1Cc0dBMVVFQ3hNVVEyeHZkV1JHYkdGeVpTQlBjbWxuYVc0Z1EwRXhKakFrCkJnTlZCQU1USFVOc2IzVmtSbXhoY21VZ1QzSnBaMmx1SUVObGNuUnBabWxqWVhSbE1JSUJJakFOQmdrcWhraUcKOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXlPODBJQ3dPV0RWTEpQNm9RenI3aXVENmtWMGNWZ01VbUx5VApKVnVYUlhZSEY3M2ZrM2pPbytCQVE1M2pmbERHUFVYc0UvNlV6VDRoUDVYTlVLaUNXaitvYy84eE1BSWsxcWwrClZnbFI1MDBUQ0FtZDliazNZVkxiZjBSejVMMUQ0WGJmOEVzamhOUVV2Z3Y0dTZoQzdnRmdrVGplc1dIZjg0K04KNERETDdmTjFQZHR4RVBiVWZrbGN1MUZSdXdlMk9QNkFEMlJvdkphNWZwODRHcVY2TDAzdjY2RjFtMnBST1VmRwpFdWpRNG4zSms2cUx5NHZTTENzOGJlOGRBRW5QcDgyZ2NRZk9mUVlIS2JTUWhiQnMwK01vK3lkTHpHSzFRdklRCnVPcDZRT1BtM0lac09Eb0VCdG5kMTh2amx6Y1JSdE94cjNzaFovdmNWY0o0YUJNZDVRSURBUUFCbzRJQkpEQ0MKQVNBd0RnWURWUjBQQVFIL0JBUURBZ1dnTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGQlFjRApBVEFNQmdOVkhSTUJBZjhFQWpBQU1CMEdBMVVkRGdRV0JCVHgyYmFCUGlvWjZUd2U3THhnaTViUS82cUFkakFmCkJnTlZIU01FR0RBV2dCUWs2Rk5YWFh3MFFJZXA2NVRidXVFV2VQd3BwREJBQmdnckJnRUZCUWNCQVFRME1ESXcKTUFZSUt3WUJCUVVITUFHR0pHaDBkSEE2THk5dlkzTndMbU5zYjNWa1pteGhjbVV1WTI5dEwyOXlhV2RwYmw5agpZVEFsQmdOVkhSRUVIakFjZ2cwcUxtSnNkV1ZxWVhrdVpHVjJnZ3RpYkhWbGFtRjVMbVJsZGpBNEJnTlZIUjhFCk1UQXZNQzJnSzZBcGhpZG9kSFJ3T2k4dlkzSnNMbU5zYjNWa1pteGhjbVV1WTI5dEwyOXlhV2RwYmw5allTNWoKY213d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFDS0ZVRXhYbFB5KzlPRytJc1VWN1NXS09Udkk0b2JrUkd6bwpOckhudWZ3dGtxa0dzUGErUU5LbUk1UVNaYTVMS1YybWZJdWsrNE12U1FvZklmbUFUb1JEWEdQK041aWxEbWRSCk5rS2ttSUpZL242UzM3MGdZN0JQMjFwNjJKUnZkVUU5ZmV5RU1iMUdNbGNINjN6MUQxMzZZOWlvQ1FnYWNFZVUKOFZSVWFPZkJvby9sVzlYbXA1ZDZzcTBic2tybUhRN1ZSTjUxZCtsL0RvY2lkU2xZcHQxbXljSUN3c1F4U0dpMApYN1pMTXhHdCtDVG9jcFRFbkdrQ2t0NnhrKzVJUElXaHYvVnZuTnlQNUwxM0ZRN1d4QzFsaUIwcVdKMkEwcWpoCkR2cmxPNUpsclNWWEtNc0hwSWRFN3pJZ29JUUc2cnU1N1V4UjJyeko0d0VrSXcrd1ZyMD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRREk3elFnTEE1WU5Vc2sKL3FoRE92dUs0UHFSWFJ4V0F4U1l2Sk1sVzVkRmRnY1h2ZCtUZU02ajRFQkRuZU4rVU1ZOVJld1QvcFROUGlFLwpsYzFRcUlKYVA2aHovekV3QWlUV3FYNVdDVkhuVFJNSUNaMzF1VGRoVXR0L1JIUGt2VVBoZHQvd1N5T0UxQlMrCkMvaTdxRUx1QVdDUk9ONnhZZC96ajQzZ01NdnQ4M1U5MjNFUTl0UitTVnk3VVZHN0I3WTQvb0FQWkdpOGxybCsKbnpnYXBYb3ZUZS9yb1hXYmFsRTVSOFlTNk5EaWZjbVRxb3ZMaTlJc0t6eHQ3eDBBU2MrbnphQnhCODU5QmdjcAp0SkNGc0d6VDR5ajdKMHZNWXJWQzhoQzQ2bnBBNCtiY2htdzRPZ1FHMmQzWHkrT1hOeEZHMDdHdmV5Rm4rOXhWCnduaG9FeDNsQWdNQkFBRUNnZ0VBRFpWeU9peVFTYkZNbzdNZGovSDRXR0t1UGM2RUlHSno3WUZ1Rnl2eWRZMHQKbkpMRy94Sy9NWC96Q0Q4dnhuWFNlUWoxbFVKMEw4M2Y5SXI5aHRMbGdSRmxvM1hnanVUT05iN2VuaFZpTnBkVQp6b25MNW5VL2c3SlV5VzFJd25GekdkWnQvREl3TkFZY1l0NnZVWXhsL2U0VTU2eG5EYW5XdUlIL2J1VU5uRWZtCjZNcnlIblhseDA0TzZzOElLSCtXNUxDam1ma1Jac0VveE9Damt6T2hucmdCTk4xSWxWSWhhMDhLOXBxRk5qM0wKaWd4MVNYZDhqaHNZUHJxaDNkak0rVUNKSitKMURuMjhaUmoyZUtqWDMvZmFDbGFpOEFzUVBtQTJScjZYZ3kzeApaR090dzRPL2Nxb1hnOUFDU05NanRNK3VtaXM3QzNncWp6VUZ4MWwvNFFLQmdRRFNvSGVDY1dXSUJQV3grR01hCnBSMENHejQ0eDNPRUhnNWYza2RvK3BDK0I3RnYxREV5bktqUUY4VFBYaWJXVGZVL1pBRG5DVmIvU1Z2L1QyNnkKT2NlaW44UGRqdXkraE05ZUxjWXU4QVZiK0VKOHFMUUsrZE5QZm1nYXZJMWR2V3U2Tm8wSTNSSzZWUkpiQXEyWgpLcjdsVEloMjdZMDRMajlaaUIxU01YZ0J0UUtCZ1FEME9EbEVCNHRHa1l2dTVLMkFoaDBmdnlIaUVoUTd5dWUzCmZXdG1VYnRxV0ZlM1R5UG9JTWJWMTM3MkNIdlh6MFNEMXRwYzJxa3hxN3c4d1BYM3RJZGk5NWxMbVMzZm8zWFoKdTNTYVo4enozTlAwR3JXK1Y0c3hHb1VnbWgwL2lzOXJoaWxXZGF5bU5SWEQ5U0MyUU5IdTU5NDY2UjFkczNnbgpiZlJlUkw4SmNRS0JnREptVjNLTk0rQmlYM0Jnb1VaRThEWUswczYvV3pMb0JrU0dhY3dDK1JPZnY2T2t3TWo5Cmw1K0RzSUoyWXhDd3d0aVNVMnoxWFMzbEhmQnZ6MnN5VEVUcnVmQ1FQTEl5RVhUVnV6Q01HcHd4UWFlV3JzNVoKaldqZU5JY0JTMHA5QXdRaC9ZbDdiUG5OVllFVm1QaW5zOW9tZ0JrRkt0K2dvV1FKSUFzRTcxUnBBb0dCQUxNSgphTW43c2RuNUo1bnA0VnhBZGFkcGFvQ2VlbURmUG9KaEd0UTNCT3RRZW5Xek9nS1p6TXJHSVpoaTNjOTNicVlzClk0Y0E4bHFzcU9IdElDVUpIdHVwNHFMdVdCZ0VjSWcvaVpzTWo4OFRTL3MvZlk5ZUJIZnFGa0N4V3RIVGhINHkKSzZucnVMZGNZV2w0RWhRcWJ2enkxUk5oQkp0Rno4Y3dMNTdRVFRDeEFvR0FjZFFkbzk3bzZmS0dQa3kvREpnNApoTGlLbHVkTjF3bmJaTHFVM0UwNzBwVDhJQkx3TFNpTUNpYXRXWWtScHFpdUQyMSsya1p4SE1NdnNoZWJXQmFmCmVqWU9jcHVvQ3VWdWc1K1dlbmc0cmUxSTl2c2czUE5WYkdrNW1xNmZDbndsbGFnMkEvSVBVRFFOaUpCVGM1WUQKc25udzhVTjNBTFBMSTlPVVd1eXJUckk9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRREk3elFnTEE1WU5Vc2sKL3FoRE92dUs0UHFSWFJ4V0F4U1l2Sk1sVzVkRmRnY1h2ZCtUZU02ajRFQkRuZU4rVU1ZOVJld1QvcFROUGlFLwpsYzFRcUlKYVA2aHovekV3QWlUV3FYNVdDVkhuVFJNSUNaMzF1VGRoVXR0L1JIUGt2VVBoZHQvd1N5T0UxQlMrCkMvaTdxRUx1QVdDUk9ONnhZZC96ajQzZ01NdnQ4M1U5MjNFUTl0UitTVnk3VVZHN0I3WTQvb0FQWkdpOGxybCsKbnpnYXBYb3ZUZS9yb1hXYmFsRTVSOFlTNk5EaWZjbVRxb3ZMaTlJc0t6eHQ3eDBBU2MrbnphQnhCODU5QmdjcAp0SkNGc0d6VDR5ajdKMHZNWXJWQzhoQzQ2bnBBNCtiY2htdzRPZ1FHMmQzWHkrT1hOeEZHMDdHdmV5Rm4rOXhWCnduaG9FeDNsQWdNQkFBRUNnZ0VBRFpWeU9peVFTYkZNbzdNZGovSDRXR0t1UGM2RUlHSno3WUZ1Rnl2eWRZMHQKbkpMRy94Sy9NWC96Q0Q4dnhuWFNlUWoxbFVKMEw4M2Y5SXI5aHRMbGdSRmxvM1hnanVUT05iN2VuaFZpTnBkVQp6b25MNW5VL2c3SlV5VzFJd25GekdkWnQvREl3TkFZY1l0NnZVWXhsL2U0VTU2eG5EYW5XdUlIL2J1VU5uRWZtCjZNcnlIblhseDA0TzZzOElLSCtXNUxDam1ma1Jac0VveE9Damt6T2hucmdCTk4xSWxWSWhhMDhLOXBxRk5qM0wKaWd4MVNYZDhqaHNZUHJxaDNkak0rVUNKSitKMURuMjhaUmoyZUtqWDMvZmFDbGFpOEFzUVBtQTJScjZYZ3kzeApaR090dzRPL2Nxb1hnOUFDU05NanRNK3VtaXM3QzNncWp6VUZ4MWwvNFFLQmdRRFNvSGVDY1dXSUJQV3grR01hCnBSMENHejQ0eDNPRUhnNWYza2RvK3BDK0I3RnYxREV5bktqUUY4VFBYaWJXVGZVL1pBRG5DVmIvU1Z2L1QyNnkKT2NlaW44UGRqdXkraE05ZUxjWXU4QVZiK0VKOHFMUUsrZE5QZm1nYXZJMWR2V3U2Tm8wSTNSSzZWUkpiQXEyWgpLcjdsVEloMjdZMDRMajlaaUIxU01YZ0J0UUtCZ1FEME9EbEVCNHRHa1l2dTVLMkFoaDBmdnlIaUVoUTd5dWUzCmZXdG1VYnRxV0ZlM1R5UG9JTWJWMTM3MkNIdlh6MFNEMXRwYzJxa3hxN3c4d1BYM3RJZGk5NWxMbVMzZm8zWFoKdTNTYVo4enozTlAwR3JXK1Y0c3hHb1VnbWgwL2lzOXJoaWxXZGF5bU5SWEQ5U0MyUU5IdTU5NDY2UjFkczNnbgpiZlJlUkw4SmNRS0JnREptVjNLTk0rQmlYM0Jnb1VaRThEWUswczYvV3pMb0JrU0dhY3dDK1JPZnY2T2t3TWo5Cmw1K0RzSUoyWXhDd3d0aVNVMnoxWFMzbEhmQnZ6MnN5VEVUcnVmQ1FQTEl5RVhUVnV6Q01HcHd4UWFlV3JzNVoKaldqZU5JY0JTMHA5QXdRaC9ZbDdiUG5OVllFVm1QaW5zOW9tZ0JrRkt0K2dvV1FKSUFzRTcxUnBBb0dCQUxNSgphTW43c2RuNUo1bnA0VnhBZGFkcGFvQ2VlbURmUG9KaEd0UTNCT3RRZW5Xek9nS1p6TXJHSVpoaTNjOTNicVlzClk0Y0E4bHFzcU9IdElDVUpIdHVwNHFMdVdCZ0VjSWcvaVpzTWo4OFRTL3MvZlk5ZUJIZnFGa0N4V3RIVGhINHkKSzZucnVMZGNZV2w0RWhRcWJ2enkxUk5oQkp0Rno4Y3dMNTdRVFRDeEFvR0FjZFFkbzk3bzZmS0dQa3kvREpnNApoTGlLbHVkTjF3bmJaTHFVM0UwNzBwVDhJQkx3TFNpTUNpYXRXWWtScHFpdUQyMSsya1p4SE1NdnNoZWJXQmFmCmVqWU9jcHVvQ3VWdWc1K1dlbmc0cmUxSTl2c2czUE5WYkdrNW1xNmZDbndsbGFnMkEvSVBVRFFOaUpCVGM1WUQKc25udzhVTjNBTFBMSTlPVVd1eXJUckk9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
--- ---
# Service (no selector) pointing to external edge1 # Service (no selector) pointing to external edge1
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: voice-bridge name: voice-bridge
namespace: voice namespace: voice
spec: spec:
ports: ports:
- name: twiml - name: twiml
port: 8766 port: 8766
targetPort: 8766 targetPort: 8766
protocol: TCP protocol: TCP
- name: websocket - name: websocket
port: 8765 port: 8765
targetPort: 8765 targetPort: 8765
protocol: TCP protocol: TCP
--- ---
# Manual Endpoints for edge1 (outside K8s) # Manual Endpoints for edge1 (outside K8s)
apiVersion: v1 apiVersion: v1
kind: Endpoints kind: Endpoints
metadata: metadata:
name: voice-bridge name: voice-bridge
namespace: voice namespace: voice
subsets: subsets:
- addresses: - addresses:
- ip: 10.0.57.15 - ip: 10.0.57.15
ports: ports:
- name: twiml - name: twiml
port: 8766 port: 8766
protocol: TCP protocol: TCP
- name: websocket - name: websocket
port: 8765 port: 8765
protocol: TCP protocol: TCP
--- ---
# TwiML webhook: voice.bluejay.dev -> edge1:8766 # TwiML webhook: voice.bluejay.dev -> edge1:8766
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: voice-twiml name: voice-twiml
namespace: voice namespace: voice
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- kind: Rule - kind: Rule
match: Host(`voice.bluejay.dev`) match: Host(`voice.bluejay.dev`)
services: services:
- name: voice-bridge - name: voice-bridge
port: 8766 port: 8766
tls: tls:
secretName: cf-origin-bluejay-dev secretName: cf-origin-bluejay-dev
--- ---
# WebSocket media stream: voice-ws.bluejay.dev -> edge1:8765 # WebSocket media stream: voice-ws.bluejay.dev -> edge1:8765
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: voice-ws name: voice-ws
namespace: voice namespace: voice
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- kind: Rule - kind: Rule
match: Host(`voice-ws.bluejay.dev`) match: Host(`voice-ws.bluejay.dev`)
services: services:
- name: voice-bridge - name: voice-bridge
port: 8765 port: 8765
tls: tls:
secretName: cf-origin-bluejay-dev secretName: cf-origin-bluejay-dev
--- ---
# NetworkPolicy: allow Traefik ingress only # NetworkPolicy: allow Traefik ingress only
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
name: voice-netpol name: voice-netpol
namespace: voice namespace: voice
spec: spec:
podSelector: {} podSelector: {}
policyTypes: policyTypes:
- Ingress - Ingress
- Egress - Egress
ingress: ingress:
- from: - from:
- namespaceSelector: - namespaceSelector:
matchLabels: matchLabels:
kubernetes.io/metadata.name: traefik-system kubernetes.io/metadata.name: traefik-system
egress: egress:
- to: - to:
- ipBlock: - ipBlock:
cidr: 10.0.57.15/32 cidr: 10.0.57.15/32
ports: ports:
- port: 8765 - port: 8765
protocol: TCP protocol: TCP
- port: 8766 - port: 8766
protocol: TCP protocol: TCP
- to: - to:
- namespaceSelector: - namespaceSelector:
matchLabels: matchLabels:
kubernetes.io/metadata.name: kube-system kubernetes.io/metadata.name: kube-system
ports: ports:
- port: 53 - port: 53
protocol: UDP protocol: UDP
- port: 53 - port: 53
protocol: TCP protocol: TCP

View File

@@ -1,360 +1,360 @@
# Zabbix 7.2 Monitoring Stack # Zabbix 7.2 Monitoring Stack
# PostgreSQL 16 + Zabbix Server + Zabbix Web (nginx) # PostgreSQL 16 + Zabbix Server + Zabbix Web (nginx)
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
# Credentials sourced from 1Password via OnePasswordItem CRD (zabbix-credentials) # Credentials sourced from 1Password via OnePasswordItem CRD (zabbix-credentials)
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: zabbix name: zabbix
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# PostgreSQL 16 StatefulSet # PostgreSQL 16 StatefulSet
apiVersion: apps/v1 apiVersion: apps/v1
kind: StatefulSet kind: StatefulSet
metadata: metadata:
name: zabbix-postgres name: zabbix-postgres
namespace: zabbix namespace: zabbix
labels: labels:
app: zabbix-postgres app: zabbix-postgres
spec: spec:
serviceName: zabbix-postgres serviceName: zabbix-postgres
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: zabbix-postgres app: zabbix-postgres
template: template:
metadata: metadata:
labels: labels:
app: zabbix-postgres app: zabbix-postgres
spec: spec:
containers: containers:
- name: postgres - name: postgres
image: postgres:16-alpine image: postgres:16-alpine
args: args:
- "-c" - "-c"
- "shared_buffers=256MB" - "shared_buffers=256MB"
- "-c" - "-c"
- "effective_cache_size=512MB" - "effective_cache_size=512MB"
- "-c" - "-c"
- "work_mem=16MB" - "work_mem=16MB"
- "-c" - "-c"
- "maintenance_work_mem=128MB" - "maintenance_work_mem=128MB"
- "-c" - "-c"
- "random_page_cost=1.1" - "random_page_cost=1.1"
- "-c" - "-c"
- "effective_io_concurrency=200" - "effective_io_concurrency=200"
- "-c" - "-c"
- "max_connections=50" - "max_connections=50"
- "-c" - "-c"
- "checkpoint_completion_target=0.9" - "checkpoint_completion_target=0.9"
- "-c" - "-c"
- "wal_buffers=8MB" - "wal_buffers=8MB"
ports: ports:
- containerPort: 5432 - containerPort: 5432
name: postgres name: postgres
env: env:
- name: POSTGRES_USER - name: POSTGRES_USER
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: zabbix-credentials name: zabbix-credentials
key: DB-User key: DB-User
- name: POSTGRES_PASSWORD - name: POSTGRES_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: zabbix-credentials name: zabbix-credentials
key: DB-Password key: DB-Password
- name: POSTGRES_DB - name: POSTGRES_DB
value: zabbix value: zabbix
volumeMounts: volumeMounts:
- name: zabbix-postgres-data - name: zabbix-postgres-data
mountPath: /var/lib/postgresql/data mountPath: /var/lib/postgresql/data
subPath: pgdata subPath: pgdata
resources: resources:
requests: requests:
memory: 512Mi memory: 512Mi
cpu: 200m cpu: 200m
limits: limits:
memory: 1Gi memory: 1Gi
cpu: "1" cpu: "1"
livenessProbe: livenessProbe:
exec: exec:
command: command:
- pg_isready - pg_isready
- -U - -U
- zabbix - zabbix
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
exec: exec:
command: command:
- pg_isready - pg_isready
- -U - -U
- zabbix - zabbix
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 5 periodSeconds: 5
volumeClaimTemplates: volumeClaimTemplates:
- metadata: - metadata:
name: zabbix-postgres-data name: zabbix-postgres-data
spec: spec:
accessModes: [ReadWriteOnce] accessModes: [ReadWriteOnce]
resources: resources:
requests: requests:
storage: 10Gi storage: 10Gi
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: zabbix-postgres name: zabbix-postgres
namespace: zabbix namespace: zabbix
spec: spec:
selector: selector:
app: zabbix-postgres app: zabbix-postgres
ports: ports:
- port: 5432 - port: 5432
targetPort: 5432 targetPort: 5432
name: postgres name: postgres
clusterIP: None clusterIP: None
--- ---
# Zabbix Server # Zabbix Server
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: zabbix-server name: zabbix-server
namespace: zabbix namespace: zabbix
labels: labels:
app: zabbix-server app: zabbix-server
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: zabbix-server app: zabbix-server
template: template:
metadata: metadata:
labels: labels:
app: zabbix-server app: zabbix-server
spec: spec:
containers: containers:
- name: zabbix-server - name: zabbix-server
image: zabbix/zabbix-server-pgsql:7.2-alpine-latest image: zabbix/zabbix-server-pgsql:7.2-alpine-latest
ports: ports:
- containerPort: 10051 - containerPort: 10051
name: trapper name: trapper
env: env:
- name: DB_SERVER_HOST - name: DB_SERVER_HOST
value: zabbix-postgres value: zabbix-postgres
- name: DB_SERVER_PORT - name: DB_SERVER_PORT
value: "5432" value: "5432"
- name: POSTGRES_USER - name: POSTGRES_USER
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: zabbix-credentials name: zabbix-credentials
key: DB-User key: DB-User
- name: POSTGRES_PASSWORD - name: POSTGRES_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: zabbix-credentials name: zabbix-credentials
key: DB-Password key: DB-Password
- name: POSTGRES_DB - name: POSTGRES_DB
value: zabbix value: zabbix
- name: ZBX_CACHESIZE - name: ZBX_CACHESIZE
value: "64M" value: "64M"
- name: ZBX_VALUECACHESIZE - name: ZBX_VALUECACHESIZE
value: "64M" value: "64M"
- name: ZBX_HISTORYCACHESIZE - name: ZBX_HISTORYCACHESIZE
value: "32M" value: "32M"
- name: ZBX_TRENDCACHESIZE - name: ZBX_TRENDCACHESIZE
value: "8M" value: "8M"
- name: ZBX_STARTPOLLERS - name: ZBX_STARTPOLLERS
value: "10" value: "10"
- name: ZBX_STARTPOLLERSUNREACHABLE - name: ZBX_STARTPOLLERSUNREACHABLE
value: "3" value: "3"
resources: resources:
requests: requests:
memory: 256Mi memory: 256Mi
cpu: 100m cpu: 100m
limits: limits:
memory: 1Gi memory: 1Gi
cpu: "1" cpu: "1"
livenessProbe: livenessProbe:
tcpSocket: tcpSocket:
port: 10051 port: 10051
initialDelaySeconds: 60 initialDelaySeconds: 60
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
tcpSocket: tcpSocket:
port: 10051 port: 10051
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 5 periodSeconds: 5
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: zabbix-server name: zabbix-server
namespace: zabbix namespace: zabbix
spec: spec:
selector: selector:
app: zabbix-server app: zabbix-server
ports: ports:
- port: 10051 - port: 10051
targetPort: 10051 targetPort: 10051
name: trapper name: trapper
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: zabbix-trapper name: zabbix-trapper
namespace: zabbix namespace: zabbix
annotations: annotations:
metallb.universe.tf/loadBalancerIPs: 10.0.56.203 metallb.universe.tf/loadBalancerIPs: 10.0.56.203
spec: spec:
type: LoadBalancer type: LoadBalancer
selector: selector:
app: zabbix-server app: zabbix-server
ports: ports:
- port: 10051 - port: 10051
targetPort: 10051 targetPort: 10051
name: trapper name: trapper
protocol: TCP protocol: TCP
--- ---
# Zabbix Web (nginx + PostgreSQL) # Zabbix Web (nginx + PostgreSQL)
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: zabbix-web name: zabbix-web
namespace: zabbix namespace: zabbix
labels: labels:
app: zabbix-web app: zabbix-web
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: zabbix-web app: zabbix-web
template: template:
metadata: metadata:
labels: labels:
app: zabbix-web app: zabbix-web
spec: spec:
containers: containers:
- name: zabbix-web - name: zabbix-web
image: zabbix/zabbix-web-nginx-pgsql:7.2-alpine-latest image: zabbix/zabbix-web-nginx-pgsql:7.2-alpine-latest
ports: ports:
- containerPort: 8080 - containerPort: 8080
name: http name: http
env: env:
- name: ZBX_SERVER_HOST - name: ZBX_SERVER_HOST
value: zabbix-server value: zabbix-server
- name: ZBX_SERVER_NAME - name: ZBX_SERVER_NAME
value: "BlueJay NOC" value: "BlueJay NOC"
- name: PHP_TZ - name: PHP_TZ
value: America/Chicago value: America/Chicago
- name: DB_SERVER_HOST - name: DB_SERVER_HOST
value: zabbix-postgres value: zabbix-postgres
- name: DB_SERVER_PORT - name: DB_SERVER_PORT
value: "5432" value: "5432"
- name: POSTGRES_USER - name: POSTGRES_USER
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: zabbix-credentials name: zabbix-credentials
key: DB-User key: DB-User
- name: POSTGRES_PASSWORD - name: POSTGRES_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: zabbix-credentials name: zabbix-credentials
key: DB-Password key: DB-Password
- name: POSTGRES_DB - name: POSTGRES_DB
value: zabbix value: zabbix
- name: ZBX_ADMIN_PASSWORD - name: ZBX_ADMIN_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: zabbix-credentials name: zabbix-credentials
key: password key: password
- name: ZBX_MEMORYLIMIT - name: ZBX_MEMORYLIMIT
value: "256M" value: "256M"
- name: PHP_FPM_PM_MAX_CHILDREN - name: PHP_FPM_PM_MAX_CHILDREN
value: "10" value: "10"
- name: PHP_FPM_PM_START_SERVERS - name: PHP_FPM_PM_START_SERVERS
value: "3" value: "3"
- name: PHP_FPM_PM_MIN_SPARE_SERVERS - name: PHP_FPM_PM_MIN_SPARE_SERVERS
value: "2" value: "2"
- name: PHP_FPM_PM_MAX_SPARE_SERVERS - name: PHP_FPM_PM_MAX_SPARE_SERVERS
value: "5" value: "5"
- name: PHP_FPM_PM_MAX_REQUESTS - name: PHP_FPM_PM_MAX_REQUESTS
value: "500" value: "500"
resources: resources:
requests: requests:
memory: 256Mi memory: 256Mi
cpu: 100m cpu: 100m
limits: limits:
memory: 768Mi memory: 768Mi
cpu: 500m cpu: 500m
livenessProbe: livenessProbe:
httpGet: httpGet:
path: / path: /
port: 8080 port: 8080
initialDelaySeconds: 60 initialDelaySeconds: 60
timeoutSeconds: 5 timeoutSeconds: 5
periodSeconds: 10 periodSeconds: 10
readinessProbe: readinessProbe:
httpGet: httpGet:
path: / path: /
port: 8080 port: 8080
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 5 periodSeconds: 5
timeoutSeconds: 5 timeoutSeconds: 5
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: zabbix-web name: zabbix-web
namespace: zabbix namespace: zabbix
spec: spec:
selector: selector:
app: zabbix-web app: zabbix-web
ports: ports:
- port: 8080 - port: 8080
targetPort: 8080 targetPort: 8080
name: http name: http
--- ---
# TLS Certificate via cert-manager # TLS Certificate via cert-manager
apiVersion: cert-manager.io/v1 apiVersion: cert-manager.io/v1
kind: Certificate kind: Certificate
metadata: metadata:
name: zabbix-tls name: zabbix-tls
namespace: zabbix namespace: zabbix
spec: spec:
secretName: zabbix-tls secretName: zabbix-tls
issuerRef: issuerRef:
name: step-ca-acme name: step-ca-acme
kind: ClusterIssuer kind: ClusterIssuer
dnsNames: dnsNames:
- zabbix.iamworkin.lan - zabbix.iamworkin.lan
--- ---
# Traefik IngressRoute # Traefik IngressRoute
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: zabbix-web name: zabbix-web
namespace: zabbix namespace: zabbix
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`zabbix.iamworkin.lan`) - match: Host(`zabbix.iamworkin.lan`)
kind: Rule kind: Rule
services: services:
- name: zabbix-web - name: zabbix-web
port: 8080 port: 8080
tls: tls:
secretName: zabbix-tls secretName: zabbix-tls
--- ---
# 1Password secret sync — creates zabbix-credentials K8s Secret # 1Password secret sync — creates zabbix-credentials K8s Secret
# Fields: DB-User, DB-Password, username, password, URL # Fields: DB-User, DB-Password, username, password, URL
apiVersion: onepassword.com/v1 apiVersion: onepassword.com/v1
kind: OnePasswordItem kind: OnePasswordItem
metadata: metadata:
name: zabbix-credentials name: zabbix-credentials
namespace: zabbix namespace: zabbix
spec: spec:
itemPath: vaults/IAmWorkin/items/Zabbix Admin itemPath: vaults/IAmWorkin/items/Zabbix Admin