Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: SSL on IRC connection #629

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions zulip/integrations/bridge_with_irc/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# IRC <--> Zulip bridge

For a how-to guide, please see:

https://zulipchat.com/integrations/doc/irc

## SSL warning

It can be tricky to get SSL to work, but if you are not using it, everyone's
chats may be leaking through the bridge for any MitM attack.

## Adding SSL certificates for IRC servers

This section could need a bit of additional experience and elaboration.

If your IRC server uses a CA that your system doesn't trust or some other
mechanism of self-signing, please consider adding that as a basic CA in your
system-wide platform. We have not written command-line options for storing
and trusting extra certificates for the bridge.

## Usage

```
Expand All @@ -9,6 +27,7 @@
`--stream` is a Zulip stream.
`--topic` is a Zulip topic, is optionally specified, defaults to "IRC".
`--nickserv-pw` is the IRC nick password.
`--no-ssl` leaks everything in free text

IMPORTANT: Make sure the bot is subscribed to the relevant Zulip stream!!

Expand Down
7 changes: 6 additions & 1 deletion zulip/integrations/bridge_with_irc/irc-mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
argparse.ArgumentParser(usage=usage), allow_provisioning=True
)
parser.add_argument("--irc-server", default=None)
parser.add_argument("--port", default=6667)
parser.add_argument("--port", default=6697)
parser.add_argument("--nick-prefix", default=None)
parser.add_argument("--channel", default=None)
parser.add_argument("--no-ssl", default=False)
parser.add_argument("--stream", default="general")
parser.add_argument("--topic", default="IRC")
parser.add_argument("--nickserv-pw", default="")
Expand All @@ -54,6 +55,9 @@
if options.irc_server is None or options.nick_prefix is None or options.channel is None:
parser.error("Missing required argument")

if options.no_ssl:
print("You are not using SSL.")

nickname = options.nick_prefix + "_zulip"
bot = IRCBot(
zulip_client,
Expand All @@ -64,5 +68,6 @@
options.irc_server,
options.nickserv_pw,
options.port,
use_ssl=not options.no_ssl,
)
bot.start()
39 changes: 36 additions & 3 deletions zulip/integrations/bridge_with_irc/irc_mirror_backend.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import multiprocessing as mp
import ssl
from typing import Any, Dict

import irc.bot
import irc.strings
import irc.connection
from irc import schedule
from irc.client import Event, ServerConnection, ip_numstr_to_quad
from irc.client_aio import AioReactor


class AioReactorWithScheduler(AioReactor):
scheduler_class = schedule.DefaultScheduler

def __init__(self, *args: Any, **kwargs: Any) -> None:
super(AioReactorWithScheduler, self).__init__()
scheduler = self.scheduler_class()
assert isinstance(scheduler, schedule.IScheduler)
self.scheduler = scheduler


class IRCBot(irc.bot.SingleServerIRCBot):
reactor_class = AioReactor
reactor_class = AioReactorWithScheduler

def __init__(
self,
Expand All @@ -20,17 +32,37 @@ def __init__(
server: str,
nickserv_password: str = "",
port: int = 6667,
use_ssl: bool = True,
ssl_connection_factory: irc.connection.Factory = None,
) -> None:
self.channel = channel # type: irc.bot.Channel
self.zulip_client = zulip_client
self.stream = stream
self.topic = topic
self.IRC_DOMAIN = server
self.nickserv_password = nickserv_password

# Use SSL for IRC server
self.use_ssl = use_ssl
if use_ssl:
if ssl_connection_factory:
self.connection_factory = ssl_connection_factory
else:
self.connection_factory = irc.connection.AioFactory(
ssl=ssl.create_default_context()
)
else:
self.connection_factory = irc.connection.AioFactory()

connect_params = {}
connect_params["connect_factory"] = self.connection_factory

# Make sure the bot is subscribed to the stream
self.check_subscription_or_die()
# Initialize IRC bot after proper connection to Zulip server has been confirmed.
irc.bot.SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname)
irc.bot.SingleServerIRCBot.__init__(
self, [irc.bot.ServerSpec(server, port)], nickname, nickname, **connect_params
)

def zulip_sender(self, sender_string: str) -> str:
nick = sender_string.split("!")[0]
Expand All @@ -40,6 +72,7 @@ def connect(self, *args: Any, **kwargs: Any) -> None:
# Taken from
# https://github.com/jaraco/irc/blob/main/irc/client_aio.py,
# in particular the method of AioSimpleIRCClient
kwargs["connect_factory"] = self.connection_factory
self.c = self.reactor.loop.run_until_complete(self.connection.connect(*args, **kwargs))
print("Listening now. Please send an IRC message to verify operation")

Expand Down