# UnrealIRCd + Anope IRC Services # ArgoCD managed - BlueJay Lab # Credentials: 1Password → OnePasswordItem → K8s Secret → initContainer sed injection --- apiVersion: v1 kind: Namespace metadata: name: irc labels: app.kubernetes.io/part-of: bluejay-infra --- # 1Password → K8s Secret sync apiVersion: onepassword.com/v1 kind: OnePasswordItem metadata: name: irc-credentials namespace: irc spec: itemPath: "vaults/IAmWorkin/items/IRC UnrealIRCd" --- # TLS Certificate for IRC apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: irc-tls namespace: irc spec: secretName: irc-tls issuerRef: name: step-ca-acme kind: ClusterIssuer dnsNames: - irc.iamworkin.lan --- # UnrealIRCd configuration template (passwords replaced by placeholders) apiVersion: v1 kind: ConfigMap metadata: name: unrealircd-config-template namespace: irc data: unrealircd.conf: | /* BlueJay Lab IRC - UnrealIRCd 6.x config */ /* Managed by ArgoCD */ /* Credentials injected from 1Password at pod startup */ include "modules.default.conf"; include "help/help.conf"; include "operclass.default.conf"; include "snomasks.default.conf"; loadmodule "cloak_sha256"; me { name "irc.iamworkin.lan"; info "BlueJay Lab IRC Server"; sid 001; } admin { "BlueJay Lab IRC"; "admin@iamwork.in"; } class clients { pingfreq 90; maxclients 500; sendq 200k; recvq 8000; } class opers { pingfreq 90; maxclients 50; sendq 1M; recvq 8000; } class servers { pingfreq 60; connfreq 15; maxclients 10; sendq 20M; } allow { mask *; class clients; maxperip 5; } listen { ip *; port 6667; } listen { ip *; port 6697; options { tls; } tls-options { certificate "/app/conf/tls/server.cert.pem"; key "/app/conf/tls/server.key.pem"; } } listen { ip *; port 8067; } oper bluejay { mask *; password "__OPER_PASSWORD__"; operclass netadmin-with-override; class opers; } drpass { restart "__OPER_PASSWORD__"; die "__OPER_PASSWORD__"; } link services.iamworkin.lan { incoming { mask *; } password "__LINK_PASSWORD__"; class servers; } ulines { services.iamworkin.lan; } log { source { all; \\!debug; } destination { channel "#ops"; } } set { network-name "BlueJayIRC"; default-server "irc.iamworkin.lan"; services-server "services.iamworkin.lan"; stats-server "stats.iamworkin.lan"; help-channel "#general"; cloak-keys { "ZWKeb8YevNiL45Xdh2p5u4tv2xksWgb8YPQSvmerBmNObyGbTDGnU4PNomZaLbZ1D9M2Cy6njM1XLJUkJhAx1oY3coBdZoPykEo7"; "KqRaLeA6ijOnWDdCqYtJ6rb1VgR8lYnU9Sey7cbRhi3PsGzD5gZONJXyUdbJ7bD26QKCuiDydBsccUVKC3lYN0HJ9sGTlOYR3c2m"; "2I4oopLDY79Fr4Mucy63EVOfkelVV23nESPWoqMnP1pUc8Yg0D4RK1mVtxyEhdTPpLFyKgG4fRlb6R33eHoQe7yi7moOu4W1Waw6"; } kline-address "admin@iamwork.in"; maxchannelsperuser 25; anti-flood { everyone { connect-flood 3:60; } } options { hide-ulines; show-connect-info; } /* TLS config */ tls { certificate "/app/conf/tls/server.cert.pem"; key "/app/conf/tls/server.key.pem"; trusted-ca-file "/etc/ssl/certs/ca-certificates.crt"; } /* Allow plaintext for server-to-server links (Anope is internal) */ plaintext-policy { server allow; } } --- # Anope configuration template (passwords replaced by placeholders) apiVersion: v1 kind: ConfigMap metadata: name: anope-config-template namespace: irc data: services.conf: | define { name = "services.host" value = "services.iamworkin.lan" } uplink { host = "unrealircd.irc.svc.cluster.local" port = 8067 password = "__LINK_PASSWORD__" } serverinfo { name = "services.iamworkin.lan" description = "BlueJay IRC Services" pid = "/anope/data/services.pid" motd = "/anope/data/services.motd" } module { name = "unreal4" } networkinfo { networkname = "BlueJayIRC" nicklen = 31 userlen = 10 hostlen = 64 chanlen = 32 } options { casemap = "ascii" strictpasswords = yes readtimeout = 5s warningtimeout = 4h } module { name = "enc_sha256" } /* Service pseudo-client definitions */ service { nick = "NickServ" user = "services" host = "services.host" gecos = "Nickname Registration Service" } service { nick = "ChanServ" user = "services" host = "services.host" gecos = "Channel Registration Service" } service { nick = "OperServ" user = "services" host = "services.host" gecos = "Operator Service" } service { nick = "BotServ" user = "services" host = "services.host" gecos = "Bot Service" } service { nick = "HostServ" user = "services" host = "services.host" gecos = "vHost Service" } service { nick = "MemoServ" user = "services" host = "services.host" gecos = "Memo Service" } service { nick = "Global" user = "services" host = "services.host" gecos = "Global Noticer" } /* Module configurations */ module { name = "nickserv" client = "NickServ" defaults = "kill_quick ns_secure ns_private hide_email" registration = "none" expire = 90d } module { name = "ns_identify" } module { name = "ns_register" } module { name = "ns_set" } module { name = "ns_drop" } module { name = "ns_recover" } module { name = "ns_info" } module { name = "ns_list" } module { name = "ns_access" } module { name = "ns_group" } module { name = "chanserv" client = "ChanServ" defaults = "keeptopic peace cs_secure" expire = 14d } module { name = "cs_register" } module { name = "cs_set" } module { name = "cs_access" } module { name = "cs_ban" } module { name = "cs_kick" } module { name = "cs_mode" } module { name = "cs_topic" } module { name = "cs_info" } module { name = "cs_list" } module { name = "cs_drop" } module { name = "operserv" client = "OperServ" } module { name = "os_akill" } module { name = "os_mode" } module { name = "os_kick" } module { name = "os_kill" } module { name = "os_list" } module { name = "os_stats" } module { name = "os_reload" } module { name = "os_shutdown" } module { name = "botserv" client = "BotServ" defaults = "dontkickops fantasy greet" } module { name = "bs_bot" } module { name = "bs_assign" } module { name = "hostserv" client = "HostServ" } module { name = "hs_set" } module { name = "hs_request" } module { name = "memoserv" client = "MemoServ" maxmemos = 20 } module { name = "ms_send" } module { name = "ms_read" } module { name = "ms_del" } module { name = "ms_list" } module { name = "global" client = "Global" } module { name = "gl_global" } opertype { name = "Services Root" commands = "*" privs = "*" } oper { name = "bluejay" type = "Services Root" } module { name = "db_flatfile" database = "/anope/data/anope.db" fork = no } log { target = "services.log" admin = "*" override = "chanserv/* nickserv/* operserv/*" commands = "chanserv/* nickserv/* operserv/*" servers = "*" channels = "*" users = "connect disconnect" } --- # UnrealIRCd PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: unrealircd-data namespace: irc spec: accessModes: [ReadWriteOnce] resources: requests: storage: 1Gi --- # Anope PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: anope-data namespace: irc spec: accessModes: [ReadWriteOnce] resources: requests: storage: 1Gi --- # UnrealIRCd Deployment apiVersion: apps/v1 kind: Deployment metadata: name: unrealircd namespace: irc labels: app: unrealircd spec: replicas: 1 selector: matchLabels: app: unrealircd template: metadata: labels: app: unrealircd spec: initContainers: - name: inject-credentials image: busybox:1.36 command: ["sh", "-c"] args: - | OPER_PW=$(cat /secrets/password) LINK_PW=$(cat /secrets/Link-Password) sed -e "s|__OPER_PASSWORD__|${OPER_PW}|g" \ -e "s|__LINK_PASSWORD__|${LINK_PW}|g" \ /config-template/unrealircd.conf > /injected-config/unrealircd.conf echo "Credentials injected into unrealircd.conf" volumeMounts: - name: irc-credentials mountPath: /secrets readOnly: true - name: unrealircd-config-template mountPath: /config-template readOnly: true - name: injected-config mountPath: /injected-config - name: copy-tls image: busybox:1.36 command: ["sh", "-c"] args: - | cp /tls-secret/tls.crt /tls/server.cert.pem cp /tls-secret/tls.key /tls/server.key.pem chmod 644 /tls/server.cert.pem chmod 644 /tls/server.key.pem chown 1000:1000 /tls/server.cert.pem /tls/server.key.pem 2>/dev/null || true chmod 777 /data volumeMounts: - name: irc-tls-secret mountPath: /tls-secret readOnly: true - name: irc-tls mountPath: /tls - name: unrealircd-data mountPath: /data containers: - name: unrealircd image: djlegolas/unrealircd:6.1.9.1 ports: - containerPort: 6667 name: irc-plain - containerPort: 6697 name: irc-tls - containerPort: 8067 name: services-link volumeMounts: - name: injected-config mountPath: /app/conf/unrealircd.conf subPath: unrealircd.conf - name: unrealircd-data mountPath: /app/data - name: irc-tls mountPath: /app/conf/tls resources: requests: memory: 64Mi cpu: 50m limits: memory: 256Mi cpu: 250m volumes: - name: irc-credentials secret: secretName: irc-credentials - name: unrealircd-config-template configMap: name: unrealircd-config-template - name: injected-config emptyDir: {} - name: unrealircd-data persistentVolumeClaim: claimName: unrealircd-data - name: irc-tls-secret secret: secretName: irc-tls - name: irc-tls emptyDir: {} --- # Anope IRC Services Deployment apiVersion: apps/v1 kind: Deployment metadata: name: anope namespace: irc labels: app: anope spec: replicas: 1 selector: matchLabels: app: anope template: metadata: labels: app: anope spec: initContainers: - name: inject-credentials image: busybox:1.36 command: ["sh", "-c"] args: - | LINK_PW=$(cat /secrets/Link-Password) sed -e "s|__LINK_PASSWORD__|${LINK_PW}|g" \ /config-template/services.conf > /injected-config/services.conf echo "Credentials injected into services.conf" volumeMounts: - name: irc-credentials mountPath: /secrets readOnly: true - name: anope-config-template mountPath: /config-template readOnly: true - name: injected-config mountPath: /injected-config - name: fix-perms image: busybox:1.36 command: ["sh", "-c"] args: - | mkdir -p /data/db /data/logs /data/runtime touch /data/anope.db /data/services.motd chmod 666 /data/anope.db chown -R 10000:10000 /data 2>/dev/null || chmod -R 777 /data echo "Anope data dir prepared: $(ls -la /data/anope.db)" volumeMounts: - name: anope-data mountPath: /data containers: - name: anope image: anope/anope:latest volumeMounts: - name: injected-config mountPath: /anope/conf/services.conf subPath: services.conf - name: anope-data mountPath: /anope/data resources: requests: memory: 64Mi cpu: 25m limits: memory: 128Mi cpu: 100m volumes: - name: irc-credentials secret: secretName: irc-credentials - name: anope-config-template configMap: name: anope-config-template - name: injected-config emptyDir: {} - name: anope-data persistentVolumeClaim: claimName: anope-data --- # UnrealIRCd Service apiVersion: v1 kind: Service metadata: name: unrealircd namespace: irc spec: selector: app: unrealircd ports: - port: 6667 targetPort: 6667 name: irc-plain - port: 6697 targetPort: 6697 name: irc-tls - port: 8067 targetPort: 8067 name: services-link --- # Anope Service apiVersion: v1 kind: Service metadata: name: anope namespace: irc spec: selector: app: anope ports: - port: 8067 targetPort: 8067 name: services-link --- # Traefik IngressRouteTCP - IRC plain (6667) apiVersion: traefik.io/v1alpha1 kind: IngressRouteTCP metadata: name: irc-plain namespace: irc spec: entryPoints: - irc routes: - match: HostSNI(`*`) services: - name: unrealircd port: 6667 --- # Traefik IngressRouteTCP - IRC TLS passthrough (6697) apiVersion: traefik.io/v1alpha1 kind: IngressRouteTCP metadata: name: irc-tls namespace: irc spec: entryPoints: - irctls routes: - match: HostSNI(`*`) services: - name: unrealircd port: 6697 tls: passthrough: true