replace the linux-only firewall patching with a portable exposure model
The catalogue publishes 51 compose files with the bare form 'ports: [80]',
which binds a random host port on 0.0.0.0: every service answers the internet.
ufw-docker existed to take that back afterwards, as root, on linux only,
because docker writes its own firewall rules and ufw never sees those ports.
Publishing where you mean to solves it at the source. Verified against the
daemon: '- 80' gives 0.0.0.0:32768, '127.0.0.1::80' gives 127.0.0.1:32769.
Same on macOS and on linux, no privilege, and visible in docker ps.
A stack binds with ${MYOS_BIND_PRIVATE|PUBLIC|MESH} and declares what it
means with <PREFIX>_SERVICE[_<port>]_EXPOSE. myos expose reads the resolved
compose configuration and reports what would be opened; --strict fails when a
port faces the world without declaring it, which is what an agent runs against
a server it did not set up.
This commit is contained in:
@@ -82,6 +82,10 @@ See `references/conventions.md`.
|
||||
|
||||
## Rules
|
||||
|
||||
- Check what a stack opens before starting it on a server that faces the
|
||||
internet: `myos expose <stack>`. A port shown on `0.0.0.0` answers the world,
|
||||
and on linux the host firewall does not see it, because docker writes its own
|
||||
rules. Bind it instead: `ports: ["${MYOS_BIND_PRIVATE}::<port>"]`.
|
||||
- Never run `myos clean` on a host stack: it removes images **and volumes**,
|
||||
including the certificates.
|
||||
- Secrets belong in a file outside the repository, never in a compose file.
|
||||
|
||||
@@ -27,6 +27,8 @@ myos [options] <command> [stack...] [VAR=value...] [-- args...]
|
||||
| `ls [--groups]` | the stacks and groups myos can see |
|
||||
| `env [VAR...]` | resolved variables |
|
||||
| `env-update` | fill the `.env` from the `.env.dist` templates |
|
||||
| `expose [--strict]` | what the stacks publish, and to whom |
|
||||
| `export [--make]` | every setting of the stacks, as `KEY=value` |
|
||||
| `doctor` | check the installation |
|
||||
| `version` | the myos version |
|
||||
|
||||
|
||||
@@ -168,6 +168,52 @@ A line may refer to a variable defined further down. A variable that already
|
||||
has a value keeps it: the `.env` records choices, it never overwrites them, and
|
||||
running the command twice changes nothing.
|
||||
|
||||
## What a stack publishes, and to whom
|
||||
|
||||
On linux docker writes its own firewall rules, so a port published with
|
||||
`ports: ["8080:80"]` answers the internet whatever the host firewall says. The
|
||||
portable answer is to publish where you mean to, which behaves the same on
|
||||
linux and on macOS and needs no privilege:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
ports:
|
||||
- "${MYOS_BIND_PRIVATE}::8080" # this host only, reached through fabio
|
||||
gateway:
|
||||
ports:
|
||||
- "${MYOS_BIND_PUBLIC}:443:443" # the internet, on purpose
|
||||
peer:
|
||||
ports:
|
||||
- "${MYOS_BIND_MESH}::7946" # the private network between the hosts
|
||||
```
|
||||
|
||||
| scope | address | for |
|
||||
|---|---|---|
|
||||
| `private` | `127.0.0.1` | everything the load balancer reaches for you. The default. |
|
||||
| `public` | `0.0.0.0` | a load balancer, a public DNS or mail service |
|
||||
| `mesh` | the mesh interface | services shared between the hosts of a fleet |
|
||||
|
||||
`MYOS_BIND_PUBLIC`, `MYOS_BIND_PRIVATE` and `MYOS_BIND_MESH` override the
|
||||
addresses; `MYOS_MESH_IFACE` names the interface when it is not one of
|
||||
easytier, tun0, tailscale0, mycelium or wg0.
|
||||
|
||||
A stack also declares what it means, so an audit can tell a deliberate choice
|
||||
from an oversight:
|
||||
|
||||
```sh
|
||||
<PREFIX>_SERVICE_EXPOSE=public # the whole stack
|
||||
<PREFIX>_SERVICE_443_EXPOSE=public # one port
|
||||
```
|
||||
|
||||
`<PREFIX>` is `HOST_<name>` for a host stack, `USER_<name>` for a user stack,
|
||||
`<name>` otherwise.
|
||||
|
||||
```sh
|
||||
myos expose # what each stack publishes, and its declared scope
|
||||
myos expose --strict # exits 1 when a port faces the world undeclared
|
||||
```
|
||||
|
||||
## Groups
|
||||
|
||||
A group is a lowercase name whose value lists stacks. It can live in a `.env`,
|
||||
|
||||
Reference in New Issue
Block a user