英文:
Docker hiding IP of calling container
问题
免责声明:
我是一个网络新手。如果我没有使用正确的词汇来描述我的问题,请原谅。
背景:
我有几个要在CoreOS上运行的应用程序容器(Docker)。每个容器必须具有不同的IAM角色。由于可能会有N个容器在同一主机上运行,每个容器都具有不同的IAM角色,这就导致使用EC2实例元数据(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)来检索实例凭据存在问题。每个容器都需要一组不同的凭据(因为每个容器都具有不同的角色)。
为了解决这个问题,我偶然发现了这个项目https://github.com/dump247/docker-ec2-metadata,它是一个使用iptables规则拦截对http://169.254.169.254/latest/meta-data/iam/security-credentials/的调用,并根据调用容器的IAM角色提供正确凭据的Go代理。
重新路由调用的iptables规则(参见https://github.com/dump247/docker-ec2-metadata/blob/master/README.md)如下:
获取主机IP地址。如果您愿意,可以使用其他机制。
注意,IP不能是127.0.0.1,因为无法对环回进行DNAT。
PROXY_IP=$(ifconfig eth0 | grep -Eo "inet addr:[0-9.]+" | grep -Eo "[0-9.]+")
代理服务运行的端口。默认为18000。
PROXY_PORT=18000
丢弃任何不来自docker容器的代理服务流量
iptables
-I INPUT
-p tcp
--dport ${PROXY_PORT}
! -i docker0
-j DROP
将来自docker容器的请求重定向到代理服务
iptables
-t nat
-I PREROUTING
-p tcp
-d 169.254.169.254 --dport 80
-j DNAT
--to-destination ${PROXY_IP}:${PROXY_PORT}
-i docker0
由于我们在CoreOS上运行,我们希望将该代理部署在Docker容器中。我们不想直接在EC2主机上安装代理。
假设我有2个运行具有不同IAM角色的应用程序的容器:
- containerA:IP:172.17.0.7,IAM角色:roleA
- containerB:IP:172.17.0.8,IAM角色:roleB
当代理拦截对http://169.254.169.254/latest/meta-data/iam/security-credentials/的请求时,代理需要知道请求容器的IP(例如172.17.0.8),以便使用Docker客户端(https://github.com/fsouza/go-dockerclient)检索请求容器的IAM角色(该角色通过环境变量传递给容器,例如:docker run -e IAM_ROLE=arn:aws:iam::123456789012:role/roleA --name containerA myapp
)
问题:
在代理原生运行在主机上时,检索请求容器的IP(在我们的示例中为172.17.0.[7, 8])工作得非常好。以下是代理在主机上原生运行时的工作原理:
containerA调用http://169.254.169.254 -> iptables路由 -> 调用被重定向到localhost:18000 -> 代理容器将containerA的IP视为传入请求的源
然而,当代理在Docker容器中运行时,请求容器的IP是docker0接口的IP(例如172.17.42.1),这使得无法确定请求容器。以下是代理在容器中运行时的工作原理:
containerA调用http://169.254.169.254 -> iptables路由 -> 调用被重定向到localhost:18000 -> Docker将该调用路由到代理容器 -> 代理容器将docker0的IP视为传入请求的源
问题:
如何确保代理将看到的IP地址是请求容器的IP(例如containerA),而不是docker0接口的IP?
英文:
Disclaimer:
I am a networking newbie. Forgive me if I don't use proper vocabulary to describe my problem.
Context:
So I have a couple of app containers (Docker) that are to run on CoreOS. Each container must have different IAM roles. Since there will be potentially be N containers with N different IAM roles running on the same host, this poses a problem to use the EC2 Instance Metadata (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) to retrieve the instance credentials. Each container needs a different set of credentials (since each container will have different roles).
In order to fix that problem, I stumbled upon this project https://github.com/dump247/docker-ec2-metadata which is a Go proxy that intercepts (using iptables rules) calls to http://169.254.169.254/latest/meta-data/iam/security-credentials/ and provides proper credentials based on the calling container’s IAM role.
The iptables rule (see https://github.com/dump247/docker-ec2-metadata/blob/master/README.md) to reroute calls is:
# Get the host IP address. You can use a different mechanism if you wish.
# Note that IP can not be 127.0.0.1 because DNAT for loopback is not possible.
PROXY_IP=$(ifconfig eth0 | grep -Eo "inet addr:[0-9.]+" | grep -Eo "[0-9.]+")
# Port that the proxy service runs on. Default is 18000.
PROXY_PORT=18000
# Drop any traffic to the proxy service that is NOT coming from docker containers
iptables \
-I INPUT \
-p tcp \
--dport ${PROXY_PORT} \
! -i docker0 \
-j DROP
# Redirect any requests from docker containers to the proxy service
iptables \
-t nat \
-I PREROUTING \
-p tcp \
-d 169.254.169.254 --dport 80 \
-j DNAT \
--to-destination ${PROXY_IP}:${PROXY_PORT} \
-i docker0
Since we’re running on CoreOS, we want to deploy that proxy inside a Docker container. We don’t want to install the proxy directly on the EC2 host.
Suppose I have 2 containers running 2 apps with different IAM roles:
- containerA: IP: 172.17.0.7, IAM role: roleA
- containerB: IP: 172.17.0.8, IAM role: roleB
When the proxy intercepts a request to http://169.254.169.254/latest/meta-data/iam/security-credentials/ , the proxy needs to know the requesting container’s IP (e.g. 172.17.0.8), in order to use the Docker client (https://github.com/fsouza/go-dockerclient) to retrieve the requesting container’s IAM role (which is passed to the container using an environment variable, e.g.: docker run -e IAM_ROLE=arn:aws:iam::123456789012:role/roleA --name containerA myapp
)
Problem:
Retrieving the requesting container’s IP (in our example, 172.17.0.[7, 8]) works perfectly fine when the proxy runs natively on the EC2 host. Here's my mental model of what happens when the proxy runs natively on the host:
containerA calls http://169.254.169.254 -> iptables routing happens -> call is redirected to localhost:18000 -> the proxy container sees containerA's IP as the incoming request's source
However, it does not work so well when the proxy runs inside a Docker container. Note that I start the proxy using docker run -p 18000:18000 -v /var/run/docker.sock:/var/run/docker.sock ec2-metadata-proxy
When the proxy runs in a Docker container, the requesting container’s IP is that of the docker0 interface (e.g. 172.17.42.1), making it impossible to determine the requesting container. Again, here's my mental model of what happens when the proxy runs in a container:
containerA calls http://169.254.169.254 -> iptables routing happens -> call is redirected to localhost:18000 -> Docker routes that call to the proxy container -> the proxy container sees docker0's IP as the incoming request's source
Question:
How can I make sure that the IP address that the proxy will see is that of the requesting container (e.g. containerA), and not that of the docker0 interface?
答案1
得分: 0
经过一些尝试,我发现在--net=host
模式下运行代理容器可以解决这个问题。
通过使用这个选项,代理容器与主机共享相同的网络堆栈,从而绕过了docker0桥接。
英文:
After a bit of experimenting, I found that running the proxy container in --net=host
mode solved the problem.
By using this option, the proxy container shares the same networking stack as the host, thus bypassing the the docker0 bridge.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论