From 7c3a38d3793bea3999f4a55c3bdc0426bf52f7cb Mon Sep 17 00:00:00 2001 From: Karim Date: Fri, 5 Apr 2019 08:51:57 +0100 Subject: [PATCH 1/2] Support for docker images that set the USER directive. Changes the docker user to root for the init script to configure the network, then drops to the configured user (or root if one is not defined) for continuing booting the image. --- gns3server/compute/docker/docker_vm.py | 4 ++++ gns3server/compute/docker/resources/init.sh | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index ed5e0009..68d9c20f 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -311,6 +311,7 @@ class DockerVM(BaseNode): "Tty": True, "OpenStdin": True, "StdinOnce": False, + "User": "root", "HostConfig": { "CapAdd": ["ALL"], "Privileged": True, @@ -342,6 +343,9 @@ class DockerVM(BaseNode): # Give the information to the container the list of volume path mounted params["Env"].append("GNS3_VOLUMES={}".format(":".join(self._volumes))) + # Pass user configured for image to init script + params["Env"].append("GNS3_USER={}".format(image_infos.get("Config", {"User": ""})["User"])) + variables = self.project.variables if not variables: variables = [] diff --git a/gns3server/compute/docker/resources/init.sh b/gns3server/compute/docker/resources/init.sh index 54bbeefc..bc33ea1c 100755 --- a/gns3server/compute/docker/resources/init.sh +++ b/gns3server/compute/docker/resources/init.sh @@ -87,6 +87,9 @@ done ifup -a -f # continue normal docker startup -PATH="$OLD_PATH" -exec "$@" - +GNS3_CMD="PATH=$OLD_PATH exec" +while test "$#" -gt 0 ; do + GNS3_CMD="${GNS3_CMD} \"${1//\"/\\\"}\"" + shift +done +exec su ${GNS3_USER-root} -p -c "$GNS3_CMD" From 6df93d4db0045335c5d20ae3396b766600f65e2c Mon Sep 17 00:00:00 2001 From: Karim Date: Fri, 5 Apr 2019 11:06:35 +0100 Subject: [PATCH 2/2] Updated tests for setting docker user --- gns3server/compute/docker/docker_vm.py | 9 +++--- tests/compute/docker/test_docker_vm.py | 42 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 68d9c20f..e59321b0 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -311,7 +311,6 @@ class DockerVM(BaseNode): "Tty": True, "OpenStdin": True, "StdinOnce": False, - "User": "root", "HostConfig": { "CapAdd": ["ALL"], "Privileged": True, @@ -320,7 +319,7 @@ class DockerVM(BaseNode): "Volumes": {}, "Env": ["container=docker"], # Systemd compliant: https://github.com/GNS3/gns3-server/issues/573 "Cmd": [], - "Entrypoint": image_infos.get("Config", {"Entrypoint": []})["Entrypoint"] + "Entrypoint": image_infos.get("Config", {"Entrypoint": []}).get("Entrypoint") } if params["Entrypoint"] is None: @@ -331,7 +330,7 @@ class DockerVM(BaseNode): except ValueError as e: raise DockerError("Invalid start command '{}': {}".format(self._start_command, e)) if len(params["Cmd"]) == 0: - params["Cmd"] = image_infos.get("Config", {"Cmd": []})["Cmd"] + params["Cmd"] = image_infos.get("Config", {"Cmd": []}).get("Cmd") if params["Cmd"] is None: params["Cmd"] = [] if len(params["Cmd"]) == 0 and len(params["Entrypoint"]) == 0: @@ -344,7 +343,9 @@ class DockerVM(BaseNode): params["Env"].append("GNS3_VOLUMES={}".format(":".join(self._volumes))) # Pass user configured for image to init script - params["Env"].append("GNS3_USER={}".format(image_infos.get("Config", {"User": ""})["User"])) + if image_infos.get("Config", {"User": ""}).get("User"): + params["User"] = "root" + params["Env"].append("GNS3_USER={}".format(image_infos.get("Config", {"User": ""})["User"])) variables = self.project.variables if not variables: diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index 44e02577..63dcb537 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -415,6 +415,48 @@ def test_create_image_not_available(loop, project, manager): assert vm._cid == "e90e34656806" mock_pull.assert_called_with("ubuntu:latest") +def test_create_with_user(loop, project, manager): + + response = { + "Id": "e90e34656806", + "Warnings": [], + "Config" : { + "User" : "test", + }, + } + with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]) as mock_list_images: + with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: + vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu:latest") + loop.run_until_complete(asyncio.ensure_future(vm.create())) + mock.assert_called_with("POST", "containers/create", data={ + "Tty": True, + "OpenStdin": True, + "StdinOnce": False, + "User": "root", + "HostConfig": + { + "CapAdd": ["ALL"], + "Binds": [ + "{}:/gns3:ro".format(get_resource("compute/docker/resources")), + "{}:/gns3volumes/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network")) + ], + "Privileged": True + }, + "Volumes": {}, + "NetworkDisabled": True, + "Name": "test", + "Hostname": "test", + "Image": "ubuntu:latest", + "Env": [ + "container=docker", + "GNS3_MAX_ETHERNET=eth0", + "GNS3_VOLUMES=/etc/network", + "GNS3_USER=test" + ], + "Entrypoint": ["/gns3/init.sh"], + "Cmd": ["/bin/sh"] + }) + assert vm._cid == "e90e34656806" def test_get_container_state(loop, vm): response = {