Mitmproxy through Docker
Table of contents:
-
Using Mitmproxy in Docker container
- Why?
-
Simple steps to open mitmproxy and run http/s requests and responses through it, but inside container
- Refresher on Mitmproxy from previous setup post (maybe you prefare bare metal)
- For testing purposes in this article I creat my_network docker network
- Starting mitmproxy in Docker or Podman
- Run nginx server in docker container
- Now the meat with HTTPie and curl
- Summary
Using Mitmproxy in Docker container
UPDATED on 2025-03-17 01:31:00.98401443 +0000
Why?
Using Docker or Podman (somehow a docker command equivalent) and with Podman Desktop, almost a Docker Desktop, you can abstract your commands into another layer, securing your host OS from unintentional failures, and thus safe-proof your transparent proxy journey. Although the commands written into terminal are not simple and easy to understand in the first place, they might get you going further along the way of abstracting even more, like using devcontainers in VS Code, and even if you're not intend to use Visual Studio Code for development purposes, you might as well use it as just a devcontainer server... And I will show you just that and more, in this text.
Simple steps to open mitmproxy and run http/s requests and responses through it, but inside container
Refresher on Mitmproxy from previous setup post (maybe you prefare bare metal)
Previous post on setting up Mitmproxy
If you need a refresher on what Mitmproxy is, or why you might be using it, the link up here is where you might find some more answers, but in short, it's a transparent proxy inteded for use with web applications, and web requests you send with programs like Postman, Httpie or simply curl. It allows you to intercep, inspect and modify the request and response of them so you might do anything you can imagine to your App, API or whatever.
For testing purposes in this article I creat my_network docker network
To create a network, in which you can connect in between docker containers, so that I can emulate a real world scenario, like connecting to webserver, or a website I use this docker command:
shell:
docker network create my_network
Starting mitmproxy in Docker or Podman
If you're here to watch - youtube like introduction to mitmproxy in docker container
So you, don't have time to read, right? You like the youtube, right? OK. Here you go!
But first, check your local
config
Why?
You might want to preserve configuration of your docker containers, so to do just that save a config file on your host machine, and include it in every invocations of docker run command. There you can also find certificates files, so you don't have to manually add the after each re-run of the docker containers. I did not find the way to have many, of mitmproxy instances at once with the same certificates, but with different? No problem, just save your configuration files in other folder, and you're done - just don't forget to change the attached folder to the other...
How?
Create, if it does not exist, folder named ~/.mitmproxy and in there create, if it does not already exist, a config file named config.yml. In there you can put your configurations. The documentation for your possibilities.
For me it's just this:
yaml:
# Port on which the http proxy will be listening
# I want it to be distinct, so it's outside of usual web apps range
listen_port: 8899
Then in some terminal multiplexer like tmux or zellij or terminal emulator in such of kitty or warp run your new docker commads... We will be using docker run --rm syntax so it also removes the container after it's stopped, but you might omit that to preserve logs, or add some more functionality to mitmproxy or mitmdump using Python, but for now we will just stick to simple things like running your first request. And action!
Run mitmproxy in docker container
shell:
docker run --rm -it -v ~/.mitmproxy:/home/mitmproxy/.mitmproxy -p 8899:8899 --network my-network --name proxy mitmproxy/mitmproxy
It's as simple as that. This will fetch the latest build of mitmproxy from hub.docker.com, or rather docker.io and then run the Dockerfile it's build with, which in our case will run the mitmproxy with specifide configuration files in ~/.mitmproxy folder and certificates stored there (it will create new for us, if there is none).
You might want to quickly inspect the following website, where the developers of mitmproxy post their work on Docker Hub. I will cover mitmproxy, the mitmdump and mitmweb is out of scope of this article.
What I can do now?
Now you're basically done. Cooked. Frag out!
From now on, you can refer to your mitmproxy instance proxy as http://localhost:8899 for example using curl to connect to this website:
shell:
curl -x http://localhost:8899 https://neosb.net/blog/mitmproxy-through-docker --insecure
Run nginx server in docker container
To show you some examples of usage, I will first containerize nginx server in another tab, pane or terminal. You can skip this part and confidently start using mitmproxy, even with your browser. To do it, visit website http://mitm.it and follow on the guide to install the required certificates for your web broweser (Firefox is recommended), or operationg system. Then install FoxyProxy if you used Firefox and set it up for the host: localhost, port: 8899 switch the proxy on... But hey, we're talking nginx here...
shell:
docker run --rm --network my-network -p 9999:80 --name webserver nginx:latest
Now that you have a server in my network, you can refer to it as webserver from other docker containers, and proxy:8899 to connect through proxy using alpine/curl container. I will show it to you how...
Now the meat with HTTPie and curl
Run HTTP request using HTTPie in docker container
Let's say that first, you want to test how the httpie works in docker.
shell:
docker run --rm -v ~/.httpie:/root/.httpie --network my-network --env HTTP_PROXY="http://proxy:8899" --env HTTPS_PROXY="http://proxy:8899" alpine/httpie GET webserver --body --verify=no
This is full command, you should use to test ANY webserver, you can switch webserver into any domain. I will show it later, now let's just run it, and see how flows are populating mitmproxy. As it should. You might even want to remove --body from this invocation, because in default settings, httpie outputs also headers.
HTTP request using curl in docker container
shell:
docker run --rm --network my-network --env HTTP_PROXY="http://proxy:8899" --env HTTPS_PROXY="http://proxy:8899" alpine/curl --silent --insecure webserver
The above code does completely the same thing that we have done previosly with httpie. We just do it in curl, it more widely adopted, so you might find answers quickly for your qeustions using curl. The choice is yours, output is similar. We can check this with:
Ensure we have got exactly the same output from HTTPia that in curl
shell:
diff <(docker run --rm -v ~/.httpie:/root/.httpie --network my-network --env HTTP_PROXY="http://proxy:8899" --env HTTPS_PROXY="http://proxy:8899" alpine/httpie GET webserver --body --verify=no) \
<(docker run --rm --network my-network --env HTTP_PROXY="http://proxy:8899" --env HTTPS_PROXY="http://proxy:8899" alpine/curl --silent --insecure webserver)
What is the difference?
HTTPie has it's own GUI app, httpie in commandline interface has different syntax, you could say - easier to grasp. Nevertheless, using one over another is not an option since the key to getting nailed you request is not the hammer, you need the payload - the request, and how you get it - it's up to you 🚀
HTTPS request with HTTPie in docker container to JSON endpoint with jq parsing
Now, let's move to something a little bit less trivial. We want to pars a JSON output. How to do it? We can use a jq parser, that will let us easily travers through many branches and trees. For starters, I will GET from the Internet, dummy /hello endpoint of httpie.io website. There is some JSON! So let's get dirty with it...
shell:
docker run --rm -v ~/.httpie:/root/.httpie --entrypoint=https --network my-network --env HTTP_PROXY="http://proxy:8899" --env HTTPS_PROXY="http://proxy:8899" alpine/httpie GET httpie.io/hello --body --verify=no | docker run --rm -i backplane/jq ".links .homepage"
Like you see, we have got back the base address where the HTTPie can be found. And now, I will explain somethig. You can get a field refering to it with .field_name syntax, and I have got deep into 2-levels finding the .homepage in .links.
HTTPS request with curl in docker container to JSON endpoint with jq parsing
shell:
docker run --rm --network my-network --env HTTP_PROXY="http://proxy:8899" --env HTTPS_PROXY="http://proxy:8899" alpine/curl --silent --insecure https://httpie.io/hello | docker run --rm -i backplane/jq "."
While you don't alway know the name of the fields, or how many elements, there is in an array, or the overall structure of the JSON, you would want then to invoke the jq with "." argument, and even maybe saving it first as a reference for the future with backplane/jq "." > my_json.json.
Summary
It's the most popular way to get things done, but it can be done, as you see now. I've always wanted to work with Docker, err... Podman... Desktop... Whatever.
Securing your host system with another layer of abstraction is a shiny feature, however, I could not get the mitmweb working, and we didn't touched on the mitmdump mode, but still putting mitmproxy into a docker container is not bad at all, and it might the thing you expect from modern transparent proxy in terminal (CLI).
Comments:
Make first impression!