## ADDED Requirements ### Requirement: Trojan is installed on the landing server The trojan role SHALL download and install the Trojan binary (trojan-go or trojan-gfw) from release artifacts to a configurable path. #### Scenario: Fresh installation - **WHEN** the trojan role runs on a landing server without Trojan installed - **THEN** the specified version of the Trojan binary is downloaded and installed - **THEN** the binary is executable and owned by a dedicated service user #### Scenario: Version upgrade - **WHEN** the Trojan version variable is changed and the playbook is re-run - **THEN** the binary is replaced with the new version - **THEN** the service is restarted ### Requirement: Trojan runs as a systemd service The trojan role SHALL create a systemd unit file for Trojan and ensure it is enabled and started. #### 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 (with `CAP_NET_BIND_SERVICE` for port 443) ### Requirement: TLS certificate is provisioned via Let's Encrypt The trojan role SHALL use certbot to obtain a TLS certificate for the landing server's domain, with automatic renewal. #### Scenario: Certificate provisioning - **WHEN** the trojan role runs with a configured domain name - **THEN** certbot obtains a TLS certificate for that domain - **THEN** the certificate and key are accessible to the Trojan service #### Scenario: Certificate auto-renewal - **WHEN** the certificate is within 30 days of expiry - **THEN** certbot renews it automatically via systemd timer or cron - **THEN** the Trojan service is reloaded after renewal ### 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, presenting as a normal HTTPS server to non-Trojan clients. #### 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 web server (camouflage) ### Requirement: Trojan configuration is templated The landing server's Trojan configuration SHALL be generated from a Jinja2 template with variables for password, domain, TLS paths, and fallback settings. #### Scenario: Configuration is generated from template - **WHEN** the trojan role runs - **THEN** a JSON config file is rendered from template - **THEN** the config file permissions are restricted (readable only by the service user) #### Scenario: Configuration change triggers restart - **WHEN** a variable in `group_vars/landing.yml` is changed and the playbook is re-run - **THEN** the configuration file is updated - **THEN** the Trojan service is restarted ### Requirement: Trojan credentials are managed via Ansible Vault The landing server's Trojan password SHALL be defined in `group_vars` and encrypted with Ansible Vault. #### Scenario: Password is not in plaintext in repository - **WHEN** the trojan role generates the configuration - **THEN** the password comes from an Ansible Vault-encrypted variable - **THEN** no plaintext password exists in version-controlled files