Skip to content

Commit

Permalink
Initial seeder version
Browse files Browse the repository at this point in the history
  • Loading branch information
rgaudin committed Jan 13, 2025
1 parent 026a986 commit b865244
Show file tree
Hide file tree
Showing 17 changed files with 1,919 additions and 0 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/bittorrent-seeder_ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: BitTorrent Seeder

on:
push:
branches:
- 'main'
paths:
- 'bittorrent-seeder/**'
workflow_dispatch:

jobs:

bittorrent-tracker:
name: Deploy BitTorrent Seeder Image
runs-on: ubuntu-22.04
steps:
- uses: actions/[email protected]
- name: Publish Docker Image
uses: openzim/docker-publish-action@v10
with:
image-name: kiwix/bittorrent-seeder
on-master: latest
restrict-to: kiwix/container-images
context: bittorrent-seeder
registries: ghcr.io
credentials:
GHCRIO_USERNAME=${{ secrets.GHCR_USERNAME }}
GHCRIO_TOKEN=${{ secrets.GHCR_TOKEN }}
40 changes: 40 additions & 0 deletions bittorrent-seeder/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
FROM alpine:edge

ENV SHELL=bash

RUN set -e \
# add testing repo for qbittorrent-cli
&& echo "https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
&& apk update \
&& apk --no-cache add dumb-init bash \
&& apk --no-cache add python3 py3-pip \
# the daemon with webui \
&& apk --no-cache add qbittorrent-nox \
# for convenience
&& apk --no-cache add qbittorrent-cli

ENV NO_DAEMON=""
ENV QBT_TORRENTING_PORT=6901
ENV QBT_HOST=localhost
ENV QBT_PORT=80
ENV QBT_USERNAME=admin
ENV QBT_PASSWORD=


COPY entrypoint.sh /usr/local/bin/entrypoint
COPY pyproject.toml README.md tasks.py /src/
COPY src/ /src/src
COPY gen-password.py /usr/local/bin/gen-password
COPY get-pbkdf2.py /usr/local/bin/get-pbkdf2

RUN set -e \
&& pip install --break-system-packages /src/ \
&& kiwix-seeder --help

EXPOSE 80
EXPOSE 6901
VOLUME /data
WORKDIR /data

ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/entrypoint"]
CMD ["kiwix-seeder", "--loop"]
1 change: 1 addition & 0 deletions bittorrent-seeder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Kiwix Seeder
97 changes: 97 additions & 0 deletions bittorrent-seeder/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/bin/sh

function start_qbt {
echo "Starting a qbittorrent-nox process (set NO_DAEMON if you dont want to)"

QBT_HOST="${QBT_HOST:-localhost}"
QBT_PORT="${QBT_PORT:-80}"
QBT_USERNAME="${QBT_USERNAME:-admin}"
QBT_TORRENTING_PORT="${QBT_TORRENTING_PORT:-6901}"

QBT_MAX_CONNECTIONS="${QBT_MAX_CONNECTIONS:-500}"
QBT_MAX_CONNECTIONS_PER_TORRENT="${QBT_MAX_CONNECTIONS_PER_TORRENT:-100}"
QBT_MAX_UPLOADS="${QBT_MAX_UPLOADS:-20}"
QBT_MAX_UPLOADS_PER_TORRENT="${QBT_MAX_UPLOADS_PER_TORRENT:-5}"

if [ "x${QBT_PASSWORD}" = "x" ]; then
QBT_PASSWORD=$(gen-password)
echo "Generated web-ui password: ${QBT_PASSWORD}"
fi
PKBF2_PASSWORD=$(get-pbkdf2 "${QBT_PASSWORD}")

QBITTORRENT_CONFIG_FILE=/root/.config/qBittorrent/qBittorrent.conf
mkdir -p $(dirname $QBITTORRENT_CONFIG_FILE)
cat <<EOF > $QBITTORRENT_CONFIG_FILE
[BitTorrent]
MergeTrackersEnabled=true
Session\DefaultSavePath=/data
Session\AddExtensionToIncompleteFiles=true
Session\MaxConnections=${QBT_MAX_CONNECTIONS}
Session\MaxConnectionsPerTorrent=${QBT_MAX_CONNECTIONS_PER_TORRENT}
Session\MaxUploads=${QBT_MAX_UPLOADS}
Session\MaxUploadsPerTorrent=${QBT_MAX_UPLOADS_PER_TORRENT}
Session\Port=${QBT_TORRENTING_PORT}
Session\Preallocation=true
Session\QueueingSystemEnabled=false
Session\SSL\Port=30154
[LegalNotice]
Accepted=true
[Meta]
MigrationVersion=8
[Preferences]
General\Locale=en
WebUI\Enabled=true
WebUI\Port=${QBT_PORT}
WebUI\Username=${QBT_USERNAME}
WebUI\Password_PBKDF2="@ByteArray(${PKBF2_PASSWORD})"
WebUI\LocalHostAuth=true
WebUI\HostHeaderValidation=false
WebUI\CSRFProtection=false
[Core]
AutoDeleteAddedTorrentFile=Always
[Application]
FileLogger\Age=5
FileLogger\AgeType=0
FileLogger\Backup=true
FileLogger\DeleteOld=true
FileLogger\Enabled=true
FileLogger\MaxSizeBytes=1048576
FileLogger\Path=/var/log
GUI\Notifications\TorrentAdded=false
EOF

QBT_CONFIG_FILE=/root/.config/qbt/.qbt.toml
mkdir -p $(dirname $QBT_CONFIG_FILE)
cat <<EOF > $QBT_CONFIG_FILE
[qbittorrent]
addr = "http://${QBT_HOST}:${QBT_PORT}" # qbittorrent webui-api hostname/ip
login = "${QBT_USERNAME}" # qbittorrent webui-api user
password = "${QBT_PASSWORD}" # qbittorrent webui-api password
#basicUser = "user" # qbittorrent webui-api basic auth user
#basicPass = "password" # qbittorrent webui-api basic auth password
[rules]
enabled = true # enable or disable rules
max_active_downloads = 2 # set max active downloads
EOF

mkdir -p ~/.config/qbt && touch ~/.config/qbt/.qbt.toml


# qbittorrent-nox --help
qbittorrent-nox --save-path=/data --daemon
rc=$?
echo "rc=${rc}"
}

if [ "x${NO_DAEMON}" = "x" ]; then
start_qbt
fi

exec "$@"
21 changes: 21 additions & 0 deletions bittorrent-seeder/gen-password.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python

"""command line to generate a short (8chars) password from alphanum"""

import secrets
import string
import sys


def gen_password() -> str:
alphabet = string.ascii_letters + string.digits
return "".join(secrets.choice(alphabet) for _ in range(8))


def main() -> int:
print(gen_password()) # noqa: T201
return 0


if __name__ == "__main__":
sys.exit(main())
38 changes: 38 additions & 0 deletions bittorrent-seeder/get-pbkdf2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python

""" command line to generate a base64-encoded PBKDF2 version of a password
similar to how qBittorrent stores web-ui password in its config file.
Format is salt:HMAC"""

import base64
import hashlib
import secrets
import sys


def asb64(data: bytes) -> str:
return base64.b64encode(data).decode("ASCII")


def get_pbkdf2_for(password: str) -> str:
salt = secrets.token_bytes(16)
hmac = hashlib.pbkdf2_hmac(
hash_name="sha512",
password=password.encode("UTF-8"),
salt=salt,
iterations=100000,
)
return f"{asb64(salt)}:{asb64(hmac)}"


def main() -> int:
if len(sys.argv) != 2: # noqa: PLR2004
print(f"Usage: {sys.argv[0]} CLEAR_PASSWORD") # noqa: T201
return 1
print(get_pbkdf2_for(sys.argv[1])) # noqa: T201
return 0


if __name__ == "__main__":
sys.exit(main())
Loading

0 comments on commit b865244

Please sign in to comment.