Files
bluejay-infra/apps/irc/irc.yaml
2026-04-16 19:49:53 -05:00

1340 lines
32 KiB
YAML

# UnrealIRCd + Anope IRC Services + The Lounge web client
# 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
---
# TLS Certificate for The Lounge web IRC
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: webirc-tls
namespace: irc
spec:
secretName: webirc-tls
issuerRef:
name: step-ca-acme
kind: ClusterIssuer
dnsNames:
- webirc.iamworkin.lan
---
# The Lounge configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: thelounge-config
namespace: irc
data:
config.js: |
"use strict";
module.exports = {
public: true,
host: "0.0.0.0",
port: 9000,
reverseProxy: true,
maxHistory: 2500,
theme: "thelounge-theme-flowercore",
prefetch: false,
disableMediaPreview: true,
fileUpload: {
enable: false
},
defaults: {
name: "BlueJayIRC",
host: "unrealircd.irc.svc.cluster.local",
port: 6667,
password: "",
tls: false,
rejectUnauthorized: true,
nick: "BlueJayWeb%%",
username: "bluejayweb",
realname: "BlueJay Web IRC",
join: "#general"
},
lockNetwork: true,
leaveMessage: "BlueJay Web IRC"
};
---
# FlowerCore / Blue Jay theme package for The Lounge
apiVersion: v1
kind: ConfigMap
metadata:
name: thelounge-flowercore-theme
namespace: irc
data:
package.json: |
{
"name": "thelounge-theme-flowercore",
"version": "1.0.0",
"description": "FlowerCore Blue Jay theme for The Lounge",
"main": "package.json",
"keywords": [
"thelounge",
"thelounge-theme"
],
"thelounge": {
"type": "theme",
"name": "FlowerCore Blue Jay",
"css": "theme.css",
"files": [
"bluejay-logo.svg",
"bluejay-bg.svg"
]
}
}
theme.css: |
:root {
--fc-bg: #0a1628;
--fc-surface: #111d33;
--fc-surface-2: #162844;
--fc-border: #1e3a5f;
--fc-accent: #2b8aff;
--fc-accent-soft: rgba(43, 138, 255, 0.22);
--fc-gold: #ffb300;
--fc-text: #e8edf5;
--fc-text-muted: #9db1c8;
--fc-success: #3db86a;
--fc-danger: #e84545;
--body-color: var(--fc-text);
--body-color-muted: var(--fc-text-muted);
--body-bg-color: var(--fc-bg);
--button-color: var(--fc-accent);
--button-text-color-hover: #ffffff;
--overlay-bg-color: rgba(5, 12, 22, 0.84);
--link-color: #8bc3ff;
--window-bg-color: var(--fc-surface);
--window-heading-color: #f5f8ff;
--date-marker-color: rgba(43, 138, 255, 0.45);
--unread-marker-color: rgba(255, 179, 0, 0.6);
--highlight-bg-color: rgba(43, 138, 255, 0.12);
--highlight-border-color: var(--fc-gold);
--upload-progressbar-color: var(--fc-gold);
}
body {
background:
radial-gradient(circle at top right, rgba(43, 138, 255, 0.22), transparent 30%),
linear-gradient(145deg, rgba(10, 22, 40, 0.98), rgba(12, 25, 46, 0.98)),
url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
color: var(--fc-text);
font-family: "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
}
a,
a:focus,
a:hover {
color: var(--link-color);
}
.window,
#confirm-dialog,
#context-menu,
.mentions-popup,
.textcomplete-menu {
background: rgba(17, 29, 51, 0.97);
border: 1px solid var(--fc-border);
box-shadow: 0 24px 60px rgba(3, 10, 18, 0.55);
}
#loading .window,
#confirm-dialog {
background:
linear-gradient(180deg, rgba(14, 30, 54, 0.98), rgba(17, 29, 51, 0.98)),
url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
border-color: rgba(43, 138, 255, 0.4);
}
#loading .logo,
#loading .logo-inverted,
#sidebar .logo,
#sidebar .logo-inverted {
display: none !important;
}
#loading-status-container,
#sidebar .logo-container {
position: relative;
}
#loading-status-container::before {
content: "";
display: block;
width: 96px;
height: 96px;
margin: 0 auto 16px;
background: url("/packages/thelounge-theme-flowercore/bluejay-logo.svg") center / contain no-repeat;
filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.45));
}
#loading-page-message::before {
content: "FlowerCore IRC";
display: block;
margin-bottom: 10px;
color: #ffffff;
font-size: 30px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
#loading-page-message::after {
content: "Blue Jay web chat for iamworkin.lan";
display: block;
margin-top: 10px;
color: var(--fc-text-muted);
font-size: 14px;
letter-spacing: 0.08em;
text-transform: uppercase;
}
#loading-page-message {
color: var(--fc-text);
font-size: 15px;
line-height: 1.7;
max-width: 30rem;
text-align: center;
}
#sidebar {
background:
linear-gradient(180deg, rgba(10, 22, 40, 0.98), rgba(17, 29, 51, 0.98)),
url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
border-right: 1px solid var(--fc-border);
color: #d5e3f5;
}
#sidebar .logo-container {
padding: 20px 14px 8px;
}
#sidebar .logo-container::before {
content: "";
display: block;
width: 72px;
height: 72px;
margin: 0 auto 10px;
background: url("/packages/thelounge-theme-flowercore/bluejay-logo.svg") center / contain no-repeat;
filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.35));
}
#sidebar .logo-container::after {
content: "FlowerCore IRC";
display: block;
color: #ffffff;
font-size: 18px;
font-weight: 700;
letter-spacing: 0.06em;
text-align: center;
text-transform: uppercase;
}
#sidebar .network {
margin-bottom: 16px;
}
.channel-list-item,
#footer button {
border-radius: 10px;
transition: background-color 0.2s ease, box-shadow 0.2s ease, color 0.2s ease;
}
.channel-list-item:hover,
#footer button:hover {
background: rgba(43, 138, 255, 0.12);
color: #ffffff;
}
.channel-list-item.active,
#footer button.active {
background: linear-gradient(90deg, rgba(43, 138, 255, 0.24), rgba(22, 40, 68, 0.92));
box-shadow: inset 3px 0 0 var(--fc-gold);
color: #ffffff;
}
.channel-list-item[data-type="lobby"] {
color: #8bc3ff;
}
.channel-list-item .badge {
background: rgba(255, 255, 255, 0.08);
color: var(--fc-text-muted);
}
.channel-list-item .badge.highlight {
background: var(--fc-gold);
color: #08111e;
font-weight: 700;
}
#footer {
background: rgba(10, 22, 40, 0.92);
border-top: 1px solid var(--fc-border);
}
#viewport .lt,
#viewport .rt,
#chat button.close,
#chat button.menu,
#chat button.mentions,
#chat button.search,
#form #submit,
#form #upload,
.password-container .reveal-password span {
color: #8bc3ff;
}
#viewport .lt:hover,
#viewport .rt:hover,
#chat button.close:hover,
#chat button.menu:hover,
#chat button.mentions:hover,
#chat button.search:hover,
#form #submit:hover,
#form #upload:hover,
.password-container .reveal-password span:hover {
background: rgba(43, 138, 255, 0.16);
border-radius: 8px;
color: #ffffff;
opacity: 1;
}
#chat .header {
background: linear-gradient(135deg, #0e1e36, #1a3a6a, #2b8aff);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
color: #ffffff;
}
.header .title,
.header .topic,
#chat .header button {
color: #ffffff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
}
#chat .messages {
background:
linear-gradient(180deg, rgba(17, 29, 51, 0.98), rgba(11, 21, 39, 0.98)),
radial-gradient(circle at top right, rgba(43, 138, 255, 0.08), transparent 35%);
}
#chat .msg {
border-radius: 6px;
transition: background-color 0.2s ease;
}
#chat .msg:hover {
background: rgba(255, 255, 255, 0.03);
}
#chat .chat-view[data-type="channel"] .msg.highlight,
.mentions-popup .msg .content {
background: linear-gradient(90deg, rgba(43, 138, 255, 0.16), rgba(255, 179, 0, 0.1));
box-shadow: inset 3px 0 0 var(--fc-gold);
}
#chat .msg-statusmsg {
background: rgba(255, 179, 0, 0.18);
color: #ffe4a0;
}
#chat .msg[data-type="monospace_block"] .text {
background: rgba(8, 17, 30, 0.94);
border: 1px solid rgba(43, 138, 255, 0.22);
border-radius: 10px;
box-shadow: inset 3px 0 0 var(--fc-gold);
color: var(--fc-text);
display: inline-block;
line-height: 1.7;
max-width: min(100%, 44rem);
padding: 10px 12px;
white-space: pre-wrap;
}
#chat .msg[data-command="motd"] .text {
background:
linear-gradient(180deg, rgba(14, 30, 54, 0.96), rgba(8, 17, 30, 0.96)),
url("/packages/thelounge-theme-flowercore/bluejay-bg.svg");
border-color: rgba(255, 179, 0, 0.28);
color: #f4f8ff;
}
#chat .msg[data-command="motd"] .from {
color: #9dd3ff;
font-weight: 700;
}
#chat .msg[data-command="motd"] a {
color: #8bc3ff;
font-weight: 700;
}
#chat .userlist,
#form,
.mentions-popup,
.textcomplete-menu,
#context-menu {
background: rgba(17, 29, 51, 0.98);
border-color: var(--fc-border);
}
#chat .userlist .count,
#chat .user-mode:before {
background: rgba(8, 17, 30, 0.95);
}
.input,
#connect input,
#connect select,
#settings input,
#settings select,
#settings textarea,
#form #nick,
#chat .userlist .search,
form.message-search input,
.jump-to-input .input,
.password-container input {
background: rgba(8, 17, 30, 0.72);
border: 1px solid var(--fc-border);
border-radius: 10px;
color: var(--fc-text);
}
#form {
border-top: 1px solid var(--fc-border);
padding: 8px;
}
#form #nick {
color: #8bc3ff;
line-height: 28px;
}
#form #input {
margin: 0 8px;
min-height: 36px;
padding: 8px 10px;
}
::placeholder,
.jump-to-input .input::placeholder,
form.message-search input::placeholder {
color: rgba(232, 237, 245, 0.45);
}
.jump-to-input:before,
#chat .count:before {
color: rgba(232, 237, 245, 0.45);
}
.btn {
background: linear-gradient(180deg, rgba(24, 62, 112, 0.28), rgba(14, 30, 54, 0.38));
border-color: var(--fc-accent);
border-radius: 999px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
color: var(--fc-text);
letter-spacing: 0.12em;
}
.btn:hover,
.btn:focus,
.btn:disabled {
background: linear-gradient(180deg, #2b8aff, #1c6fe3);
color: #ffffff;
}
.btn:active,
.btn:focus,
.input:focus {
box-shadow: 0 0 0 3px rgba(43, 138, 255, 0.26);
}
#version-checker,
#settings .settings-sync-panel,
#connect .connect-sasl-external {
background: rgba(8, 17, 30, 0.78);
border: 1px solid var(--fc-border);
color: var(--fc-text);
}
#version-checker.loading {
border-left: 3px solid var(--fc-accent);
color: #9dd3ff;
}
#version-checker.up-to-date {
border-left: 3px solid var(--fc-success);
color: #87e9a9;
}
#version-checker.new-packages,
#version-checker.new-version {
border-left: 3px solid var(--fc-gold);
color: #ffe4a0;
}
#version-checker.error,
#settings .error,
#sign-in .error {
border-left: 3px solid var(--fc-danger);
color: #ffb3b3;
}
#upload-progressbar {
box-shadow: 0 0 14px rgba(255, 179, 0, 0.75);
}
::-webkit-scrollbar:hover {
background-color: rgba(255, 255, 255, 0.04);
}
::-webkit-scrollbar-thumb:vertical {
background: linear-gradient(180deg, rgba(43, 138, 255, 0.72), rgba(30, 58, 95, 0.95));
}
::-webkit-scrollbar-thumb:vertical:active {
background: linear-gradient(180deg, rgba(255, 179, 0, 0.85), rgba(43, 138, 255, 0.9));
}
@media (max-width: 768px) {
#sidebar {
box-shadow: 0 0 28px rgba(0, 0, 0, 0.5);
}
#chat .header {
padding-right: 6px;
}
}
bluejay-logo.svg: |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
<ellipse cx="14" cy="18" rx="10" ry="9" fill="#3B7BC0"/>
<ellipse cx="12" cy="20" rx="6" ry="6" fill="#C8DFFF"/>
<path d="M10 14 C8 10, 16 8, 22 12 C24 14, 22 18, 18 18 C14 18, 10 16, 10 14Z" fill="#2B5A8A"/>
<path d="M12 12.5 L21 11" stroke="#fff" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
<circle cx="22" cy="11" r="6" fill="#3B7BC0"/>
<path d="M20 6 L22 1 L24 3 L22 7Z" fill="#2B5A8A"/>
<path d="M21 5 L22.5 2 L23.5 4Z" fill="#5BA3FF"/>
<path d="M16 14 C18 16, 22 16, 26 14" stroke="#0d1520" stroke-width="1.8" fill="none" stroke-linecap="round"/>
<circle cx="24" cy="10" r="2" fill="#fff"/>
<circle cx="24.5" cy="9.5" r="1" fill="#0d1520"/>
<path d="M27 11 L31 11.5 L27 13Z" fill="#364b6b"/>
<path d="M4 16 L1 13 L3 17 L1 20 L5 18Z" fill="#2B5A8A"/>
<circle cx="10" cy="23" r="2" fill="#FFB300"/>
<circle cx="10" cy="23" r="1" fill="#FFCA40"/>
</svg>
bluejay-bg.svg: |
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<defs>
<pattern id="hex" x="0" y="0" width="60" height="52" patternUnits="userSpaceOnUse">
<path d="M30 0 L60 15 L60 37 L30 52 L0 37 L0 15 Z" fill="none" stroke="rgba(43,138,255,0.04)" stroke-width="0.5"/>
</pattern>
</defs>
<rect width="200" height="200" fill="url(#hex)"/>
<circle cx="30" cy="0" r="1" fill="rgba(255,179,0,0.06)"/>
<circle cx="90" cy="0" r="1" fill="rgba(255,179,0,0.06)"/>
<circle cx="150" cy="0" r="1" fill="rgba(255,179,0,0.06)"/>
<circle cx="0" cy="52" r="1" fill="rgba(43,138,255,0.06)"/>
<circle cx="60" cy="52" r="1" fill="rgba(43,138,255,0.06)"/>
<circle cx="120" cy="52" r="1" fill="rgba(43,138,255,0.06)"/>
<circle cx="180" cy="52" r="1" fill="rgba(43,138,255,0.06)"/>
</svg>
---
# 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;
}
}
ircd.motd: |
- BlueJay IRC -
Welcome to BlueJayIRC on iamworkin.lan.
Web IRC: https://webirc.iamworkin.lan
Channels: #general, #ops, #alerts
Keep it keyboard-first, practical, and kind.
Managed from bluejay-infra via ArgoCD.
---
# 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.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
strategy:
type: Recreate
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-config-template
mountPath: /app/conf/ircd.motd
subPath: ircd.motd
readOnly: true
- 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
---
# The Lounge web IRC Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: thelounge
namespace: irc
labels:
app: thelounge
spec:
replicas: 1
selector:
matchLabels:
app: thelounge
template:
metadata:
labels:
app: thelounge
spec:
initContainers:
- name: install-flowercore-theme
image: ghcr.io/thelounge/thelounge:4.4.3
command:
- sh
- -lc
- |
set -eu
THELOUNGE_HOME=/var/opt/thelounge thelounge install file:/flowercore-theme
volumeMounts:
- name: thelounge-config
mountPath: /var/opt/thelounge/config.js
subPath: config.js
- name: thelounge-packages
mountPath: /var/opt/thelounge/packages
- name: thelounge-flowercore-theme
mountPath: /flowercore-theme
containers:
- name: thelounge
image: ghcr.io/thelounge/thelounge:4.4.3
ports:
- containerPort: 9000
name: http
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 30
periodSeconds: 20
resources:
requests:
memory: 64Mi
cpu: 50m
limits:
memory: 256Mi
cpu: 250m
volumeMounts:
- name: thelounge-config
mountPath: /var/opt/thelounge/config.js
subPath: config.js
- name: thelounge-packages
mountPath: /var/opt/thelounge/packages
volumes:
- name: thelounge-config
configMap:
name: thelounge-config
- name: thelounge-packages
emptyDir: {}
- name: thelounge-flowercore-theme
configMap:
name: thelounge-flowercore-theme
---
# 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
---
# The Lounge web IRC Service
apiVersion: v1
kind: Service
metadata:
name: thelounge
namespace: irc
spec:
selector:
app: thelounge
ports:
- port: 9000
targetPort: 9000
name: http
---
# 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
---
# Traefik IngressRoute - The Lounge web IRC
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: webirc
namespace: irc
spec:
entryPoints:
- websecure
routes:
- match: Host(`webirc.iamworkin.lan`)
kind: Rule
services:
- name: thelounge
port: 9000
tls:
secretName: webirc-tls