Sign inSign up

irc2torrent/flood_qbittorrent_irc2torrent

By irc2torrent

•Updated 26 days ago

IRC announce bot + qBittorrent + Flood, hardened and shell-less, non-root, read-only rootfs

Buildkit cache
Image
Networking
0

710

irc2torrent/flood_qbittorrent_irc2torrent repository overview

⁠flood_qbittorrent_irc2torrent

An IRC announce bot, qBittorrent and Flood in a single hardened container.

Announce channels are how a lot of trackers publish new releases in real time. Polling an RSS feed means waiting out the poll interval; sitting on the channel means acting the moment the line appears. irc2torrent connects, listens, filters against your regexes, fetches the .torrent, and hands it to qBittorrent — usually within a second of the announce.

The runtime is built on Docker Hardened Images: no shell, no busybox, no package manager, running as uid 1000 with a read-only root filesystem.

Prefer rTorrent? irc2torrent/flood_rtorrent_irc2torrent⁠ is the same bot and the same Flood with rakshasa rTorrent instead, carrying a fix for a long-standing bug that makes rTorrent read 4–100× more from disk than it uploads. Pick this image if you would rather configure a client through a web UI than an .rtorrent.rc.


⁠Quick start

docker run -d --name qbittorrent \
  --read-only --security-opt no-new-privileges:true \
  --tmpfs /tmp --stop-timeout 30 \
  -v qbt-config:/config \
  -v /srv/downloads:/data \
  -p 3000:3000 -p 8080:8080 -p 50000:50000 -p 50000:50000/udp \
  irc2torrent/flood_qbittorrent_irc2torrent:latest

qBittorrent's WebUI is on http://localhost:8080⁠, Flood on http://localhost:3000⁠. For Flood, choose qBittorrent → http://127.0.0.1:8080.

The bot needs no configuration to reach qBittorrent. The image ships a config that bypasses authentication for connections from inside the container, and writes an options.toml already pointing at it.

That bypass is keyed on the peer address, so a request arriving through the published port comes from the Docker bridge rather than 127.0.0.1 and still meets the login form. Set a WebUI password anyway if you expose 8080 beyond the host — qBittorrent prints a temporary one to the log on first start until you do.

--stop-timeout 30 matters. Docker defaults to 10 seconds and then SIGKILLs everything, which costs qBittorrent its fastresume data — and a qBittorrent that lost its fastresume rechecks the whole library on the next start, which on a full seedbox is hours of disk I/O.

On first run the bot writes default configs into /config/.config/ and exits. Edit them and start the container again.


⁠Configuration

Two files, both created with working defaults on first run: irc.toml for the connection, options.toml for everything else.

# Releases you want. A line is taken if it matches ANY of these...
regex_for_downloads_match = [
    "Some Release.*2160p.*",
    "Another Release.*S02.*1080p.*WEB.*"
]

# ...and dropped if it matches ANY of these. Reject wins over match.
regex_for_downloads_reject_match = [
    "(?i).*NORDIC.*",
    "(?i).*GERMAN.*"
]

# How to pull the release name and torrent id out of an announce line.
# The two named captures are required; the rest depends on your network.
regex_for_announce_match = '''.*Name:'(?P<name>.*)' uploaded by.*https://tracker.example.org/torrent/(?P<id>\d+)'''

# The tracker. The key is a label of your choosing and is what gets logged.
[platform.YourTracker]
download_url_template = "https://tracker.example.org/rss/download/{id}/{key}/{file}"
rss_key               = "XXXXXXXXXXXXXXXXXXXX"
torrent_dir           = "/data/.torrents"

[[clients]]

[clients.qBittorrent]
url      = "http://127.0.0.1:8080"
username = ""      # leave empty where auth is bypassed for localhost
password = ""

save_path and category are optional; empty means whatever qBittorrent is already configured to use. The bot only logs in when the server asks it to, so an install with authentication bypassed for localhost needs no credentials at all.

Set torrent_dir to a path under /data or /config. The root filesystem is read-only, so it has to be somewhere writable — /data/.torrents is the natural choice for this image.

There is no tracker-specific code anywhere. A network is an announce regex plus a download_url_template — {id}, {name}, {file} and {key} are the placeholders. Adding a network is configuration, never code.

Three things worth knowing:

  • Matching is unanchored, so leading and trailing .* do nothing. "2160p" and ".*2160p.*" behave identically.
  • Use (?i) for case rather than listing variants. One (?i).*NORDIC.* covers NORDiC, Nordic and the rest.
  • regex_for_announce_match is the one you have to write yourself, since every network announces differently. Watch the channel by hand for a minute first.

{name} and {id} come verbatim from an IRC message, so every substituted value is percent-encoded and the host is re-checked before the request is made. A placeholder in the host or port is rejected at startup — otherwise a crafted release name could point the request, and your rss_key, at someone else's server.

Most of options.toml reloads live, no restart: the filters, the tracker block, and all notification settings. platform, clients, [telegram] and [slack] are read once at startup and log a line saying so rather than pretending to have applied.


⁠Paths and ports

PathPurpose
/configAll persistent state — qBittorrent profile, Flood database, bot config. Back this up.
/config/.config/options.tomlFilters, tracker, client, notifications.
/config/.config/irc.tomlIRC connection settings.
/config/qBittorrent/config/qBittorrent.confqBittorrent's own config. Seeded on first start and never overwritten afterwards.
/dataTorrent data.
PortPurpose
3000Flood web UI
8080qBittorrent WebUI
50000 tcp+udpPeer traffic

⁠Environment variables

Everything has a working default; you normally need to set none of them.

VariableDefaultPurpose
IRC2TORRENT_SUPERVISE1Run as container init. Unset to run the bot alone.
IRC2TORRENT_SHUTDOWN_GRACE8Seconds children get after SIGTERM before SIGKILL. Below docker stop's 10s default on purpose. Raise this and --stop-timeout together.
IRC2TORRENT_RAW_CHILD_LOGS0Child output is captured and prefixed [qbittorrent] / [flood]. Set 1 for plain inheritance, keeping Flood's output as machine-parseable JSON.
QBITTORRENT_WEBUI_PORT8080WebUI port, and the port the supervisor waits on before starting Flood.
QBITTORRENT_PROFILE/configPassed as --profile.
FLOOD_OPTION_AUTH(unset)default or none. Set default if Flood is reachable beyond localhost.
FLOOD_OPTION_PORT3000Flood web UI port.
PUID / PGID1000Ownership of /config and /data.
⁠Remote syslog (new in 0.17.0)

Set IRC2TORRENT_SYSLOG and everything the bot logs goes to a collector as well as to docker logs — no shipper, no sidecar, no file to tail. Messages are RFC 3164, which QNAP QuLog Center, rsyslog, syslog-ng and Synology all accept.

-e IRC2TORRENT_SYSLOG=udp://192.168.1.10:514
-e IRC2TORRENT_SYSLOG_HOSTNAME=irc2torrent
VariableDefaultPurpose
IRC2TORRENT_SYSLOG(off)udp://host[:port], tcp://host[:port], unix, or unix:/path. Bare host[:port] means UDP. Port defaults to 514. IPv6 needs brackets.
IRC2TORRENT_SYSLOG_TAGirc2torrentProgram name in the header — what collectors group by.
IRC2TORRENT_SYSLOG_LEVELinfoThreshold for this sink alone.
IRC2TORRENT_SYSLOG_FACILITYdaemondaemon, user, local0–local7, …
IRC2TORRENT_SYSLOG_HOSTNAME(system hostname)Defaults to the short container id, which changes on every recreate — set it if you group by host.
IRC2TORRENT_SYSLOG_CHILD_LOGS0Also relay qBittorrent and Flood output to the collector.

UDP is the default deliberately: the sink is synchronous, so a TCP target that stops answering can stall the bot. A target that cannot be opened is logged once and then ignored — never fatal.


⁠Notifications and IRC commands

Optional, both off by default.

Notifications go to email, ntfy, IRC private message, Telegram or Slack, on events like a torrent added, a download finished, disk running low, or a daily summary. Two settings keep an announce channel from becoming a firehose: digest_seconds (default 300) buffers events into one message with repeats collapsed into a count, and max_per_hour (default 20) is a hard ceiling that reports how many it suppressed. As of 0.17.0 a notification held for an absent owner expires after hold_seconds (default 15 minutes) instead of arriving as a weekend-long backlog of things that already resolved.

Commands over IRC let you query and control the bot from a private message. This exposes a control interface, so it is off by default. A nickname is not a credential — on a network without enforced registration anyone can take yours the moment you drop off — so require_identified (on by default) asks services who the sender is and refuses unless they are logged in to an account matching your configured name.


⁠How it fits together

One container, no s6-overlay. irc2torrent runs as PID 1, starts and reaps qBittorrent and Flood, installs its own SIGTERM/SIGINT handlers, and waits for the WebUI port to answer before starting Flood, so Flood's first connection succeeds.

Qt loads its TLS backend with dlopen, so it appears in no ldd listing. Miss it and qBittorrent starts, seeds, and silently never announces to an HTTPS tracker — the build copies the whole plugin tree and asserts the TLS backend landed.


⁠Security

irc2torrent parses untrusted input from a network it does not control, so it is built to be contained: non-root, read-only root filesystem, no shell or package manager in the runtime image, no-new-privileges.

A full security audit preceded 0.2.0 — a high-severity arbitrary file write reachable from a crafted IRC announce, three remotely triggerable panics, and dependency work that took cargo audit from 18 vulnerabilities to 0. All of it is fixed as of 0.2.0; run that or newer. cargo audit gates CI on every build.


⁠Tags

  • latest — the most recent release
  • 0.17.0, 0.16.0, … — pinned versions

⁠Scope

This ships no tracker configuration and no announce patterns for any particular network. What you connect to, and whether you are entitled to what you download, is yours to sort out.


Source, full documentation and issues: https://github.com/irc2torrent/irc2torrent⁠

Licensed under MIT. qBittorrent is GPL-2.0-or-later, Flood is GPL-3.0; each retains its own license.

Tag summary

Content type

Image

Digest

sha256:2f04f6617…

Size

98.4 MB

Last updated

26 days ago

docker pull irc2torrent/flood_qbittorrent_irc2torrent