Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Ports and firewalls

What to open for Ductus, in each direction, and how to get the exact list for a firewall request. The short answer: in, one port for the web UI and API, and each interface's own listener; out, the database and whatever your interfaces connect to. Nothing else: no thick client, no extra ports for the web UI, no cluster port.

What connects to the engine (in)

PortWhatWho connectsOpen it to
7700 (api.listen)The REST API and the web UI, one port for bothPeople's browsers, the ductus command line from other machines, monitoring (Prometheus), scriptsDevelopers' and operators' networks, the monitoring server. Listens on 127.0.0.1 (this machine only) until you change it.
Each interface's portIts MLLP or HTTP listener, from its interface.yamlThe systems that send to it: the EHR, a lab system, another engineThe sending systems' addresses only.

The API and web UI listen on this machine only by default (api.listen: 127.0.0.1:7700), so opening them to the network is a choice you make. When you do, give them a certificate and an allow list, in ductus.yaml:

# ductus.yaml, in part
api:
  listen: 0.0.0.0:7700
  tls: { certificate: ductus-api }          # a certificates: entry in resources.yaml
  allow: [127.0.0.1, 10.1.50.0/24]          # this machine, and the operators' subnet

Without tls, passwords and tokens cross the network readable and the web UI's sign-in doesn't work at all (its cookie is only sent over HTTPS: nuance). Keep 127.0.0.1 in the list, or commands run on the server itself are refused (nuance). Developers need this port and whatever they deploy with (SSH, Git), nothing more.

Interfaces' listeners: each MLLP source has a port of its own; HTTP sources can share one, by path. ductus new interface picks a free one from the ranges in ductus.yaml (ports: MLLP 6661–6699, HTTP 8080–8099 by default). Ask the firewall team for a range once, set ports: to it, and new interfaces land inside it with no new request:

# ductus.yaml, in part
ports:
  mllp: 7000-7099     # the range the firewall team opened
  http: 9080-9099

The engine checks who may connect itself too: allow: on a source lists the senders' addresses, subnets or host names, and anyone else is disconnected before a byte is read (who may connect). Keep the firewall rules as well: each catches the other's mistakes (nuance).

A port below 1024 (MLLP on 104, say) needs a capability under the Linux package's service; the unit file says which two lines to add with systemctl edit ductus. Above 1024 needs nothing.

What the engine connects to (out)

ToPortWhen
Its PostgreSQL5432 (or the URL's)Always.
Receivers of MLLP destinationsas configuredFor each destination: { mllp: … }.
HTTP destinations, APIs (api(), fetch()), webhooks, the heartbeat443 (or the URL's)For each one configured.
Databases interfaces query (db())Postgres 5432; SQL Server 1433, and UDP 1434 for a named instance (the SQL Server Browser)For each databases: entry.
SFTP servers22 (or the entry's)For each sftp: entry.
The directory (Active Directory, OpenLDAP)636 (ldaps), or 389 with StartTLSWith auth.ldap.
The mail server587 (STARTTLS), 465 (TLS) or 25With alerts.smtp.
DNS53To look up every host name above.
Maven Central (repo1.maven.org, 443)Only ductus dev, once per user, on developers' machines. Never on a server.

The engine never connects anywhere to "phone home": no update checks, no telemetry.

The exact list for a firewall request

ductus ports prints every port a project listens on and every host it connects to, for the environment you name, with conflicts (two interfaces on one port) flagged:

For production, give it --env prod. Here, the project from Your first interface, with TLS:

$ ductus ports
environment: dev

Listening
  PORT  PROTOCOL       HOST       INTERFACE          ROUTE    TLS                                                            ALLOWED
  6661  MLLP over TLS  0.0.0.0    adt-in                      presents ductus-mllp, clients need a certificate from test-ca  anyone
  7700  HTTP           127.0.0.1  (engine REST API)  /api/v1  none                                                           anyone

Connects to
  (nothing)

✓ no port conflicts

ductus doctor, run on the engine's server, then checks each outbound connection from there: DNS, TCP (refused, or no answer at all, which is a firewall), TLS, and each system's login. For each one it prints the source address the connection left from: that is the address a firewall request must name, which on a server with several networks isn't always the one you'd guess. --report doctor.txt saves it as text for the network team or a vendor (doctor.json as JSON) (what a pass means).

Opening a range on the server itself

# RHEL, Rocky, Alma (firewalld)
sudo firewall-cmd --permanent --add-port=6661-6699/tcp && sudo firewall-cmd --reload
# Ubuntu (ufw)
sudo ufw allow from 10.20.30.0/24 to any port 6661:6699 proto tcp
# Windows
New-NetFirewallRule -DisplayName "Ductus MLLP" -Direction Inbound -Protocol TCP -LocalPort 6661-6699 -RemoteAddress 10.20.30.0/24 -Action Allow