gns3-server icon indicating copy to clipboard operation
gns3-server copied to clipboard

Security issue with Docker

Open grossmj opened this issue 3 years ago • 7 comments

It has been reported that it is really easy to access the host filesystem from inside a Docker container which obviously isn't something we want. Here is the report:

Just do: mount /dev/sda* /mnt This will give you root access to the file system.

This isn’t a gns3-thing per-se and I don’t think rootless docker can do what you are doing with containers.

grossmj avatar Aug 23 '21 06:08 grossmj

The reason for this is the option --privileged, that GNS3 uses to start containers.

The same issue arises outside GNS3, when using --privileged, here an example:

behlers@iMac:~$ docker run -ti alpine
/ # mount /dev/sda4 /mnt
mount: permission denied (are you root?)
/ # exit
behlers@iMac:~$ docker run -ti --privileged alpine
/ # mount /dev/sda4 /mnt
/ # ls /mnt
bin             initrd.img.old  opt             sys
boot            lib             proc            tmp
dev             lib64           root            usr
etc             lost+found      run             var
home            media           sbin            vmlinuz
initrd.img      mnt             srv             vmlinuz.old
/ # umount /mnt
/ # exit
behlers@iMac:~$ 

To stop this, someone has to research, what capabilities are really needed for GNS3. Perhaps the handling of docker containers can be changed to run with fewer capabilities. Or some capabilities can be dropped after running the injected /gns3/init.sh. But all that is a lot of work.

b-ehlers avatar Aug 23 '21 07:08 b-ehlers

Thanks for the input. If I remember correctly, we need that to move interfaces to the network namespace of a container allowing it to communicate with other nodes in a GNS3 topology.

https://github.com/GNS3/gns3-server/blob/2.2/gns3server/compute/docker/docker_vm.py#L912L916

grossmj avatar Aug 23 '21 08:08 grossmj

I'm not sure, docker needs extended privileges for that. I think moving the interfaces to the namespace is done outside docker by ubridge. Therefore ubridge runs with the capabilities cap_net_admin and cap_net_raw.

I assume docker needs some privileges to set IP addresses and routes within the container. Also the handling of persistent directories (uses bind mount) may need some privileges. There may be more reasons for extended privileges, that needs some investigation.

b-ehlers avatar Aug 23 '21 08:08 b-ehlers

Checked some privilege needs of https://github.com/GNS3/gns3-server/blob/master/gns3server/compute/docker/resources/init.sh:

IP setup works by using only cap_net_admin:

behlers@iMac:~$ docker run -ti alpine
/ # ip link set eth0 down
ip: ioctl 0x8914 failed: Operation not permitted
/ # ip address add 1.1.1.1/24 dev eth0
ip: RTNETLINK answers: Operation not permitted
/ # ip route add 1.2.3.0/24 via 127.0.0.1
ip: RTNETLINK answers: Operation not permitted
/ # exit
behlers@iMac:~$ 
behlers@iMac:~$ docker run -ti --cap-add cap_net_admin alpine
/ # ip link set eth0 down
/ # ip link set eth0 up
/ # ip address add 1.1.1.1/24 dev eth0
/ # ip route add 1.2.3.0/24 via 127.0.0.1
/ # exit
behlers@iMac:~$ 

But most critical is the mount --bind in https://github.com/GNS3/gns3-server/blob/731152c75a2a6172b9b10ed381651fd812b87cbd/gns3server/compute/docker/resources/init.sh#L43

Allowing that mount would also allow any other mount, which results in this issue. So the implementation of persistent direcoties has to be rewritten to work without mount --bind.

b-ehlers avatar Aug 23 '21 12:08 b-ehlers

You are right, we need a workaround for mount --bind "/gns3volumes$i" "$i"

Also, I tried to at least run without privileges by changing https://github.com/GNS3/gns3-server/blob/2.2/gns3server/compute/docker/docker_vm.py#L339L343 to this:

            "HostConfig": {
                "CapAdd": ["SYS_ADMIN", "NET_ADMIN"],
                "SecurityOptions": ["apparmor:unconfined"],  # https://github.com/moby/moby/issues/16429
                "Binds": mount_binds,
            },

Unfortunately, mount gets a permission deny. I tried a few workaround without any success...

Screenshot from 2021-08-23 22-21-27

Screenshot from 2021-08-23 22-20-59

grossmj avatar Aug 23 '21 13:08 grossmj

The only other solution I can think of would be to use volumes...

https://docs.docker.com/storage/volumes/

grossmj avatar Aug 23 '21 13:08 grossmj

This will have to wait for version >= 3.0 since there will be a lot of changes to make it work with volumes

grossmj avatar Aug 28 '21 09:08 grossmj