GNS3 / gns3-server

GNS3 server
GNU General Public License v3.0
798 stars 263 forks source link

Security issue with Docker #1944

Open grossmj opened 3 years ago

grossmj commented 3 years ago

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.

ghost commented 3 years ago

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.

grossmj commented 3 years ago

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

ghost commented 3 years ago

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.

ghost commented 3 years ago

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.

grossmj commented 3 years ago

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 commented 3 years ago

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

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

grossmj commented 3 years ago

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