Same CoreDNS iamworkin.lan template + ndots:5 hijack as the irc-notify fix.
Anope services (nickserv/chanserv/memo) have been disconnected from unrealircd
for weeks ("Host is unreachable" every 3s). Thelounge server defaults pointed
at the same broken FQDN.
Short name unrealircd.irc.svc resolves to the ClusterIP directly.
1340 lines
32 KiB
YAML
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",
|
|
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"
|
|
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
|