← Paracosm·Docs/Handbook
Contents · Serving a Paracosm to other machines safely

Serving a Paracosm to other machines safely

A Paracosm serves one machine by default: PARACOSM_EXTERNAL_HOST=127.0.0.1 puts that address in every URL the world hands out, so no other machine can log in. quickstart.md section 12 has the steps to open it to a LAN or the internet. This chapter is about doing that safely: what crosses the network in clear, the two ways to add TLS, and which ports to open and which never to.

Untested. Everything below is taken from the OpenSimulator 0.9.3.0 source and its example configs, and none of it has been run: this house has only ever served itself. Try it on a spare machine first.

What travels, and how

traffic port protocol encrypted by default
login (XML-RPC at /), get_grid_info, the capabilities (inventory, textures, meshes, uploads, the event queue), map tiles, script HTTP-in (llRequestURL), the profile service PARACOSM_PORT TCP (9000) HTTP no
the region circuit: movement, chat, object updates, sound triggers PARACOSM_PORT UDP (9000, the region's InternalPort) LLUDP no, and it has no TLS option
OpenSim's REST console PARACOSM_CONSOLE_PORT TCP (9001) HTTP no
WorldBridge 9002 TCP, 127.0.0.1 only HTTP + bearer token no
the viewer's LEAP helper bridge (on the viewer's machine) 9004 TCP, 127.0.0.1 only HTTP + bearer token no

What that means:

Option 1: OpenSim's own HTTPS listener

OpenSim can serve its main HTTP server over TLS itself. The keys, all in [Network] of OpenSim.ini (OpenSim.ini.example documents them; NetworkServersInfo.loadFromConfiguration reads them):

key default meaning
http_listener_ssl false serve the main server over HTTPS
http_listener_sslport 9001 the HTTPS port. The default collides with Paracosm's REST console port (9001): pick another
http_listener_cn localhost the certificate's host name. It must equal the region's ExternalHostName (PARACOSM_EXTERNAL_HOST), or region creation fails with "main http cert CN doesn't match region External IP"
http_listener_cert_path empty the certificate, PKCS#12 (.p12)
http_listener_cert_pass empty its password

What changes (RegionApplicationBase, OpenSimBase.CreateRegion): the HTTPS server becomes the main server, the region's ServerURI, and so its capabilities, become https://<ExternalHostName>:<sslport>/, and the plain server on http_listener_port keeps running beside it. The example config warns: "the main unsecure port will still open for some services". The FreeSWITCH voice account URL, for one, is always built as http://.

Getting a certificate ([Startup], read in OpenSimBase.Initialize):

Then point the login at the HTTPS port: [Const] BaseURL = https://${Const|BaseHostname} and PublicPort = the SSL port (both build the login URI that get_grid_info returns), and give viewers https://<host>:<sslport>/ as the login URI. In Paracosm these are overlay keys: put them in config/paracosm.ini (the PublicPort line there reads ${PARACOSM_PORT} today) and run make_config.py --force. The HTTPS port must be opened as well as the plain one.

Option 2: a reverse proxy in front of the login

A TLS reverse proxy (nginx, Caddy) on the same machine can serve https://world.example.org/ and forward to http://127.0.0.1:9000. This protects what goes through it: the login and get_grid_info. It does not protect the capabilities or the UDP circuit, because the login response sends the viewer straight to http://<ExternalHostName>:9000/CAPS/... and to UDP 9000. Port 9000 (TCP and UDP) must still be open to the viewers. Use it when the password exchange is what you want off the wire and the rest can stay in clear.

For the viewer to log in through the proxy, both the URI the person types and the one get_grid_info returns must be the proxy's: set [GridInfoService] login = https://world.example.org/ in config/StandaloneCommon-overlay.ini (upstream's value is ${Const|BaseURL}:${Const|PublicPort}/). The login service rate-limits by client address ([LoginService] DOSMaxRequestsInTimeFrame, 5 per 10 s by default); behind a proxy every login comes from 127.0.0.1, so set DOSAllowXForwardedForHeader = true there, and only there, and have the proxy set X-Forwarded-For.

Caddy (it obtains and renews the certificate itself when the name resolves to this machine):

world.example.org {
    reverse_proxy 127.0.0.1:9000
}

nginx:

server {
    listen 443 ssl;
    server_name world.example.org;
    ssl_certificate     /etc/letsencrypt/live/world.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/world.example.org/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        client_max_body_size 64m;
    }
}

Do not proxy the whole simulator port to the internet on 443 and close 9000: the viewer would still be told to use 9000 for everything after the login.

WorldBridge and the REST console

The firewall

port open?
PARACOSM_PORT TCP (9000) yes, to the viewers that should reach the world
PARACOSM_PORT UDP (9000) yes, the same
the HTTPS port (option 1) or 443 (option 2) yes, if you use one
PARACOSM_CONSOLE_PORT TCP (9001) never
WorldBridge TCP (9002) never (it is loopback-bound anyway)
the viewer's LEAP bridge TCP (9004) never (loopback-bound, on the viewer's machine)

Windows (an elevated PowerShell):

New-NetFirewallRule -DisplayName "Paracosm TCP" -Direction Inbound -Protocol TCP -LocalPort 9000 -Action Allow
New-NetFirewallRule -DisplayName "Paracosm UDP" -Direction Inbound -Protocol UDP -LocalPort 9000 -Action Allow

Linux with ufw:

ufw allow 9000/tcp
ufw allow 9000/udp
ufw deny 9001/tcp

Behind a router, forward TCP and UDP 9000 (and the HTTPS port) to the server, and set PARACOSM_EXTERNAL_HOST to the public name. A viewer on the server's own LAN then reaches the world by its public name only if the router loops traffic back to itself (NAT loopback); the profile service does not depend on that, because Paracosm's ProfileServiceURL is loopback.

Checklist for an exposed world

  1. The estate owner's and every account's password changed from anything in a template.
  2. PARACOSM_CONSOLE_USER empty, or 9001 firewalled and reached over SSH.
  3. WorldBridge untouched (loopback), the AI on the server machine.
  4. TLS for the login at least (option 1 or 2).
  5. Decide about the profile service's open JSON-RPC (config.md): keep it, or turn full profiles off.
  6. [Permissions] region_manager_is_god = true (the overlay): make estate managers only of people you trust.
  7. Backups running (backup.md).