3

For users of my web service running on Ubuntu Server 16.04 or 18.04, I’d like to integrate features from such useful programs as ImageMagick. For example, I’d like to crop their profile pictures or create thumbnails.

But because software in general has bugs, and especially ImageMagick has a lot of them [1] [2], it would obviously best to isolate the execution of ImageMagick from the rest of the server, right?

So what’s the best way to isolate ImageMagick from a security perspective, taking into account that the setup should be as simple as possible and that ImageMagick will have to run every few seconds (or even multiple times per second at peak times)? Ideally, ImageMagick would not only be isolated from the host machine, but ImageMagick executions (with the data they operate on) would be isolated from each other as well.

I guess one can use a VM or containers (e.g. Docker) for this? Are containers better-suited because they are faster to set up and tear down again?

Moreover, what’s a good way to get started? I have taken a look at various manuals, but don’t know where exactly to start and which components I need.

What I have so far is the following. Though I don’t know if that’s actually secure, and the costly installation of packages should only be done once, if possible. Moreover, I’m not sure if this actually allows for parallel execution by multiple users.

Dockerfile:

FROM ubuntu:16.04

RUN apt-get update
RUN apt-get install -y imagemagick

VOLUME ["/my-images"]
WORKDIR /my-images

ENTRYPOINT ["convert"]

Initialization:

docker build -t my-imagemagick .

Usage:

docker run --rm --volume=$(pwd):/my-images:rw my-imagemagick -resize 500 /my-images/input.jpg /my-images/output.jpg
  • I wouldn't go for containerization at all but simply use child or worker processes that immediately drop all privileges and only communicate via "messages" (through pipes, sockets, shared memory, signals etc.). It’s not like pure (image) data processing tasks require any system resources except CPU time and virtual memory plus the communication channels set up initially. Have a look at the capabilities(7) manual page. A wrapper program written in C can achieve that. – David Foerster Sep 19 '18 at 19:08
  • @DavidFoerster Thanks! But the web server is already running in an unprivileged (non-root) process (as www-data) and calls ImageMagick. I don’t see how I can drop even those non-root privileges from ImageMagick to prevent it from having the same permissions as the web server, while also preventing privilege escalation through vulnerabilities. I think containerization doesn’t sound bad for this. – caw Sep 19 '18 at 22:22
  • I see that you didn't read the linked manual page. Processes have many more different privileges than those that separate those owned by regular from those owned by super-users. A process (or thread in some cases) can drop privileges to a point where it can't make any system calls at all. In this case you need to keep exactly the bare minimum required to send and receive messages across previously opened inter-process communication channels. – David Foerster Sep 20 '18 at 12:44
  • @DavidFoerster Of course I did. But it starts with the following: “Linux divides the privileges traditionally associated with superuser into distinct units, known as capabilities, which can be independently enabled and disabled.” And then it goes on to list capabilities that, from my understanding, would all be rights that the web server (and thus ImageMagick) doesn’t have, anyway. Further, this is apparently more complex than containerization for the application developer to implement, especially for someone new to those capabilities restricting ImageMagick without breaking its functionality. – caw Sep 20 '18 at 19:04
  • 1
    Oops, I actually meant a different “capabilities” mechanism: seccomp. I’m embarrassed and apologise for my slightly rude assumption. I see some libraries out there that deal with the nitty-gritty so application developers or system maintainers don't have to. – David Foerster Sep 20 '18 at 21:22
0
+50

Containers in general

A docker container or KVM behaves like a real system. You can even go into a container and make changes there, no problem. Work with the bash, install packages, and so on (just in case you forgot something or want to check something).

The advantage of a container is, that it is much easier to upgrade and rebuild. With your own docker registry, you could even have differently named versions alongside each other.

Store the user data in an extra container

I'd suggest storing the data in a data container attached to the docker container. This would prevent the container to have mount points which end up on a folder on your real server. Instead, use a data container and attach/detach the data container, when you rebuild the ImageMagick docker container and reattach the data container to it at any given time. No data lost, but you have all your files in a container which also can be moved quite easily.

About security

As mentioned above a container behaves like a real system, so it also can be hacked like a real system. Means your attacker still can get hold of customer data and root access to this machine. Breaking out of a docker container ain't that easy but might be possible, due to missing patches, etc. I myself have a simple trick I use to make it harder for an attacker. I simply rebuild the container per crontab every few hours/a day. So if an attacker gains access, he'd have to start all over again a few hours/a day later.

Also try to attach an nginx container to it, so that ImageMagick is triggered by a reverse proxy instead of giving access to ImageMagick itself. It is easy to build up a container production chain nginx --> Imagemagick --> data container

So you'd end up with 2-3 containers which are chained together into an isolated production chain.

Hth,

Jens

P.S.: To get more into detail you'd need more knowledge about docker. But your suggested way is the right one in my opinion.

P.P.S.: You could also keep it old school and simply use a chroot environment. But harder to set up, harder to maintain, located on your server itself.

|improve this answer|||||
  • Thank you! So you’d argue for 2-3 containers shared by all users, instead of possibly thousands of ad-hoc containers per user and per task, right? That would still bring the advantage of ImageMagick being isolated from the host system and all its resources, while providing much better performance, right? It wouldn’t isolate a user’s images from those of other users, but at least no attacker could (without an additional Docker exploit) access the host’s resources (e.g. a database). Anyway, why proxy through a second container when, eventually, somebody must communicate with the host? – caw Sep 12 '18 at 19:48
  • I think we should move this conversation to a chat? That would be much better I thin.... – s1mmel Sep 13 '18 at 7:52
  • A container consists of a layered image, which can be "reused/cloned" by other containers, but It doesn't make sense to spawn a container for one request and then remove it and redo this like 1000 times, maybe even in parallel. 1. It would be too slow to spawn them every time 2. You'd need a lot of resources. A real scaleout needs to be done amongst servers to evenly balance the load. As long as you only have 1 server please be aware thatthe ressources are shared amongst the containers. libvirtd/QEMU is really good at balancing the loads and handling the sharing of cpu & memory. – s1mmel Sep 13 '18 at 8:07
0

You can use one container that resizes all images located in mounted folder. You need to make slide changes in Docker file and build Docker image.

FROM ubuntu:16.04

RUN apt-get update
RUN apt-get install -y imagemagick

VOLUME ["/my-images"]
WORKDIR /my-images

CMD ["magick", "-help"]

Build Docker image.

docker build -t my-imagemagick .

Run docker container and resize images located in /my-images folder at once.

docker run --rm -v "$( pwd ):/my-images" my-imagemagick find -type f -iname '*.jpg' -o -iname '*.jpeg' -exec convert {} -verbose -resize "500x500>" {} \;

You can also run command as cronjob to schedule it. TO allow multiple users on your system to run this command. You need to add users in docker group.

|improve this answer|||||
  • Thanks! Instead of running my-imagemagick, why do you run ketan/imagemagick, i.e. not using the Dockerfile at all? Has this been by mistake? And why do you replace ENTRYPOINT ["convert"] with CMD ["magick", "-help"] in the Dockerfile to prepare replacing convert -resize 500 /my-images/input.jpg /my-images/output.jpg with find -type f -iname '*.jpg' -o -iname '*.jpeg' -exec convert {} -verbose -resize "500x500>" {} \;. This simply changes the semantics of what is done inside the container. It does not affect the container itself or its properties regarding security. – caw Sep 17 '18 at 6:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.