From a0c246169c00ef784c11deae580af314ae30cefb Mon Sep 17 00:00:00 2001 From: Joachim Lusiardi Date: Thu, 19 Feb 2015 19:27:52 +0100 Subject: [PATCH] fixed problem with listening on different IPs which is not possible in the classical way in docker --- README.md | 34 +++++++++++++++++++++++++++------- nginx_proxy.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1cd2833..594e19b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,6 @@ # Automated Nginx reverse Proxy for Docker Webservices This image attaches to the docker event queue and creates/removes proxy settings in the contained nginx. -## Starting the container -The container is started as: - -`docker run --name auto_proxy -d -v /var/run/docker.sock:/var/run/docker.sock -p 80:80 docker_nginx_auto_proxy` - -The socket must be handed in so the container can get the events. - ## How it works Containers that should be proxied neet meta information in the environment variable *PROXY_DATA* available. This variable must be of the following format: @@ -20,3 +13,30 @@ The following options are possible: * **port**(optional, defaults to 80) the port on the target container * **ip**(optional, defaults to listen on all IPs) the IP on which the proxy should listen. * **location**(optional) if the proxied web application is not running on the /-path + +## Starting the container + +Since the container uses Docker's internal event reporting, it needs access to the daemon. At the +moment, only access via UNIX socket ist possible. Because of that, the socket has to be handed +into the container (*-v /var/run/docker.sock:/var/run/docker.sock*). + +### Single IP / All IPs +This option is used if your Docker Host has only one IP or if there is no need to differentiate between different IPs regarding wether a Web App is available on it. + +Run the container like this: +`docker run --name auto_proxy -d -v /var/run/docker.sock:/var/run/docker.sock -p 80:80 docker_nginx_auto_proxy` + +That means that the container exposes all Wep Apps on all IPs. Do **not** use the *ip* option from above on the target containers. The *PROXY_DATA* environment variables would be something like: +`PROXY_DATA=server_name:cooldomain.test.com,port:8080,location=/webApp` + +### Multiple IPs +This option is used if your Docker Host has multiple IPs (perhaps a public IP in the internet and a private IP on a VPN). It is possible to expose some Web Apps only to the private network. + +One container must be started for each IP that should host Web Apps. For example, if there is a public IP of 1.2.3.4 and a private IP 10.1.2.3, then 2 Containers would be started: +`docker run --name auto_proxy_public -d -v /var/run/docker.sock:/var/run/docker.sock -p 1.2.3.4:80:80 docker_nginx_auto_proxy` +`docker run --name auto_proxy_private -d -v /var/run/docker.sock:/var/run/docker.sock -p 10.1.2.3:80:80 docker_nginx_auto_proxy +` +If a target container does **not** have the *ip* option set, it listens on **all** IP adresses and will be handled by both containers. If a container uses, e.g. +`PROXY_DATA=server_name:cooldomain.test.com,port:8080,location=/webApp,ip=10.1.2.3` +Then it will be only available on the private 10.1.2.3 IP (perhaps using a VPN). + diff --git a/nginx_proxy.py b/nginx_proxy.py index 3a7de2f..6b54c4b 100755 --- a/nginx_proxy.py +++ b/nginx_proxy.py @@ -83,13 +83,18 @@ def handle_container(id): env_vars = analyse_env_vars(inspect_data) if 'PROXY_DATA' in env_vars: proxy_data = analyse_proxy_data(env_vars) + container_listen_ip = get_if_available(proxy_data, 'ip', '0.0.0.0') + if container_listen_ip != '0.0.0.0' and container_listen_ip not in listen_ips: + logging.info('container "%s"(%s) does not listen on %s.', extract_name(inspect_data), container_listen_ip, str(listen_ips)) + return + logging.info('container "%s"(%s) is allowed to listen on %s.', extract_name(inspect_data), container_listen_ip, str(listen_ips)) substitutes = { 'containername': extract_name(inspect_data), 'ip': extract_ip(inspect_data), 'location': get_if_available(proxy_data, 'location', ''), 'name': get_if_available(proxy_data, 'server_name', ''), 'port': get_if_available(proxy_data, 'port', 80), - 'listen': get_if_available(proxy_data, 'ip', '*') + ':80' + 'listen': '*:80' } logging.info('writing to %sproxy_%s', target_path, id) with open(target_path + '/proxy_'+id, 'w') as file: @@ -108,6 +113,26 @@ def get_pid(): with open(pid_file, 'r') as file: return int(file.read()) +def get_docker_id(): + """This function extracts the container's id from /proc/self/cgroup.""" + id = '' + with open('/proc/self/cgroup', 'r') as file: + lines = file.read().split('\n') + for line in lines: + index = line.find('docker-') + if index != -1: + id = line[index+7:-6] + break + return id + +def get_listen_ips(): + inspect_data = client.inspect_container(get_docker_id()) + logging.info('count %s', len(inspect_data['NetworkSettings']['Ports']['80/tcp'])) + ips = [] + for data in inspect_data['NetworkSettings']['Ports']['80/tcp']: + ips.append(data['HostIp']) + return ips + def setup_logging(): logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO) @@ -123,6 +148,12 @@ if __name__ == '__main__': client = Client(base_url='unix://var/run/docker.sock', version='1.15') + listen_ips = get_listen_ips() + logging.info('Listening on %s.', listen_ips) + if len(listen_ips) != 1: + logging.error('auto_proxy should only listen on 1 IP at a time, everything else can have security implications.') + exit() + # handle all running containers existing at startup of this container container_ids = client.containers(quiet=True) for container_id in container_ids: