diff --git a/apps/telephony/telephony.yaml b/apps/telephony/telephony.yaml index 45a3c51..8d222d3 100644 --- a/apps/telephony/telephony.yaml +++ b/apps/telephony/telephony.yaml @@ -1,349 +1,349 @@ -# FlowerCore.Telephony - Blazor Server + REST API + Twilio IVR -# ArgoCD managed - BlueJay Lab -# Credentials: 1Password → OnePasswordItem CRD → K8s Secret (twilio-credentials) -# TTS: Piper on edge1 (10.0.57.15:8500) -# Public: telephony.flowercore.io via Cloudflare origin cert ---- -apiVersion: v1 -kind: Namespace -metadata: - name: telephony - labels: - app.kubernetes.io/part-of: bluejay-infra ---- -# Cloudflare Origin Certificate for *.flowercore.io + *.iamwork.in (15-year RSA) -apiVersion: v1 -kind: Secret -metadata: - name: cf-origin-flowercore-io - namespace: telephony -type: kubernetes.io/tls -data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVvRENDQTRpZ0F3SUJBZ0lVSXN4c1NKV1VRL0tqZ09ldk81YnNuVi9rZVE4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZc3hDekFKQmdOVkJBWVRBbFZUTVJrd0Z3WURWUVFLRXhCRGJHOTFaRVpzWVhKbExDQkpibU11TVRRdwpNZ1lEVlFRTEV5dERiRzkxWkVac1lYSmxJRTl5YVdkcGJpQlRVMHdnUTJWeWRHbG1hV05oZEdVZ1FYVjBhRzl5CmFYUjVNUll3RkFZRFZRUUhFdzFUWVc0Z1JuSmhibU5wYzJOdk1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGgKTUI0WERUSTJNRE14TURFMk16TXdNRm9YRFRReE1ETXdOakUyTXpNd01Gb3dZakVaTUJjR0ExVUVDaE1RUTJ4dgpkV1JHYkdGeVpTd2dTVzVqTGpFZE1Cc0dBMVVFQ3hNVVEyeHZkV1JHYkdGeVpTQlBjbWxuYVc0Z1EwRXhKakFrCkJnTlZCQU1USFVOc2IzVmtSbXhoY21VZ1QzSnBaMmx1SUVObGNuUnBabWxqWVhSbE1JSUJJakFOQmdrcWhraUcKOXcwQkFRRUZBQU9DQVE0QU1JSUJDZ0tDQVFFQXV0QmpkQ0xEdHdMQlZCU0Y1ZU1OMkt3ckIxTmZmRVhRMjlRRAo1aVR0dzJFcEZXNVJJSllkMjNrYUpCMU5jZXpHWlg4a0Q0cGEyWHpFZW1MVEtJNWw0MU11b3FoWjczNVE3U3RWCkVjRFFTT2ZYTkZQdFMwb0hqb0pRdGF2QjM0ZmJNR3l4Mmx0MU9HUzRNMGtLUWpBNWR6OTJQYjNyZ1RKR0JhOW4KeTZtVThncjRuUHRSdklxZ3NxdjRtMFA3dVU1YjE3NzU1Y2JLSDVoMzIxWHVjMDU4Tzl4M2JHQ0NuRUJXWDdqeApjRGhkUEs1Ri9XRjVBQnl5cFhIQ0ZxUUd4M1NVbmtCQ0ZQSmRabnMra3BHVUZWZGhud3B6NjBtNnlJSzQ0eVR4CjZqR3JOTFEyM1dOK2gwU1lCZU5vb2JBWThydkpiVlZEaGJqSVhBTWtFNGQzVll1TlhRSURBUUFCbzRJQklqQ0MKQVI0d0RnWURWUjBQQVFIL0JBUURBZ1dnTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGQlFjRApBVEFNQmdOVkhSTUJBZjhFQWpBQU1CMEdBMVVkRGdRV0JCUkt1NkJVUDZ0N2dpbFRPay9FdEdKQ3R6N3dTREFmCkJnTlZIU01FR0RBV2dCUWs2Rk5YWFh3MFFJZXA2NVRidXVFV2VQd3BwREJBQmdnckJnRUZCUWNCQVFRME1ESXcKTUFZSUt3WUJCUVVITUFHR0pHaDBkSEE2THk5dlkzTndMbU5zYjNWa1pteGhjbVV1WTI5dEwyOXlhV2RwYmw5agpZVEFqQmdOVkhSRUVIREFhZ2d3cUxtbGhiWGR2Y21zdWFXNkNDbWxoYlhkdmNtc3VhVzR3T0FZRFZSMGZCREV3Ckx6QXRvQ3VnS1lZbmFIUjBjRG92TDJOeWJDNWpiRzkxWkdac1lYSmxMbU52YlM5dmNtbG5hVzVmWTJFdVkzSnMKTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDSjMvTGNleE5pb0lWdUxoemhmbTZCeDV2SWk3T25CaHF1WUlDdwplNnArZ0prdE16ZFJQcDV0bk03dllBWmxMajVJOTByWDRuczhJc3dEbzJBN2wwYTRGZVJFclFmRklsZXQzbjIyCjUxVTZYVElCSks5c1FZT0FkU3pJUzV1OUNKSFpBUTF5WmxSd3BBR3RVWnhxL1dpcGFWUTRwNXhrcEJNMVlZSlAKNW1jQ09HcFErSnpORlpQc2daYUJncDBYL1BBZkNJRkkyZld5QWE2elBqRm0rdDVXUXIrZlBaT2VUS2VIbWVzVgo3UlZxUUdEb3Q0eTY1NklEdmdmU2ZLRnFIRW9XNDJVbDBxQ05hMS9keEJld3NIS1VWWE1ETkdiQlNVQjM4TG9YCm1OQ3hJQlVOUjR0TG1CQUxZT3hVMnZhSWRCd0xBc2YrcndnVnVjUGpCUTc2VWMwUQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzYwR04wSXNPM0FzRlUKRklYbDR3M1lyQ3NIYTE5OFJkRGIxQVBtSk8zRFlTa1ZibEVnbGgzYmVSb2tIVTF4N01abGZ5UVBpbHJaZk1SNgpZdE1vam1YalV5NmlxRm52ZmxEdEsxVVJ3TkJJNTljMFUrMUxTZ2VPZ2xDMXE4SGZoOXN3YkxIYVczVTRaTGd6ClNRcENNRGwzUDNZOXZldUJNa1lGcjJmTHFaVHlDdmljKzFHOGlxQ3lxL2liUS91NVRsdlh2dm5seHNvZm1IZmIKVmU1elRudzczSGRzWUlLY1FGWmZ1UEZ3T0YwOHJrWDlZWGtBSExLbGNjSVdwQWJIZEpTZVFFSVU4bDFtZXo2UwprWlFWVjJHZkNuUHJTYnJJZ3JqakpQSHFNYXMwdERiZFkzNkhSSmdGNDJpaHNCanl1OGx0VlVPRnVNaGNBeVFUCmgzZFZpNDFkQWdNQkFBRUNnZ0VBTGlseXZkNmVTcEYvZUxtV2lhTVV4NUxwa2dhWHpITkxCQnNNZUpqcytLL0EKVVdlZ1crTkVUdmlLalZ5QlI5SzRocG1IYldDa2lPUDBBQUwrQnlKQ3lvekNOQmJTSEdRejlwc1R5dzZBV1ZlUwpuYjlVWGx1VmFQRktKTTRqbXNydERuYjVic25WT2lGblErTDdTalkwNlFMUlFybjBvUWp0ZFJldUdBMFlQVU90CkhSYzNsMFg2ZHJqdkJYY2prWTQwWm9ZYkRrelJnU1JWbWVOUGFIbjZPR0NtYUVUMXVyK01qYVZ2ME9lbEdIWncKVzljSEIxaHNxRzUvMWU3V0RQN0l0cjkwTmg4ay81NVhiK3lQUnhsRFd5bWtZMzIvdFBtZzdESTRKV2tRRWt3cgpIZUtwODVTcE5ta1liRnVpVFppeU8zZDZ0aXZHNHhFZW8rSzFVVFU4c1FLQmdRRFRNSEU1RDFYVC9HbGR5VHNsCllrODRVL1N0NXUrK2RIUEt1Wmw2dVB0UGgxV1lrdnFRcmdrL05YanVud2xGN0Y3b2tWOGdPeWxreTYwYTZkcXIKeXZwN1ZJdXYzekVlc2h2NjNWMlpaVkMzcXZYSzFheit3Zmx3NitCZmVuRlY5S2NENHN0dTdwOFRPWmFGN01CUgo3YXZzaXVXbWtqdmM1TlVLRmVDRTY0SnZFUUtCZ1FEaWMrbWlNLzBodDN1ajhuOXgyMDFQZFNqbEpVaUc1NjNNCnRYZlBCdDJRT0NhaVluUFNFdTdXdm5pQWRFL2xrMm91cFRWam9LYmZPbDFyQjd6UzVhc2kxdVdDZDhlUy9UWGIKdU5iRmlNMDB4L3JxalMydCtQbTd4MVhrYTB4TFNSRDNmZ0tSQldSN3pscStkYWZ1WE1qelUxRnh5dTIycGphRgpIMEl3NEpCUmpRS0JnUUNOaWhMb0Rob1V5RCtKNXJzb00vb3FJMEtDWnB0WlJzendHbkg5cVFwdFk2Ti9iVXBYCk92emhpeUh3czAvUXVEbG5uejVrNktHMmR6Y2VLWXN2eGdzWUt6S3ZmV043VWgya2hVWWM3NlVvWTREMkh6MGgKUkxtNzc2cGg4enNRUTdiSHlQRlUrTUpPYlRNdnNOdTRUUlVEcEplRGl0QnFIRWVYeWMrKzVlUjJNUUtCZ0h2UgptVHVoWlpVYitEVEtrVGkyQ20yWnlBU1RBRGNUVW9xTjVyYUNNSDk4MUZNUnRmWjFkN1pmYXhBQmlQWWtSbmkrCnlKUnk4UXM1cEg2ek9tR3VSb2JFTGJYS3ZJcjRmSXhwWXJXYmVXaVV0L09yd2dCUUZHekNMNHEzeUgyWnMvYy8KSlRRYVdMa0JPY2pPR0VaUzRXVjZkeHZiTTJNZE9zNUxLeXdDZmFhNUFvR0FIQUE1eEN0dndOZE4xeExndkZ3RApPK2lyMDl1bXMxOFBzSVpmK1ZrWGtpcHF4MWNUT0hEanpPR01yWXV0M2FFeE00Zjd2ckFHRFMyY2pwZjM0T1JxCit4Y2gwWlNaQ2FDZmlnZG9OelNkcDFLcmo0cnFKdG5ZdS9CNDlDQlVoSDBNaCtSRWswQ0hHOVE4b3FOWFk0V0wKbVVOVTZMYUkwQWtvSzNVb2tWQVJEYXM9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K ---- -# 1Password → K8s Secret sync for Twilio credentials -# Creates secret "twilio-credentials" with fields: AccountSid, AuthToken, DefaultFromNumber -apiVersion: onepassword.com/v1 -kind: OnePasswordItem -metadata: - name: twilio-credentials - namespace: telephony -spec: - itemPath: "vaults/IAmWorkin/items/Twilio Account" ---- -# Application configuration overlay -apiVersion: v1 -kind: ConfigMap -metadata: - name: telephony-config - namespace: telephony -data: - appsettings.Production.json: | - { - "Telephony": { - "Provider": "asterisk", - "Twilio": { - "VoiceUrl": "https://telephony.flowercore.io/api/twilio/webhooks/voice/incoming", - "StatusCallbackUrl": "https://telephony.flowercore.io/api/twilio/webhooks/voice/status" - }, - "Asterisk": { - "BaseUrl": "http://localhost:8088", - "Username": "flowercore", - "Password": "bluejay-asterisk-ari", - "Application": "flowercore-pbx", - "ReconnectDelaySeconds": 5, - "MaxReconnectDelaySeconds": 60 - } - }, - "Ari": { - "BaseUrl": "http://localhost:8088", - "Username": "flowercore", - "Password": "bluejay-asterisk-ari", - "Application": "flowercore-pbx", - "ReconnectDelaySeconds": 5, - "MaxReconnectDelaySeconds": 60 - }, - "Tts": { - "PiperUrl": "http://10.0.57.15:8500", - "DefaultEngine": "piper", - "SampleRate": 8000 - }, - "DatabaseProvider": "Sqlite", - "ConnectionStrings": { - "DefaultConnection": "Data Source=/data/telephony.db" - }, - "Kestrel": { - "Endpoints": { - "Http": { "Url": "http://0.0.0.0:5100" } - } - } - } ---- -# Persistent volume for SQLite database -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: telephony-data - namespace: telephony -spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 5Gi ---- -# Telephony web application -apiVersion: apps/v1 -kind: Deployment -metadata: - name: telephony-web - namespace: telephony - labels: - app: telephony-web -spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: telephony-web - template: - metadata: - labels: - app: telephony-web - spec: - securityContext: - fsGroup: 1654 - initContainers: - - name: fix-data-perms - image: busybox:latest - command: ["sh", "-c", "chown -R 1654:1654 /data"] - volumeMounts: - - name: telephony-data - mountPath: /data - hostNetwork: true - dnsPolicy: ClusterFirstWithHostNet - affinity: - podAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: - app: asterisk - topologyKey: kubernetes.io/hostname - containers: - - name: telephony-web - image: localhost/fc-telephony-web:latest - imagePullPolicy: Never - ports: - - containerPort: 5100 - name: http - env: - - name: Telephony__Twilio__AccountSid - valueFrom: - secretKeyRef: - name: twilio-credentials - key: AccountSid - optional: true - - name: Telephony__Twilio__AuthToken - valueFrom: - secretKeyRef: - name: twilio-credentials - key: AuthToken - optional: true - - name: Telephony__Twilio__DefaultFromNumber - valueFrom: - secretKeyRef: - name: twilio-credentials - key: DefaultFromNumber - optional: true - volumeMounts: - - name: telephony-config - mountPath: /app/appsettings.Production.json - subPath: appsettings.Production.json - readOnly: true - - name: telephony-data - mountPath: /data - resources: - requests: - memory: 256Mi - cpu: 100m - limits: - memory: 1Gi - cpu: "1" - livenessProbe: - httpGet: - path: /health - port: 5100 - initialDelaySeconds: 30 - periodSeconds: 10 - readinessProbe: - httpGet: - path: /health - port: 5100 - initialDelaySeconds: 10 - periodSeconds: 5 - volumes: - - name: telephony-config - configMap: - name: telephony-config - - name: telephony-data - persistentVolumeClaim: - claimName: telephony-data ---- -# ClusterIP service -apiVersion: v1 -kind: Service -metadata: - name: telephony-web - namespace: telephony -spec: - selector: - app: telephony-web - ports: - - port: 5100 - targetPort: 5100 - name: http ---- -# Traefik IngressRoute — public via Cloudflare (primary: flowercore.io) -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: telephony-web - namespace: telephony -spec: - entryPoints: - - websecure - routes: - - kind: Rule - match: Host(`telephony.flowercore.io`) - services: - - name: telephony-web - port: 5100 - - kind: Rule - match: Host(`telephony.iamwork.in`) - services: - - name: telephony-web - port: 5100 - tls: - secretName: cf-origin-flowercore-io ---- -# NetworkPolicy: deny-all baseline + Traefik ingress + SIP/RTP ingress + DNS egress + TTS egress -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: telephony-netpol - namespace: telephony -spec: - podSelector: {} - policyTypes: - - Ingress - - Egress - ingress: - # Allow Traefik ingress controller - - from: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: traefik-system - # Allow Selenium Grid for automated UI testing - - from: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: selenium - ports: - - port: 5100 - protocol: TCP - # Allow SIP/RTP from external sources (Yealink phones, Twilio SIP trunk) - - from: - - ipBlock: - cidr: 0.0.0.0/0 - ports: - - port: 5060 - protocol: UDP - - port: 5060 - protocol: TCP - - port: 10000 - endPort: 20000 - protocol: UDP - egress: - # Allow DNS resolution (CoreDNS in kube-system) - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: kube-system - ports: - - port: 53 - protocol: UDP - - port: 53 - protocol: TCP - # Allow Piper TTS on edge1 (10.0.57.15:8500) - - to: - - ipBlock: - cidr: 10.0.57.15/32 - ports: - - port: 8500 - protocol: TCP - # Allow Twilio API outbound (HTTPS) - - to: - - ipBlock: - cidr: 0.0.0.0/0 - except: - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - ports: - - port: 443 - protocol: TCP - # Allow SIP/RTP responses (Asterisk → phones and Twilio) - - to: - - ipBlock: - cidr: 0.0.0.0/0 - ports: - - port: 5060 - protocol: UDP - - port: 5060 - protocol: TCP - - port: 10000 - endPort: 20000 - protocol: UDP - # Allow 1Password Connect for secret sync - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: onepassword-system ---- -# TLS Certificate for internal hostname via cert-manager -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: telephony-internal-tls - namespace: telephony -spec: - secretName: telephony-internal-tls - issuerRef: - name: step-ca-acme - kind: ClusterIssuer - dnsNames: - - telephony.iamworkin.lan ---- -# Traefik IngressRoute — internal LAN access -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: telephony-web-internal - namespace: telephony -spec: - entryPoints: - - websecure - routes: - - kind: Rule - match: Host(`telephony.iamworkin.lan`) - services: - - name: telephony-web - port: 5100 - tls: - secretName: telephony-internal-tls - - - - - +# FlowerCore.Telephony - Blazor Server + REST API + Twilio IVR +# ArgoCD managed - BlueJay Lab +# Credentials: 1Password → OnePasswordItem CRD → K8s Secret (twilio-credentials) +# TTS: Piper on edge1 (10.0.57.15:8500) +# Public: telephony.flowercore.io via Cloudflare origin cert +--- +apiVersion: v1 +kind: Namespace +metadata: + name: telephony + labels: + app.kubernetes.io/part-of: bluejay-infra +--- +# Cloudflare Origin Certificate for *.flowercore.io + *.iamwork.in (15-year RSA) +apiVersion: v1 +kind: Secret +metadata: + name: cf-origin-flowercore-io + namespace: telephony +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVvRENDQTRpZ0F3SUJBZ0lVSXN4c1NKV1VRL0tqZ09ldk81YnNuVi9rZVE4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZc3hDekFKQmdOVkJBWVRBbFZUTVJrd0Z3WURWUVFLRXhCRGJHOTFaRVpzWVhKbExDQkpibU11TVRRdwpNZ1lEVlFRTEV5dERiRzkxWkVac1lYSmxJRTl5YVdkcGJpQlRVMHdnUTJWeWRHbG1hV05oZEdVZ1FYVjBhRzl5CmFYUjVNUll3RkFZRFZRUUhFdzFUWVc0Z1JuSmhibU5wYzJOdk1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGgKTUI0WERUSTJNRE14TURFMk16TXdNRm9YRFRReE1ETXdOakUyTXpNd01Gb3dZakVaTUJjR0ExVUVDaE1RUTJ4dgpkV1JHYkdGeVpTd2dTVzVqTGpFZE1Cc0dBMVVFQ3hNVVEyeHZkV1JHYkdGeVpTQlBjbWxuYVc0Z1EwRXhKakFrCkJnTlZCQU1USFVOc2IzVmtSbXhoY21VZ1QzSnBaMmx1SUVObGNuUnBabWxqWVhSbE1JSUJJakFOQmdrcWhraUcKOXcwQkFRRUZBQU9DQVE0QU1JSUJDZ0tDQVFFQXV0QmpkQ0xEdHdMQlZCU0Y1ZU1OMkt3ckIxTmZmRVhRMjlRRAo1aVR0dzJFcEZXNVJJSllkMjNrYUpCMU5jZXpHWlg4a0Q0cGEyWHpFZW1MVEtJNWw0MU11b3FoWjczNVE3U3RWCkVjRFFTT2ZYTkZQdFMwb0hqb0pRdGF2QjM0ZmJNR3l4Mmx0MU9HUzRNMGtLUWpBNWR6OTJQYjNyZ1RKR0JhOW4KeTZtVThncjRuUHRSdklxZ3NxdjRtMFA3dVU1YjE3NzU1Y2JLSDVoMzIxWHVjMDU4Tzl4M2JHQ0NuRUJXWDdqeApjRGhkUEs1Ri9XRjVBQnl5cFhIQ0ZxUUd4M1NVbmtCQ0ZQSmRabnMra3BHVUZWZGhud3B6NjBtNnlJSzQ0eVR4CjZqR3JOTFEyM1dOK2gwU1lCZU5vb2JBWThydkpiVlZEaGJqSVhBTWtFNGQzVll1TlhRSURBUUFCbzRJQklqQ0MKQVI0d0RnWURWUjBQQVFIL0JBUURBZ1dnTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGQlFjRApBVEFNQmdOVkhSTUJBZjhFQWpBQU1CMEdBMVVkRGdRV0JCUkt1NkJVUDZ0N2dpbFRPay9FdEdKQ3R6N3dTREFmCkJnTlZIU01FR0RBV2dCUWs2Rk5YWFh3MFFJZXA2NVRidXVFV2VQd3BwREJBQmdnckJnRUZCUWNCQVFRME1ESXcKTUFZSUt3WUJCUVVITUFHR0pHaDBkSEE2THk5dlkzTndMbU5zYjNWa1pteGhjbVV1WTI5dEwyOXlhV2RwYmw5agpZVEFqQmdOVkhSRUVIREFhZ2d3cUxtbGhiWGR2Y21zdWFXNkNDbWxoYlhkdmNtc3VhVzR3T0FZRFZSMGZCREV3Ckx6QXRvQ3VnS1lZbmFIUjBjRG92TDJOeWJDNWpiRzkxWkdac1lYSmxMbU52YlM5dmNtbG5hVzVmWTJFdVkzSnMKTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDSjMvTGNleE5pb0lWdUxoemhmbTZCeDV2SWk3T25CaHF1WUlDdwplNnArZ0prdE16ZFJQcDV0bk03dllBWmxMajVJOTByWDRuczhJc3dEbzJBN2wwYTRGZVJFclFmRklsZXQzbjIyCjUxVTZYVElCSks5c1FZT0FkU3pJUzV1OUNKSFpBUTF5WmxSd3BBR3RVWnhxL1dpcGFWUTRwNXhrcEJNMVlZSlAKNW1jQ09HcFErSnpORlpQc2daYUJncDBYL1BBZkNJRkkyZld5QWE2elBqRm0rdDVXUXIrZlBaT2VUS2VIbWVzVgo3UlZxUUdEb3Q0eTY1NklEdmdmU2ZLRnFIRW9XNDJVbDBxQ05hMS9keEJld3NIS1VWWE1ETkdiQlNVQjM4TG9YCm1OQ3hJQlVOUjR0TG1CQUxZT3hVMnZhSWRCd0xBc2YrcndnVnVjUGpCUTc2VWMwUQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzYwR04wSXNPM0FzRlUKRklYbDR3M1lyQ3NIYTE5OFJkRGIxQVBtSk8zRFlTa1ZibEVnbGgzYmVSb2tIVTF4N01abGZ5UVBpbHJaZk1SNgpZdE1vam1YalV5NmlxRm52ZmxEdEsxVVJ3TkJJNTljMFUrMUxTZ2VPZ2xDMXE4SGZoOXN3YkxIYVczVTRaTGd6ClNRcENNRGwzUDNZOXZldUJNa1lGcjJmTHFaVHlDdmljKzFHOGlxQ3lxL2liUS91NVRsdlh2dm5seHNvZm1IZmIKVmU1elRudzczSGRzWUlLY1FGWmZ1UEZ3T0YwOHJrWDlZWGtBSExLbGNjSVdwQWJIZEpTZVFFSVU4bDFtZXo2UwprWlFWVjJHZkNuUHJTYnJJZ3JqakpQSHFNYXMwdERiZFkzNkhSSmdGNDJpaHNCanl1OGx0VlVPRnVNaGNBeVFUCmgzZFZpNDFkQWdNQkFBRUNnZ0VBTGlseXZkNmVTcEYvZUxtV2lhTVV4NUxwa2dhWHpITkxCQnNNZUpqcytLL0EKVVdlZ1crTkVUdmlLalZ5QlI5SzRocG1IYldDa2lPUDBBQUwrQnlKQ3lvekNOQmJTSEdRejlwc1R5dzZBV1ZlUwpuYjlVWGx1VmFQRktKTTRqbXNydERuYjVic25WT2lGblErTDdTalkwNlFMUlFybjBvUWp0ZFJldUdBMFlQVU90CkhSYzNsMFg2ZHJqdkJYY2prWTQwWm9ZYkRrelJnU1JWbWVOUGFIbjZPR0NtYUVUMXVyK01qYVZ2ME9lbEdIWncKVzljSEIxaHNxRzUvMWU3V0RQN0l0cjkwTmg4ay81NVhiK3lQUnhsRFd5bWtZMzIvdFBtZzdESTRKV2tRRWt3cgpIZUtwODVTcE5ta1liRnVpVFppeU8zZDZ0aXZHNHhFZW8rSzFVVFU4c1FLQmdRRFRNSEU1RDFYVC9HbGR5VHNsCllrODRVL1N0NXUrK2RIUEt1Wmw2dVB0UGgxV1lrdnFRcmdrL05YanVud2xGN0Y3b2tWOGdPeWxreTYwYTZkcXIKeXZwN1ZJdXYzekVlc2h2NjNWMlpaVkMzcXZYSzFheit3Zmx3NitCZmVuRlY5S2NENHN0dTdwOFRPWmFGN01CUgo3YXZzaXVXbWtqdmM1TlVLRmVDRTY0SnZFUUtCZ1FEaWMrbWlNLzBodDN1ajhuOXgyMDFQZFNqbEpVaUc1NjNNCnRYZlBCdDJRT0NhaVluUFNFdTdXdm5pQWRFL2xrMm91cFRWam9LYmZPbDFyQjd6UzVhc2kxdVdDZDhlUy9UWGIKdU5iRmlNMDB4L3JxalMydCtQbTd4MVhrYTB4TFNSRDNmZ0tSQldSN3pscStkYWZ1WE1qelUxRnh5dTIycGphRgpIMEl3NEpCUmpRS0JnUUNOaWhMb0Rob1V5RCtKNXJzb00vb3FJMEtDWnB0WlJzendHbkg5cVFwdFk2Ti9iVXBYCk92emhpeUh3czAvUXVEbG5uejVrNktHMmR6Y2VLWXN2eGdzWUt6S3ZmV043VWgya2hVWWM3NlVvWTREMkh6MGgKUkxtNzc2cGg4enNRUTdiSHlQRlUrTUpPYlRNdnNOdTRUUlVEcEplRGl0QnFIRWVYeWMrKzVlUjJNUUtCZ0h2UgptVHVoWlpVYitEVEtrVGkyQ20yWnlBU1RBRGNUVW9xTjVyYUNNSDk4MUZNUnRmWjFkN1pmYXhBQmlQWWtSbmkrCnlKUnk4UXM1cEg2ek9tR3VSb2JFTGJYS3ZJcjRmSXhwWXJXYmVXaVV0L09yd2dCUUZHekNMNHEzeUgyWnMvYy8KSlRRYVdMa0JPY2pPR0VaUzRXVjZkeHZiTTJNZE9zNUxLeXdDZmFhNUFvR0FIQUE1eEN0dndOZE4xeExndkZ3RApPK2lyMDl1bXMxOFBzSVpmK1ZrWGtpcHF4MWNUT0hEanpPR01yWXV0M2FFeE00Zjd2ckFHRFMyY2pwZjM0T1JxCit4Y2gwWlNaQ2FDZmlnZG9OelNkcDFLcmo0cnFKdG5ZdS9CNDlDQlVoSDBNaCtSRWswQ0hHOVE4b3FOWFk0V0wKbVVOVTZMYUkwQWtvSzNVb2tWQVJEYXM9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +--- +# 1Password → K8s Secret sync for Twilio credentials +# Creates secret "twilio-credentials" with fields: AccountSid, AuthToken, DefaultFromNumber +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: twilio-credentials + namespace: telephony +spec: + itemPath: "vaults/IAmWorkin/items/Twilio Account" +--- +# Application configuration overlay +apiVersion: v1 +kind: ConfigMap +metadata: + name: telephony-config + namespace: telephony +data: + appsettings.Production.json: | + { + "Telephony": { + "Provider": "asterisk", + "Twilio": { + "VoiceUrl": "https://telephony.flowercore.io/api/twilio/webhooks/voice/incoming", + "StatusCallbackUrl": "https://telephony.flowercore.io/api/twilio/webhooks/voice/status" + }, + "Asterisk": { + "BaseUrl": "http://localhost:8088", + "Username": "flowercore", + "Password": "bluejay-asterisk-ari", + "Application": "flowercore-pbx", + "ReconnectDelaySeconds": 5, + "MaxReconnectDelaySeconds": 60 + } + }, + "Ari": { + "BaseUrl": "http://localhost:8088", + "Username": "flowercore", + "Password": "bluejay-asterisk-ari", + "Application": "flowercore-pbx", + "ReconnectDelaySeconds": 5, + "MaxReconnectDelaySeconds": 60 + }, + "Tts": { + "PiperUrl": "http://10.0.57.15:8500", + "DefaultEngine": "piper", + "SampleRate": 8000 + }, + "DatabaseProvider": "Sqlite", + "ConnectionStrings": { + "DefaultConnection": "Data Source=/data/telephony.db" + }, + "Kestrel": { + "Endpoints": { + "Http": { "Url": "http://0.0.0.0:5100" } + } + } + } +--- +# Persistent volume for SQLite database +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: telephony-data + namespace: telephony +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 5Gi +--- +# Telephony web application +apiVersion: apps/v1 +kind: Deployment +metadata: + name: telephony-web + namespace: telephony + labels: + app: telephony-web +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: telephony-web + template: + metadata: + labels: + app: telephony-web + spec: + securityContext: + fsGroup: 1654 + initContainers: + - name: fix-data-perms + image: busybox:latest + command: ["sh", "-c", "chown -R 1654:1654 /data"] + volumeMounts: + - name: telephony-data + mountPath: /data + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + affinity: + podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app: asterisk + topologyKey: kubernetes.io/hostname + containers: + - name: telephony-web + image: localhost/fc-telephony-web:v20260324d + imagePullPolicy: Never + ports: + - containerPort: 5100 + name: http + env: + - name: Telephony__Twilio__AccountSid + valueFrom: + secretKeyRef: + name: twilio-credentials + key: AccountSid + optional: true + - name: Telephony__Twilio__AuthToken + valueFrom: + secretKeyRef: + name: twilio-credentials + key: AuthToken + optional: true + - name: Telephony__Twilio__DefaultFromNumber + valueFrom: + secretKeyRef: + name: twilio-credentials + key: DefaultFromNumber + optional: true + volumeMounts: + - name: telephony-config + mountPath: /app/appsettings.Production.json + subPath: appsettings.Production.json + readOnly: true + - name: telephony-data + mountPath: /data + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 1Gi + cpu: "1" + livenessProbe: + httpGet: + path: /health + port: 5100 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 5100 + initialDelaySeconds: 10 + periodSeconds: 5 + volumes: + - name: telephony-config + configMap: + name: telephony-config + - name: telephony-data + persistentVolumeClaim: + claimName: telephony-data +--- +# ClusterIP service +apiVersion: v1 +kind: Service +metadata: + name: telephony-web + namespace: telephony +spec: + selector: + app: telephony-web + ports: + - port: 5100 + targetPort: 5100 + name: http +--- +# Traefik IngressRoute — public via Cloudflare (primary: flowercore.io) +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: telephony-web + namespace: telephony +spec: + entryPoints: + - websecure + routes: + - kind: Rule + match: Host(`telephony.flowercore.io`) + services: + - name: telephony-web + port: 5100 + - kind: Rule + match: Host(`telephony.iamwork.in`) + services: + - name: telephony-web + port: 5100 + tls: + secretName: cf-origin-flowercore-io +--- +# NetworkPolicy: deny-all baseline + Traefik ingress + SIP/RTP ingress + DNS egress + TTS egress +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: telephony-netpol + namespace: telephony +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress + ingress: + # Allow Traefik ingress controller + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: traefik-system + # Allow Selenium Grid for automated UI testing + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: selenium + ports: + - port: 5100 + protocol: TCP + # Allow SIP/RTP from external sources (Yealink phones, Twilio SIP trunk) + - from: + - ipBlock: + cidr: 0.0.0.0/0 + ports: + - port: 5060 + protocol: UDP + - port: 5060 + protocol: TCP + - port: 10000 + endPort: 20000 + protocol: UDP + egress: + # Allow DNS resolution (CoreDNS in kube-system) + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + # Allow Piper TTS on edge1 (10.0.57.15:8500) + - to: + - ipBlock: + cidr: 10.0.57.15/32 + ports: + - port: 8500 + protocol: TCP + # Allow Twilio API outbound (HTTPS) + - to: + - ipBlock: + cidr: 0.0.0.0/0 + except: + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + ports: + - port: 443 + protocol: TCP + # Allow SIP/RTP responses (Asterisk → phones and Twilio) + - to: + - ipBlock: + cidr: 0.0.0.0/0 + ports: + - port: 5060 + protocol: UDP + - port: 5060 + protocol: TCP + - port: 10000 + endPort: 20000 + protocol: UDP + # Allow 1Password Connect for secret sync + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: onepassword-system +--- +# TLS Certificate for internal hostname via cert-manager +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: telephony-internal-tls + namespace: telephony +spec: + secretName: telephony-internal-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - telephony.iamworkin.lan +--- +# Traefik IngressRoute — internal LAN access +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: telephony-web-internal + namespace: telephony +spec: + entryPoints: + - websecure + routes: + - kind: Rule + match: Host(`telephony.iamworkin.lan`) + services: + - name: telephony-web + port: 5100 + tls: + secretName: telephony-internal-tls + + + + +