Thesis: A self-hosted e-sign / signing host should not advertise SSH until someone names the CIDRs. Break-glass is console browser SSH, not a standing open port.
What we shipped first
Early images opened 22 so operators could patch and debug. That felt convenient. It also meant every scan saw a login surface on a box that holds signing material and evidence workflow secrets.
We wanted a default that matched how often we actually need shell: almost never. Day-to-day work uses the app and APIs. Shell is for break-glass.
The working shape
The SSH allow-list variable defaults to an empty list. Port 22 is absent from the firewall until that list is non-empty. When we need a shell, we use Lightsail or console browser SSH — or temporarily add a CIDR and remove it after. API keys stay in secrets, not in a world-reachable sshd.
variable "signing_host_ssh_allowed_cidrs" {
type = list(string)
description = "CIDRs allowed on port 22. Empty list closes SSH (use console browser SSH)."
default = []
}
# Port 22 exists only when the list is non-empty
dynamic "port_info" {
for_each = length(var.signing_host_ssh_allowed_cidrs) > 0 ? [1] : []
content {
protocol = "tcp"
from_port = 22
to_port = 22
cidrs = var.signing_host_ssh_allowed_cidrs
}
}
Checklist
- Default the SSH CIDR list to empty so port 22 is not published.
- Prefer console / browser SSH for break-glass over a standing allow-list.
- If you open a CIDR, time-box it and close the port again.
- Keep signing API keys in secrets management — not beside an open sshd.
Related: we put WAF on CloudFront and the Function URL stayed wide open. How we deliver: methodology.
Engineering commentary only — not audit, legal, or certification advice.