diff --git a/.dockerignore b/.dockerignore index 3d8a25cce..f42859922 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,16 +1,16 @@ -.git -.git-blame-ignore -.github -.gitignore -.vscode -bin/ -config.toml -config.toml.local -cSpell.json -data.db -docker/ -NOTICE -README.md -rustfmt.toml -storage/ -target/ +/.git +/.git-blame-ignore +/.github +/.gitignore +/.vscode +/bin/ +/tracker.* +/cSpell.json +/data.db +/docker/bin/ +/NOTICE +/README.md +/rustfmt.toml +/storage/ +/target/ +/etc/ diff --git a/.env.local b/.env.local deleted file mode 100644 index fefed56c4..000000000 --- a/.env.local +++ /dev/null @@ -1 +0,0 @@ -TORRUST_TRACKER_USER_UID=1000 \ No newline at end of file diff --git a/.github/workflows/container.yaml b/.github/workflows/container.yaml new file mode 100644 index 000000000..27a2dc93c --- /dev/null +++ b/.github/workflows/container.yaml @@ -0,0 +1,125 @@ +name: Container + +on: + push: + tags-ignore: + - "!v*" + pull_request: + branches: + - "develop" + - "main" + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + name: Test (Docker) + runs-on: ubuntu-latest + + steps: + - id: setup + name: Setup Toolchain + uses: docker/setup-buildx-action@v2 + + - id: build + name: Build + uses: docker/build-push-action@v4 + with: + file: ./Containerfile + push: false + load: true + tags: torrust-tracker:local + cache-from: type=gha + cache-to: type=gha,mode=max + + - id: inspect + name: Inspect + run: docker image inspect torrust-tracker:local + + - id: checkout + name: Checkout Repository + uses: actions/checkout@v3 + + - id: compose + name: Compose + run: docker compose build + + context: + name: Context + needs: test + runs-on: ubuntu-latest + + outputs: + continue: ${{ steps.check.outputs.continue }} + + steps: + - id: check + name: Check Context + run: | + if [[ "${{ github.event_name }}" == "push" && ( "${{ github.ref }}" == "refs/heads/main" || "${{ github.ref }}" == "refs/heads/develop" || "${{ github.ref }}" == "refs/heads/docker" ) ]] || + [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ "${{ github.repository }}" == "torrust/torrust-tracker" ]]; then + echo "Context is torrust/torrust-tracker, and push is: main, develop, docker, or a tag of v*.*.*" + echo "continue=true" >> $GITHUB_OUTPUT + fi + fi + + secrets: + name: Secrets + needs: context + environment: dockerhub-torrust + if: needs.context.outputs.continue == 'true' + runs-on: ubuntu-latest + + outputs: + continue: ${{ steps.check.outputs.continue }} + + steps: + - id: check + name: Check + env: + DOCKER_HUB_ACCESS_TOKEN: "${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}" + if: "${{ env.DOCKER_HUB_ACCESS_TOKEN != '' }}" + run: echo "continue=true" >> $GITHUB_OUTPUT + + publish: + name: Publish + environment: dockerhub-torrust + needs: secrets + if: needs.secrets.outputs.continue == 'true' + runs-on: ubuntu-latest + + steps: + - id: meta + name: Docker meta + uses: docker/metadata-action@v4 + with: + images: | + "${{ secrets.DOCKER_HUB_USERNAME }}/${{secrets.DOCKER_HUB_REPOSITORY_NAME }}" + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + + - id: login + name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - id: setup + name: Setup Toolchain + uses: docker/setup-buildx-action@v2 + + - name: Build and push + uses: docker/build-push-action@v4 + with: + file: ./Containerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/publish_docker_image.yml b/.github/workflows/publish_docker_image.yml deleted file mode 100644 index 1dd65e3a7..000000000 --- a/.github/workflows/publish_docker_image.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Publish docker image - -on: - push: - branches: - - "main" - - "develop" - tags: - - "v*" - -env: - # Azure file share volume mount requires the Linux container run as root - # https://learn.microsoft.com/en-us/azure/container-instances/container-instances-volume-azure-files#limitations - # TORRUST_TRACKER_RUN_AS_USER: root - TORRUST_TRACKER_RUN_AS_USER: appuser - -jobs: - check-secret: - runs-on: ubuntu-latest - environment: dockerhub-torrust - outputs: - publish: ${{ steps.check.outputs.publish }} - steps: - - id: check - env: - DOCKER_HUB_USERNAME: "${{ secrets.DOCKER_HUB_USERNAME }}" - if: "${{ env.DOCKER_HUB_USERNAME != '' }}" - run: echo "publish=true" >> $GITHUB_OUTPUT - - test: - needs: check-secret - if: needs.check-secret.outputs.publish == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - components: llvm-tools-preview - - uses: Swatinem/rust-cache@v2 - - name: Run Tests - run: cargo test - - dockerhub: - needs: test - if: needs.check-secret.outputs.publish == 'true' - runs-on: ubuntu-latest - environment: dockerhub-torrust - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: | - # For example: torrust/tracker - "${{ secrets.DOCKER_HUB_USERNAME }}/${{secrets.DOCKER_HUB_REPOSITORY_NAME }}" - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build and push - uses: docker/build-push-action@v4 - with: - context: . - file: ./Dockerfile - build-args: | - RUN_AS_USER=${{ env.TORRUST_TRACKER_RUN_AS_USER }} - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/test_docker.yml b/.github/workflows/test_docker.yml deleted file mode 100644 index a62965878..000000000 --- a/.github/workflows/test_docker.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Test docker build - -on: - push: - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build docker image - uses: docker/build-push-action@v4 - with: - context: . - file: ./Dockerfile - push: false - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Build docker-compose images - run: docker compose build diff --git a/.gitignore b/.gitignore index 6b58dcb45..2d8d0b8bd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,10 @@ /.coverage/ /.idea/ /.vscode/launch.json -/config.toml +/tracker.toml /data.db /database.db /database.json.bz2 /storage/ /target +/tracker.* diff --git a/.vscode/settings.json b/.vscode/settings.json index 78239b757..3bf0969e9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,23 +1,23 @@ { - "[rust]": { - "editor.formatOnSave": true - }, - "rust-analyzer.checkOnSave": true, - "rust-analyzer.check.command": "clippy", - "rust-analyzer.check.allTargets": true, - "rust-analyzer.check.extraArgs": [ - "--", - "-D", - "clippy::correctness", - "-D", - "clippy::suspicious", - "-W", - "clippy::complexity", - "-W", - "clippy::perf", - "-W", - "clippy::style", - "-W", - "clippy::pedantic", - ], + "[rust]": { + "editor.formatOnSave": true + }, + "rust-analyzer.checkOnSave": true, + "rust-analyzer.check.command": "clippy", + "rust-analyzer.check.allTargets": true, + "rust-analyzer.check.extraArgs": [ + "--", + "-D", + "clippy::correctness", + "-D", + "clippy::suspicious", + "-W", + "clippy::complexity", + "-W", + "clippy::perf", + "-W", + "clippy::style", + "-W", + "clippy::pedantic", + ], } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index bc88e9bf5..0b7c9d0d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3096,7 +3096,7 @@ dependencies = [ [[package]] name = "torrust-tracker" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" dependencies = [ "aquatic_udp_protocol", "async-trait", @@ -3142,7 +3142,7 @@ dependencies = [ [[package]] name = "torrust-tracker-configuration" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" dependencies = [ "config", "log", @@ -3157,7 +3157,7 @@ dependencies = [ [[package]] name = "torrust-tracker-located-error" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" dependencies = [ "log", "thiserror", @@ -3165,7 +3165,7 @@ dependencies = [ [[package]] name = "torrust-tracker-primitives" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" dependencies = [ "derive_more", "serde", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "torrust-tracker-test-helpers" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" dependencies = [ "lazy_static", "rand", diff --git a/Cargo.toml b/Cargo.toml index a265f32ef..9c94ea10d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,21 @@ version.workspace = true [workspace.package] license-file = "COPYRIGHT" -authors = ["Nautilus Cyberneering , Mick van Dijke "] +authors = [ + "Nautilus Cyberneering , Mick van Dijke ", +] edition = "2021" repository = "https://github.com/torrust/torrust-tracker" -version = "3.0.0-alpha.3" +version = "3.0.0-alpha.4" [dependencies] -tokio = { version = "1.29", features = ["rt-multi-thread", "net", "sync", "macros", "signal"] } +tokio = { version = "1.29", features = [ + "rt-multi-thread", + "net", + "sync", + "macros", + "signal", +] } serde = { version = "1.0", features = ["derive"] } serde_bencode = "^0.2" serde_json = "1.0" @@ -41,11 +49,11 @@ uuid = { version = "1", features = ["v4"] } axum = "0.6.20" axum-server = { version = "0.5", features = ["tls-rustls"] } axum-client-ip = "0.4.1" -tower-http = { version= "0.4.3", features = ["compression-full"] } +tower-http = { version = "0.4.3", features = ["compression-full"] } bencode = { version = "1.0.0-alpha.1", path = "contrib/bencode" } -torrust-tracker-primitives = { version = "3.0.0-alpha.3", path = "packages/primitives" } -torrust-tracker-configuration = { version = "3.0.0-alpha.3", path = "packages/configuration" } -torrust-tracker-located-error = { version = "3.0.0-alpha.3", path = "packages/located-error" } +torrust-tracker-primitives = { version = "3.0.0-alpha.4", path = "packages/primitives" } +torrust-tracker-configuration = { version = "3.0.0-alpha.4", path = "packages/configuration" } +torrust-tracker-located-error = { version = "3.0.0-alpha.4", path = "packages/located-error" } multimap = "0.9" hyper = "0.14" @@ -56,7 +64,7 @@ serde_urlencoded = "0.7" serde_repr = "0.1" serde_bytes = "0.11" local-ip-address = "0.5" -torrust-tracker-test-helpers = { version = "3.0.0-alpha.3", path = "packages/test-helpers" } +torrust-tracker-test-helpers = { version = "3.0.0-alpha.4", path = "packages/test-helpers" } [workspace] members = [ diff --git a/Containerfile b/Containerfile new file mode 100644 index 000000000..a73f31dfe --- /dev/null +++ b/Containerfile @@ -0,0 +1,138 @@ +# syntax=docker/dockerfile:latest + +# Torrust Tracker + +## Builder Image +FROM rust:latest as chef +WORKDIR /tmp +RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash +RUN cargo binstall --no-confirm cargo-chef cargo-nextest + +## Tester Image +FROM rust:slim as tester +WORKDIR /tmp + +RUN apt-get update; apt-get install -y curl sqlite3; apt-get autoclean +RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash +RUN cargo binstall --no-confirm cargo-nextest + +COPY ./share/ /app/share/torrust +RUN mkdir -p /app/share/torrust/default/database/; \ + sqlite3 /app/share/torrust/default/database/tracker.sqlite3.db "VACUUM;" + +COPY ./contrib/dev-tools/su-exec/ /tmp/su-exec/ +RUN cc -Wall -Werror -g /tmp/su-exec/su-exec.c -o /tmp/su-exec/su-exec + + +## Chef Prepare (look at project and see wat we need) +FROM chef AS recipe +WORKDIR /build/src +COPY . /build/src +RUN cargo chef prepare --recipe-path /build/recipe.json + + +## Cook (debug) +FROM chef AS dependencies_debug +WORKDIR /build/src +COPY --from=recipe /build/recipe.json /build/recipe.json +RUN cargo chef cook --tests --benches --examples --workspace --all-targets --all-features --recipe-path /build/recipe.json +RUN cargo nextest archive --tests --benches --examples --workspace --all-targets --all-features --archive-file /build/temp.tar.zst ; rm -f /build/temp.tar.zst + +## Cook (release) +FROM chef AS dependencies +WORKDIR /build/src +COPY --from=recipe /build/recipe.json /build/recipe.json +RUN cargo chef cook --tests --benches --examples --workspace --all-targets --all-features --recipe-path /build/recipe.json --release +RUN cargo nextest archive --tests --benches --examples --workspace --all-targets --all-features --archive-file /build/temp.tar.zst --release ; rm -f /build/temp.tar.zst + + +## Build Archive (debug) +FROM dependencies_debug AS build_debug +WORKDIR /build/src +COPY . /build/src +RUN cargo nextest archive --tests --benches --examples --workspace --all-targets --all-features --archive-file /build/torrust-tracker-debug.tar.zst + +## Build Archive (release) +FROM dependencies AS build +WORKDIR /build/src +COPY . /build/src +RUN cargo nextest archive --tests --benches --examples --workspace --all-targets --all-features --archive-file /build/torrust-tracker.tar.zst --release + + +# Extract and Test (debug) +FROM tester as test_debug +WORKDIR /test +COPY . /test/src/ +COPY --from=build_debug \ + /build/torrust-tracker-debug.tar.zst \ + /test/torrust-tracker-debug.tar.zst +RUN cargo nextest run --workspace-remap /test/src/ --extract-to /test/src/ --no-run --archive-file /test/torrust-tracker-debug.tar.zst +RUN cargo nextest run --workspace-remap /test/src/ --target-dir-remap /test/src/target/ --cargo-metadata /test/src/target/nextest/cargo-metadata.json --binaries-metadata /test/src/target/nextest/binaries-metadata.json + +RUN mkdir -p /app/bin/; cp -l /test/src/target/debug/torrust-tracker /app/bin/torrust-tracker +RUN mkdir /app/lib/; cp -l $(realpath $(ldd /app/bin/torrust-tracker | grep "libz\.so\.1" | awk '{print $3}')) /app/lib/libz.so.1 +RUN chown -R root:root /app; chmod -R u=rw,go=r,a+X /app; chmod -R a+x /app/bin + +# Extract and Test (release) +FROM tester as test +WORKDIR /test +COPY . /test/src +COPY --from=build \ + /build/torrust-tracker.tar.zst \ + /test/torrust-tracker.tar.zst +RUN cargo nextest run --workspace-remap /test/src/ --extract-to /test/src/ --no-run --archive-file /test/torrust-tracker.tar.zst +RUN cargo nextest run --workspace-remap /test/src/ --target-dir-remap /test/src/target/ --cargo-metadata /test/src/target/nextest/cargo-metadata.json --binaries-metadata /test/src/target/nextest/binaries-metadata.json + +RUN mkdir -p /app/bin/; cp -l /test/src/target/release/torrust-tracker /app/bin/torrust-tracker +RUN mkdir -p /app/lib/; cp -l $(realpath $(ldd /app/bin/torrust-tracker | grep "libz\.so\.1" | awk '{print $3}')) /app/lib/libz.so.1 +RUN chown -R root:root /app; chmod -R u=rw,go=r,a+X /app; chmod -R a+x /app/bin + + +## Runtime +FROM gcr.io/distroless/cc:debug as runtime +RUN ["/busybox/cp", "-sp", "/busybox/sh","/busybox/cat","/busybox/ls","/busybox/env", "/bin/"] +COPY --from=tester --chmod=0555 /tmp/su-exec/su-exec /bin/su-exec + +ARG TORRUST_TRACKER_PATH_CONFIG="/etc/torrust/tracker/tracker.toml" +ARG TORRUST_TRACKER_DATABASE="sqlite3" +ARG USER_ID=1000 +ARG UDP_PORT=6969 +ARG HTTP_PORT=7070 +ARG API_PORT=1212 + +ENV TORRUST_TRACKER_PATH_CONFIG=${TORRUST_TRACKER_PATH_CONFIG} +ENV TORRUST_TRACKER_DATABASE=${TORRUST_TRACKER_DATABASE} +ENV USER_ID=${USER_ID} +ENV UDP_PORT=${UDP_PORT} +ENV HTTP_PORT=${HTTP_PORT} +ENV API_PORT=${API_PORT} +ENV TZ=Etc/UTC + +EXPOSE ${UDP_PORT}/udp +EXPOSE ${HTTP_PORT}/tcp +EXPOSE ${API_PORT}/tcp + +RUN mkdir -p /var/lib/torrust/tracker /var/log/torrust/tracker /etc/torrust/tracker + +ENV ENV=/etc/profile +COPY --chmod=0555 ./share/container/entry_script_sh /usr/local/bin/entry.sh + +VOLUME ["/var/lib/torrust/tracker","/var/log/torrust/tracker","/etc/torrust/tracker"] + +ENV RUNTIME="runtime" +ENTRYPOINT ["/usr/local/bin/entry.sh"] + + +## Torrust-Tracker (debug) +FROM runtime as debug +ENV RUNTIME="debug" +COPY --from=test_debug /app/ /usr/ +RUN env +CMD ["sh"] + +## Torrust-Tracker (release) (default) +FROM runtime as release +ENV RUNTIME="release" +COPY --from=test /app/ /usr/ +# HEALTHCHECK CMD ["/usr/bin/wget", "--no-verbose", "--tries=1", "--spider", "localhost:${API_PORT}/version"] +CMD ["/usr/bin/torrust-tracker"] diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 96d21fa84..000000000 --- a/Dockerfile +++ /dev/null @@ -1,80 +0,0 @@ -FROM clux/muslrust:stable AS chef -WORKDIR /app -RUN cargo install cargo-chef - - -FROM chef AS planner -WORKDIR /app -COPY . . -RUN cargo chef prepare --recipe-path recipe.json - - -FROM chef as development -WORKDIR /app -ARG UID=1000 -ARG RUN_AS_USER=appuser -ARG TRACKER_UDP_PORT=6969 -ARG TRACKER_HTTP_PORT=7070 -ARG TRACKER_API_PORT=1212 -# Add the app user for development -ENV USER=appuser -ENV UID=$UID -RUN adduser --uid "${UID}" "${USER}" -# Build dependencies -COPY --from=planner /app/recipe.json recipe.json -RUN cargo chef cook --recipe-path recipe.json -# Build the application -COPY . . -RUN cargo build --bin torrust-tracker -USER $RUN_AS_USER:$RUN_AS_USER -EXPOSE $TRACKER_UDP_PORT/udp -EXPOSE $TRACKER_HTTP_PORT/tcp -EXPOSE $TRACKER_API_PORT/tcp -CMD ["cargo", "run"] - - -FROM chef AS builder -WORKDIR /app -ARG UID=1000 -# Add the app user for production -ENV USER=appuser -ENV UID=$UID -RUN adduser \ - --disabled-password \ - --gecos "" \ - --home "/nonexistent" \ - --shell "/sbin/nologin" \ - --no-create-home \ - --uid "${UID}" \ - "${USER}" -# Build dependencies -COPY --from=planner /app/recipe.json recipe.json -RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json -# Build the application -COPY . . -RUN cargo build --release --target x86_64-unknown-linux-musl --bin torrust-tracker -# Strip the binary -# More info: https://github.com/LukeMathWalker/cargo-chef/issues/149 -RUN strip /app/target/x86_64-unknown-linux-musl/release/torrust-tracker - - -FROM alpine:latest -WORKDIR /app -ARG RUN_AS_USER=appuser -ARG TRACKER_UDP_PORT=6969 -ARG TRACKER_HTTP_PORT=7070 -ARG TRACKER_API_PORT=1212 -RUN apk --no-cache add ca-certificates -ENV TZ=Etc/UTC -ENV RUN_AS_USER=$RUN_AS_USER -COPY --from=builder /etc/passwd /etc/passwd -COPY --from=builder /etc/group /etc/group -COPY --from=builder --chown=$RUN_AS_USER \ - /app/target/x86_64-unknown-linux-musl/release/torrust-tracker \ - /app/torrust-tracker -RUN chown -R $RUN_AS_USER:$RUN_AS_USER /app -USER $RUN_AS_USER:$RUN_AS_USER -EXPOSE $TRACKER_UDP_PORT/udp -EXPOSE $TRACKER_HTTP_PORT/tcp -EXPOSE $TRACKER_API_PORT/tcp -ENTRYPOINT ["/app/torrust-tracker"] \ No newline at end of file diff --git a/README.md b/README.md index b419c12c1..832af0d85 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,35 @@ # Torrust Tracker -[![Build & Release](https://github.com/torrust/torrust-tracker/actions/workflows/build_release.yml/badge.svg)](https://github.com/torrust/torrust-tracker/actions/workflows/build_release.yml) [![CI](https://github.com/torrust/torrust-tracker/actions/workflows/test_build_release.yml/badge.svg)](https://github.com/torrust/torrust-tracker/actions/workflows/test_build_release.yml) [![Publish crate](https://github.com/torrust/torrust-tracker/actions/workflows/publish_crate.yml/badge.svg)](https://github.com/torrust/torrust-tracker/actions/workflows/publish_crate.yml) [![Publish docker image](https://github.com/torrust/torrust-tracker/actions/workflows/publish_docker_image.yml/badge.svg)](https://github.com/torrust/torrust-tracker/actions/workflows/publish_docker_image.yml) [![Test](https://github.com/torrust/torrust-tracker/actions/workflows/test.yml/badge.svg)](https://github.com/torrust/torrust-tracker/actions/workflows/test.yml) [![Test docker build](https://github.com/torrust/torrust-tracker/actions/workflows/test_docker.yml/badge.svg)](https://github.com/torrust/torrust-tracker/actions/workflows/test_docker.yml) [![Coverage](https://github.com/torrust/torrust-tracker/actions/workflows/coverage.yaml/badge.svg)](https://github.com/torrust/torrust-tracker/actions/workflows/coverage.yaml) +[![container_wf_b]][container_wf] [![coverage_wf_b]][coverage_wf] [![testing_wf_b]][testing_wf] -Torrust Tracker is a lightweight but incredibly high-performance and feature-rich BitTorrent tracker written in [Rust](https://www.rust-lang.org/). +Torrust Tracker is a lightweight but incredibly high-performance and feature-rich BitTorrent tracker written in [Rust Language][rust]. It aims to provide a reliable and efficient solution for serving torrents to a vast number of peers while maintaining a high level of performance, robustness, extensibility, security, usability and with community-driven development. +_We have a [container guide][containers.md] to get started with Docker or Podman_ + ## Key Features -* [X] Multiple UDP server and HTTP(S) server blocks for socket binding are possible. -* [X] Full IPv4 and IPv6 support for both UDP and HTTP(S). -* [X] Private & Whitelisted mode. -* [X] Built-in API. -* [X] Torrent whitelisting. -* [X] Peer authentication using time-bound keys. -* [X] [newTrackon](https://newtrackon.com/) check is supported for both HTTP and UDP, where IPv4 and IPv6 are properly handled. -* [X] SQLite3 and MySQL persistence, loading and saving of the torrent hashes and downloads completed count. -* [X] Comprehensive documentation. -* [X] A complete suite of tests. See [code coverage](https://app.codecov.io/gh/torrust/torrust-tracker) report. +* [x] Multiple UDP server and HTTP(S) server blocks for socket binding are possible. +* [x] Full IPv4 and IPv6 support for both UDP and HTTP(S). +* [x] Private & Whitelisted mode. +* [x] Built-in API. +* [x] Torrent whitelisting. +* [x] Peer authentication using time-bound keys. +* [x] [newTrackon][newtrackon] check is supported for both HTTP and UDP, where IPv4 and IPv6 are properly handled. +* [x] SQLite3 and MySQL persistence, loading and saving of the torrent hashes and downloads completed count. +* [x] Comprehensive documentation. +* [x] A complete suite of tests. See our [code coverage report][coverage]. ## Implemented BEPs -* [BEP 3](https://www.bittorrent.org/beps/bep_0003.html): The BitTorrent Protocol. -* [BEP 7](https://www.bittorrent.org/beps/bep_0007.html): IPv6 Support. -* [BEP 15](http://www.bittorrent.org/beps/bep_0015.html): UDP Tracker Protocol for BitTorrent. -* [BEP 23](http://bittorrent.org/beps/bep_0023.html): Tracker Returns Compact Peer Lists. -* [BEP 27](http://bittorrent.org/beps/bep_0027.html): Private Torrents. -* [BEP 48](http://bittorrent.org/beps/bep_0048.html): Tracker Protocol Extension: Scrape. +* [BEP 03]: The BitTorrent Protocol. +* [BEP 07]: IPv6 Support. +* [BEP 15]: UDP Tracker Protocol for BitTorrent. +* [BEP 23]: Tracker Returns Compact Peer Lists. +* [BEP 27]: Private Torrents. +* [BEP 48]: Tracker Protocol Extension: Scrape. + ## Getting Started @@ -35,19 +38,36 @@ Requirements: * Rust Stable `1.68` * You might have problems compiling with a machine or docker container with low resources. It has been tested with docker containers with 6 CPUs, 7.5 GM of memory and 2GB of swap. -You can follow the [documentation](https://docs.rs/torrust-tracker/) to install and use Torrust Tracker in different ways, but if you want to give it a quick try, you can use the following commands: +You can follow the [documentation] to install and use Torrust Tracker in different ways, but if you want to give it a quick try, you can use the following commands: ```s git clone https://github.com/torrust/torrust-tracker.git \ && cd torrust-tracker \ && cargo build --release \ - && mkdir -p ./storage/database \ - && mkdir -p ./storage/ssl_certificates + && mkdir -p ./storage/tracker/lib/database \ + && mkdir -p ./storage/tracker/lib/tls ``` -And then run `cargo run` twice. The first time to generate the `config.toml` file and the second time to run the tracker with the default configuration. +### Configuration + +The [default configuration folder: `/share/default/config`][share.default.config]: + +- Contains the [development default][src.bootstrap.config.default] i.e: [`tracker.development.sqlite3.toml`][tracker.development.sqlite3.toml]. + +- Also contains the container defaults: [`sqlite3`][tracker.container.sqlite3.toml] and [`mysql`][tracker.container.mysql.toml]. + +To override the default configuration there is two options: -After running the tracker these services will be available: +- Configure a different configuration path by setting the [`TORRUST_TRACKER_PATH_CONFIG`][src.bootstrap.config.path.config] environmental variable. + +- Supply the entire configuration via the [`TORRUST_TRACKER_CONFIG`][src.bootstrap.config.config] environmental variable. + + +> NOTE: It is recommended for production you override the `api admin token` by placing your secret in the [`ENV_VAR_API_ADMIN_TOKEN`][src.bootstrap.config.admin.token] environmental variable. + + +### Services +After running the tracker these services will be available (as defined in the default configuration): * UDP tracker: `udp://127.0.0.1:6969/announce`. * HTTP tracker: `http://127.0.0.1:6969/announce`. @@ -55,10 +75,10 @@ After running the tracker these services will be available: ## Documentation -* [Crate documentation](https://docs.rs/torrust-tracker/). -* [API `v1`](https://docs.rs/torrust-tracker/3.0.0-alpha.3/torrust_tracker/servers/apis/v1). -* [HTTP Tracker](https://docs.rs/torrust-tracker/3.0.0-alpha.3/torrust_tracker/servers/http). -* [UDP Tracker](https://docs.rs/torrust-tracker/3.0.0-alpha.3/torrust_tracker/servers/udp). +* [Crate documentation] +* [API `v1`] +* [HTTP Tracker] +* [UDP Tracker] ## Contributing @@ -67,14 +87,65 @@ We welcome contributions from the community! How can you contribute? * Bug reports and feature requests. -* Code contributions. You can start by looking at the issues labeled ["good first issues"](https://github.com/torrust/torrust-tracker/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). -* Documentation improvements. Check the [documentation](https://docs.rs/torrust-tracker/) and [API documentation](https://docs.rs/torrust-tracker/3.0.0-alpha.3/torrust_tracker/servers/apis/v1) for typos, errors, or missing information. -* Participation in the community. You can help by answering questions in the [discussions](https://github.com/torrust/torrust-tracker/discussions). +* Code contributions. You can start by looking at the issues labeled "[good first issues]". +* Documentation improvements. Check the [documentation] and [API documentation] for typos, errors, or missing information. +* Participation in the community. You can help by answering questions in the [discussions]. ## License -The project is licensed under a dual license. See [COPYRIGHT](./COPYRIGHT). +The project is licensed under a dual license. See [COPYRIGHT]. ## Acknowledgments -This project was a joint effort by [Nautilus Cyberneering GmbH](https://nautilus-cyberneering.de/) and [Dutch Bits](https://dutchbits.nl). Also thanks to [Naim A.](https://github.com/naim94a/udpt) and [greatest-ape](https://github.com/greatest-ape/aquatic) for some parts of the code. Further added features and functions thanks to [Power2All](https://github.com/power2all). +This project was a joint effort by [Nautilus Cyberneering GmbH][nautilus] and [Dutch Bits]. Also thanks to [Naim A.] and [greatest-ape] for some parts of the code. Further added features and functions thanks to [Power2All]. + + + +[container_wf]: https://github.com/torrust/torrust-tracker/actions/workflows/container.yaml +[container_wf_b]: https://github.com/torrust/torrust-tracker/actions/workflows/container.yaml/badge.svg +[coverage_wf]: https://github.com/torrust/torrust-tracker/actions/workflows/coverage.yaml +[coverage_wf_b]: https://github.com/torrust/torrust-tracker/actions/workflows/coverage.yaml/badge.svg +[testing_wf]: https://github.com/torrust/torrust-tracker/actions/workflows/testing.yaml +[testing_wf_b]: https://github.com/torrust/torrust-tracker/actions/workflows/testing.yaml/badge.svg + +[rust]: https://www.rust-lang.org/ +[newtrackon]: https://newtrackon.com/ +[coverage]: https://app.codecov.io/gh/torrust/torrust-tracker + +[BEP 03]: https://www.bittorrent.org/beps/bep_0003.html +[BEP 07]: https://www.bittorrent.org/beps/bep_0007.html +[BEP 15]: http://www.bittorrent.org/beps/bep_0015.html +[BEP 23]: http://bittorrent.org/beps/bep_0023.html +[BEP 27]: http://bittorrent.org/beps/bep_0027.html +[BEP 48]: http://bittorrent.org/beps/bep_0048.html + +[containers.md]: ./docs/containers.md + +[share.default.config]: ./share/default/config/ +[tracker.development.sqlite3.toml]: ./share/default/config/tracker.development.sqlite3.toml +[src.bootstrap.config.default]: ./src/bootstrap/config.rs#L18 +[tracker.container.sqlite3.toml]: ./share/default/config/tracker.container.sqlite3.toml +[tracker.container.mysql.toml]: ./share/default/config/tracker.container.mysql.toml +[share.container.entry_script_sh.default]: ./share/container/entry_script_sh#L10 + +[src.bootstrap.config.path.config]: ./src/bootstrap/config.rs#L15 +[src.bootstrap.config.config]: ./src/bootstrap/config.rs#L11 +[src.bootstrap.config.admin.token]: ./src/bootstrap/config.rs#L12 + +[Crate documentation]: https://docs.rs/torrust-tracker/ +[API `v1`]: https://docs.rs/torrust-tracker/3.0.0-alpha.4/torrust_tracker/servers/apis/v1 +[HTTP Tracker]: https://docs.rs/torrust-tracker/3.0.0-alpha.4/torrust_tracker/servers/http +[UDP Tracker]: https://docs.rs/torrust-tracker/3.0.0-alpha.4/torrust_tracker/servers/udp + +[good first issues]: https://github.com/torrust/torrust-tracker/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 +[documentation]: https://docs.rs/torrust-tracker/ +[API documentation]: https://docs.rs/torrust-tracker/3.0.0-alpha.4/torrust_tracker/servers/apis/v1 +[discussions]: https://github.com/torrust/torrust-tracker/discussions + +[COPYRIGHT]: ./COPYRIGHT + +[nautilus]: https://nautilus-cyberneering.de/ +[Dutch Bits]: https://dutchbits.nl +[Naim A.]: https://github.com/naim94a/udpt +[greatest-ape]: https://github.com/greatest-ape/aquatic +[Power2All]: https://github.com/power2all diff --git a/bin/install-demo.sh b/bin/install-demo.sh deleted file mode 100755 index 1b829ca1d..000000000 --- a/bin/install-demo.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Single command to setup and run the tracker using the pre-built image. - -# Check if 'storage' directory exists -if [ -d "./storage" ]; then - echo "Warning: 'storage' directory already exists. Please remove or rename it before proceeding." - exit 1 -fi - -# Check if 'config.toml' file exists in the current directory -if [ -f "./config.toml" ]; then - echo "Warning: 'config.toml' file already exists in the root directory. Please remove or rename it before proceeding." - exit 1 -fi - -# Check if SQLite3 is installed -if ! command -v sqlite3 &> /dev/null; then - echo "Warning: SQLite3 is not installed on your system. Please install it and retry." - exit 1 -fi - -wget https://raw.githubusercontent.com/torrust/torrust-tracker/v3.0.0-alpha.3/config.toml.local -O config.toml \ - && mkdir -p ./storage/database \ - && mkdir -p ./storage/ssl_certificates \ - && touch ./storage/database/data.db \ - && echo ";" | sqlite3 ./storage/database/data.db diff --git a/bin/install.sh b/bin/install.sh deleted file mode 100755 index 82ea940d0..000000000 --- a/bin/install.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# This script is only intended to be used for local development or testing environments. - -# Generate the default settings file if it does not exist -if ! [ -f "./config.toml" ]; then - cp ./config.toml.local ./config.toml -fi - -# Generate storage directory if it does not exist -mkdir -p "./storage/database" - -# Generate the sqlite database if it does not exist -if ! [ -f "./storage/database/data.db" ]; then - # todo: it should get the path from config.toml and only do it when we use sqlite - touch ./storage/database/data.db - echo ";" | sqlite3 ./storage/database/data.db -fi diff --git a/cSpell.json b/cSpell.json index 6a68e045d..fc9db42b7 100644 --- a/cSpell.json +++ b/cSpell.json @@ -1,8 +1,10 @@ { "words": [ + "adduser", "alekitto", "appuser", "Arvid", + "autoclean", "AUTOINCREMENT", "automock", "Avicora", @@ -12,6 +14,7 @@ "bencoded", "beps", "binascii", + "binstall", "Bitflu", "bools", "bufs", @@ -26,12 +29,17 @@ "codegen", "completei", "connectionless", + "Containerfile", + "curr", + "Cyberneering", "datetime", + "distroless", "dockerhub", "downloadedi", "dtolnay", "filesd", "Freebox", + "gecos", "Grcov", "hasher", "hexlify", @@ -45,11 +53,14 @@ "infoschema", "Intermodal", "intervali", + "keyout", "lcov", "leecher", "leechers", "libsqlite", "libtorrent", + "libz", + "LOGNAME", "Lphant", "metainfo", "middlewares", @@ -58,16 +69,21 @@ "myacicontext", "Naim", "nanos", + "newkey", "nextest", "nocapture", + "nologin", + "nonroot", "Norberg", "numwant", "oneshot", "ostr", "Pando", "proot", + "proto", "Quickstart", "Rasterbar", + "realpath", "reannounce", "repr", "reqwest", @@ -80,8 +96,10 @@ "rustfmt", "Rustls", "Seedable", + "serde", "Shareaza", "sharktorrent", + "SHLVL", "socketaddr", "sqllite", "subsec", @@ -89,6 +107,7 @@ "Swiftbit", "taiki", "thiserror", + "tlsv", "Torrentstorm", "torrust", "torrustracker", @@ -100,6 +119,7 @@ "uroot", "Vagaa", "Vuze", + "Werror", "whitespaces", "XBTT", "Xeon", @@ -107,5 +127,10 @@ "Xunlei", "xxxxxxxxxxxxxxxxxxxxd", "yyyyyyyyyyyyyyyyyyyyd" + ], + "enableFiletypes": [ + "dockerfile", + "shellscript", + "toml" ] -} +} \ No newline at end of file diff --git a/compose.yaml b/compose.yaml index 49f3055a8..02f95bccc 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,12 +1,11 @@ name: torrust services: - tracker: - build: - context: . - target: development - user: ${TORRUST_TRACKER_USER_UID:-1000}:${TORRUST_TRACKER_USER_UID:-1000} + image: torrust-tracker:release tty: true + environment: + - TORRUST_TRACKER_DATABASE=${TORRUST_TRACKER_DATABASE:-mysql} + - TORRUST_TRACKER_API_ADMIN_TOKEN=${TORRUST_TRACKER_API_ADMIN_TOKEN:-MyAccessToken} networks: - server_side ports: @@ -14,19 +13,24 @@ services: - 7070:7070 - 1212:1212 volumes: - - ./:/app - - ~/.cargo:/home/appuser/.cargo + - ./storage/tracker/lib:/var/lib/torrust/tracker:Z + - ./storage/tracker/log:/var/log/torrust/tracker:Z + - ./storage/tracker/etc:/etc/torrust/tracker:Z depends_on: - mysql mysql: image: mysql:8.0 - command: '--default-authentication-plugin=mysql_native_password' + command: "--default-authentication-plugin=mysql_native_password" healthcheck: - test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent'] + test: + [ + "CMD-SHELL", + 'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent', + ] interval: 3s retries: 5 - start_period: 30s + start_period: 30s environment: - MYSQL_ROOT_HOST=% - MYSQL_ROOT_PASSWORD=root_secret_password @@ -44,4 +48,4 @@ networks: server_side: {} volumes: - mysql_data: {} \ No newline at end of file + mysql_data: {} diff --git a/contrib/bencode/src/reference/decode.rs b/contrib/bencode/src/reference/decode.rs index d2aa180f8..d18dffda0 100644 --- a/contrib/bencode/src/reference/decode.rs +++ b/contrib/bencode/src/reference/decode.rs @@ -1,3 +1,5 @@ +#![allow(clippy::should_panic_without_expect)] + use std::collections::btree_map::Entry; use std::collections::BTreeMap; use std::str::{self}; diff --git a/contrib/dev-tools/containers/docker-build.sh b/contrib/dev-tools/containers/docker-build.sh new file mode 100755 index 000000000..39143910f --- /dev/null +++ b/contrib/dev-tools/containers/docker-build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Building docker image ..." + +docker build --target release --tag torrust-tracker:release --file Containerfile . diff --git a/contrib/dev-tools/containers/docker-install.sh b/contrib/dev-tools/containers/docker-install.sh new file mode 100755 index 000000000..6034e8233 --- /dev/null +++ b/contrib/dev-tools/containers/docker-install.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./contrib/dev-tools/containers/docker-build.sh diff --git a/contrib/dev-tools/containers/docker-run-local.sh b/contrib/dev-tools/containers/docker-run-local.sh new file mode 100755 index 000000000..05e23f4a0 --- /dev/null +++ b/contrib/dev-tools/containers/docker-run-local.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/ + +docker run -it \ + --env USER_ID"$(id -u)" \ + --publish 6969:6969/udp \ + --publish 7070:7070/tcp \ + --publish 1212:1212/tcp \ + --volume ./storage/tracker/lib:/var/lib/torrust/tracker:rw \ + --volume ./storage/tracker/log:/var/log/torrust/tracker:rw \ + --volume ./storage/tracker/etc:/etc/torrust/tracker:rw \ + torrust-tracker:release diff --git a/contrib/dev-tools/containers/docker-run-public.sh b/contrib/dev-tools/containers/docker-run-public.sh new file mode 100755 index 000000000..73bcf600a --- /dev/null +++ b/contrib/dev-tools/containers/docker-run-public.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/ + +docker run -it \ + --env USER_ID"$(id -u)" \ + --publish 6969:6969/udp \ + --publish 7070:7070/tcp \ + --publish 1212:1212/tcp \ + --volume ./storage/tracker/lib:/var/lib/torrust/tracker:rw \ + --volume ./storage/tracker/log:/var/log/torrust/tracker:rw \ + --volume ./storage/tracker/etc:/etc/torrust/tracker:rw \ + torrust/tracker:latest diff --git a/contrib/dev-tools/init/install-local.sh b/contrib/dev-tools/init/install-local.sh new file mode 100755 index 000000000..f9806a0b8 --- /dev/null +++ b/contrib/dev-tools/init/install-local.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# This script is only intended to be used for local development or testing environments. + +# Generate storage directory if it does not exist +mkdir -p ./storage/tracker/lib/database + +# Generate the sqlite database if it does not exist +if ! [ -f "./storage/tracker/lib/database/sqlite3.db" ]; then + # todo: it should get the path from tracker.toml and only do it when we use sqlite + sqlite3 ./storage/tracker/lib/database/sqlite3.db "VACUUM;" +fi diff --git a/contrib/dev-tools/su-exec/LICENSE b/contrib/dev-tools/su-exec/LICENSE new file mode 100644 index 000000000..f623b904e --- /dev/null +++ b/contrib/dev-tools/su-exec/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 ncopa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/contrib/dev-tools/su-exec/Makefile b/contrib/dev-tools/su-exec/Makefile new file mode 100644 index 000000000..bda768957 --- /dev/null +++ b/contrib/dev-tools/su-exec/Makefile @@ -0,0 +1,17 @@ + +CFLAGS ?= -Wall -Werror -g +LDFLAGS ?= + +PROG := su-exec +SRCS := $(PROG).c + +all: $(PROG) + +$(PROG): $(SRCS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +$(PROG)-static: $(SRCS) + $(CC) $(CFLAGS) -o $@ $^ -static $(LDFLAGS) + +clean: + rm -f $(PROG) $(PROG)-static diff --git a/contrib/dev-tools/su-exec/README.md b/contrib/dev-tools/su-exec/README.md new file mode 100644 index 000000000..2b0517377 --- /dev/null +++ b/contrib/dev-tools/su-exec/README.md @@ -0,0 +1,46 @@ +# su-exec +switch user and group id, setgroups and exec + +## Purpose + +This is a simple tool that will simply execute a program with different +privileges. The program will be executed directly and not run as a child, +like su and sudo does, which avoids TTY and signal issues (see below). + +Notice that su-exec depends on being run by the root user, non-root +users do not have permission to change uid/gid. + +## Usage + +```shell +su-exec user-spec command [ arguments... ] +``` + +`user-spec` is either a user name (e.g. `nobody`) or user name and group +name separated with colon (e.g. `nobody:ftp`). Numeric uid/gid values +can be used instead of names. Example: + +```shell +$ su-exec apache:1000 /usr/sbin/httpd -f /opt/www/httpd.conf +``` + +## TTY & parent/child handling + +Notice how `su` will make `ps` be a child of a shell while `su-exec` +just executes `ps` directly. + +```shell +$ docker run -it --rm alpine:edge su postgres -c 'ps aux' +PID USER TIME COMMAND + 1 postgres 0:00 ash -c ps aux + 12 postgres 0:00 ps aux +$ docker run -it --rm -v $PWD/su-exec:/sbin/su-exec:ro alpine:edge su-exec postgres ps aux +PID USER TIME COMMAND + 1 postgres 0:00 ps aux +``` + +## Why reinvent gosu? + +This does more or less exactly the same thing as [gosu](https://github.com/tianon/gosu) +but it is only 10kb instead of 1.8MB. + diff --git a/contrib/dev-tools/su-exec/su-exec.c b/contrib/dev-tools/su-exec/su-exec.c new file mode 100644 index 000000000..499071c6e --- /dev/null +++ b/contrib/dev-tools/su-exec/su-exec.c @@ -0,0 +1,109 @@ +/* set user and group id and exec */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static char *argv0; + +static void usage(int exitcode) +{ + printf("Usage: %s user-spec command [args]\n", argv0); + exit(exitcode); +} + +int main(int argc, char *argv[]) +{ + char *user, *group, **cmdargv; + char *end; + + uid_t uid = getuid(); + gid_t gid = getgid(); + + argv0 = argv[0]; + if (argc < 3) + usage(0); + + user = argv[1]; + group = strchr(user, ':'); + if (group) + *group++ = '\0'; + + cmdargv = &argv[2]; + + struct passwd *pw = NULL; + if (user[0] != '\0') { + uid_t nuid = strtol(user, &end, 10); + if (*end == '\0') + uid = nuid; + else { + pw = getpwnam(user); + if (pw == NULL) + err(1, "getpwnam(%s)", user); + } + } + if (pw == NULL) { + pw = getpwuid(uid); + } + if (pw != NULL) { + uid = pw->pw_uid; + gid = pw->pw_gid; + } + + setenv("HOME", pw != NULL ? pw->pw_dir : "/", 1); + + if (group && group[0] != '\0') { + /* group was specified, ignore grouplist for setgroups later */ + pw = NULL; + + gid_t ngid = strtol(group, &end, 10); + if (*end == '\0') + gid = ngid; + else { + struct group *gr = getgrnam(group); + if (gr == NULL) + err(1, "getgrnam(%s)", group); + gid = gr->gr_gid; + } + } + + if (pw == NULL) { + if (setgroups(1, &gid) < 0) + err(1, "setgroups(%i)", gid); + } else { + int ngroups = 0; + gid_t *glist = NULL; + + while (1) { + int r = getgrouplist(pw->pw_name, gid, glist, &ngroups); + + if (r >= 0) { + if (setgroups(ngroups, glist) < 0) + err(1, "setgroups"); + break; + } + + glist = realloc(glist, ngroups * sizeof(gid_t)); + if (glist == NULL) + err(1, "malloc"); + } + } + + if (setgid(gid) < 0) + err(1, "setgid(%i)", gid); + + if (setuid(uid) < 0) + err(1, "setuid(%i)", uid); + + execvp(cmdargv[0], cmdargv); + err(1, "%s", cmdargv[0]); + + return 1; +} diff --git a/docker/README.md b/docker/README.md deleted file mode 100644 index 207dadbbc..000000000 --- a/docker/README.md +++ /dev/null @@ -1,289 +0,0 @@ -# Docker - -## Requirements - -- Docker version 20.10.21 -- You need to create the `storage` directory with this structure and files: - -```s -$ tree storage/ -storage/ -├── database -│   └── data.db -└── ssl_certificates - ├── localhost.crt - └── localhost.key -``` - -> NOTE: you only need the `ssl_certificates` directory and certificates in case you have enabled SSL for the one HTTP tracker or the API. - -## Demo environment - -You can run a single command to setup the tracker with the default -configuration and run it using the pre-built public docker image: - -```s -curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/torrust/torrust-tracker/v3.0.0-alpha.3/bin/install-demo.sh | bash -export TORRUST_TRACKER_USER_UID=1000 \ - && docker run -it \ - --user="$TORRUST_TRACKER_USER_UID" \ - --publish 6969:6969/udp \ - --publish 7070:7070/tcp \ - --publish 1212:1212/tcp \ - --volume "$(pwd)/storage":"/app/storage" \ - --volume "$(pwd)/config.toml":"/app/config.toml":ro \ - torrust/tracker:3.0.0-alpha.3 -``` - -This is intended to be used to run a quick demo of the application. - -## Dev environment - -When using docker you have to bind the exposed ports to the wildcard address `0.0.0.0`, so you can access the application from the host machine. - -The default API configuration uses `127.0.0.1`, so you have to change it to: - -```toml -[http_api] -bind_address = "0.0.0.0:1212" -``` - -Otherwise, the API will be only accessible from inside the container. - -### With docker - -Build and run locally. You can build the docker image locally: - -```s -docker context use default -export TORRUST_TRACKER_USER_UID=1000 -./docker/bin/build.sh $TORRUST_TRACKER_USER_UID -./bin/install.sh -./docker/bin/run-local-image.sh $TORRUST_TRACKER_USER_UID -``` - -Or you can run locally using the pre-built docker image: - -```s -docker context use default -export TORRUST_TRACKER_USER_UID=1000 -./bin/install.sh -./docker/bin/run-public-image.sh $TORRUST_TRACKER_USER_UID -``` - -In both cases, you will need to: - -- Create the SQLite DB (`data.db`) if you are going to use SQLite. -- Create the configuration file (`config.toml`) before running the tracker. -- Replace the user UID (`1000`) with yours. - -> NOTICE: that the `./bin/install.sh` can setup the application for you. But it -uses a predefined configuration. - -Remember to switch to your default docker context `docker context use default` -and to change the API default configuration in `config.toml` to make it -available from the host machine: - -```toml -[http_api] -bind_address = "0.0.0.0:1212" -``` - -### With docker-compose - -The docker-compose configuration includes the MySQL service configuration. If you want to use MySQL instead of SQLite you have to change your `config.toml` configuration: - -```toml -db_driver = "MySQL" -db_path = "mysql://db_user:db_user_secret_password@mysql:3306/torrust_tracker" -``` - -If you want to inject an environment variable into docker-compose you can use the file `.env`. There is a template `.env.local`. - -Build and run it locally: - -```s -docker compose up --build -``` - -After running the "up" command you will have two running containers: - -```s -$ docker ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -06feacb91a9e torrust-tracker "cargo run" 18 minutes ago Up 4 seconds 0.0.0.0:1212->1212/tcp, :::1212->1212/tcp, 0.0.0.0:7070->7070/tcp, :::7070->7070/tcp, 0.0.0.0:6969->6969/udp, :::6969->6969/udp torrust-tracker-1 -34d29e792ee2 mysql:8.0 "docker-entrypoint.s…" 18 minutes ago Up 5 seconds (healthy) 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp torrust-mysql-1 -``` - -And you should be able to use the application, for example making a request to the API: - - - -You can stop the containers with: - -```s -docker compose down -``` - -Additionally, you can delete all resources (containers, volumes, networks) with: - -```s -docker compose down -v -``` - -### Access Mysql with docker - -These are some useful commands for MySQL. - -Open a shell in the MySQL container using docker or docker-compose. - -```s -docker exec -it torrust-mysql-1 /bin/bash -docker compose exec mysql /bin/bash -``` - -Connect to MySQL from inside the MySQL container or from the host: - -```s -mysql -h127.0.0.1 -uroot -proot_secret_password -``` - -The when MySQL container is started the first time, it creates the database, user, and permissions needed. -If you see the error "Host is not allowed to connect to this MySQL server" you can check that users have the right permissions in the database. Make sure the user `root` and `db_user` can connect from any host (`%`). - -```s -mysql> SELECT host, user FROM mysql.user; -+-----------+------------------+ -| host | user | -+-----------+------------------+ -| % | db_user | -| % | root | -| localhost | mysql.infoschema | -| localhost | mysql.session | -| localhost | mysql.sys | -| localhost | root | -+-----------+------------------+ -6 rows in set (0.00 sec) -``` - -If the database, user or permissions are not created the reason could be the MySQL container volume can be corrupted. Delete it and start again the containers. - -### SSL Certificates - -You can use a certificate for localhost. You can create your [localhost certificate](https://letsencrypt.org/docs/certificates-for-localhost/#making-and-trusting-your-own-certificates) and use it in the `storage` folder and the configuration file (`config.toml`). For example: - -The storage folder must contain your certificates: - -```s -$ tree storage/ -storage/ -├── database -│   └── data.db -└── ssl_certificates - ├── localhost.crt - └── localhost.key -``` - -You have not enabled it in your `config.toml` file: - -```toml -... -[[http_trackers]] -enabled = true -bind_address = "0.0.0.0:7070" -ssl_enabled = true -ssl_cert_path = "./storage/ssl_certificates/localhost.crt" -ssl_key_path = "./storage/ssl_certificates/localhost.key" - -[http_api] -enabled = true -bind_address = "0.0.0.0:1212" -ssl_enabled = true -ssl_cert_path = "./storage/ssl_certificates/localhost.crt" -ssl_key_path = "./storage/ssl_certificates/localhost.key" -... -``` - -> NOTE: you can enable it independently for each HTTP tracker or the API. - -If you enable the SSL certificate for the API, for example, you can load the API with this URL: - - - -## Prod environment - -In this section, you will learn how to deploy the tracker to a single docker container in Azure Container Instances. - -> NOTE: Azure Container Instances is a solution when you want to run an isolated container. If you need full container orchestration, including service discovery across multiple containers, automatic scaling, and coordinated application upgrades, we recommend [Kubernetes](https://kubernetes.io/). - -Deploy to Azure Container Instance following [docker documentation](https://docs.docker.com/cloud/aci-integration/). - -You have to create the ACI context and the storage: - -```s -docker context create aci myacicontext -docker context use myacicontext -docker volume create test-volume --storage-account torrustracker -``` - -You need to create all the files needed by the application in the storage dir `storage/database`. - -And finally, you can run the container: - -```s -docker run \ - --publish 6969:6969/udp \ - --publish 7070:7070/tcp \ - --publish 1212:1212/tcp \ - --volume torrustracker/test-volume:/app/storage \ - registry.hub.docker.com/torrust/tracker:latest -``` - -Detach from container logs when the container starts. By default, the command line stays attached and follows container logs. - -```s -docker run \ - --detach - --publish 6969:6969/udp \ - --publish 7070:7070/tcp \ - --publish 1212:1212/tcp \latest - --volume torrustracker/test-volume:/app/storage \ - registry.hub.docker.com/torrust/tracker:latest -``` - -You should see something like this: - -```s -[+] Running 2/2 - ⠿ Group intelligent-hawking Created 5.0s - ⠿ intelligent-hawking Created 41.7s -2022-12-08T18:39:19.697869300+00:00 [torrust_tracker::logging][INFO] logging initialized. -2022-12-08T18:39:19.712651100+00:00 [torrust_tracker::jobs::udp_tracker][INFO] Starting UDP server on: 0.0.0.0:6969 -2022-12-08T18:39:19.712792700+00:00 [torrust_tracker::jobs::tracker_api][INFO] Starting Torrust API server on: 0.0.0.0:1212 -2022-12-08T18:39:19.725124+00:00 [torrust_tracker::jobs::tracker_api][INFO] Torrust API server started -``` - -You can see the container with: - -```s -$ docker ps -CONTAINER ID IMAGE COMMAND STATUS PORTS -intelligent-hawking registry.hub.docker.com/torrust/tracker:latest Running 4.236.213.57:6969->6969/udp, 4.236.213.57:1212->1212/tcp -``` - -After a while, you can use the tracker API `http://4.236.213.57:1212/api/v1/stats?token=MyAccessToken` and the UDP tracker with your BitTorrent client using this tracker announce URL `udp://4.236.213.57:6969`. - -> NOTES: -> -> - [There is no support for mounting a single file](https://docs.docker.com/cloud/aci-container-features/#persistent-volumes), or mounting a subfolder from an `Azure File Share`. -> - [ACI does not allow port mapping](https://docs.docker.com/cloud/aci-integration/#exposing-ports). -> - [Azure file share volume mount requires the Linux container run as root](https://learn.microsoft.com/en-us/azure/container-instances/container-instances-volume-azure-files#limitations). -> - It can take some minutes until the public IP for the ACI container is available. -> - You can use the Azure web UI to download files from the storage. For example, the SQLite database. -> - [It seems you can only expose web interfaces on port 80 on Azure Container Instances](https://stackoverflow.com/a/56768087/3012842). Not official documentation! - -## Links - -- [Deploying Docker containers on Azure](https://docs.docker.com/cloud/aci-integration/). -- [Docker run options for ACI containers](https://docs.docker.com/cloud/aci-container-features/). -- [Quickstart: Deploy a container instance in Azure using the Docker CLI](https://learn.microsoft.com/en-us/azure/container-instances/quickstart-docker-cli). diff --git a/docker/bin/build.sh b/docker/bin/build.sh deleted file mode 100755 index d77d1ad34..000000000 --- a/docker/bin/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -TORRUST_TRACKER_USER_UID=${TORRUST_TRACKER_USER_UID:-1000} -TORRUST_TRACKER_RUN_AS_USER=${TORRUST_TRACKER_RUN_AS_USER:-appuser} - -echo "Building docker image ..." -echo "TORRUST_TRACKER_USER_UID: $TORRUST_TRACKER_USER_UID" -echo "TORRUST_TRACKER_RUN_AS_USER: $TORRUST_TRACKER_RUN_AS_USER" - -docker build \ - --build-arg UID="$TORRUST_TRACKER_USER_UID" \ - --build-arg RUN_AS_USER="$TORRUST_TRACKER_RUN_AS_USER" \ - -t torrust-tracker . diff --git a/docker/bin/install.sh b/docker/bin/install.sh deleted file mode 100755 index a58969378..000000000 --- a/docker/bin/install.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -./docker/bin/build.sh -./bin/install.sh diff --git a/docker/bin/run-local-image.sh b/docker/bin/run-local-image.sh deleted file mode 100755 index 86465baeb..000000000 --- a/docker/bin/run-local-image.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -TORRUST_TRACKER_USER_UID=${TORRUST_TRACKER_USER_UID:-1000} -TORRUST_TRACKER_CONFIG=$(cat config.toml) - -docker run -it \ - --user="$TORRUST_TRACKER_USER_UID" \ - --publish 6969:6969/udp \ - --publish 7070:7070/tcp \ - --publish 1212:1212/tcp \ - --env TORRUST_TRACKER_CONFIG="$TORRUST_TRACKER_CONFIG" \ - --volume "$(pwd)/storage":"/app/storage" \ - torrust-tracker diff --git a/docker/bin/run-public-image.sh b/docker/bin/run-public-image.sh deleted file mode 100755 index 50407f91b..000000000 --- a/docker/bin/run-public-image.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -TORRUST_TRACKER_USER_UID=${TORRUST_TRACKER_USER_UID:-1000} -TORRUST_TRACKER_CONFIG=$(cat config.toml) - -docker run -it \ - --user="$TORRUST_TRACKER_USER_UID" \ - --publish 6969:6969/udp \ - --publish 7070:7070/tcp \ - --publish 1212:1212/tcp \ - --env TORRUST_TRACKER_CONFIG="$TORRUST_TRACKER_CONFIG" \ - --volume "$(pwd)/storage":"/app/storage" \ - torrust/tracker \ No newline at end of file diff --git a/docs/containers.md b/docs/containers.md new file mode 100644 index 000000000..b9aa05a7a --- /dev/null +++ b/docs/containers.md @@ -0,0 +1,428 @@ +# Containers (Docker or Podman) + +## Demo environment +It is simple to setup the tracker with the default +configuration and run it using the pre-built public docker image: + + +With Docker: + +```sh +docker run -it torrust/tracker:latest +``` + +or with Podman: + +```sh +podman run -it torrust/tracker:latest +``` + + +## Requirements +- Tested with recent versions of Docker or Podman. + +## Volumes +The [Containerfile](../Containerfile) (i.e. the Dockerfile) Defines Three Volumes: + +```Dockerfile +VOLUME ["/var/lib/torrust/tracker","/var/log/torrust/tracker","/etc/torrust/tracker"] +``` + +When instancing the container image with the `docker run` or `podman run` command, we map these volumes to the local storage: + +```s +./storage/tracker/lib -> /var/lib/torrust/tracker +./storage/tracker/log -> /var/log/torrust/tracker +./storage/tracker/etc -> /etc/torrust/tracker +``` + +> NOTE: You can adjust this mapping for your preference, however this mapping is the default in our guides and scripts. + +### Pre-Create Host-Mapped Folders: +Please run this command where you wish to run the container: + +```sh +mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/ +``` + +### Matching Ownership ID's of Host Storage and Container Volumes +It is important that the `torrust` user has the same uid `$(id -u)` as the host mapped folders. In our [entry script](../share/container/entry_script_sh), installed to `/usr/local/bin/entry.sh` inside the container, switches to the `torrust` user created based upon the `USER_UID` environmental variable. + +When running the container, you may use the `--env USER_ID="$(id -u)"` argument that gets the current user-id and passes to the container. + +### Mapped Tree Structure +Using the standard mapping defined above produces this following mapped tree: + +```s +storage/tracker/ +├── lib +│ ├── database +│ │   └── sqlite3.db => /var/lib/torrust/tracker/database/sqlite3.db [auto populated] +│ └── tls +│ ├── localhost.crt => /var/lib/torrust/tracker/tls/localhost.crt [user supplied] +│ └── localhost.key => /var/lib/torrust/tracker/tls/localhost.key [user supplied] +├── log => /var/log/torrust/tracker (future use) +└── etc + └── tracker.toml => /etc/torrust/tracker/tracker.toml [auto populated] +``` + +> NOTE: you only need the `tls` directory and certificates in case you have enabled SSL. + +## Building the Container + +### Clone and Change into Repository + +```sh +# Inside your dev folder +git clone https://github.com/torrust/torrust-tracker.git; cd torrust-tracker +``` + +### (Docker) Setup Context +Before starting, if you are using docker, it is helpful to reset the context to the default: + +```sh +docker context use default +``` + +### (Docker) Build + +```sh +# Release Mode +docker build --target release --tag torrust-tracker:release --file Containerfile . + +# Debug Mode +docker build --target debug --tag torrust-tracker:debug --file Containerfile . +``` + +### (Podman) Build + +```sh +# Release Mode +podman build --target release --tag torrust-tracker:release --file Containerfile . + +# Debug Mode +podman build --target debug --tag torrust-tracker:debug --file Containerfile . +``` + +## Running the Container + +### Basic Run +No arguments are needed for simply checking the container image works: + +#### (Docker) Run Basic + +```sh +# Release Mode +docker run -it torrust-tracker:release + +# Debug Mode +docker run -it torrust-tracker:debug +``` +#### (Podman) Run Basic + +```sh +# Release Mode +podman run -it torrust-tracker:release + +# Debug Mode +podman run -it torrust-tracker:debug +``` + +### Arguments +The arguments need to be placed before the image tag. i.e. + +`run [arguments] torrust-tracker:release` + +#### Environmental Variables: +Environmental variables are loaded through the `--env`, in the format `--env VAR="value"`. + +The following environmental variables can be set: + +- `TORRUST_TRACKER_PATH_CONFIG` - The in-container path to the tracker configuration file, (default: `"/etc/torrust/tracker/tracker.toml"`). +- `TORRUST_TRACKER_API_ADMIN_TOKEN` - Override of the admin token. If set, this value overrides any value set in the config. +- `TORRUST_TRACKER_DATABASE` - The database type used for the container, (options: `sqlite3`, `mysql`, default `sqlite3`). Please Note: This dose not override the database configuration within the `.toml` config file. +- `TORRUST_TRACKER_CONFIG` - Load config from this environmental variable instead from a file, (i.e: `TORRUST_TRACKER_CONFIG=$(cat tracker-tracker.toml)`). +- `USER_ID` - The user id for the runtime crated `torrust` user. Please Note: This user id should match the ownership of the host-mapped volumes, (default `1000`). +- `UDP_PORT` - The port for the UDP tracker. This should match the port used in the configuration, (default `6969`). +- `HTTP_PORT` - The port for the HTTP tracker. This should match the port used in the configuration, (default `7070`). +- `API_PORT` - The port for the tracker API. This should match the port used in the configuration, (default `1212`). + + +### Sockets +Socket ports used internally within the container can be mapped to with the `--publish` argument. + +The format is: `--publish [optional_host_ip]:[host_port]:[container_port]/[optional_protocol]`, for example: `--publish 127.0.0.1:8080:80/tcp`. + +The default ports can be mapped with the following: + +```s +--publish 0.0.0.0:7070:7070/tcp \ +--publish 0.0.0.0:6969:6969/udp \ +--publish 0.0.0.0:1212:1212/tcp \ +``` + +> NOTE: Inside the container it is necessary to expose a socket with the wildcard address `0.0.0.0` so that it may be accessible from the host. Verify that the configuration that the sockets are wildcard. + +### Volumes +By default the container will use install volumes for `/var/lib/torrust/tracker`, `/var/log/torrust/tracker`, and `/etc/torrust/tracker`, however for better administration it good to make these volumes host-mapped. + +The argument to host-map volumes is `--volume`, with the format: `--volume=[host-src:]container-dest[:]`. + +The default mapping can be supplied with the following arguments: + +```s +--volume ./storage/tracker/lib:/var/lib/torrust/tracker:Z \ +--volume ./storage/tracker/log:/var/log/torrust/tracker:Z \ +--volume ./storage/tracker/etc:/etc/torrust/tracker:Z \ +``` + + +Please not the `:Z` at the end of the podman `--volume` mapping arguments, this is to give read-write permission on SELinux enabled systemd, if this doesn't work on your system, you can use `:rw` instead. + +## Complete Example: + +### With Docker + +```sh +## Setup Docker Default Context +docker context use default + +## Build Container Image +docker build --target release --tag torrust-tracker:release --file Containerfile . + +## Setup Mapped Volumes +mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/ + +## Run Torrust Tracker Container Image +docker run -it \ + --env TORRUST_TRACKER_API_ADMIN_TOKEN="MySecretToken" \ + --env USER_ID"$(id -u)" \ + --publish 0.0.0.0:7070:7070/tcp \ + --publish 0.0.0.0:6969:6969/udp \ + --publish 0.0.0.0:1212:1212/tcp \ + --volume ./storage/tracker/lib:/var/lib/torrust/tracker:Z \ + --volume ./storage/tracker/log:/var/log/torrust/tracker:Z \ + --volume ./storage/tracker/etc:/etc/torrust/tracker:Z \ + torrust-tracker:release +``` + +### With Podman + +```sh +## Build Container Image +podman build --target release --tag torrust-tracker:release --file Containerfile . + +## Setup Mapped Volumes +mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/ + +## Run Torrust Tracker Container Image +podman run -it \ + --env TORRUST_TRACKER_API_ADMIN_TOKEN="MySecretToken" \ + --env USER_ID"$(id -u)" \ + --publish 0.0.0.0:7070:7070/tcp \ + --publish 0.0.0.0:6969:6969/udp \ + --publish 0.0.0.0:1212:1212/tcp \ + --volume ./storage/tracker/lib:/var/lib/torrust/tracker:Z \ + --volume ./storage/tracker/log:/var/log/torrust/tracker:Z \ + --volume ./storage/tracker/etc:/etc/torrust/tracker:Z \ + torrust-tracker:release +``` + +## Docker Compose + +The docker-compose configuration includes the MySQL service configuration. If you want to use MySQL instead of SQLite you should verify the `/etc/torrust/tracker/tracker.toml` (i.e `./storage/tracker/etc/tracker.toml`) configuration: + +```toml +db_driver = "MySQL" +db_path = "mysql://db_user:db_user_secret_password@mysql:3306/torrust_tracker" +``` + +### Build and Run: + +```sh +docker build --target release --tag torrust-tracker:release --file Containerfile . + +mkdir -p ./storage/tracker/lib/ ./storage/tracker/log/ ./storage/tracker/etc/ + +USER_ID=$(id -u) \ + TORRUST_TRACKER_API_ADMIN_TOKEN="MySecretToken" \ + docker compose up --build +``` + +After running the `compose up` command you will have two running containers: + +```s +$ docker ps +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +06feacb91a9e torrust-tracker "cargo run" 18 minutes ago Up 4 seconds 0.0.0.0:1212->1212/tcp, :::1212->1212/tcp, 0.0.0.0:7070->7070/tcp, :::7070->7070/tcp, 0.0.0.0:6969->6969/udp, :::6969->6969/udp torrust-tracker-1 +34d29e792ee2 mysql:8.0 "docker-entrypoint.s…" 18 minutes ago Up 5 seconds (healthy) 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp torrust-mysql-1 +``` + +And you should be able to use the application, for example making a request to the API: + + + +You can stop the containers with: + +```s +docker compose down +``` + +Additionally, you can delete all resources (containers, volumes, networks) with: + +```s +docker compose down -v +``` + +### Access Mysql with docker + +These are some useful commands for MySQL. + +Open a shell in the MySQL container using docker or docker-compose. + +```s +docker exec -it torrust-mysql-1 /bin/bash +docker compose exec mysql /bin/bash +``` + +Connect to MySQL from inside the MySQL container or from the host: + +```s +mysql -h127.0.0.1 -uroot -proot_secret_password +``` + +The when MySQL container is started the first time, it creates the database, user, and permissions needed. +If you see the error "Host is not allowed to connect to this MySQL server" you can check that users have the right permissions in the database. Make sure the user `root` and `db_user` can connect from any host (`%`). + +```s +mysql> SELECT host, user FROM mysql.user; ++-----------+------------------+ +| host | user | ++-----------+------------------+ +| % | db_user | +| % | root | +| localhost | mysql.infoschema | +| localhost | mysql.session | +| localhost | mysql.sys | +| localhost | root | ++-----------+------------------+ +6 rows in set (0.00 sec) +``` + +If the database, user or permissions are not created the reason could be the MySQL container volume can be corrupted. Delete it and start again the containers. + +### SSL Certificates + +You can use a certificate for localhost. You can create your [localhost certificate](https://letsencrypt.org/docs/certificates-for-localhost/#making-and-trusting-your-own-certificates) and use it in the `storage` folder and the configuration file (`tracker.toml`). For example: + +The storage folder must contain your certificates: + +```s +storage/tracker/lib/tls + ├── localhost.crt + └── localhost.key +``` + +You have not enabled it in your `tracker.toml` file: + +```toml + +[[http_trackers]] +# ... +ssl_enabled = true +# ... + +[http_api] +# ... +ssl_enabled = true +# ... + +``` + +> NOTE: you can enable it independently for each HTTP tracker or the API. + +If you enable the SSL certificate for the API, for example, you can load the API with this URL: + + + +## Prod environment + +In this section, you will learn how to deploy the tracker to a single docker container in Azure Container Instances. + +> NOTE: Azure Container Instances is a solution when you want to run an isolated container. If you need full container orchestration, including service discovery across multiple containers, automatic scaling, and coordinated application upgrades, we recommend [Kubernetes](https://kubernetes.io/). + +Deploy to Azure Container Instance following [docker documentation](https://docs.docker.com/cloud/aci-integration/). + +You have to create the ACI context and the storage: + +```s +docker context create aci myacicontext +docker context use myacicontext +docker volume create test-volume --storage-account torrustracker +``` + +You need to create all the files needed by the application in the storage dir `storage/lib/database`. + +And finally, you can run the container: + +```s +docker run \ + --publish 6969:6969/udp \ + --publish 7070:7070/tcp \ + --publish 1212:1212/tcp \ + --volume torrustracker/lib:/var/lib/torrust/tracker:rw \ + --volume torrustracker/log:/var/log/torrust/tracker:rw \ + --volume torrustracker/etc:/etc/torrust/tracker:rw \ + registry.hub.docker.com/torrust/tracker:latest +``` + +Detach from container logs when the container starts. By default, the command line stays attached and follows container logs. + +```s +docker run \ + --detach + --publish 6969:6969/udp \ + --publish 7070:7070/tcp \ + --publish 1212:1212/tcp \latest + --volume torrustracker/lib:/var/lib/torrust/tracker:rw \ + --volume torrustracker/log:/var/log/torrust/tracker:rw \ + --volume torrustracker/etc:/etc/torrust/tracker:rw \ + registry.hub.docker.com/torrust/tracker:latest +``` + +You should see something like this: + +```s +[+] Running 2/2 + ⠿ Group intelligent-hawking Created 5.0s + ⠿ intelligent-hawking Created 41.7s +2022-12-08T18:39:19.697869300+00:00 [torrust_tracker::logging][INFO] logging initialized. +2022-12-08T18:39:19.712651100+00:00 [torrust_tracker::jobs::udp_tracker][INFO] Starting UDP server on: 0.0.0.0:6969 +2022-12-08T18:39:19.712792700+00:00 [torrust_tracker::jobs::tracker_api][INFO] Starting Torrust API server on: 0.0.0.0:1212 +2022-12-08T18:39:19.725124+00:00 [torrust_tracker::jobs::tracker_api][INFO] Torrust API server started +``` + +You can see the container with: + +```s +$ docker ps +CONTAINER ID IMAGE COMMAND STATUS PORTS +intelligent-hawking registry.hub.docker.com/torrust/tracker:latest Running 4.236.213.57:6969->6969/udp, 4.236.213.57:1212->1212/tcp +``` + +After a while, you can use the tracker API `http://4.236.213.57:1212/api/v1/stats?token=MyAccessToken` and the UDP tracker with your BitTorrent client using this tracker announce URL `udp://4.236.213.57:6969`. + +> NOTES: +> +> - [There is no support for mounting a single file](https://docs.docker.com/cloud/aci-container-features/#persistent-volumes), or mounting a subfolder from an `Azure File Share`. +> - [ACI does not allow port mapping](https://docs.docker.com/cloud/aci-integration/#exposing-ports). +> - [Azure file share volume mount requires the Linux container run as root](https://learn.microsoft.com/en-us/azure/container-instances/container-instances-volume-azure-files#limitations). +> - It can take some minutes until the public IP for the ACI container is available. +> - You can use the Azure web UI to download files from the storage. For example, the SQLite database. +> - [It seems you can only expose web interfaces on port 80 on Azure Container Instances](https://stackoverflow.com/a/56768087/3012842). Not official documentation! + +## Links + +- [Deploying Docker containers on Azure](https://docs.docker.com/cloud/aci-integration/). +- [Docker run options for ACI containers](https://docs.docker.com/cloud/aci-container-features/). +- [Quickstart: Deploy a container instance in Azure using the Docker CLI](https://learn.microsoft.com/en-us/azure/container-instances/quickstart-docker-cli). diff --git a/packages/configuration/Cargo.toml b/packages/configuration/Cargo.toml index 9b91534e9..e3ca1d932 100644 --- a/packages/configuration/Cargo.toml +++ b/packages/configuration/Cargo.toml @@ -14,8 +14,8 @@ config = "0.13" toml = "0.7" log = { version = "0.4", features = ["release_max_level_info"] } thiserror = "1.0" -torrust-tracker-primitives = { version = "3.0.0-alpha.3", path = "../primitives" } -torrust-tracker-located-error = { version = "3.0.0-alpha.3", path = "../located-error" } +torrust-tracker-primitives = { version = "3.0.0-alpha.4", path = "../primitives" } +torrust-tracker-located-error = { version = "3.0.0-alpha.4", path = "../located-error" } [dev-dependencies] uuid = { version = "1", features = ["v4"] } diff --git a/packages/configuration/src/lib.rs b/packages/configuration/src/lib.rs index 6de0e3ed7..059316a26 100644 --- a/packages/configuration/src/lib.rs +++ b/packages/configuration/src/lib.rs @@ -4,7 +4,7 @@ //! Torrust Tracker, which is a `BitTorrent` tracker server. //! //! The configuration is loaded from a [TOML](https://toml.io/en/) file -//! `config.toml` in the project root folder or from an environment variable +//! `tracker.toml` in the project root folder or from an environment variable //! with the same content as the file. //! //! When you run the tracker without a configuration file, a new one will be @@ -67,7 +67,7 @@ //! storage/ //! ├── database //! │ └── data.db -//! └── ssl_certificates +//! └── tls //! ├── localhost.crt //! └── localhost.key //! ``` @@ -176,14 +176,14 @@ //! [[http_trackers]] //! enabled = true //! ... -//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt" -//! ssl_key_path = "./storage/ssl_certificates/localhost.key" +//! ssl_cert_path = "./storage/tracker/lib/tls/localhost.crt" +//! ssl_key_path = "./storage/tracker/lib/tls/localhost.key" //! //! [http_api] //! enabled = true //! ... -//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt" -//! ssl_key_path = "./storage/ssl_certificates/localhost.key" +//! ssl_cert_path = "./storage/tracker/lib/tls/localhost.crt" +//! ssl_key_path = "./storage/tracker/lib/tls/localhost.key" //! ``` //! //! ## Default configuration @@ -194,7 +194,7 @@ //! log_level = "info" //! mode = "public" //! db_driver = "Sqlite3" -//! db_path = "./storage/database/data.db" +//! db_path = "./storage/tracker/lib/database/sqlite3.db" //! announce_interval = 120 //! min_announce_interval = 120 //! max_peer_timeout = 900 @@ -239,6 +239,67 @@ use thiserror::Error; use torrust_tracker_located_error::{Located, LocatedError}; use torrust_tracker_primitives::{DatabaseDriver, TrackerMode}; +/// Information required for loading config +#[derive(Debug, Default, Clone)] +pub struct Info { + tracker_toml: String, + api_admin_token: Option, +} + +impl Info { + /// Build Configuration Info + /// + /// # Examples + /// + /// ``` + /// use torrust_tracker_configuration::Info; + /// + /// let result = Info::new(env_var_config, env_var_path_config, default_path_config, env_var_api_admin_token); + /// assert_eq!(result, ); + /// ``` + /// + /// # Errors + /// + /// Will return `Err` if unable to obtain a configuration. + /// + #[allow(clippy::needless_pass_by_value)] + pub fn new( + env_var_config: String, + env_var_path_config: String, + default_path_config: String, + env_var_api_admin_token: String, + ) -> Result { + let tracker_toml = if let Ok(tracker_toml) = env::var(&env_var_config) { + println!("Loading configuration from env var {env_var_config} ..."); + + tracker_toml + } else { + let config_path = if let Ok(config_path) = env::var(env_var_path_config) { + println!("Loading configuration file: `{config_path}` ..."); + + config_path + } else { + println!("Loading default configuration file: `{default_path_config}` ..."); + + default_path_config + }; + + fs::read_to_string(config_path) + .map_err(|e| Error::UnableToLoadFromConfigFile { + source: (Arc::new(e) as Arc).into(), + })? + .parse() + .map_err(|_e: std::convert::Infallible| Error::Infallible)? + }; + let api_admin_token = env::var(env_var_api_admin_token).ok(); + + Ok(Self { + tracker_toml, + api_admin_token, + }) + } +} + /// Configuration for each UDP tracker. #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] pub struct UdpTracker { @@ -298,6 +359,12 @@ pub struct HttpApi { pub access_tokens: HashMap, } +impl HttpApi { + fn override_admin_token(&mut self, api_admin_token: &str) { + self.access_tokens.insert("admin".to_string(), api_admin_token.to_string()); + } +} + impl HttpApi { /// Checks if the given token is one of the token in the configuration. #[must_use] @@ -323,7 +390,7 @@ pub struct Configuration { pub db_driver: DatabaseDriver, /// Database connection string. The format depends on the database driver. /// For `Sqlite3`, the format is `path/to/database.db`, for example: - /// `./storage/database/data.db`. + /// `./storage/tracker/lib/database/sqlite3.db`. /// For `Mysql`, the format is `mysql://db_user:db_user_password:port/db_name`, for /// example: `root:password@localhost:3306/torrust`. pub db_path: String, @@ -411,9 +478,17 @@ pub enum Error { source: LocatedError<'static, dyn std::error::Error + Send + Sync>, }, + #[error("Unable to load from Config File: {source}")] + UnableToLoadFromConfigFile { + source: LocatedError<'static, dyn std::error::Error + Send + Sync>, + }, + /// Unable to load the configuration from the configuration file. #[error("Failed processing the configuration: {source}")] ConfigError { source: LocatedError<'static, ConfigError> }, + + #[error("The error for errors that can never happen.")] + Infallible, } impl From for Error { @@ -431,7 +506,7 @@ impl Default for Configuration { log_level: Option::from(String::from("info")), mode: TrackerMode::Public, db_driver: DatabaseDriver::Sqlite3, - db_path: String::from("./storage/database/data.db"), + db_path: String::from("./storage/tracker/lib/database/sqlite3.db"), announce_interval: 120, min_announce_interval: 120, max_peer_timeout: 900, @@ -471,6 +546,10 @@ impl Default for Configuration { } impl Configuration { + fn override_api_admin_token(&mut self, api_admin_token: &str) { + self.http_api.override_admin_token(api_admin_token); + } + /// Returns the tracker public IP address id defined in the configuration, /// and `None` otherwise. #[must_use] @@ -514,26 +593,25 @@ impl Configuration { Ok(config) } - /// Loads the configuration from the environment variable. The whole - /// configuration must be in the environment variable. It contains the same - /// configuration as the configuration file with the same format. + /// Loads the configuration from the `Info` struct. The whole + /// configuration in toml format is included in the `info.tracker_toml` string. + /// + /// Optionally will override the admin api token. /// /// # Errors /// /// Will return `Err` if the environment variable does not exist or has a bad configuration. - pub fn load_from_env_var(config_env_var_name: &str) -> Result { - match env::var(config_env_var_name) { - Ok(config_toml) => { - let config_builder = Config::builder() - .add_source(File::from_str(&config_toml, FileFormat::Toml)) - .build()?; - let config = config_builder.try_deserialize()?; - Ok(config) - } - Err(e) => Err(Error::UnableToLoadFromEnvironmentVariable { - source: (Arc::new(e) as Arc).into(), - }), - } + pub fn load(info: &Info) -> Result { + let config_builder = Config::builder() + .add_source(File::from_str(&info.tracker_toml, FileFormat::Toml)) + .build()?; + let mut config: Configuration = config_builder.try_deserialize()?; + + if let Some(ref token) = info.api_admin_token { + config.override_api_admin_token(token); + }; + + Ok(config) } /// Saves the configuration to the configuration file. @@ -567,7 +645,7 @@ mod tests { let config = r#"log_level = "info" mode = "public" db_driver = "Sqlite3" - db_path = "./storage/database/data.db" + db_path = "./storage/tracker/lib/database/sqlite3.db" announce_interval = 120 min_announce_interval = 120 on_reverse_proxy = false diff --git a/packages/test-helpers/Cargo.toml b/packages/test-helpers/Cargo.toml index e9d86a589..4e6c70e66 100644 --- a/packages/test-helpers/Cargo.toml +++ b/packages/test-helpers/Cargo.toml @@ -10,5 +10,5 @@ version.workspace = true [dependencies] lazy_static = "1.4" rand = "0.8.5" -torrust-tracker-configuration = { version = "3.0.0-alpha.3", path = "../configuration"} -torrust-tracker-primitives = { version = "3.0.0-alpha.3", path = "../primitives"} +torrust-tracker-configuration = { version = "3.0.0-alpha.4", path = "../configuration"} +torrust-tracker-primitives = { version = "3.0.0-alpha.4", path = "../primitives"} diff --git a/rustfmt.toml b/rustfmt.toml index 3e878b271..abbed5eda 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,3 @@ max_width = 130 imports_granularity = "Module" group_imports = "StdExternalCrate" - diff --git a/share/container/entry_script_sh b/share/container/entry_script_sh new file mode 100644 index 000000000..88a832b40 --- /dev/null +++ b/share/container/entry_script_sh @@ -0,0 +1,81 @@ +#!/bin/sh +set -x + +to_lc() { echo "$1" | tr '[:upper:]' '[:lower:]'; } +clean() { echo "$1" | tr -d -c 'a-zA-Z0-9-' ; } +cmp_lc() { [ "$(to_lc "$(clean "$1")")" = "$(to_lc "$(clean "$2")")" ]; } + + +inst() { + if [ -n "$1" ] && [ -n "$2" ] && [ -e "$1" ] && [ ! -e "$2" ]; then + install -D -m 0640 -o torrust -g torrust "$1" "$2"; fi; } + + +# Add torrust user, based upon supplied user-id. +if [ -z "$USER_ID" ] && [ "$USER_ID" -lt 1000 ]; then + echo "ERROR: USER_ID is not set, or less than 1000" + exit 1 +fi + +adduser --disabled-password --shell "/bin/sh" --uid "$USER_ID" "torrust" + +# Configure Permissions for Torrust Folders +mkdir -p /var/lib/torrust/tracker/database/ /etc/torrust/tracker/ +chown -R "${USER_ID}":"${USER_ID}" /var/lib/torrust /var/log/torrust /etc/torrust +chmod -R 2770 /var/lib/torrust /var/log/torrust /etc/torrust + + +# Install the database and config: +if [ -n "$TORRUST_TRACKER_DATABASE" ]; then + if cmp_lc "$TORRUST_TRACKER_DATABASE" "sqlite3"; then + + # Select sqlite3 empty database + default_database="/usr/share/torrust/default/database/tracker.sqlite3.db" + + # Select sqlite3 default configuration + default_config="/usr/share/torrust/default/config/tracker.container.sqlite3.toml" + + elif cmp_lc "$TORRUST_TRACKER_DATABASE" "mysql"; then + + # (no database file needed for mysql) + + # Select default mysql configuration + default_config="/usr/share/torrust/default/config/tracker.container.mysql.toml" + + else + echo "Error: Unsupported Database Type: \"$TORRUST_TRACKER_DATABASE\"." + echo "Please Note: Supported Database Types: \"sqlite3\", \"mysql\"." + exit 1 + fi +else + echo "Error: \"\$TORRUST_TRACKER_DATABASE\" was not set!"; exit 1; +fi + +install_config="/etc/torrust/tracker/tracker.toml" +install_database="/var/lib/torrust/tracker/database/sqlite3.db" + +inst "$default_config" "$install_config" +inst "$default_database" "$install_database" + +# Make Minimal Message of the Day +if cmp_lc "$RUNTIME" "runtime"; then + printf '\n in runtime \n' >> /etc/motd; +elif cmp_lc "$RUNTIME" "debug"; then + printf '\n in debug mode \n' >> /etc/motd; +elif cmp_lc "$RUNTIME" "release"; then + printf '\n in release mode \n' >> /etc/motd; +else + echo "ERROR: running in unknown mode: \"$RUNTIME\""; exit 1 +fi + +if [ -e "/usr/share/torrust/container/message" ]; then + cat "/usr/share/torrust/container/message" >> /etc/motd; chmod 0644 /etc/motd +fi + +# Load message of the day from Profile +echo '[ ! -z "$TERM" -a -r /etc/motd ] && cat /etc/motd' >> /etc/profile + +cd /home/torrust || exit 1 + +# Switch to torrust user +exec /bin/su-exec torrust "$@" diff --git a/share/container/message b/share/container/message new file mode 100644 index 000000000..cd88b44ae --- /dev/null +++ b/share/container/message @@ -0,0 +1,2 @@ + +run 'torrust-tracker' to start tracker diff --git a/share/default/config/tracker.container.mysql.toml b/share/default/config/tracker.container.mysql.toml new file mode 100644 index 000000000..60da25db2 --- /dev/null +++ b/share/default/config/tracker.container.mysql.toml @@ -0,0 +1,39 @@ +log_level = "info" +mode = "public" +db_driver = "MySQL" +db_path = "mysql://db_user:db_user_secret_password@mysql:3306/torrust_tracker" +announce_interval = 120 +min_announce_interval = 120 +on_reverse_proxy = false +external_ip = "0.0.0.0" +tracker_usage_statistics = true +persistent_torrent_completed_stat = false +max_peer_timeout = 900 +inactive_peer_cleanup_interval = 600 +remove_peerless_torrents = true + +[[udp_trackers]] +enabled = false +bind_address = "0.0.0.0:6969" + +[[http_trackers]] +enabled = false +bind_address = "0.0.0.0:7070" +ssl_enabled = false +ssl_cert_path = "/var/lib/torrust/tracker/tls/localhost.crt" +ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key" + +[http_api] +enabled = true +bind_address = "0.0.0.0:1212" +ssl_enabled = false +ssl_cert_path = "/var/lib/torrust/tracker/tls/localhost.crt" +ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key" + + +# Please override the admin token setting the +# `TORRUST_TRACKER_API_ADMIN_TOKEN` +# environmental variable! + +[http_api.access_tokens] +admin = "MyAccessToken" diff --git a/share/default/config/tracker.container.sqlite3.toml b/share/default/config/tracker.container.sqlite3.toml new file mode 100644 index 000000000..64cf75518 --- /dev/null +++ b/share/default/config/tracker.container.sqlite3.toml @@ -0,0 +1,39 @@ +log_level = "info" +mode = "public" +db_driver = "Sqlite3" +db_path = "/var/lib/torrust/tracker/database/sqlite3.db" +announce_interval = 120 +min_announce_interval = 120 +on_reverse_proxy = false +external_ip = "0.0.0.0" +tracker_usage_statistics = true +persistent_torrent_completed_stat = false +max_peer_timeout = 900 +inactive_peer_cleanup_interval = 600 +remove_peerless_torrents = true + +[[udp_trackers]] +enabled = false +bind_address = "0.0.0.0:6969" + +[[http_trackers]] +enabled = false +bind_address = "0.0.0.0:7070" +ssl_enabled = false +ssl_cert_path = "/var/lib/torrust/tracker/tls/localhost.crt" +ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key" + +[http_api] +enabled = true +bind_address = "0.0.0.0:1212" +ssl_enabled = false +ssl_cert_path = "/var/lib/torrust/tracker/tls/localhost.crt" +ssl_key_path = "/var/lib/torrust/tracker/tls/localhost.key" + + +# Please override the admin token setting the +# `TORRUST_TRACKER_API_ADMIN_TOKEN` +# environmental variable! + +[http_api.access_tokens] +admin = "MyAccessToken" diff --git a/config.toml.local b/share/default/config/tracker.development.sqlite3.toml similarity index 92% rename from config.toml.local rename to share/default/config/tracker.development.sqlite3.toml index be6a11a56..be1877469 100644 --- a/config.toml.local +++ b/share/default/config/tracker.development.sqlite3.toml @@ -1,7 +1,7 @@ log_level = "info" mode = "public" db_driver = "Sqlite3" -db_path = "./storage/database/data.db" +db_path = "./storage/tracker/lib/database/sqlite3.db" announce_interval = 120 min_announce_interval = 120 on_reverse_proxy = false diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 727bf59f7..858fd59fc 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -1,30 +1,28 @@ //! Initialize configuration from file or env var. //! //! All environment variables are prefixed with `TORRUST_TRACKER_BACK_`. -use std::env; -use std::path::Path; -use torrust_tracker_configuration::{Configuration, Error}; +use torrust_tracker_configuration::{Configuration, Info}; // Environment variables -/// The whole `config.toml` file content. It has priority over the config file. +/// The whole `tracker.toml` file content. It has priority over the config file. /// Even if the file is not on the default path. const ENV_VAR_CONFIG: &str = "TORRUST_TRACKER_CONFIG"; +const ENV_VAR_API_ADMIN_TOKEN: &str = "TORRUST_TRACKER_API_ADMIN_TOKEN"; -/// The `config.toml` file location. -pub const ENV_VAR_CONFIG_PATH: &str = "TORRUST_TRACKER_CONFIG_PATH"; +/// The `tracker.toml` file location. +pub const ENV_VAR_PATH_CONFIG: &str = "TORRUST_TRACKER_PATH_CONFIG"; // Default values - -const ENV_VAR_DEFAULT_CONFIG_PATH: &str = "./config.toml"; +pub const DEFAULT_PATH_CONFIG: &str = "./share/default/config/tracker.development.sqlite3.toml"; /// It loads the application configuration from the environment. /// /// There are two methods to inject the configuration: /// -/// 1. By using a config file: `config.toml`. -/// 2. Environment variable: `TORRUST_TRACKER_CONFIG`. The variable contains the same contents as the `config.toml` file. +/// 1. By using a config file: `tracker.toml`. +/// 2. Environment variable: `TORRUST_TRACKER_CONFIG`. The variable contains the same contents as the `tracker.toml` file. /// /// Environment variable has priority over the config file. /// @@ -33,37 +31,27 @@ const ENV_VAR_DEFAULT_CONFIG_PATH: &str = "./config.toml"; /// # Panics /// /// Will panic if it can't load the configuration from either -/// `./config.toml` file or the env var `TORRUST_TRACKER_CONFIG`. +/// `./tracker.toml` file or the env var `TORRUST_TRACKER_CONFIG`. #[must_use] pub fn initialize_configuration() -> Configuration { - if env::var(ENV_VAR_CONFIG).is_ok() { - println!("Loading configuration from env var {ENV_VAR_CONFIG} ..."); + let info = Info::new( + ENV_VAR_CONFIG.to_string(), + ENV_VAR_PATH_CONFIG.to_string(), + DEFAULT_PATH_CONFIG.to_string(), + ENV_VAR_API_ADMIN_TOKEN.to_string(), + ) + .unwrap(); + + Configuration::load(&info).unwrap() +} - Configuration::load_from_env_var(ENV_VAR_CONFIG).unwrap() - } else { - let config_path = env::var(ENV_VAR_CONFIG_PATH).unwrap_or_else(|_| ENV_VAR_DEFAULT_CONFIG_PATH.to_string()); +#[cfg(test)] +mod tests { - println!("Loading configuration from configuration file: `{config_path}` ..."); + #[test] + fn it_should_load_with_default_config() { + use crate::bootstrap::config::initialize_configuration; - load_from_file_or_create_default(&config_path).unwrap() - } -} - -/// Loads the configuration from the configuration file. If the file does -/// not exist, it will create a default configuration file and return an -/// error. -/// -/// # Errors -/// -/// Will return `Err` if `path` does not exist or has a bad configuration. -fn load_from_file_or_create_default(path: &str) -> Result { - if Path::new(&path).is_file() { - Configuration::load_from_file(path) - } else { - println!("Missing configuration file."); - println!("Creating a default configuration file: `{path}` ..."); - let config = Configuration::create_default_configuration_file(path)?; - println!("Please review the config file: `{path}` and restart the tracker if needed."); - Ok(config) + drop(initialize_configuration()); } } diff --git a/src/lib.rs b/src/lib.rs index 28bac9244..c862d373a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,16 +112,16 @@ //! storage/ //! ├── database //! │   └── data.db -//! └── ssl_certificates +//! └── tls //! ├── localhost.crt //! └── localhost.key //! ``` //! -//! The default configuration expects a directory `./storage/database` to be writable by the tracker process. +//! The default configuration expects a directory `./storage/tracker/lib/database` to be writable by the tracker process. //! //! By default the tracker uses `SQLite` and the database file name `data.db`. //! -//! You only need the `ssl_certificates` directory in case you are setting up SSL for the HTTP tracker or the tracker API. +//! You only need the `tls` directory in case you are setting up SSL for the HTTP tracker or the tracker API. //! Visit [`HTTP`](crate::servers::http) or [`API`](crate::servers::apis) if you want to know how you can use HTTPS. //! //! ## Install from sources @@ -130,8 +130,8 @@ //! git clone https://github.com/torrust/torrust-tracker.git \ //! && cd torrust-tracker \ //! && cargo build --release \ -//! && mkdir -p ./storage/database \ -//! && mkdir -p ./storage/ssl_certificates +//! && mkdir -p ./storage/tracker/lib/database \ +//! && mkdir -p ./storage/tracker/lib/tls //! ``` //! //! ## Run with docker @@ -142,7 +142,7 @@ //! # Configuration //! //! In order to run the tracker you need to provide the configuration. If you run the tracker without providing the configuration, -//! the tracker will generate the default configuration the first time you run it. It will generate a `config.toml` file with +//! the tracker will generate the default configuration the first time you run it. It will generate a `tracker.toml` file with //! in the root directory. //! //! The default configuration is: @@ -151,7 +151,7 @@ //! log_level = "info" //! mode = "public" //! db_driver = "Sqlite3" -//! db_path = "./storage/database/data.db" +//! db_path = "./storage/tracker/lib/database/sqlite3.db" //! announce_interval = 120 //! min_announce_interval = 120 //! max_peer_timeout = 900 @@ -188,18 +188,18 @@ //! //! For more information about each service and options you can visit the documentation for the [torrust-tracker-configuration crate](https://docs.rs/torrust-tracker-configuration). //! -//! Alternatively to the `config.toml` file you can use one environment variable `TORRUST_TRACKER_CONFIG` to pass the configuration to the tracker: +//! Alternatively to the `tracker.toml` file you can use one environment variable `TORRUST_TRACKER_CONFIG` to pass the configuration to the tracker: //! //! ```text -//! TORRUST_TRACKER_CONFIG=$(cat config.toml) +//! TORRUST_TRACKER_CONFIG=$(cat tracker.toml) //! cargo run //! ``` //! -//! In the previous example you are just setting the env var with the contents of the `config.toml` file. +//! In the previous example you are just setting the env var with the contents of the `tracker.toml` file. //! -//! The env var contains the same data as the `config.toml`. It's particularly useful in you are [running the tracker with docker](https://github.com/torrust/torrust-tracker/tree/develop/docker). +//! The env var contains the same data as the `tracker.toml`. It's particularly useful in you are [running the tracker with docker](https://github.com/torrust/torrust-tracker/tree/develop/docker). //! -//! > NOTE: The `TORRUST_TRACKER_CONFIG` env var has priority over the `config.toml` file. +//! > NOTE: The `TORRUST_TRACKER_CONFIG` env var has priority over the `tracker.toml` file. //! //! # Usage //! diff --git a/src/servers/apis/mod.rs b/src/servers/apis/mod.rs index eb278bf3c..afed9ff12 100644 --- a/src/servers/apis/mod.rs +++ b/src/servers/apis/mod.rs @@ -28,8 +28,8 @@ //! enabled = true //! bind_address = "0.0.0.0:1212" //! ssl_enabled = false -//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt" -//! ssl_key_path = "./storage/ssl_certificates/localhost.key" +//! ssl_cert_path = "./storage/tracker/lib/tls/localhost.crt" +//! ssl_key_path = "./storage/tracker/lib/tls/localhost.key" //! //! [http_api.access_tokens] //! admin = "MyAccessToken" @@ -41,7 +41,7 @@ //! When you run the tracker with enabled API, you will see the following message: //! //! ```text -//! Loading configuration from config file ./config.toml +//! Loading configuration from config file ./tracker.toml //! 023-03-28T12:19:24.963054069+01:00 [torrust_tracker::bootstrap::logging][INFO] logging initialized. //! ... //! 023-03-28T12:19:24.964138723+01:00 [torrust_tracker::bootstrap::jobs::tracker_apis][INFO] Starting Torrust APIs server on: http://0.0.0.0:1212 @@ -116,8 +116,8 @@ //! enabled = true //! bind_address = "0.0.0.0:1212" //! ssl_enabled = true -//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt" -//! ssl_key_path = "./storage/ssl_certificates/localhost.key" +//! ssl_cert_path = "./storage/tracker/lib/tls/localhost.crt" +//! ssl_key_path = "./storage/tracker/lib/tls/localhost.key" //! //! [http_api.access_tokens] //! admin = "MyAccessToken" diff --git a/src/servers/apis/v1/middlewares/auth.rs b/src/servers/apis/v1/middlewares/auth.rs index 41af09031..3e8f74d0c 100644 --- a/src/servers/apis/v1/middlewares/auth.rs +++ b/src/servers/apis/v1/middlewares/auth.rs @@ -11,7 +11,7 @@ //! The token must be one of the `access_tokens` in the tracker //! [HTTP API configuration](torrust_tracker_configuration::HttpApi). //! -//! The configuration file `config.toml` contains a list of tokens: +//! The configuration file `tracker.toml` contains a list of tokens: //! //! ```toml //! [http_api.access_tokens] diff --git a/src/tracker/databases/driver.rs b/src/tracker/databases/driver.rs index 4ff9314d2..19cb7046e 100644 --- a/src/tracker/databases/driver.rs +++ b/src/tracker/databases/driver.rs @@ -18,7 +18,7 @@ use super::{Builder, Database}; /// use torrust_tracker_primitives::DatabaseDriver; /// /// let db_driver = DatabaseDriver::Sqlite3; -/// let db_path = "./storage/database/data.db".to_string(); +/// let db_path = "./storage/tracker/lib/database/sqlite3.db".to_string(); /// let database = databases::driver::build(&db_driver, &db_path); /// ``` /// diff --git a/src/tracker/mod.rs b/src/tracker/mod.rs index 6823e8fe8..040751e12 100644 --- a/src/tracker/mod.rs +++ b/src/tracker/mod.rs @@ -320,7 +320,7 @@ //! log_level = "debug" //! mode = "public" //! db_driver = "Sqlite3" -//! db_path = "./storage/database/data.db" +//! db_path = "./storage/tracker/lib/database/sqlite3.db" //! announce_interval = 120 //! min_announce_interval = 120 //! max_peer_timeout = 900 diff --git a/tests/servers/http/v1/contract.rs b/tests/servers/http/v1/contract.rs index b508dfc39..2e24af6b7 100644 --- a/tests/servers/http/v1/contract.rs +++ b/tests/servers/http/v1/contract.rs @@ -67,11 +67,12 @@ mod for_all_config_modes { // Vuze (bittorrent client) docs: // https://wiki.vuze.com/w/Announce - use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}; use std::str::FromStr; use local_ip_address::local_ip; use reqwest::Response; + use tokio::net::TcpListener; use torrust_tracker::shared::bit_torrent::info_hash::InfoHash; use torrust_tracker::tracker::peer; use torrust_tracker_test_helpers::configuration; @@ -594,6 +595,13 @@ mod for_all_config_modes { #[tokio::test] async fn should_increase_the_number_of_tcp6_connections_handled_in_statistics() { + if TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)) + .await + .is_err() + { + return; // we cannot bind to a ipv6 socket, so we will skip this test + } + let test_env = running_test_environment::(configuration::ephemeral_ipv6()).await; Client::bind(*test_env.bind_address(), IpAddr::from_str("::1").unwrap()) @@ -651,6 +659,13 @@ mod for_all_config_modes { #[tokio::test] async fn should_increase_the_number_of_tcp6_announce_requests_handled_in_statistics() { + if TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)) + .await + .is_err() + { + return; // we cannot bind to a ipv6 socket, so we will skip this test + } + let test_env = running_test_environment::(configuration::ephemeral_ipv6()).await; Client::bind(*test_env.bind_address(), IpAddr::from_str("::1").unwrap()) @@ -830,9 +845,10 @@ mod for_all_config_modes { // Vuze (bittorrent client) docs: // https://wiki.vuze.com/w/Scrape - use std::net::IpAddr; + use std::net::{IpAddr, Ipv6Addr, SocketAddrV6}; use std::str::FromStr; + use tokio::net::TcpListener; use torrust_tracker::shared::bit_torrent::info_hash::InfoHash; use torrust_tracker::tracker::peer; use torrust_tracker_test_helpers::configuration; @@ -1027,6 +1043,13 @@ mod for_all_config_modes { #[tokio::test] async fn should_increase_the_number_ot_tcp6_scrape_requests_handled_in_statistics() { + if TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)) + .await + .is_err() + { + return; // we cannot bind to a ipv6 socket, so we will skip this test + } + let test_env = running_test_environment::(configuration::ephemeral_ipv6()).await; let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap();