Dynamically expose ports to services at runtime via environment variables.
Rules will be created via iptables
that should be functionally the same as exposing ports via docker-compose.yml
,
only without requiring the main services to restart or pushing a new release.
Note that your app must be running in bridge networking mode (the default). Host networking does not require this block.
- Running DNS server in bridge networking, and binding port 53 at runtime in a way that doesn't conflict with dnsmasq on the host OS.
- Allow setting the exposed host ports on a per-device basis with environment vars rather than fleet-wide.
- Expose ports temporarily for debugging without restarting containers.
To run this project, you will need the following environment variables in your container:
FORWARD_SERVICE
: Name of another service in your compose file where the traffic will be forwarded.FORWARD_PORT
: Which port to expose on your host to forward traffic to the defined service.FORWARD_PROTOCOLS
: (optional) Eithertcp
,udp
, ortcp udp
. Default is both.FORWARD_COMMENT
: (optional) Comment used when creating rules to ensure they are cleaned up.
See https://www.balena.io/docs/learn/manage/variables/ for more info on setting device variables.
To run this project, you will need the following labels on the dnat
service:
io.balena.features.balena-socket=1
: Bind mounts the balena container engine socket into the container and sets the environment variableDOCKER_HOST
with the socket location for use by docker clients.
See https://www.balena.io/docs/reference/supervisor/docker-compose/#labels for more info on supervisor labels.
- The
dnat
service will resolve the bridge address of your forward service. - A temporary container be executed with host networking and elevated privileges in order to modify iptables on the host.
- The
dnat
service will restart if the forward service changes it's bridge address. - Rules are cleaned up before/after running based on a comment used during creation.
You can run multiple instances of this block to forward ports to multiple services,
but you should set your own FORWARD_COMMENT
for one of them so they don't try to clean up each-others rules.
Add this block to your fleet application:
- Clone or download this repo into a subdirectory of your project, eg.
./dnat
. - Using the included docker-compose file for reference and add this block to your project's
docker-compose.yml
.
services:
# example only, your service goes here
mywebapp:
image: nginx:1.21.4-alpine
# dnat will expose these ports for you!!
# ports:
# - 80:80/tcp
# tell dnat to forward ports to your service
dnat:
build: dnat
labels:
io.balena.features.balena-socket: 1
environment:
FORWARD_SERVICE: mywebapp
FORWARD_PORT: 80
FORWARD_PROTOCOLS: tcp
depends_on:
- mywebapp
You can follow the service logs to see the iptables rules being created.
12.11.21 12:22:05 (-0500) -A POSTROUTING -s 172.18.0.2/32 -d 172.18.0.2/32 -p tcp -m tcp --dport 80 -m comment --comment my-custom-rules -j MASQUERADE
12.11.21 12:22:05 (-0500) -A DOCKER -p tcp -m tcp --dport 80 -m comment --comment my-custom-rules -j DNAT --to-destination 172.18.0.2:80
12.11.21 12:22:05 (-0500) -A DOCKER -d 172.18.0.2/32 -p tcp -m tcp --dport 80 -m comment --comment my-custom-rules -j ACCEPT