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