spec.md 5.7 KB

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