# ejabberd — XMPP server # Vhost: xmpp.the-fulfillment.org # Unpinned — scheduler places freely, local-path PVC for Mnesia data # TLS terminated externally by Caddy at venture ingress VPS # (HTTP admin/BOSH/websocket on 5280 is served plain; Caddy fronts it) # (c2s/s2s use ejabberd's auto-generated self-signed cert for STARTTLS # until real certs are mounted — see "TLS notes" below) # No STUN/TURN — add later when available # No MQTT — skipped # # Storage: # Mnesia (on-disk, via PVC) — default backend for most modules # PostgreSQL (cluster-local `postgres` service) — only mod_mam (archive + archive_prefs) # DB schema loaded from /home/ejabberd/sql/pg.new.sql into ejabberd_db. # SQL password lives in ejabberd-secret and is injected into the rendered # config at pod start by the `render-config` init container (sed). # # NodePorts: # 32378 — XMPP c2s (5222) # 32381 — XMPP s2s federation (5269) # 32382 — HTTP admin / BOSH / websocket / HTTP upload (5280) # # Deploy: # kubectl create secret generic ejabberd-secret \ # --from-literal=admin-password='' \ # --from-literal=erlang-cookie='' \ # --from-literal=db-password='' # # # Create DB + user + schema (one-shot, from a host with kubectl): # kubectl exec -i deploy/postgres -- psql -U postgres <'; # CREATE DATABASE ejabberd_db ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' # TEMPLATE template0 OWNER ejabberd_user; # SQL # kubectl exec deploy/ejabberd -- cat /home/ejabberd/sql/pg.new.sql \ # | kubectl exec -i deploy/postgres -- psql -U ejabberd_user -d ejabberd_db # # kubectl apply -f ejabberd.yaml -n # # First startup does NOT auto-register the admin (CTL_ON_CREATE entrypoint hook # doesn't expand shell vars). Register manually: # kubectl exec deploy/ejabberd -- ejabberdctl register admin xmpp.the-fulfillment.org # # Admin web UI: http://:32382/admin # (login: admin@xmpp.the-fulfillment.org / ) # # TLS notes: # For production federation, mount real certs for xmpp.the-fulfillment.org # at /home/ejabberd/conf/server.pem (PEM with privkey + fullchain) and # set certfiles in the ConfigMap to point at it. Many federating servers # reject self-signed s2s peers. # # User registration is CLOSED by default — admin must create accounts via: # kubectl exec deploy/ejabberd -- ejabberdctl register xmpp.the-fulfillment.org --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ejabberd-data-pvc spec: accessModes: - ReadWriteOnce storageClassName: local-path resources: requests: storage: 10Gi --- apiVersion: v1 kind: ConfigMap metadata: name: ejabberd-config data: ejabberd.yml: | hosts: - "xmpp.the-fulfillment.org" loglevel: info ## ejabberd will auto-generate a self-signed cert on first run. ## Replace with real certs for production federation. certfiles: [] ## PostgreSQL — used only by mod_mam (see modules section). ## Password is substituted into a rendered copy of this file at pod ## start by the `render-config` init container. sql_type: pgsql sql_server: postgres sql_port: 5432 sql_database: ejabberd_db sql_username: ejabberd_user sql_password: "__SQL_PASSWORD__" listen: - port: 5222 ip: "::" module: ejabberd_c2s max_stanza_size: 262144 shaper: c2s_shaper access: c2s starttls: true - port: 5269 ip: "::" module: ejabberd_s2s_in max_stanza_size: 524288 - port: 5280 ip: "::" module: ejabberd_http request_handlers: "/admin": ejabberd_web_admin "/api": mod_http_api "/bosh": mod_bosh "/ws": ejabberd_http_ws "/upload": mod_http_upload s2s_use_starttls: optional acl: admin: user: - "admin@xmpp.the-fulfillment.org" local: user_regexp: "" access_rules: local: allow: local c2s: deny: blocked allow: all announce: allow: admin configure: allow: admin muc_create: allow: local pubsub_createnode: allow: local register: deny: all trusted_network: allow: loopback api_permissions: "console commands": from: ejabberd_ctl who: all what: "*" "admin access": who: access: allow: acl: admin what: - "*" - "!stop" - "!start" shaper: normal: rate: 3000 burst_size: 20000 fast: 100000 shaper_rules: max_user_sessions: 10 max_user_offline_messages: 5000: admin 100: all c2s_shaper: none: admin normal: all s2s_shaper: fast modules: mod_adhoc: {} mod_admin_extra: {} mod_announce: access: announce mod_avatar: {} mod_blocking: {} mod_bosh: {} mod_caps: {} mod_carboncopy: {} mod_client_state: {} mod_configure: {} mod_disco: {} mod_http_api: {} mod_http_upload: put_url: "http://@HOST@:5280/upload" max_size: 104857600 # 100 MB mod_last: {} mod_mam: db_type: sql assume_mam_usage: true default: always mod_muc: access: - allow access_admin: - allow: admin access_create: muc_create access_persistent: muc_create default_room_options: mam: true mod_offline: access_max_user_messages: max_user_offline_messages mod_ping: {} mod_privacy: {} mod_private: {} mod_pubsub: access_createnode: pubsub_createnode plugins: - flat - pep mod_push: {} mod_push_keepalive: {} mod_roster: versioning: true mod_sic: {} mod_stream_mgmt: resend_on_timeout: if_offline mod_vcard: {} mod_vcard_xupdate: {} mod_version: show_os: false --- apiVersion: apps/v1 kind: Deployment metadata: name: ejabberd spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: ejabberd template: metadata: labels: app: ejabberd spec: securityContext: fsGroup: 9000 initContainers: ## Render ejabberd.yml from template, substituting the SQL password ## from the ejabberd-secret. Avoids baking the password into the ConfigMap. - name: render-config image: busybox:1.37 env: - name: SQL_PASSWORD valueFrom: secretKeyRef: name: ejabberd-secret key: db-password command: - /bin/sh - -c - | sed "s|__SQL_PASSWORD__|${SQL_PASSWORD}|" /template/ejabberd.yml > /rendered/ejabberd.yml volumeMounts: - name: config-template mountPath: /template - name: config-rendered mountPath: /rendered containers: - name: ejabberd image: ejabberd/ecs:latest env: - name: ERLANG_NODE value: ejabberd@localhost - name: ERLANG_COOKIE valueFrom: secretKeyRef: name: ejabberd-secret key: erlang-cookie ports: - name: c2s containerPort: 5222 - name: s2s containerPort: 5269 - name: http containerPort: 5280 volumeMounts: - name: config-rendered mountPath: /home/ejabberd/conf/ejabberd.yml subPath: ejabberd.yml - name: data mountPath: /home/ejabberd/database readinessProbe: tcpSocket: port: 5222 initialDelaySeconds: 20 periodSeconds: 10 livenessProbe: tcpSocket: port: 5222 initialDelaySeconds: 60 periodSeconds: 30 volumes: - name: config-template configMap: name: ejabberd-config - name: config-rendered emptyDir: {} - name: data persistentVolumeClaim: claimName: ejabberd-data-pvc --- apiVersion: v1 kind: Service metadata: name: ejabberd spec: selector: app: ejabberd ports: - name: c2s port: 5222 targetPort: 5222 nodePort: 32378 - name: s2s port: 5269 targetPort: 5269 nodePort: 32381 - name: http port: 5280 targetPort: 5280 nodePort: 32382 type: NodePort