diff --git a/packages/configuration/src/lib.rs b/packages/configuration/src/lib.rs
index d5beca236..6b051b572 100644
--- a/packages/configuration/src/lib.rs
+++ b/packages/configuration/src/lib.rs
@@ -1,3 +1,231 @@
+//! Configuration data structures for [Torrust Tracker](https://docs.rs/torrust-tracker).
+//!
+//! This module contains the configuration data structures for the
+//! 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
+//! with the same content as the file.
+//!
+//! When you run the tracker without a configuration file, a new one will be
+//! created with the default values, but the tracker immediately exits. You can
+//! then edit the configuration file and run the tracker again.
+//!
+//! Configuration can not only be loaded from a file, but also from environment
+//! variable `TORRUST_TRACKER_CONFIG`. This is useful when running the tracker
+//! in a Docker container or environments where you do not have a persistent
+//! storage or you cannot inject a configuration file. Refer to
+//! [`Torrust Tracker documentation`](https://docs.rs/torrust-tracker) for more
+//! information about how to pass configuration to the tracker.
+//!
+//! # Table of contents
+//!
+//! - [Sections](#sections)
+//! - [Port binding](#port-binding)
+//! - [TSL support](#tsl-support)
+//! - [Generating self-signed certificates](#generating-self-signed-certificates)
+//! - [Default configuration](#default-configuration)
+//!
+//! ## Sections
+//!
+//! Each section in the toml structure is mapped to a data structure. For
+//! example, the `[http_api]` section (configuration for the tracker HTTP API)
+//! is mapped to the [`HttpApi`](HttpApi) structure.
+//!
+//! > **NOTICE**: some sections are arrays of structures. For example, the
+//! > `[[udp_trackers]]` section is an array of [`UdpTracker`](UdpTracker) since
+//! > you can have multiple running UDP trackers bound to different ports.
+//!
+//! Please refer to the documentation of each structure for more information
+//! about each section.
+//!
+//! - [`Core configuration`](crate::Configuration)
+//! - [`HTTP API configuration`](crate::HttpApi)
+//! - [`HTTP Tracker configuration`](crate::HttpTracker)
+//! - [`UDP Tracker configuration`](crate::UdpTracker)
+//!
+//! ## Port binding
+//!
+//! For the API, HTTP and UDP trackers you can bind to a random port by using
+//! port `0`. For example, if you want to bind to a random port on all
+//! interfaces, use `0.0.0.0:0`. The OS will choose a random port but the
+//! tracker will not print the port it is listening to when it starts. It just
+//! says `Starting Torrust HTTP tracker server on: http://0.0.0.0:0`. It shows
+//! the port used in the configuration file, and not the port the
+//! tracker is actually listening to. This is a planned feature, see issue
+//! [186](https://github.com/torrust/torrust-tracker/issues/186) for more
+//! information.
+//!
+//! ## TSL support
+//!
+//! For the API and HTTP tracker you can enable TSL by setting `ssl_enabled` to
+//! `true` and setting the paths to the certificate and key files.
+//!
+//! Typically, you will have a directory structure like this:
+//!
+//! ```text
+//! storage/
+//! ├── database
+//! │ └── data.db
+//! └── ssl_certificates
+//! ├── localhost.crt
+//! └── localhost.key
+//! ```
+//!
+//! where you can store all the persistent data.
+//!
+//! Alternatively, you could setup a reverse proxy like Nginx or Apache to
+//! handle the SSL/TLS part and forward the requests to the tracker. If you do
+//! that, you should set [`on_reverse_proxy`](crate::Configuration::on_reverse_proxy)
+//! to `true` in the configuration file. It's out of scope for this
+//! documentation to explain in detail how to setup a reverse proxy, but the
+//! configuration file should be something like this:
+//!
+//! For [NGINX](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/):
+//!
+//! ```text
+//! # HTTPS only (with SSL - force redirect to HTTPS)
+//!
+//! server {
+//! listen 80;
+//! server_name tracker.torrust.com;
+//!
+//! return 301 https://$host$request_uri;
+//! }
+//!
+//! server {
+//! listen 443;
+//! server_name tracker.torrust.com;
+//!
+//! ssl_certificate CERT_PATH
+//! ssl_certificate_key CERT_KEY_PATH;
+//!
+//! location / {
+//! proxy_set_header X-Forwarded-For $remote_addr;
+//! proxy_pass http://127.0.0.1:6969;
+//! }
+//! }
+//! ```
+//!
+//! For [Apache](https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html):
+//!
+//! ```text
+//! # HTTPS only (with SSL - force redirect to HTTPS)
+//!
+//!
+//! ServerAdmin webmaster@tracker.torrust.com
+//! ServerName tracker.torrust.com
+//!
+//!
+//! RewriteEngine on
+//! RewriteCond %{HTTPS} off
+//! RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
+//!
+//!
+//!
+//!
+//!
+//! ServerAdmin webmaster@tracker.torrust.com
+//! ServerName tracker.torrust.com
+//!
+//!
+//! Order allow,deny
+//! Allow from all
+//!
+//!
+//! ProxyPreserveHost On
+//! ProxyRequests Off
+//! AllowEncodedSlashes NoDecode
+//!
+//! ProxyPass / http://localhost:3000/
+//! ProxyPassReverse / http://localhost:3000/
+//! ProxyPassReverse / http://tracker.torrust.com/
+//!
+//! RequestHeader set X-Forwarded-Proto "https"
+//! RequestHeader set X-Forwarded-Port "443"
+//!
+//! ErrorLog ${APACHE_LOG_DIR}/tracker.torrust.com-error.log
+//! CustomLog ${APACHE_LOG_DIR}/tracker.torrust.com-access.log combined
+//!
+//! SSLCertificateFile CERT_PATH
+//! SSLCertificateKeyFile CERT_KEY_PATH
+//!
+//!
+//! ```
+//!
+//! ## Generating self-signed certificates
+//!
+//! For testing purposes, you can use self-signed certificates.
+//!
+//! Refer to [Let's Encrypt - Certificates for localhost](https://letsencrypt.org/docs/certificates-for-localhost/)
+//! for more information.
+//!
+//! Running the following command will generate a certificate (`localhost.crt`)
+//! and key (`localhost.key`) file in your current directory:
+//!
+//! ```s
+//! openssl req -x509 -out localhost.crt -keyout localhost.key \
+//! -newkey rsa:2048 -nodes -sha256 \
+//! -subj '/CN=localhost' -extensions EXT -config <( \
+//! printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
+//! ```
+//!
+//! You can then use the generated files in the configuration file:
+//!
+//! ```s
+//! [[http_trackers]]
+//! enabled = true
+//! ...
+//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt"
+//! ssl_key_path = "./storage/ssl_certificates/localhost.key"
+//!
+//! [http_api]
+//! enabled = true
+//! ...
+//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt"
+//! ssl_key_path = "./storage/ssl_certificates/localhost.key"
+//! ```
+//!
+//! ## Default configuration
+//!
+//! The default configuration is:
+//!
+//! ```toml
+//! log_level = "info"
+//! mode = "public"
+//! db_driver = "Sqlite3"
+//! db_path = "./storage/database/data.db"
+//! announce_interval = 120
+//! min_announce_interval = 120
+//! max_peer_timeout = 900
+//! on_reverse_proxy = false
+//! external_ip = "0.0.0.0"
+//! tracker_usage_statistics = true
+//! persistent_torrent_completed_stat = false
+//! 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 = ""
+//! ssl_key_path = ""
+//!
+//! [http_api]
+//! enabled = true
+//! bind_address = "127.0.0.1:1212"
+//! ssl_enabled = false
+//! ssl_cert_path = ""
+//! ssl_key_path = ""
+//!
+//! [http_api.access_tokens]
+//! admin = "MyAccessToken"
+//!```
use std::collections::{HashMap, HashSet};
use std::net::IpAddr;
use std::panic::Location;
@@ -14,38 +242,67 @@ use thiserror::Error;
use torrust_tracker_located_error::{Located, LocatedError};
use torrust_tracker_primitives::{DatabaseDriver, TrackerMode};
+/// Configuration for each UDP tracker.
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct UdpTracker {
+ /// Weather the UDP tracker is enabled or not.
pub enabled: bool,
+ /// The address the tracker will bind to.
+ /// The format is `ip:port`, for example `0.0.0.0:6969`. If you want to
+ /// listen to all interfaces, use `0.0.0.0`. If you want the operating
+ /// system to choose a random port, use port `0`.
pub bind_address: String,
}
+/// Configuration for each HTTP tracker.
#[serde_as]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct HttpTracker {
+ /// Weather the HTTP tracker is enabled or not.
pub enabled: bool,
+ /// The address the tracker will bind to.
+ /// The format is `ip:port`, for example `0.0.0.0:6969`. If you want to
+ /// listen to all interfaces, use `0.0.0.0`. If you want the operating
+ /// system to choose a random port, use port `0`.
pub bind_address: String,
+ /// Weather the HTTP tracker will use SSL or not.
pub ssl_enabled: bool,
+ /// Path to the SSL certificate file. Only used if `ssl_enabled` is `true`.
#[serde_as(as = "NoneAsEmptyString")]
pub ssl_cert_path: Option,
+ /// Path to the SSL key file. Only used if `ssl_enabled` is `true`.
#[serde_as(as = "NoneAsEmptyString")]
pub ssl_key_path: Option,
}
+/// Configuration for the HTTP API.
#[serde_as]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct HttpApi {
+ /// Weather the HTTP API is enabled or not.
pub enabled: bool,
+ /// The address the tracker will bind to.
+ /// The format is `ip:port`, for example `0.0.0.0:6969`. If you want to
+ /// listen to all interfaces, use `0.0.0.0`. If you want the operating
+ /// system to choose a random port, use port `0`.
pub bind_address: String,
+ /// Weather the HTTP API will use SSL or not.
pub ssl_enabled: bool,
+ /// Path to the SSL certificate file. Only used if `ssl_enabled` is `true`.
#[serde_as(as = "NoneAsEmptyString")]
pub ssl_cert_path: Option,
+ /// Path to the SSL key file. Only used if `ssl_enabled` is `true`.
#[serde_as(as = "NoneAsEmptyString")]
pub ssl_key_path: Option,
+ /// Access tokens for the HTTP API. The key is a label identifying the
+ /// token and the value is the token itself. The token is used to
+ /// authenticate the user. All tokens are valid for all endpoints and have
+ /// the all permissions.
pub access_tokens: HashMap,
}
impl HttpApi {
+ /// Checks if the given token is one of the token in the configuration.
#[must_use]
pub fn contains_token(&self, token: &str) -> bool {
let tokens: HashMap = self.access_tokens.clone();
@@ -54,14 +311,24 @@ impl HttpApi {
}
}
+/// Core configuration for the tracker.
#[allow(clippy::struct_excessive_bools)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct Configuration {
+ /// Logging level. Possible values are: `Off`, `Error`, `Warn`, `Info`,
+ /// `Debug` and `Trace`. Default is `Info`.
pub log_level: Option,
+ /// Tracker mode. See [`TrackerMode`](torrust_tracker_primitives::TrackerMode) for more information.
pub mode: TrackerMode,
// Database configuration
+ /// Database driver. Possible values are: `Sqlite3`, and `MySQL`.
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`.
+ /// 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,
/// Interval in seconds that the client should wait between sending regular
@@ -88,35 +355,77 @@ pub struct Configuration {
/// could lead to excessive load on the tracker or even getting banned by
/// the tracker for not adhering to the rules.
pub min_announce_interval: u32,
+ /// Weather the tracker is behind a reverse proxy or not.
+ /// If the tracker is behind a reverse proxy, the `X-Forwarded-For` header
+ /// sent from the proxy will be used to get the client's IP address.
pub on_reverse_proxy: bool,
+ /// The external IP address of the tracker. If the client is using a
+ /// loopback IP address, this IP address will be used instead. If the peer
+ /// is using a loopback IP address, the tracker assumes that the peer is
+ /// in the same network as the tracker and will use the tracker's IP
+ /// address instead.
pub external_ip: Option,
+ /// Weather the tracker should collect statistics about tracker usage.
+ /// If enabled, the tracker will collect statistics like the number of
+ /// connections handled, the number of announce requests handled, etc.
+ /// Refer to the [`Tracker`](https://docs.rs/torrust-tracker) for more
+ /// information about the collected metrics.
pub tracker_usage_statistics: bool,
+ /// If enabled the tracker will persist the number of completed downloads.
+ /// That's how many times a torrent has been downloaded completely.
pub persistent_torrent_completed_stat: bool,
// Cleanup job configuration
+ /// Maximum time in seconds that a peer can be inactive before being
+ /// considered an inactive peer. If a peer is inactive for more than this
+ /// time, it will be removed from the torrent peer list.
pub max_peer_timeout: u32,
+ /// Interval in seconds that the cleanup job will run to remove inactive
+ /// peers from the torrent peer list.
pub inactive_peer_cleanup_interval: u64,
+ /// If enabled, the tracker will remove torrents that have no peers.
+ /// THe clean up torrent job runs every `inactive_peer_cleanup_interval`
+ /// seconds and it removes inactive peers. Eventually, the peer list of a
+ /// torrent could be empty and the torrent will be removed if this option is
+ /// enabled.
pub remove_peerless_torrents: bool,
// Server jobs configuration
+ /// The list of UDP trackers the tracker is running. Each UDP tracker
+ /// represents a UDP server that the tracker is running and it has its own
+ /// configuration.
pub udp_trackers: Vec,
+ /// The list of HTTP trackers the tracker is running. Each HTTP tracker
+ /// represents a HTTP server that the tracker is running and it has its own
+ /// configuration.
pub http_trackers: Vec,
+ /// The HTTP API configuration.
pub http_api: HttpApi,
}
+/// Errors that can occur when loading the configuration.
#[derive(Error, Debug)]
pub enum Error {
+ /// Unable to load the configuration from the environment variable.
+ /// This error only occurs if there is no configuration file and the
+ /// `TORRUST_TRACKER_CONFIG` environment variable is not set.
#[error("Unable to load from Environmental Variable: {source}")]
UnableToLoadFromEnvironmentVariable {
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
},
+ /// If you run the tracker without providing the configuration (via the
+ /// `TORRUST_TRACKER_CONFIG` environment variable or configuration file),
+ /// the tracker will create a default configuration file but it will not
+ /// load it. It will return this error instead and you have to restart the
+ /// it.
#[error("Default configuration created at: `{path}`, please review and reload tracker, {location}")]
CreatedNewConfigHalt {
location: &'static Location<'static>,
path: String,
},
+ /// Unable to load the configuration from the configuration file.
#[error("Failed processing the configuration: {source}")]
ConfigError { source: LocatedError<'static, ConfigError> },
}
@@ -176,6 +485,8 @@ impl Default for Configuration {
}
impl Configuration {
+ /// Returns the tracker public IP address id defined in the configuration,
+ /// and `None` otherwise.
#[must_use]
pub fn get_ext_ip(&self) -> Option {
match &self.external_ip {
@@ -187,6 +498,8 @@ impl Configuration {
}
}
+ /// Loads the configuration from the configuration file.
+ ///
/// # Errors
///
/// Will return `Err` if `path` does not exist or has a bad configuration.
@@ -214,6 +527,10 @@ impl Configuration {
Ok(torrust_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.
+ ///
/// # Errors
///
/// Will return `Err` if the environment variable does not exist or has a bad configuration.
@@ -232,10 +549,13 @@ impl Configuration {
}
}
+ /// Saves the configuration to the configuration file.
+ ///
/// # Errors
///
/// Will return `Err` if `filename` does not exist or the user does not have
- /// permission to read it.
+ /// permission to read it. Will also return `Err` if the configuration is
+ /// not valid or cannot be encoded to TOML.
pub fn save_to_file(&self, path: &str) -> Result<(), Error> {
let toml_string = toml::to_string(self).expect("Could not encode TOML value");
fs::write(path, toml_string).expect("Could not write to file!");
diff --git a/src/servers/apis/mod.rs b/src/servers/apis/mod.rs
index 203f1d146..eb278bf3c 100644
--- a/src/servers/apis/mod.rs
+++ b/src/servers/apis/mod.rs
@@ -35,7 +35,7 @@
//! admin = "MyAccessToken"
//! ```
//!
-//! Refer to [torrust-tracker-configuration](https://docs.rs/torrust-tracker-configuration>)
+//! Refer to [`torrust-tracker-configuration`](torrust_tracker_configuration)
//! for more information about the API configuration.
//!
//! When you run the tracker with enabled API, you will see the following message:
@@ -99,7 +99,7 @@
//! The token label is used to identify the token. All tokens have full access
//! to the API.
//!
-//! Refer to [torrust-tracker-configuration](https://docs.rs/torrust-tracker-configuration>)
+//! Refer to [`torrust-tracker-configuration`](torrust_tracker_configuration)
//! for more information about the API configuration and to the
//! [`auth`](crate::servers::apis::v1::middlewares::auth) middleware for more
//! information about the authentication process.
diff --git a/src/servers/apis/server.rs b/src/servers/apis/server.rs
index 76396cc51..91821aa79 100644
--- a/src/servers/apis/server.rs
+++ b/src/servers/apis/server.rs
@@ -140,7 +140,7 @@ impl Launcher {
/// Starts the API server with graceful shutdown.
///
/// If TLS is enabled in the configuration, it will start the server with
- /// TLS. See [torrust-tracker-configuration](https://docs.rs/torrust-tracker-configuration>)
+ /// TLS. See [`torrust-tracker-configuration`](torrust_tracker_configuration)
/// for more information about configuration.
pub fn start(
cfg: &torrust_tracker_configuration::HttpApi,
diff --git a/src/tracker/mod.rs b/src/tracker/mod.rs
index 03853e1aa..2cabd5a82 100644
--- a/src/tracker/mod.rs
+++ b/src/tracker/mod.rs
@@ -468,7 +468,7 @@ use crate::tracker::databases::Database;
/// Typically, the `Tracker` is used by a higher application service that handles
/// the network layer.
pub struct Tracker {
- /// `Tracker` configuration. See
+ /// `Tracker` configuration. See [`torrust-tracker-configuration`](torrust_tracker_configuration)
pub config: Arc,
/// A database driver implementation: [`Sqlite3`](crate::tracker::databases::sqlite)
/// or [`MySQL`](crate::tracker::databases::mysql)