Skip to content

Instantly share code, notes, and snippets.

@inkblot
Last active January 20, 2024 22:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save inkblot/81440a9822550c44a62978137b0b1268 to your computer and use it in GitHub Desktop.
Save inkblot/81440a9822550c44a62978137b0b1268 to your computer and use it in GitHub Desktop.
ECS Task Roles - A Better Way

ECS Task Roles

ECS task roles are a great security feature that are hard to set up.

The Orthodoxy

The Amazon ECS documentation on setting up task roles tells you to do some questionable things. Among other things, it tells you to run the ECS agent with host networking (a security risk), use an iptables rule to cut off traffic from bridged containers to the host metadata (brittle), and set up additional iptables rules and sysctl settings to route 169.254.170.2:80 to the ECS agent on 127.0.0.1:51679 (brittle again).

Some Observations

Setting up things like sysctl settings and iptables rules varies a lot by host platform. Inevitably, Amazon's instructions are written for Amazon Linux and for any other distribution you're on your own. That's not terrible, but bootscripts are a solution of last resort for provisioning and on my distributions of choice, there are better options.

The whole point of the latter iptables rules and sysctl setting is effectively to bind a container port to a host port. Docker has this functionality built-in. The oddball IP address is a complication, but it's a minor one.

A Better Way

First of all, your container host needs to understand that it can respond to the 169.254.170.2 destination IP. This is easy. Just add the IP address to your loopback device. Here's what that looks like in a CoreOS cloud-config user data file:

coreos:
  units:
    - name: 00-loopback.network
      content: |
        [Match]
        Name=lo
        [Network]
        Address=127.0.0.1/8
        Address=169.254.170.2/32

And in a Debian or Ubuntu /etc/network/interfaces file (provision however you like, I use puppet):

auto lo
iface lo inet loopback

iface lo inet static
    address 169.254.70.2
    netmask 255.255.255.255

And in a bootscript:

ip addr add dev lo 169.254.170.2/32

Second, when you run the ECS agent replace --net host with -p 169.254.170.2:80:51679 to get docker to forward the port.

Just like that, you've got your ECS agent listening on 169.254.170.2:80 without host networking.

Caveats

You'll still need to block container access to the host metadata, and it's somewhat more complicated now because the ECS agent itself will still need access to it but is now huddled on the bridge with the containers you want to block. Sorry, no good solution for this yet. I think you could run the agent on its own bridge and block anything else from fetching the host metadata, but this puts us back into iptables.

With docker publisheing the ECS agent task metadata port at 169.254.170.2:80, the ECS agent itself sees port 80 as "occupied" on the host. If you've got some other container that you wanted to bind to 0.0.0.0:80 you're not completely out of luck, but this definitely seems like a bug in the ECS agent. I solved this with an Application Load Balancer, which is an evolution of an ELB that includes backend port awareness and allows a docker container managed by an ECS service to have a dynamic port binding. I think Amazon could fix this by recognizing 169.254.170.2:80 and 0.0.0.0:80 as separate, non-colliding ports.

Notes

Why is iptables brittle? I generally avoid messing with filtering on container hosts because docker inserts rules and pretends that it owns the tables. On a bad day in 2017, I lost a bunch of rules because the docker daemon restarted. Naturally, the right solution was to shoot the host and let the cluster recover, but the whole situation could have been avoided by letting docker own the netfilter tables and finding other solutions to my other problems (such solutions absolutely exist).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment