How to easily run any Linux tool on Mac, Nix or Windows

Situation 1: you’re on your Linux workstation, and there is a PHP code that you must execute; but on your workstation you have PHP 5; and this code only run under PHP 7.

Situation 2: you’re working on your MacBook laptop, and you desperately need your sqlmap tool from your Kali distribution, but you don’t have your VM.

Situation 3: You’re on your Windows PC and you immediately need an NGINX server that serve your static files from a directory.

Situation 4: No matter that platform, you have to startup your NodeJS 10 project but don’t have NodeJS installed on your platform.

Ok, let’s generalize…

Situation X: you are on platform T, and you immediately need a Linux tool Y without altering your configuration or installing additional software.


All these situations can be easily solved with a single tool, and without messing up with your computer by installing additional software or editing configurations that worked for a long time. You probably already heard about Docker, otherwise, shame on you.

Docker is an OS-level virtualization system that can potentially run any binary you have in mind; and furthermore run it in an isolate system so that it can’t touch your files and your precious working configurations.

All you need is that someone in the work already containerized your binary so that you can simply download as an image. There are already a ton of docker built images out there that are waiting for you.

Obviously, docker it is not only this: Docker is a platform for developers and sysadmins to develop, deploy, and run applications with containers — if you use it only to run your preferred binary, you’re using 1% of its features.

But let’s start from the beginning; you have to install docker on your machine —open this link https://docs.docker.com/install/overview/ and select your platform from left menu, the follow the guide.

Once you installed Docker, open your preferred Terminal or Command Prompt.


Basic concepts

First of all, let’s test if your Docker configuration is working correctly:

> docker --version
Docker version 18.03.0-ce, build 0520e24

If docker is up & running, you should see your version; no matter what it is.

All you need by docker now is the docker run command.

The first thing to understand is what is the name of the image: for official images, you usually have the name of the binary with no additions — for example, in the case of PHP, the image name is php. And what about the version? Simple as well, just the version number (7).

Now let’s run our first container, but first let’s create a goal.

Situation 1

You’re on your Linux workstation, and there is a PHP file that you must execute; but on your workstation you have PHP 5; and this file only run under PHP 7.

Ok, now let’s image that we have this simple code that only works under PHP 7 because of the spaceship operator:

<?php echo 1 <=> 0;

How we can execute this code with Docker? Let’s build our docker run command.

> docker run -it php:7
Interactive shell
php > echo 1<=>0;
1

Yes; that’s all we need.

The extraneous part is the -it flag, but that’s not difficult; since we are in interactive shell, it simply specifies that this container should:

  • -t ( — tty): allocate a pseudo-TTY
  • -i ( — interactive): keep STDIN open even if not attached

You should use them most of the time, with some exceptions.


Situation 2

You’re working on your MacBook laptop, and you desperately need your sqlmap tool from your Kali distribution, but you don’t have your VM.

Unfortunately (not su much) sqlmap hasn’t an official simple image name; but maybe someone other created — let’s do a simple research.

> docker search sqlmap
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
paoloo/sqlmap Dockered sqlmap. Build instructions: https:/… 6
k0st/alpine-sqlmap sqlmap on alpine (size: ~113 MB) 3 [OK]
jdecool/sqlmap sqlmap (Automatic SQL injection) in a contai… 2 [OK]
harshk13/kali-sqlmap Kali Linux base image with Sqlmap 1
marcomsousa/sqlmap Simple image that execute Automatic SQL inje… 1 [OK]
....

We have several one; this could happens most of the time — but for most of the cases the image should be the first one (with greater star count).

Let’s use it:

> docker run -it paoloo/sqlmap --url http://localhost
_
___ ___| |_____ ___ ___ {1.0.9.32#dev}
|_ -| . | | | .'| . |
|___|_ |_|_|_|_|__,| _|
|_| |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.
...

All arguments that are after [docker run -it {image}] are passed to the binary executed in docker; sqlmap in this case.

Simple right? Yes, but we have a cons here: sqlmap writes logs file onto the disk in ~/.sqlmap path, but since docker container run in an isolated environment, we lose everything!!

This is a feature, but in this case represents a bug for us — let’s fix it.

To enable persistence so that we don’t lose that log file, we have to create a bind mount between our workstation (host) and docker container.

Let’s decide that our host bind mount directory is under /tmp/sqlmap. This should be an empty directory created only for this purpose!

> docker run -it -v \
/tmp/sqlmap:/root/.sqlmap \
paoloo/sqlmap \
--url
http://localhost

Sound complicated? Nah.

With -v option we’ll create a bind mount — the first argument is the host path, the second one is the path on the container that we want to map.

And, in fact, everything has been saved; including our reports.

> ls -la /tmp/sqlmap/output/localhost
total 8
drwxr-xr-x 4 kopiro wheel 128 Nov 28 23:21 .
drwxr-xr-x 3 kopiro wheel 96 Nov 28 23:21 ..
-rw-r--r-- 1 kopiro wheel 0 Nov 28 23:21 log
-rw-r--r-- 1 kopiro wheel 22 Nov 28 23:21 target.txt

Situation 3

You’re on your Windows PC and you immediately need an NGINX server that serve your static files from a directory.

You should learn how it works now; so let’s complicate things by adding alpine tags concept.

As you may have notices, the first time that you run docker run, it downloads the image from the Docker Hub (https://hub.docker.com) — and this could be hundreds of hundreds GBs. This is because we downloaded the tag latest of the image (the default).

But you should know that most images have also an alpine version of the same image, that uses Linux Alpine OS — an optimized version of Linux, that occupies about 130MB.

Let’s use it in this situation: we know that image name upfront is (since is an official image) nginx; so final image name will be nginx:alpine. Wants a specific version? Use nginx:1.14-alpine.

How we know which is the directory that NGINX container uses to serve our files? How we know which port it exposes?

Answers to all your questions are in the Docker Hub: https://hub.docker.com/_/nginx/

So, by recap:

  • we have to share our directory to serve into the container; again using bind mounts: -v $(pwd):/usr/share/nginx/html — furthermore, by adding :ro at the end we are sure that container uses our files in read-only mode.
  • we must bind port exposed by the container to the host, and then communicate via TCP on our host: -p 80:80
> docker run \
-v $(pwd):/usr/share/nginx/html:ro \
-p 80:80 \
nginx:alpine
172.17.0.1 - - [29/Nov/2018:02:52:24 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36" "-"
172.17.0.1 - - [29/Nov/2018:02:52:36 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36" "-"
...

Situation 4

No matter that platform, you have to startup your NodeJS 10 project but don’t have NodeJS installed on your platform.

I think that you understood how it works; here we have to share our content and bind ports.

Additionally, since we don’t know container working directory, we’re gonna explicitly set with -w flag to a custom directory of our choice (for example /src — just don’t override an existing directory 😝):

> docker run \
-p 3000:3000 \
-v $(pwd):/src \
-w /src \
node:10-alpine \
node main.js
Example app listening on port 3000!
...

Simple and powerful enough?

Additionally, do you want a “shortcut” to just execute simple binaries without searching for 3-rd party images?

Why don’t you try my simple tool DR: https://github.com/kopiro/dr ?

I hope that you’re gonna use Docker for all your future binaries! :)