Commit Graph

79 Commits

Author SHA1 Message Date
Simon Ser edaae4d06b cmd/sojudb: use User.SetPassword 2023-11-01 00:05:03 +01:00
Simon Ser 4f7b577734 database: add NewUser 2023-11-01 00:04:57 +01:00
Simon Ser 9b9fc60f62 Rename Config.Log{Driver,Path} to MsgStore 2023-05-23 11:23:38 +02:00
Simon Ser 4c8e955946 Move DefaultUnixAdminPath to config package
We can drop the soju package import. This shrinks down sojuctl's
size significantly (18M to 3M).
2023-03-15 17:47:47 +01:00
Simon Ser 5a1cbc425f Remove legacy Go build comments 2023-02-23 10:33:48 +01:00
delthas 1ccc7ce6d2 Add a database store for messages
This adds a new config option, `logs db`, which enables storing chat
logs in the soju database.

Regular store options, CHATHISTORY options, and SEARCH operations are
supported, like the fs logs backend.

Messages are stored in a new table, Message. In order to track the list
of targets we have messages for in an optimized manner, another database
is used: MessageTarget.

All new requests are backend by indexes so should be fast even with
hundreds of thousands of messages.

A contrib script is provided for migrating existing logs fs chat logs to
the database. It can be run with eg:

  go run ./contrib/migrate-logs/ logs/ sqlite3:soju.db

Co-authored-by: Simon Ser <contact@emersion.fr>
2023-02-17 14:13:43 +01:00
Simon Ser bf931709db Drop permissions on Unix admin socket 2023-02-07 10:10:05 +01:00
delthas f57492af56 Add cmd/sojuctl
This new command enables communicating with the unix administrative
endpoint (unix+admin) that can be enabled on soju.

The syntax is just that of BouncerServ.

Examples:
    sojuctl -config soju.config help
    sojuctl -config soju.config user status
2023-02-06 15:15:09 +01:00
delthas 2713bcba34 Add administrative unix listen endpoint
This adds support for listening on a Unix socket for administrative
connections, that then use a simple protocol for communicating with
the service (BouncerServ) as an administrator with a global context.

The wire format used by the Unix socket is IRC, but without
registration or overheads.

Example session:

    >>> BOUNCERSERV
    <<< 461 * BOUNCERSERV :Not enough parameters
    >>> BOUNCERSERV :n s
    <<< :gensou FAIL BOUNCERSERV :this command must be run as a user
    >>> BOUNCERSERV :u s
    <<< :gensou PRIVMSG * :marisa: 2 networks
    <<< :gensou PRIVMSG * :alice: 1 networks
    <<< :gensou BOUNCERSERV OK
2023-02-06 15:15:09 +01:00
delthas ab235f0099 sojuctl: rename to sojudb
sojuctl will be used to control the soju deamon directly.

sojudb is a better name because it operates on the database file only.
2023-02-06 15:15:09 +01:00
Simon Ser c031e08d71 Add a default path for the config file 2023-02-06 13:16:55 +01:00
Simon Ser 63ca247354 Add infrastructure for external authentication 2023-01-30 11:53:01 +01:00
Simon Ser db49bc120f Add enable-user-on-auth config directive 2023-01-26 19:51:35 +01:00
Simon Ser 9df9880301 Add disable-inactive-user config option
This can be used to automatically disable users if they don't
actively use the bouncer for a while.
2023-01-26 19:42:29 +01:00
Simon Ser d7d9d45b45 Add a flag to disable users
Add a new flag to disable users. This can be useful to temporarily
deactivate an account without erasing data.

The user goroutine is kept alive for simplicity's sake. Most of the
infrastructure assumes that each user always has a running goroutine.
A disabled user's goroutine is responsible for sending back an error
to downstream connections, and listening for potential events to
re-enable the account.
2023-01-26 18:33:55 +01:00
Simon Ser dde4ee9518 config: drop multi-upstream-mode 2022-09-26 16:57:05 +02:00
Simon Ser 027a89a0b0 server: log when waiting for user goroutines on shutdown
Useful for debugging.
2022-09-18 21:43:31 +02:00
Simon Ser d27880e03e config: use structs to group DB/MsgAuth 2022-09-11 13:50:34 +02:00
Simon Ser e2e232fa9c config: add `message-store memory`
The old way to do this was `message-store fs ""`, which is
misleading.
2022-05-09 16:59:27 +02:00
Simon Ser 95db42e645 Move identd to separate package 2022-05-09 16:36:54 +02:00
Simon Ser 3a7dee8128 Introduce a database package 2022-05-09 15:08:04 +02:00
delthas 9647711921 Fix build on systems without syscall.Rlimit
On some systems (namely Windows), syscall.Rlimit is not defined, and
makes the build fail.

This fixes the build by making the rlimit calls only run on archs where
it is defined, defaulting to a stub on other systems.

See: 8427429c59
2022-03-21 09:39:54 +01:00
Simon Ser 8b926be817 Warn about non-FQDN hostnames
References: https://github.com/ircdocs/modern-irc/pull/148
2022-03-15 20:45:23 +01:00
Simon Ser dac003960c db: allow registering multiple metrics collectors 2022-03-08 10:36:59 +01:00
Simon Ser f2a03cf7a1 Fix incorrect listen addr in error message
The various server goroutines would always capture the last listen
addr in the loop.
2022-01-19 22:38:13 +01:00
Simon Ser d829d2ab12 Add Logger.Debugf
Makes it easy to print debugging messages which aren't targeted at
the user. See [1] for motivation.

[1]: https://dave.cheney.net/2015/11/05/lets-talk-about-logging
2021-12-06 18:35:49 +01:00
Simon Ser d722f56000 Add pprof HTTP server
This enables production debugging of the bouncer.

Closes: https://todo.sr.ht/~emersion/soju/155
2021-11-17 16:18:52 +01:00
Simon Ser ea98ac042e Add Prometheus instrumentation for the database 2021-11-17 15:58:19 +01:00
Simon Ser 8f8d7aab0e Add basic Prometheus metrics exporter
This only exports the default metrics for now.

References: https://todo.sr.ht/~emersion/soju/142
2021-11-17 15:58:18 +01:00
Simon Ser 55840312b4 Add per-user IP addresses
The new upstream-user-ip directive allows bouncer operators to
assign one IP address per user.
2021-11-17 15:07:58 +01:00
Simon Ser 0c5aa8cbff sojuctl: use background context 2021-11-17 11:54:28 +01:00
Simon Ser 3941f67380 Add config option to globally disable multi-upstream mode
Closes: https://todo.sr.ht/~emersion/soju/122
2021-11-17 11:41:11 +01:00
Simon Ser 73295e4fa7 Allow most config options to be reloaded
Closes: https://todo.sr.ht/~emersion/soju/42
2021-11-16 00:38:04 +01:00
Simon Ser 4397cd7fc2 cmd/soju: bump max number of opened files
The bouncer process may be dealing with many opened FDs. The default
on Linux is 1024. To support bouncers with a lot of users, bump
RLIMIT_NOFILE to the max as advised in [1].

[1]: http://0pointer.net/blog/file-descriptor-limits.html
2021-11-15 21:22:43 +01:00
Simon Ser 07c962018d Add title config option
Closes: https://todo.sr.ht/~emersion/soju/146
2021-11-02 22:38:07 +01:00
Simon Ser 9ec1f1a5b0 Add context args to Database interface
This is a mecanical change, which just lifts up the context.TODO()
calls from inside the DB implementations to the callers.

Future work involves properly wiring up the contexts when it makes
sense.
2021-10-18 19:15:15 +02:00
Simon Ser 2ed4491c17 Don't strip spaces at start of MOTD
This breaks ASCII art. Instead, just drop the final newline if any.
2021-10-13 12:53:43 +02:00
Simon Ser a9a066faac Add bouncer MOTD
Closes: https://todo.sr.ht/~emersion/soju/137
2021-10-13 10:58:34 +02:00
Hubert Hirtz 6e06663615 PostgreSQL support 2021-10-11 15:21:04 +02:00
Simon Ser 94dbfff11d Add max-user-networks config option 2021-10-07 20:43:10 +02:00
Chris Smith c607fd5a58 Set a higher timeout for proxyproto listeners
go-proxyproto added support for a read timeout in 0.6.0[1] and
defaulted it to 200ms. After this time if no data is read on
the socket, it is closed.

This is _really_ low if the underlying connection is a TLS
one as no data pops out the other end until the handshake is
done. It effectively limits you to TLS connections within
a 50ms RTT of your bouncer with clients that are fast enough
at responding.

It appears that HexChat on Arch is somehow slow enough at
TLS connections thant it consistently takes longer than
200ms even over localhost, meaning it outright can't connect
to soju any longer.

To make this a lot less painful, have soju pass in a read
timeout of 5 seconds. This feels like a reasonable tradeoff
between keeping (possibly malicious) connections open and
accepting the realities of network connections.

[1]: https://github.com/pires/go-proxyproto/issues/65
2021-09-19 17:00:40 +02:00
Rafael Castillo d1181b3e7a Check for TLS config in wss listeners
Previously http.Server.ListenAndServeTLS would return a not very helpful
error about a failed open. This adds a check similar to the one in the
ircs case that should make it clearer to operators what the error is.
2021-09-09 10:06:31 +02:00
Drew DeVault 61b68d6dfb db: refactor into interface
This refactors the SQLite-specific bits into db_sqlite.go. A future
patch will add PostgreSQL support.
2021-05-25 16:35:39 +02:00
Simon Ser c994ce7092 sojuctl: don't use log.Fatalf in readPassword 2021-04-19 14:11:25 +02:00
Simon Ser c375c7d922 cmd/soju: allow specifying -listen multiple times
Closes: https://todo.sr.ht/~emersion/soju/67
2021-03-31 19:02:40 +02:00
delthas 56bf73716d Increase downstream TCP keepalive interval to 1 hour
The rationale for increasing the TCP keepalive interval from 15 seconds
(default) to 1 hour follows.

- Why increasing TCP keepalives for downstream connections is not an
  issue wrt to detecting connection interruptions

The use case of TCP keepalives is detecting whether a TCP connection was
forcefully shut down without receiving any TCP FIN or RST frame, when no
data are sent from that endpoint to the other peer.

If any data is sent from the peer and is not ACKed because the
connection was interrupted, the socket will be closed after the TCP RTO
(usually a few seconds) anyway, without the need for TCP keepalives.

Therefore the only use of TCP keepalives is making sure that a peer that
is not writing anything to the socket, and is actively reading and
waiting for new stream data to be received, can, - instead of waiting
forever to receive packets that will never arrive because the connection
was interrupted -, detect this disconnection, close the connection
locally, then try to connect again to its peer.

This only makes sense from a client point-of-view. When an IRC client is
not write(2)ing anything to the socket but is simply waiting for new
messages to arrive, ie read(2)ing on the socket, it must ensure that the
connection is still alive so that any new messages will indeed be sent
to him. So that IRC client should probably enable TCP keepalives.

However, when an IRC server is not writing anything to its downstream
socket, it doesn't care if it misses any messages from its downstream
client: in any case, the downstream client will instantly detect when
its messages are not reaching its server, because of the TCP RTO
(keepalives are not even needed in the client in that specific case),
and will try to reconnect to the server.

Thus TCP keepalives should be enabled for upstream connections, in
order to make sure that soju does not miss any messages coming from
upstream servers, but TCP keepalives are not needed for downstream
connections.

- Why increasing TCP keepalives for downstream connections is not an
  issue wrt security, performance, and server socket resources
  exhaustion

TCP keepalives are orthogonal to security. Malicious clients can open
thousands of TCP connections and keep them open with minimal
bookkeeping, and TCP keepalives will not prevent attacks planning to
use up all available sockets to soju.

It is also unlikely that soju will keep many connections open, and in
the event that thousands of dead, disconnected connections are active in
soju, any upstream message that needs to be sent to downstreams will
disconnect all disconnected downstreams after the TCP RTO (a few
seconds). Performance could only be slightly affected in the few seconds
before a TCP RTO if many messages were sent to a very large number of
disconnected connections, which is extremely unlikely and not a large
impact to performance either.

- Why increasing TCP keepalives could be helpful to some clients running
  on mobile devices

In the current state of IRC, most clients running on mobile devices
(mostly running Android and iOS) will probably need to stay connected
at all times, even when the application is in background, in order to
receive private messages and highlight notifications, complete chat
history (and possibly reduced connection traffic due to avoiding all the
initial messages traffic, including all NAMES and WHO replies which
are quite large).

This means most IRC clients on mobile devices will keep a socket open at
all times, in background. When a mobile device runs on a cellular data
connection, it uses the phone wireless radio to transmit all TCP
packets, including TCP packets without user data, for example TCP
keepalives.

On a typical mobile device, a wireless radio consumes significant power
when full active, so it switches between several energy states in order
to conserve power when not in use. It typically has 3 energy states,
from Standby, when no messages are sent, to Low Power, to Full Power;
and switches modes on an average time scale of 15s. This means that any
time any TCP packet is sent from any socket on the device, the radio
switches to a high-power energy state, sends the packet, then stays on
that energy state for around 15s, then goes back to Standby. This
does include TCP keepalives.

If a TCP keepalive of 15s was used, this means that the IRC server would
force all clients running on mobile devices to send a TCP keepalive
packet at least once every 15s, which means that the radio would stay
in its high-power energy state at all times. This would consume a
very significant amount of power and use up battery much faster.

Even though it would seem at first that a mobile device would have many
different sockets open at any time; actually, a typical Android device
typically has at one background socket open, with Firebase Cloud
Messaging, for receiving instant push notifications (for example, for
the equivalent of IRC highlights on other messaging platforms), and
perhaps a socket open for the current foreground app. When the current
foreground app does not use the network, or when no app is currently
used and the phone is in sleep mode, and no notifications are sent, then
the device can effectively have no wireless radio usage at all. This
makes removing TCP keepalives extremely significant with regard to the
mobile device battery usage.

Increasing the TCP keepalive from soju lets downstream clients choose
their own keepalive interval and therefore possibly save battery for
mobile devices. Most modern mobile devices have complex heuristics for
when to sleep the CPU and wireless radio, and have specific rules for
TCP keepalives depending on the current internet connection, sleep
state, etc.

By increasing the downstream TCP keepalive to such a high period, soju
lets clients choose their most optimal TCP keepalive period, which means
that in turn clients can possibly let their mobile device platform
choose best that keepalive for them, thus letting them save battery in
those cases.
2021-03-24 18:04:44 +01:00
Simon Ser c0513013d5 Fix panic on GetCertificate
Fixes the following panic:

    panic: interface conversion: interface {} is tls.Certificate, not *tls.Certificate
2021-03-19 09:27:19 +01:00
Simon Ser 21e9fe9b3c Reload TLS certs on SIGHUP
References: https://todo.sr.ht/~emersion/soju/42
2021-03-18 14:07:03 +01:00
Simon Ser 0c0397407c Don't add "irc" in ALPN list for WebSocket servers
This is incorrect because HTTP listeners don't handle plain IRC
connections. This also prevents net/http from setting up an HTTP/2
server.
2021-03-18 11:33:30 +01:00
Simon Ser 061347f9f9 Add Unix socket listener
Closes: https://todo.sr.ht/~emersion/soju/51
2021-03-16 09:27:40 +01:00