Networking
Deployed and supported. The fields and commands described here are stable within version v1alpha1.
The network of an App is closed by default: a service only accepts connections on its
declared ports, coming from the other services of the same App (and from the Internet if it is exposed),
and goes nowhere except to the services of your tenant. Two fields open doors:
allowFrom (another App of your tenant gets in) and egress: [internet] (the service goes out).
Prerequisites
- An App being written (From docker compose).
Steps
1. Open what needs to be open
apiVersion: deploy.stackngo.io/v1alpha1
kind: App
metadata:
name: shop
spec:
services:
api:
image: registry.gitlab.com/acme/shop-api:1.4.2
ports: ["8080"]
ingress: { port: 8080 }
egress: [internet] # calls a payment provider
dependsOn: [db]
db:
image: postgres:17
user: "999"
ports: ["5432"]
allowFrom: [reporting] # the tenant's "reporting" App can read the database
volumes:
- { path: /var/lib/postgresql/data, size: 10Gi }
On the command line: sng app create … --egress-internet --allow-from reporting,
sng app patch shop --service api --egress-internet=false.
2. Apply and test
sng apply shop.yml
sng app logs shop --service api --since 5m
From api, db:5432 responds; so does https://api.paiement.example. From db, a
request to the Internet is blocked.
Verification
A small probe job tells what gets through:
apiVersion: deploy.stackngo.io/v1alpha1
kind: App
metadata:
name: netcheck
spec:
services:
closed:
kind: job
image: curlimages/curl:8.11.1
user: "100"
size: xxxs
command: ["sh", "-c", "for u in https://example.com http://169.254.169.254/; do if curl -s -m 5 -o /dev/null $u; then echo \"OK $u\"; else echo \"BLOCKED $u\"; fi; done"]
open:
kind: job
image: curlimages/curl:8.11.1
user: "100"
size: xxxs
egress: [internet]
command: ["sh", "-c", "for u in https://example.com http://169.254.169.254/; do if curl -s -m 5 -o /dev/null $u; then echo \"OK $u\"; else echo \"BLOCKED $u\"; fi; done"]
sng apply netcheck.yml
sng app logs netcheck
[netcheck-closed-3f9a1c2e-x7k2q] BLOCKED https://example.com
[netcheck-closed-3f9a1c2e-x7k2q] BLOCKED http://169.254.169.254/
[netcheck-open-8b1d7e40-p9s4m] OK https://example.com
[netcheck-open-8b1d7e40-p9s4m] BLOCKED http://169.254.169.254/
Who can reach what
Inbound to a service
| From | To | Allowed? |
|---|---|---|
| another service of the same App | a port declared in ports | yes |
| another service of the same App | an undeclared port | no |
| another App of your tenant | a declared port | yes if the App is in allowFrom |
| the Internet (over HTTPS) | the ingress port only | yes if ingress |
| another tenant | anything | never |
| anyone | a service without ports | no: it accepts nothing |
The rules are in place before the service starts: there is no window during which a service would be open.
Outbound from a service
| To | Allowed? |
|---|---|
| the services of your tenant (all Apps) | yes, but the target service must accept (see above) |
| the platform DNS | yes |
| the Internet | only with egress: [internet] |
private addresses (other tenants, private networks, 169.254.169.254) | never, even with egress: [internet] |
egress only accepts the value internet; there is no opening by address or by port.
Naming services between each other
A service db of the App shop that declares ports: ["5432"] is reachable by the other
services of the App at db:5432, exactly as in compose. With the form "5433:5432",
it is db:5433 that is exposed, to port 5432 of the container.
From another App of the tenant (allowed by allowFrom), use the same name: db:5432.
Avoid giving the same service name to two Apps of a tenant: the second is rejected
(NameConflict), because a service name is unique across the whole tenant.
allowFrom
- List of App names of your tenant (10 at most), not services.
- Opens all
portsof the service to the services of these Apps. No per-port granularity. - Requires at least one declared port, otherwise the manifest is invalid (
allowFrom : le service n'a aucun port à ouvrir). sng app patch shop --service db --allow-from ''removes all authorizations.
dependsOn and networking
dependsOn orders the creation of services; it opens no door. A service can
depend on another without being able to reach it (if the latter has no ports), and vice versa.
For api to wait for db and talk to it: dependsOn: [db] on api, ports: ["5432"] on db.
If it doesn't work
| What you see | Cause | Fix |
|---|---|---|
connection refused or timeout to db:5432 | The port is not in the ports of db, or the caller is in another App missing from allowFrom | Declare the port, or add the App to allowFrom |
| A call to an external API times out | The service does not have egress: [internet] | Add it, then sng apply |
NameConflict on a service | Another App of the tenant has a service with the same name | Rename one of the two services |
Manifest rejected: allowFrom : le service n'a aucun port à ouvrir | allowFrom on a service without ports | Declare at least one port |
Going further
- These rules apply to every service and cannot be disabled.
- Your containers receive no variable describing the other services: pass the
addresses through
env(DB_HOST: db). - Resolution of public names is always allowed: no additional opening is
needed to resolve a name before reaching it with
egress: [internet].