~singpolyma/cheogram-muc-bridge

28984fba48b1262131298c59ae8af9be646b8a29 — Stephen Paul Weber 10 months ago 3cfb36f + c82e0ee
Merge branch 'improve-readme' of https://tildegit.org/southerntofu/cheogram-muc-bridge

* 'improve-readme' of https://tildegit.org/southerntofu/cheogram-muc-bridge:
  Improve README, update example config, and include SQL schema
4 files changed, 191 insertions(+), 7 deletions(-)

D README
A README.md
M config.dhall.example
A schema.sqlite
D README => README +0 -3
@@ 1,3 0,0 @@
This is a "double-puppeting" bridge between two XMPP MUCs.

That sounds useless, but maybe not if one of the MUCs is actually a gateway to another protocol, like Biboumi.
\ No newline at end of file

A README.md => README.md +186 -0
@@ 0,0 1,186 @@
# cheogram-muc-bridge

This is a "double-puppeting" bridge between two XMPP MUCs, which means it keeps userlist in sync across a certain number of MUC chatrooms, and enables private messages across MUC servers. As XMPP enables to join MUC servers effortlessly, that may sound pretty useless. However, if one of those MUCs bridged is actually a gateway/transport to another protocol (like [biboumi](https://biboumi.louiz.org/) for IRC), you may find it very useful.

cheogram-muc-bridge is currently alpha-quality software. Please try it out and contribute feedback and patches. See the list of [Known issues](#known-issues-todo), [Getting help](#getting-help), and [Contributing](#contributing) section.

## Setup

### Setup from packages

cheogram-muc-bridge is currently not packaged for any distribution.

Alternatively, if you have a server with root access, and no web/XMPP services configured, you can setup cheogram-muc-bridge using [ansible-selfhosted](https://codeberg.org/southerntofu/ansible-selfhosted/) recipes (alpha software), like so:

```
# apt install ansible
# git clone https://codeberg.org/southerntofu/ansible-selfhosted infra
# cat << EOF > infra/config.yml
hostname: "example.com"
services: [ "mucbridge" ]
mucbridge:
  nick: "my-own-bridge"
  host: "bridge.example.com"
  chans: # List of JIDs to bridge together
    - [ { jid: "#foo@biboumi.example.net", tag: "net", nickChars: "Some \"a-z[]A-Z\"" }, { jid: "foo@muc.example.ml", tag: "ml", nickChars: "None Text" } ]
EOF
# cd infra
# roles/deploy.sh mucbridge
```

This assumes DNS records are already pointing to your server for `example.com` and `bridge.example.com`, and you don't have a firewall running.

### Setup manually from sources

#### Step 1: Install cheogram-muc-bridge

You need to have `cabal` (the Haskell build system) installed, as well as `ghc` and `c2hs`. In addition, you need the `libxml2`, `gnutls`, `zlib` and `gsasl` libraries installed. On debian, you can set it all up from the following packages: `cabal-install ghc libxml2-dev libgsasl7-dev gnutls-dev c2hs zlib1g-dev`

Download the sources, build with cabal (Haskell build system). The built program will be in `dist/build/gateway/gateway`:

```
$ git clone https://git.singpolyma.net/cheogram-muc-bridge
$ cd cheogram-muc-bridge
$ cabal update # Refresh packages list
$ cabal install --only-dependencies
$ cabal configure && cabal build
```

The built program, located in `dist/build/gateway/gateway` can then be copied to wherever you desire on your system, for example `/usr/local/bin/cheogram-muc-bridge`.

#### Step 2: Initialize the database

Now you can initialize the SQLite database using the sqlite3 CLI interface (`sqlite3` package in Debian):

```
$ sqlite3 sqlite.db < schema.sqlite
```

**ATTENTION:** In addition to having write permissions to the file, SQLite expects to have write permissions on the parent folder. So you need to make sure the parent folder is readable by cheogram-muc-bridge as well! Otherwise, you may spot `SQLite3 returned ErrorReadOnly while attempting to perform step: attempt to write a readonly database` despite the SQLite DB having correct permissions.

#### Step 3: Make a new domain name

cheogram-muc-bridge requires its own XMPP domain to operate. If you have no other XMPP virtual hosts or component on a specific domain, you may reuse it to host cheogram-muc-bridge. Otherwise, you will need to add a new record pointing to the server where your Jabber/XMPP server is hosted.

**Note:** It is assumed cheogram-muc-bridge will run on the same machine as your Jabber/XMPP server, and connection can be established via localhost. Setting up the two services on different machines is beyond the scope of this document.

#### Step 4: Register as component to your Jabber/XMPP server

Now, you need to generate a passphrase which will be a shared secret between cheogram-muc-bridge and your Jabber/XMPP server, for example using `cat /dev/urandom | tr -dc 'A-Za-z0-9@#$%&_+=' | head -c 64`. This secret will be used in the configuration file, as explained in the [Configuration](#configuration) section. Now, how to configure the component depends on the server you use. In both cases, you need a valid TLS certificate for the component domain you intend to use.

**ATTENTION:** In addition to the configuration provided here, you need to make sure that cheogram-muc-bridge is reachable via your Jabber/XMPP server using a valid TLS certificate! How to setup a TLS certificate is beyond the scope of this document, as it is usually a requirement to operate your Jabber/XMPP server.

##### prosody

```
Component "mucbridge.example.com"
  component_secret = "SECRET"

  ssl = {
    certificate = "/etc/letsencrypt/live/mucbridge.example.com/fullchain.pem",
    key = "/etc/letsencrypt/live/mucbridge.example.com/privkey.pem"
  }
```

##### ejabberd

In the section that says: '{listen,' add those two lines:

```
    {5347, ejabberd_service, [{host, "mucbridge.example.com",
                               [{password, "SECRET"}]}]},
```

**TODO:** That is according to a tutorial found on ejabberd.im, but it does not mention TLS support! Please someone who knows ejabberd well propose better guidance.

#### Step 5: Configure

Configuration is explained in the [Configuration](#configuration) section of this README. You can find an example config file [here](config.dhall.example).

#### Step 6: Run

Now, you should be able to run `cheogram-muc-bridge /path/to/config.dhall`. If you would like to deploy a service to make sure cheogram bridge is started when your machine boots, the instructions depend on your init system. In the examples below, we assume:

- the program is located in `/usr/local/bin/cheogram-muc-bridge`
- the configuration file is `/etc/cheogram/muc-bridge.dhall`
- cheogram-muc-bridge should run as user/group `cheogram/cheogram`

##### systemd

The systemd service file should be placed in `/etc/systemd/system/cheogram-muc-bridge.service`:

```
[Unit]
Description=cheogram-muc-bridge MUC2MUC bridge
After=network.target

[Service]
ExecStart=/usr/local/bin/cheogram-muc-bridge /etc/cheogram/muc-bridge.dhall
User=cheogram
Group=cheogram
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
```

Every time it is modified (or the first time it is created), you need to run `systemctl daemon-reload`. Then, you can make sure the service is enabled at boot time with `systemctl enable cheogram-muc-bridge` and/or start it immediately with `systemctl start cheogram-muc-bridge`.

## Configuration

You can find an example configuration file in the [config.dhall.example](config.dhall.example) file. It can have the following options:

- nick: the nickname the bridge should adopt when joining channels
- componentJid: the domain name name where cheogram-muc-bridge should be reachable; requires a valid TLS certificate as well as valid Jabber/XMPP server configuration, as explained in the [Setup](#setup) section
- secret: the secret key used to authenticate cheogram-muc-bridge as component to identify against the Jabber/XMPP server
- server: a dictionary with the options to connect to the Jabber/XMPP server
  - host: the domain name or IP address of the server
  - port: the port number the server is running on
- db: the path to the SQLite database, which must be initialized as explained in the [Setup](#setup) section
- mucs: a list of different channel groups, each containing containing a list of individual MUC chatrooms to bridge together, each with the following settings
  - jid: the JID address to reach the room
  - tag: the suffix to append to members of this room, automatically wrapped inside `[]` if the nickChars allows it on the other sides; illegal characters are replaced with `_`
  - nickChars: the explicit list of characters to allow as nicknames on this chatroom (usually `Some "a-zA-Z[]"` for IRC); `None Text` does not place any special restrictions

## Known issues (TODO)

cheogram-muc-bridge is alpha-quality software. Just because we haven't found a bug yet doesn't mean it's not there, so don't hesitate to report bugs so they can be fixed. Below, you will find a list of issues we are aware of:

- in some circumstances, individual chatroom members may be bridged several times back and forth, resulting in nicknames like `user[foo][bar][foo]`; this bug is currently being investigated (July 2021)
- the SQLite database is not automatically created or initialized, so make sure to follow the setup guide provided in this README
- when joining a [biboumi IRC gateway](https://biboumi.louiz.org/) chatroom where the nick is already taken, no error is provided ([upstream ticket](https://lab.louiz.org/louiz/biboumi/-/issues/3458))
- when the `tag` on a chan is deemed illegal by another chan's `nickChars`, no warning is currently produced
- `nickChars = None Text` does not enforce that [illegal JID characters](https://datatracker.ietf.org/doc/html/rfc7622#section-3.3.1) are prevented; in the future, such characters should be properly escaped as explained in [XEP-0106](https://xmpp.org/extensions/xep-0106.html) or entirely forbidden
- `nickChars = "a-z[]A-Z"` is too restrictive, and we should promote a better configuration; in particular, it prevents numbers in usernames, which are not uncommon; more broadly, it may be interesting when connecting through a biboumi gateway, to determine allowed characters from the IRC network's charset

## Getting Help

If you have any questions about this project, or wish to report a bug, please send email to: dev@singpolyma.net

## Contributing

If you have code or patches you wish to contribute, the maintainer's preferred mechanism is a git pull request. Push your changes to a git repository somewhere, for example:

```
git remote rename origin upstream
git remote add origin git@git.sr.ht:~yourname/cidfd
git push -u origin master
```

Then generate the pull request:

```
git fetch upstream master
git request-pull -p upstream/master origin
```

And copy-paste the result into a plain-text email to: dev@singpolyma.net

You may alternately use a patch-based approach as described on https://git-send-email.io

Contributions follow an inbound=outbound model -- you (or your employer) keep all copyright on your patches, but agree to license them according to this project's COPYING file.

## License

This program is released under the AGPLv3 license. You can find a copy in the [COPYING](COPYING) file.

M config.dhall.example => config.dhall.example +4 -4
@@ 1,7 1,7 @@
{
	nick = "cheogram",
	componentJid = "component.localhost",
	secret = "secret",
	nick = "cheogram-muc-bridge",
	componentJid = "bridge.example.com",
	secret = "",
	server = {
		host = "localhost",
		port = 5347


@@ 13,4 13,4 @@
			{ jid = "second@muc", tag = "second", nickChars = None Text, nickLength = None Natural }
		]
	]
}
\ No newline at end of file
}

A schema.sqlite => schema.sqlite +1 -0
@@ 0,0 1,1 @@
CREATE TABLE sessions (source_muc text not null, source_nick text not null collate nocase, target_muc text not null, target_nick text not null collate nocase, version integer, primary key (source_muc, source_nick, target_muc, target_nick));