## ADDED Requirements ### Requirement: Trojan users are configured via YAML file The trojan role SHALL read user credentials from a `users.yml` file located at the playbook root. The file SHALL define a `trojan_users` list where each entry contains `name` and `password` fields. #### Scenario: Users file exists with multiple entries - **WHEN** `users.yml` contains `trojan_users` with entries `[{name: alice, password: pass1}, {name: bob, password: pass2}]` - **THEN** the Trojan configuration includes both users #### Scenario: Users file is missing - **WHEN** `users.yml` does not exist and no `trojan_users` variable is defined elsewhere - **THEN** the playbook fails with a clear error message #### Scenario: Users file is gitignored - **WHEN** the repository is inspected - **THEN** `.gitignore` contains an entry for `users.yml` ### Requirement: Trojan binary is installed The trojan role SHALL download and install the trojan-go binary from release artifacts to `/usr/local/bin/trojan-go`. The version SHALL be configurable via `trojan_version`. #### Scenario: Fresh installation - **WHEN** the trojan role runs on a server without trojan-go installed - **THEN** the specified version of the binary is downloaded and installed - **THEN** the binary is executable #### Scenario: Version upgrade - **WHEN** `trojan_version` is changed and the playbook is re-run - **THEN** the binary is replaced with the new version - **THEN** the Trojan service is restarted ### Requirement: TLS certificate is provisioned via Let's Encrypt The trojan role SHALL use certbot to obtain a TLS certificate for the domain configured on each individual host. The trojan role SHALL ensure port 80 is allowed through UFW before attempting certificate provisioning. After provisioning or renewal, the certificate and key SHALL be copied to `/etc/trojan-go/tls/` so the service user can read them. #### Scenario: Certificate provisioning with UFW allowing port 80 - **WHEN** the trojan role runs on a host with `trojan_domain` configured - **THEN** port 80 is allowed through UFW before certbot attempts ACME validation - **THEN** certbot obtains a TLS certificate for the configured domain - **THEN** the certificate and key are copied to `/etc/trojan-go/tls/` owned by the trojan service user #### Scenario: Certificate provisioning on a second host with different domain - **WHEN** the trojan role runs on a host with `trojan_domain: "proxy2.example.com"` - **THEN** certbot obtains a TLS certificate for `proxy2.example.com` - **THEN** the certificate is independent from other hosts #### Scenario: Certificate auto-renewal - **WHEN** the certificate is within 30 days of expiry - **THEN** certbot renews it automatically - **THEN** a deploy-hook copies the renewed certs to `/etc/trojan-go/tls/` - **THEN** the Trojan service is reloaded after renewal ### Requirement: Trojan runs as a systemd service The trojan role SHALL create a systemd unit file for trojan-go and ensure it is enabled and started. The unit SHALL include both `AmbientCapabilities` and `CapabilityBoundingSet` for `CAP_NET_BIND_SERVICE`. #### Scenario: Service is running - **WHEN** the trojan role completes - **THEN** the Trojan systemd service is enabled and running - **THEN** the service runs under a dedicated non-root user - **THEN** the service user can read the TLS certificate and key files ### Requirement: Trojan listens on port 443 with TLS The Trojan service SHALL listen on port 443 and terminate TLS using the Let's Encrypt certificate. #### Scenario: Trojan accepts connections on 443 - **WHEN** a Trojan client connects to port 443 with valid credentials - **THEN** the connection is accepted and proxied #### Scenario: Non-Trojan traffic is handled by fallback - **WHEN** a non-Trojan HTTPS request arrives on port 443 - **THEN** Trojan forwards it to a local fallback endpoint (if configured) ### Requirement: Trojan configuration is templated The trojan role SHALL generate a JSON configuration file from a Jinja2 template. The config SHALL include: `run_type`, `local_addr`, `local_port`, `remote_addr`, `remote_port`, `password` array (from `trojan_users`), `ssl` section with cert/key paths, and `log_level`. #### Scenario: Multi-user configuration is generated - **WHEN** the trojan role runs with multiple users defined - **THEN** the config file contains a `password` array with all user passwords - **THEN** each user can authenticate with their respective password #### Scenario: Configuration change triggers restart - **WHEN** `users.yml` or trojan variables are changed and the playbook is re-run - **THEN** the configuration file is updated - **THEN** the Trojan service is restarted via handler ### Requirement: Trojan domain is configured per-host Each host in the `trojan` group SHALL define its own `trojan_domain` and `certbot_email` variables in the inventory. The trojan role SHALL fail with a clear error if a host lacks these variables. #### Scenario: Host defines its own domain - **WHEN** a host in inventory has `trojan_domain: "proxy.example.com"` and `certbot_email: "admin@example.com"` - **THEN** the trojan role uses these values for that host #### Scenario: Host missing domain variable - **WHEN** a host in the `trojan` group does not define `trojan_domain` - **THEN** the playbook fails with an error indicating the missing variable #### Scenario: Inventory example shows per-host domain configuration - **WHEN** the user inspects `inventory/hosts.yml.example` - **THEN** it contains per-host `trojan_domain` and `certbot_email` examples ### Requirement: Trojan port 443 is allowed through UFW The trojan role SHALL allow port 443 through UFW. #### Scenario: Firewall allows HTTPS port - **WHEN** the trojan role runs - **THEN** UFW allows incoming TCP traffic on port 443