Skip to content

fedify-dev/fedify.com.es

Repository files navigation

fedify.com.es tunnel service

This repository defines the deployment and operation of an SSH-based tunneling service at fedify.com.es. The service lets a developer expose a local HTTP port through a temporary public HTTPS URL such as https://example.fedify.com.es.

The server runs sish directly under systemd on an Ubuntu VPS. Ansible configures the host over SSH, while Certbot and Cloudflare DNS maintain the apex and wildcard TLS certificate.

Project status

The anonymous public service was deployed on July 14, 2026, after an authenticated private beta. It passed certificate, host-key, HTTP, HTTPS, WebSocket, non-HTTP-forward rejection, selective tunnel shutdown, rollback, idempotency, and post-reboot tunnel checks. The deployment uses a patched fedify-dev/sish release that rejects non-HTTP remote forwards before creating a listener and serves the apex landing page without another public listener or web-server process.

The pinned sish host key has the fingerprint SHA256:MS+vPYDnU2dceunPdykxErOjWoTKQG/Hcy0HFfQc6mg. Its public key is in client/fedify.com.es.known_hosts.

Service and abuse reports

The service is available for temporary HTTP, HTTPS, and WebSocket development tunnels. TCP tunnels, user-selected subdomains, custom domains, persistent hosting, and the sish administration interfaces are not offered.

Report phishing, malware, prohibited content, or other harmful use through the public abuse-report form. Reports should include the tunnel URL, the time it was observed, and the reason for the report. The form is public, so do not include passwords, tokens, personal data, or other confidential information.

An operator can locate the SSH peer in the sish lifecycle journal, terminate only that socket, and add a source-address deny rule if the activity continues:

sudo journalctl -u sish --since "30 minutes ago"
sudo ss --kill --ipv4 dst <client-ip> dport = :<client-port> sport = :2222
sudo ufw insert 1 deny from <client-ip> to any port 2222 proto tcp

The route must return 404 after termination; an unrelated test tunnel must remain available. Removing the entire sish service is the fallback when a single connection cannot be identified safely.

Intended system

flowchart LR
    local_app["Local application"]

    subgraph ingress["Public ingress"]
        ssh["sish SSH endpoint<br/>:2222 on fedify.com.es"]
        http["HTTP :80"] -->|"Redirect"| https["HTTPS :443<br/>*.fedify.com.es"]
        ssh -. "Registers route" .-> https
    end

    local_app ==>|"SSH reverse tunnel"| ssh
    https ==>|"Request and response through tunnel"| local_app
Loading

The deployment does not use Docker. sish runs as an unprivileged system user with only the capability needed to bind ports 80 and 443. Administrative OpenSSH remains on port 22, separate from the public sish listener.

Documentation

  • AGENTS.md contains the instructions and safety boundaries for coding agents working in this repository.
  • ARCHITECTURE.md describes the target system, traffic flow, certificate lifecycle, security boundaries, and rollback model.
  • PLAN.md contains the phased implementation checklist and acceptance criteria.

Read AGENTS.md, then ARCHITECTURE.md and PLAN.md, before making changes to the repository or the server.

Prerequisites

The operator workstation needs mise, authenticated administrative SSH access to fedify.com.es, and a trusted OpenSSH host-key entry for the administrative service on port 22. Confirm that key through the VPS provider console before the first connection. Do not bootstrap trust from ssh-keyscan alone.

Create a Cloudflare API token with Zone:Zone:Read and Zone:DNS:Edit access limited to the fedify.com.es zone. Certbot uses the read permission to find the zone ID and the edit permission to create and remove ACME TXT records. The deployment also needs a Let's Encrypt account email.

Secret setup

Install the pinned tools and create an ignored .env.production file with mode 0600:

mise install
umask 077
$EDITOR .env.production
chmod 600 .env.production
mise run vault:init

The file must contain LETSENCRYPT_EMAIL and CLOUDFLARE_API_TOKEN entries. vault:init atomically creates or replaces the encrypted production Vault and removes .env.production.

Keep both .vault-password and the encrypted production Vault in an operator backup; they are deliberately excluded from Git. Never pass the Cloudflare token as an Ansible command-line variable because process listings and shell history can expose it.

Command surface

The repository exposes its local setup, checks, and deployment through mise.

mise run check
mise run vault:init
mise run bootstrap
mise run bootstrap:firewall
mise run deploy:check
mise run deploy
mise run verify

check runs local formatting, Ansible syntax and lint checks, and ShellCheck without contacting production or opening the production Vault. deploy:check previews production changes in Ansible check and diff mode. Certificate issuance is skipped in check mode, so the preview cannot prove that ACME or Cloudflare calls will succeed. verify reads service state, listeners, firewall rules, certificate names and lifetime, the published host-key pin, and journal errors from the current sish instance without changing the server.

Deployment principles

  • Pin every deployed sish release and verify its published checksum.
  • Keep playbooks idempotent and preview production changes before applying them.
  • Store the Cloudflare API token encrypted and restrict it to DNS edits for this zone.
  • Preserve administrative SSH access while changing the firewall or service configuration.
  • Keep sish host keys and Certbot state outside versioned release directories.
  • Distribute and pin the sish host public key; reject tunnel connections when the presented key does not match.
  • Reject non-HTTP forwarding at the SSH protocol layer before relying on firewall or systemd containment.
  • Bound anonymous use with random names, connection rate limiting, process resource limits, and a documented abuse response.

The detailed requirements and acceptance checks live in PLAN.md.

First deployment

Inspect git status and the complete diff before using these commands. The bootstrap is split so the firewall cannot be enabled until the routine account has been tested in a separate SSH session:

mise run check
mise run bootstrap
ssh fedify-deploy@fedify.com.es sudo -n true
mise run bootstrap:firewall
mise run deploy:check
mise run deploy
mise run verify

FEDIFY_ADMIN_PUBLIC_KEY_FILE can select a public key other than ~/.ssh/id_ed25519.pub for the deployment account. Leave the existing root SSH session open until the second session and UFW state have both been checked.

After the first deploy, derive the sish host public key through the authenticated administrative connection. Compare its fingerprint with the key presented on port 2222 from another machine, then publish the matching [fedify.com.es]:2222 entry in client/fedify.com.es.known_hosts. The private host key under /var/lib/sish/keys must never leave the VPS.

End-to-end test

Run a local HTTP server on a machine outside the VPS, then open a tunnel with the published pin as the only SSH trust source:

python3 -m http.server 8000
ssh -p 2222 \
  -o PubkeyAuthentication=no \
  -o PasswordAuthentication=no \
  -o KbdInteractiveAuthentication=no \
  -o StrictHostKeyChecking=yes \
  -o UserKnownHostsFile=client/fedify.com.es.known_hosts \
  -o GlobalKnownHostsFile=client/fedify.com.es.known_hosts \
  -o KnownHostsCommand=none \
  -o VerifyHostKeyDNS=no \
  -o CheckHostIP=no \
  -o UpdateHostKeys=no \
  -R 80:localhost:8000 fedify.com.es

Request the printed HTTPS URL with a normal client and compare its response with the local server. Test the HTTP redirect, a deliberately wrong host-key pin, raw TCP forwards with a fixed port and -R 0, a TCP alias request, and route cleanup after SSH exits. Every non-HTTP request must receive an SSH-level rejection without restarting sish. Repeat the service and HTTPS checks after rebooting the VPS.

Upgrade, rollback, and backup

To upgrade sish, update the pinned fedify-dev/sish version, release URL, and SHA-256 value together, run the full preview, and deploy. Ansible keeps versioned releases under /opt/sish/releases. A rollback to an unpatched upstream release must also restore authenticated private-beta mode; running an unpatched binary with anonymous access would reopen raw TCP forwarding. Preview and exercise both directions before treating the path as an operating procedure.

Back up the encrypted Vault and its separately stored password, /etc/letsencrypt, and /var/lib/sish/keys. The repository and playbooks can rebuild the remaining host state. Certificate releases under /etc/sish/ssl are derived from Certbot state and do not replace an ACME backup.

Relationship to Fedify CLI and localtunnel

The fedify tunnel command will use this service. Fedify CLI depends on the localtunnel package, which opens tunnels with the system SSH client. sish was selected because it accepts the same remote-forwarding command shape without requiring users to install a service-specific client:

ssh -p 2222 \
  -o StrictHostKeyChecking=yes \
  -o UserKnownHostsFile=/path/to/fedify.com.es.known_hosts \
  -o GlobalKnownHostsFile=/path/to/fedify.com.es.known_hosts \
  -o KnownHostsCommand=none \
  -o VerifyHostKeyDNS=no \
  -o CheckHostIP=no \
  -o UpdateHostKeys=no \
  -R 80:localhost:8000 fedify.com.es

Fedify CLI will define the fedify.com.es service and pass a custom service registry to localtunnel's openTunnel() function at runtime. The endpoint will not be added to localtunnel's built-in service registry. Localtunnel 0.5.0 adds the Service.knownHosts field for pinned SSH host keys and uses strict checking when that field is present. Fedify CLI will populate it with the verified sish host key. The corresponding CLI work starts after the anonymous endpoint passes its public-mode acceptance tests.

About

fedify.com.es tunnel service

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Contributors