How To Setup Your Own IRC Network

From Alpine Linux
This material is proposed for deletion ...

Charybdis is no longer maintained since 2021; All development efforts have moved to solanum instead. (Discuss)
Make sure no other pages link here and check the page's history before deleting.

This doc aims to assist you on setup your own irc network with Alpine Linux.

We will configure two irc daemons and a simple ajax webirc client.

The irc daemons will work together sharing the channel, users and other informations.

From charybdis point of view, this configuration is called "cluster", but this word should not be understood with the common meaning of "cluster".

We assume that, as example, we want create Alpine Linux IRC Network.

We have irc1 and irc2 servers, called respectively irc1.alpinelab.lan and irc2.alpinelab.lan.

Prerequisites

Prerequisites are two PC with Alpine Linux installed (v2.5).

Packages that we are going to install are:

  • Charybdis
  • atheme-iris

Both of those packages are in edge. Charybdis is in main, atheme-iris in testing.

You can easily use this pinning edge repo:

vi /etc/apk/repositories

Add the line:

@edgem https://dl-cdn.alpinelinux.org/alpine/edge/main/

@edget https://dl-cdn.alpinelinux.org/alpine/edge/testing/

Then:

apk update

apk add charybdis@edgem

apk add atheme-iris@edget

Configure Charybdis

cp /etc/charybdis/example.conf /etc/charybdis/ircd.conf

vi /etc/charybdis/ircd.conf

Modify the file starting from /etc/charybdis/reference.conf (that is well documented).

loadmodule "extensions/chm_operonly.so";
loadmodule "extensions/extb_account.so";
loadmodule "extensions/extb_canjoin.so";
loadmodule "extensions/extb_channel.so";
loadmodule "extensions/extb_extgecos.so";
loadmodule "extensions/extb_oper.so";
loadmodule "extensions/extb_realname.so";
loadmodule "extensions/m_identify.so";
loadmodule "extensions/m_mkpasswd.so";
loadmodule "extensions/m_webirc.so";
loadmodule "extensions/sno_farconnect.so";
loadmodule "extensions/sno_globalkline.so";
loadmodule "extensions/sno_globaloper.so";

serverinfo {
	name = "irc1.alpinelab.lan";
	sid = "01A";
        description = "Alpine Linux IRC Server";
	network_name = "Alpine Linux Network";
	network_desc = "Alpine Linux IRC network.";
	hub = yes;
	default_max_clients = 10000;
	nicklen = 30;
};

admin {
	name = "admin";
	description = "Alpine Linux IRC network administrator";
	email = "ircadmin@alpinelab.lan";
};

log {
	fname_userlog = "logs/userlog";
	#fname_fuserlog = "logs/fuserlog";
	fname_operlog = "logs/operlog";
	#fname_foperlog = "logs/foperlog";
	fname_serverlog = "logs/serverlog";
	#fname_klinelog = "logs/klinelog";
	fname_killlog = "logs/killlog";
	fname_operspylog = "logs/operspylog";
	#fname_ioerrorlog = "logs/ioerror";
};

class "users" {
	ping_time = 2 minutes;
	number_per_ident = 10;
	number_per_ip = 10;
	number_per_ip_global = 50;
	cidr_ipv4_bitlen = 24;
	cidr_ipv6_bitlen = 64;
	number_per_cidr = 200;
	max_number = 100;
	sendq = 400 kbytes;
};

class "opers" {
	ping_time = 5 minutes;
	number_per_ip = 10;
	max_number = 1000;
	sendq = 1 megabyte;
};

class "server" {
	ping_time = 5 minutes;
	connectfreq = 5 minutes;
	max_number = 10;
	sendq = 4 megabytes;
};

listen {
	defer_accept = yes;
	port = 5000, 6665 .. 6669;
	sslport = 6697;
};

auth {
	user = "*@*";
	class = "users";
};

privset "local_op" {
	privs = oper:local_kill, oper:operwall;
};

privset "server_bot" {
	extends = "local_op";
	privs = oper:kline, oper:remoteban, snomask:nick_changes;
};

privset "global_op" {
	extends = "local_op";
	privs = oper:global_kill, oper:routing, oper:kline, oper:unkline, oper:xline,
		oper:resv, oper:mass_notice, oper:remoteban;
};

privset "admin" {
	extends = "global_op";
	privs = oper:admin, oper:die, oper:rehash, oper:spy;
};

operator "ircadmin" {
	user = "*@*";
	password = "MyStrongPassword";
	snomask = "+Zbfkrsuy";
	flags = ~encrypted;
	privset = "admin";
};

connect "irc2.alpinelab.lan" {
	host="10.0.2.10";
        send_password = "Password_To_Server";
	accept_password = "Password_From_Server"; 
	port = 6666;
	hub_mask = "*";
	class = "server";
	flags = compressed, topicburst, autoconn;
};

service {
	name = "services.int";
};

cluster {
	name = "*.alpinelab.lan";
	flags = kline, tkline, unkline, xline, txline, unxline, resv, tresv, unresv;
};

shared {
	oper = "*@*", "*";
	flags = all, rehash;
};

channel {
	use_invex = yes;
	use_except = yes;
	use_forward = yes;
	use_knock = yes;
	knock_delay = 5 minutes;
	knock_delay_channel = 1 minute;
	max_chans_per_user = 15;
	max_bans = 100;
	max_bans_large = 500;
	default_split_user_count = 0;
	default_split_server_count = 0;
	no_create_on_split = no;
	no_join_on_split = no;
	burst_topicwho = yes;
	kick_on_split_riding = no;
	only_ascii_channels = no;
	resv_forcepart = yes;
	channel_target_change = yes;
	disable_local_channels = no;
};

serverhide {
	flatten_links = yes;
	links_delay = 5 minutes;
	hidden = no;
	disable_hidden = no;
};

blacklist {
	host = "rbl.efnetrbl.org";
	type = ipv4;
	reject_reason = "${nick}, your IP (${ip}) is listed in EFnet's RBL. For assistance, see https://efnetrbl.org/?i=${ip}";

	/* Example of a blacklist that supports both IPv4 and IPv6 */
};

alias "NickServ" {
	target = "NickServ";
};

alias "ChanServ" {
	target = "ChanServ";
};

alias "OperServ" {
	target = "OperServ";
};

alias "MemoServ" {
	target = "MemoServ";
};

alias "NS" {
	target = "NickServ";
};

alias "CS" {
	target = "ChanServ";
};

alias "OS" {
	target = "OperServ";
};

alias "MS" {
	target = "MemoServ";
};

general {
	hide_error_messages = opers;
	hide_spoof_ips = yes;
	default_umodes = "+i";
	default_operstring = "is an IRC Operator";
	default_adminstring = "is a Server Administrator";
	servicestring = "is a Network Service";
	disable_fake_channels = no;
	tkline_expire_notices = no;
	default_floodcount = 10;
	failed_oper_notice = yes;
	dots_in_ident=2;
	min_nonwildcard = 4;
	min_nonwildcard_simple = 3;
	max_accept = 100;
	max_monitor = 100;
	anti_nick_flood = yes;
	max_nick_time = 20 seconds;
	max_nick_changes = 5;
	anti_spam_exit_message_time = 5 minutes;
	ts_warn_delta = 30 seconds;
	ts_max_delta = 5 minutes;
	client_exit = yes;
	collision_fnc = yes;
	resv_fnc = yes;
	global_snotices = yes;
	dline_with_reason = yes;
	kline_delay = 0 seconds;
	kline_with_reason = yes;
	kline_reason = "K-Lined";
	identify_service = "NickServ@services.int";
	identify_command = "IDENTIFY";
	non_redundant_klines = yes;
	warn_no_nline = yes;
	use_propagated_bans = yes;
	stats_e_disabled = no;
	stats_c_oper_only=no;
	stats_h_oper_only=no;
	stats_y_oper_only=no;
	stats_o_oper_only=yes;
	stats_P_oper_only=no;
	stats_i_oper_only=masked;
	stats_k_oper_only=masked;
	map_oper_only = no;
	operspy_admin_only = no;
	operspy_dont_care_user_info = no;
	caller_id_wait = 1 minute;
	pace_wait_simple = 1 second;
	pace_wait = 10 seconds;
	short_motd = no;
	ping_cookie = no;
	connect_timeout = 30 seconds;
	default_ident_timeout = 5;
	disable_auth = no;
	no_oper_flood = yes;
	max_targets = 4;
	client_flood_max_lines = 20;
	use_whois_actually = no;
	oper_only_umodes = operwall, locops, servnotice;
	oper_umodes = locops, servnotice, operwall, wallop;
	oper_snomask = "+s";
	burst_away = yes;
	nick_delay = 0 seconds; # 15 minutes if you want to enable this
	reject_ban_time = 1 minute;
	reject_after_count = 3;
	reject_duration = 5 minutes;
	throttle_duration = 60;
	throttle_count = 4;
	max_ratelimit_tokens = 30;
	away_interval = 30;
};

modules {
	path = "/usr/lib/charybdis/modules";
	path = "/usr/lib/charybdis/modules/autoload";
};

Relevant part of the config file are:

serverinfo {
        .
        .
	sid = "01A"; <-----------   This must be unique. You can choose two cipher and one letter.
	hub = yes;   <-----------   This works as an hub. Allows other irc server to connects.
        .
        .
}

listen {
	port = 5000, 6665 .. 6669; <---- Port where charybdis is listening. You can also bind to a specific ip adding "host" directive. If not specifyied charybdis listen on all interfaces.
	sslport = 6697;            <---- Port for SSL connection. You need a certificate in order to use this feature.
};

operator "ircadmin" {
	user = "*@*";  <----------- This is a masq used to match who can become operator. This support CIDR. If you want to allows only 10.0.0.0/24, you can choose "*@10.0.0.*".
	password = "MyStrongPassword"; <---- Password used to become IRC Operator. 
	flags = ~encrypted;            <---- Tilde "~" means not. So the password used in this block is not encrypted. Without "~", you need to write the password in this block encrypted.
	privset = "admin";
};

connect "irc2.alpinelab.lan" {                <----------- Descriptive name of the server you want to connect to.
	host="10.0.2.10";                     <----------- IP or HOST. They MUST be valid. If hostname, it MUST be an A record.
	send_password = "Password_To_Server";     <------- Password you sent TO irc2. In irc2 this is "accept_password".
	accept_password = "Password_From_Server"; <------- Password you expect to receive FROM irc2. In irc2 this is "send_password"
	flags = compressed, topicburst, autoconn; <------- Autoconn means that irc1 will try automatically to connect to irc2.
};

cluster {
	name = "*.alpinelab.lan";             <----------- Masq to indicate what servers can share the information. Those information are written in the following "flags" entry.
	flags = kline, tkline, unkline, xline, txline, unxline, resv, tresv, unresv; <--- Check IRC documentation to understand the meaning of those flags. 
};

shared {
	oper = "*@*", "*";  <----------- The user@host and the server must be on in order to set klines.               
	flags = all,rehash; <----------- flags: list of what to allow them to place. All oper will receive this.
};

You can even remove the *Serv entries. Charybdis itselfs does not have this features, you should use externa programs that works as bot for that (atheme services for example).

In the other server, irc2, configuration is pretty similar.

Those are the only difference of /etc/charybdis/ircd.conf:

serverinfo {
	sid = "02A"; 
	hub = yes;   
}

operator "ircadmin" {
	user = "*@*";  
	password = "MyStrongPassword"; 
	flags = ~encrypted;            
	privset = "admin";
};

connect "irc1.alpinelab.lan" {                
	host="10.0.1.10";                     
	send_password = "Password_From_Server"; 
	accept_password = "Password_To_Server"; 
	flags = compressed, topicburst;
};

In flags directive, connect{} block, we do not set "autoconn". This means that irc1 will automatically connect to irc2, but not the contrary.

Charybdis has a lot of other cool features, like ssl connection, spam blacklisting and so on. Look at documentation here: [1]

After having modifyied the ircd.conf in both server, fix the permissions:

chown ircd /etc/charybdis/ircd.conf

chmod 400 /etc/charybdis/ircd.conf

Configure Atheme-iris

Atheme-iris is a nice webchat written in AJAX and Python. It's a fork of the famous qwebirc.

Configuration in pretty simple.

By default, atheme-iris will listen on all interfaces. If you want modify this behaviour, change /etc/conf.d/atheme-iris and set the IP address where atheme will bind.

[execution]
args: -n -p 3989
syslog_addr:
syslog_port: 514

[irc]
server: localhost
port: 6667
ssl: false
bind_ip: 127.0.0.1
realname: https://irc1.alpinelab.lan
ident: nick
ident_string: webchat
webirc_mode: webirc
webirc_password: fish

[athemeengine]
# Leave this unset to disable all Atheme integration.
xmlrpc_path:
chan_list_enabled: true
chan_list_max_age: 120
chan_list_count: 3


[feedbackengine]
from: moo@moo.com
to: moo@moo.com
smtp_host: 127.0.0.1
smtp_port: 25

[frontend]
base_url: <nowiki>http://irc1.alpinelab.lan:9090</nowiki<
network_name: AlpineLinux
app_title: %(network_name)s Web IRC
extra_html:
initial_nick:
prompt: true
chan_prompt: true
chan_autoconnect: true
static_base_url: /
dynamic_base_url: /

[atheme]
# Even if we don't use nickserv, disabling it cause atheme-iris to not work.
# Look at https://github.com/atheme/iris/issues/12
nickserv_login: true
chan_list_on_start: true
chan_list_cloud_view: false

[ui]
dedicated_msg_window: false
dedicated_notice_window: false
hide_joinparts: false
simple_color: false
fg_color: DDDDDD
fg_sec_color: 999999
bg_color: 111111
lastpos_line: true
nick_click_query: false
nick_colors: false
nick_status: false
flash_on_mention: false
beep_on_mention: false

[adminengine]
hosts: 127.0.0.1

[proxy]
forwarded_for_header:
forwarded_for_ips: 127.0.0.1

[tuneback]
update_freq: 0.5
maxbuflen: 100000
maxsubscriptions: 1
maxlinelen: 600
dns_timeout: 5
http_ajax_request_timeout: 30
http_request_timeout: 5

In the [execution] block, parameters are overridden by /etc/conf.d/atheme-iris settings.

Replicate the same configuration in irc2.alpinelab.lan.

But in irc2 server, change the entry irc1.alpinelab.lan with irc2.alpinelab.lan.

Atheme-iris will connect to charybdis on 127.0.0.1 ip, according with this directive:

server: localhost

That's all.

Now, you can go with one browser to http://irc1.alpinelab.lan:9090 and another in http://irc2.alpinelab.lan:9090.

Login with two different users in the same channel.

You should view both users on both webclients.

Happy chatting.


Note:

When you login into webchat, you will see "webchat@127.0.0.1".

If you're wondering how change 127.0.0.1 with a spoofed address, you need another auth{} block in charybdis. Look at /etc/charybdis/reference.conf for details.

If you want the real ip address of the client, you need to setup cgi:irc with atheme-iris, and Charybdis will use the module called _mwebirc to "glue" himself with atheme-iris. Cgiirc is available in edge/testing repository.


If you want to remove the button "Menu" which appears in the top-left position into webchat, then:

vim /var/lib/atheme-iris/css/qui.mcss

Look for .dropdown-tab and then add "display: none;" as follows:

.qwebirc-qui .outertabbar .dropdown-tab {
  float: left;
  width: 24px;
  cursor: pointer;
  cursor: hand;
  display: none; <---- This is what you need to add.
}

Enjoy!

Useful links

IRC Commands

https://en.wikipedia.org/wiki/List_of_Internet_Relay_Chat_commands

*-Line flags

https://en.wikipedia.org/wiki/IRCd#K-line