├── .github └── workflows │ └── build-and-push-docker-multiarch-image.yml ├── Dockerfile ├── LICENSE ├── README.md ├── blogpost.md ├── entrypoint.sh ├── index.html ├── kubernetes ├── README.md └── multiool-daemonset.yml ├── leatherman-wave.jpg ├── nginx.conf ├── press-release.html └── press-release.md /.github/workflows/build-and-push-docker-multiarch-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image Build and Push - CI Workflow 2 | 3 | on: 4 | push: 5 | branches: 6 | # master handles: "latest"; and tags "minimal" and "alpine-minimal" 7 | - master 8 | # alpine-extra handles itself "alpine-extra"; and tag "extra" 9 | - alpine-extra 10 | 11 | tags: 12 | - minimal 13 | - alpine-minimal 14 | - extra 15 | 16 | jobs: 17 | build-and-push-muti-arch-docker-image: 18 | runs-on: ubuntu-latest 19 | env: 20 | DOCKER_IMAGE: network-multitool 21 | DOCKERHUB_ORGNAME: praqma 22 | DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} 23 | # 24 | # DOCKERHUB_USERNAME and DOCKERHUB_TOKEN are to be set in the GITHUB WEB UI, 25 | # under RepoName -> Settings -> Secrets 26 | 27 | steps: 28 | 29 | - name: Checkout 30 | uses: actions/checkout@v2 31 | 32 | - name: Prepare Docker Image Tags 33 | id: prep 34 | run: | 35 | SHORT_REF=$(basename ${GITHUB_REF}) 36 | SHORT_HASH=${GITHUB_SHA::7} 37 | TAGS="" 38 | if [[ ! -z "${SHORT_REF}" && "${SHORT_REF}" == "master" ]]; then 39 | echo "Found git commit on master branch. Setting docker image tag as: 'latest'" 40 | TAG=${DOCKERHUB_ORGNAME}/${DOCKER_IMAGE}:latest 41 | fi 42 | if [[ ! -z "${SHORT_REF}" && "${SHORT_REF}" != "master" ]]; then 43 | echo "Setting docker image tag as: '${SHORT_REF}'" 44 | TAG=${DOCKERHUB_ORGNAME}/${DOCKER_IMAGE}:${SHORT_REF} 45 | fi 46 | TAGS="${TAG},${DOCKERHUB_ORGNAME}/${DOCKER_IMAGE}:${SHORT_HASH}" 47 | echo "Complete Docker image-name and tags are setup as: ${TAGS}" 48 | echo ::set-output name=tags::${TAGS} 49 | 50 | - name: Set up QEMU 51 | uses: docker/setup-qemu-action@v1 52 | 53 | - name: Set up Docker Buildx 54 | uses: docker/setup-buildx-action@v1 55 | 56 | - name: Login to DockerHub 57 | uses: docker/login-action@v1 58 | with: 59 | username: ${{ secrets.DOCKERHUB_USERNAME }} 60 | password: ${{ secrets.DOCKERHUB_TOKEN }} 61 | 62 | - name: Build and push 63 | uses: docker/build-push-action@v2 64 | with: 65 | context: . 66 | file: ./Dockerfile 67 | platforms: linux/386,linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x 68 | push: true 69 | tags: ${{ steps.prep.outputs.tags }} 70 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.13 2 | 3 | MAINTAINER Kamran Azeem & Henrik Høegh (kamranazeem@gmail.com, henrikrhoegh@gmail.com) 4 | 5 | EXPOSE 80 443 1180 11443 6 | 7 | # Install some tools in the container and generate self-signed SSL certificates. 8 | # Packages are listed in alphabetical order, for ease of readability and ease of maintenance. 9 | RUN apk update \ 10 | && apk add bash bind-tools busybox-extras curl \ 11 | iproute2 iputils jq mtr \ 12 | net-tools nginx openssl \ 13 | perl-net-telnet procps tcpdump tcptraceroute wget \ 14 | && mkdir /certs /docker \ 15 | && chmod 700 /certs \ 16 | && openssl req \ 17 | -x509 -newkey rsa:2048 -nodes -days 3650 \ 18 | -keyout /certs/server.key -out /certs/server.crt -subj '/CN=localhost' 19 | 20 | 21 | # Copy a simple index.html to eliminate text (index.html) noise which comes with default nginx image. 22 | # (I created an issue for this purpose here: https://github.com/nginxinc/docker-nginx/issues/234) 23 | 24 | COPY index.html /usr/share/nginx/html/ 25 | 26 | COPY press-release.md /root/ 27 | COPY press-release.html /root/ 28 | 29 | 30 | # Copy a custom/simple nginx.conf which contains directives 31 | # to redirected access_log and error_log to stdout and stderr. 32 | # Note: Don't use '/etc/nginx/conf.d/' directory for nginx virtual hosts anymore. 33 | # This 'include' will be moved to the root context in Alpine 3.14. 34 | 35 | COPY nginx.conf /etc/nginx/nginx.conf 36 | 37 | COPY entrypoint.sh /docker/entrypoint.sh 38 | 39 | 40 | # Start nginx in foreground: 41 | CMD ["/usr/sbin/nginx", "-g", "daemon off;"] 42 | 43 | 44 | 45 | # Note: If you have not included the "bash" package, then it is "mandatory" to add "/bin/sh" 46 | # in the ENTNRYPOINT instruction. 47 | # Otherwise you will get strange errors when you try to run the container. 48 | # Such as: 49 | # standard_init_linux.go:219: exec user process caused: no such file or directory 50 | 51 | # Run the startup script as ENTRYPOINT, which does few things and then starts nginx. 52 | ENTRYPOINT ["/bin/sh", "/docker/entrypoint.sh"] 53 | 54 | 55 | 56 | 57 | 58 | ################################################################################################### 59 | 60 | # Build and Push (to dockerhub) instructions: 61 | # ------------------------------------------- 62 | # docker build -t local/network-multitool . 63 | # docker tag local/network-multitool praqma/network-multitool 64 | # docker login 65 | # docker push praqma/network-multitool 66 | 67 | 68 | # Pull (from dockerhub): 69 | # ---------------------- 70 | # docker pull praqma/network-multitool 71 | 72 | 73 | # Usage - on Docker: 74 | # ------------------ 75 | # docker run --rm -it praqma/network-multitool /bin/bash 76 | # OR 77 | # docker run -d praqma/network-multitool 78 | # OR 79 | # docker run -p 80:80 -p 443:443 -d praqma/network-multitool 80 | # OR 81 | # docker run -e HTTP_PORT=1180 -e HTTPS_PORT=11443 -p 1180:1180 -p 11443:11443 -d praqma/network-multitool 82 | 83 | 84 | # Usage - on Kubernetes: 85 | # --------------------- 86 | # kubectl run multitool --image=praqma/network-multitool 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Praqma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `Praqma/Network-Multitool` is now `wbitt/Network-Multitool` 2 | ## 05 Jan 2022 - Important note about name/org change: 3 | Few years ago, I created this tool with Henrik Høegh, as `praqma/network-multitool`. Praqma was bought by another company, and now the "Praqma" brand is being dismantled. This means the network-multitool's git and docker repositories must go. Since, I was the one maintaining the docker image for all these years, it was decided by the current representatives of the company to hand it over to me so I can continue maintaining it. So, apart from a small change in the repository name/url, nothing has changed. 4 | 5 | The existing/old/previous container image `praqma/network-multitool` will continue to work and will remain available for **"some time"** - may be for a couple of months - not sure though. 6 | 7 | - Kamran Azeem 8 | 9 | 10 | ## Some important URLs: 11 | * The new official github repository for this tool is: [https://github.com/wbitt/Network-MultiTool](https://github.com/wbitt/Network-MultiTool) 12 | * The docker repository to pull this image is now: [https://hub.docker.com/r/wbitt/network-multitool](https://hub.docker.com/r/wbitt/network-multitool) 13 | 14 | Or: 15 | 16 | ``` 17 | docker pull wbitt/network-multitool 18 | ``` 19 | 20 | # Network-Multitool 21 | A (**multi-arch**) multitool for container/network testing and troubleshooting. The main docker image is based on Alpine Linux. There is a Fedora variant to be used in environments which require the image to be based only on RedHat Linux, or any of it's derivatives. 22 | 23 | The container image contains lots of tools, as well as a `nginx` web server, which listens on port `80` and `443` by default. The web server helps to run this container-image in a straight-forward way, so you can simply `exec` into the container and use various tools. 24 | 25 | ## Supported platforms: 26 | * linux/386 27 | * linux/amd64 28 | * linux/arm/v7 29 | * linux/arm64 30 | 31 | ## Downloadable from Docker Hub: 32 | * [https://hub.docker.com/r/praqma/network-multitool/](https://hub.docker.com/r/praqma/network-multitool/) (Automated multi-arch Build) 33 | 34 | ## Variants / image tags: 35 | * **latest**, minimal, alpine-minimal ( The main/default **'minimal'** image - Alpine based ) 36 | * extra, alpine-extra (Alpine based image - with **extra tools** ) 37 | * openshift , openshift-minimal (openshift compatible - **minimal**) - Ports: **1180, 11443** 38 | * openshift-extra (openshift compatible with **extra tools**) - Ports: **1180, 11443** 39 | * fedora, fedora-minimal ( **'Minimal'** Fedora based image ) 40 | 41 | 42 | ### Important notes about openshift variant: 43 | Openshift is very strict about how a container image should run. So, the **openshift variant** of the multitool has the following limitations / changes: 44 | 45 | * Runs as non-root ; which means some tools (e.g. `traceroute`, `tcptraceroute`, etc, will not work) 46 | * Listens on ports `1180` and `11443` - **not** `80` and `443` 47 | * Some executable files are manually set as `setuid`, so those tools remain usable. Tools set with `setuid` are: 48 | * apk 49 | * arping 50 | * busybox 51 | * mii-tool 52 | * tcpdump 53 | * tcptraceroute 54 | * traceroute 55 | * tshark 56 | 57 | Remember, this *multitool* is purely a troubleshooting tool, and should be used as such. It is not designed to abuse openshift (or any system's) security, nor should it be used to do so. 58 | 59 | 60 | ## Tools included in "latest, minimal, alpine-minimal , openshift, openshift-minimal": 61 | * apk package manager 62 | * Nginx Web Server (port `80`, port `443`) - with customizable ports! 63 | * awk, cut, diff, find, grep, sed, vi editor, wc 64 | * curl, wget 65 | * dig, nslookup 66 | * ip, ifconfig, route 67 | * traceroute, tracepath, mtr, tcptraceroute (for layer 4 packet tracing) 68 | * ping, arp, arping 69 | * ps, netstat 70 | * gzip, cpio, tar 71 | * telnet client 72 | * tcpdump 73 | * jq 74 | * bash 75 | 76 | **Size:** 16 MB compressed, 38 MB uncompressed 77 | 78 | ## Tools included in "extra, alpine-extra, openshift-extra": 79 | All tools from "minimal", plus: 80 | * iperf3 81 | * ethtool, mii-tool, route 82 | * nmap 83 | * ss 84 | * tshark 85 | * ssh client, lftp client, rsync, scp 86 | * netcat (nc), socat 87 | * ApacheBench (ab) 88 | * mysql & postgresql client 89 | * git 90 | 91 | **Size:** 64 MB compressed, 220 MB uncompressed 92 | 93 | 94 | ## Tools included in "fedora, fedora-minimal": 95 | * YUM package manager 96 | * Nginx Web Server (port 80, port 443) - customizable ports! 97 | * wget, curl 98 | * dig, nslookup 99 | * ip, ifconfig, route, traceroute, tracepath, mtr 100 | * ping, arp, arping 101 | * ps, netstat 102 | * gzip, cpio, tar 103 | * telnet client 104 | * awk, cut, diff, find, grep, sed, vi editor, wc 105 | * jq 106 | * `/bin/sh` shell interpreter - not `/bin/bash` 107 | 108 | **Size:** 72 MB uncompressed 109 | 110 | 111 | **Note:** The SSL certificates are generated for "localhost", are self signed, and placed in `/certs/` directory. During your testing, ignore the certificate warning/error. While using curl, you can use `-k` to ignore SSL certificate warnings/errors. 112 | 113 | ------ 114 | 115 | # How to use this image? 116 | ## How to use this image in normal **container/pod network** ? 117 | 118 | ### Docker: 119 | ``` 120 | $ docker run -d praqma/network-multitool 121 | ``` 122 | 123 | Then: 124 | 125 | ``` 126 | $ docker exec -it container-name /bin/bash 127 | ``` 128 | 129 | 130 | ### Kubernetes: 131 | 132 | Create single pod - without a deployment: 133 | ``` 134 | $ kubectl run multitool --image=praqma/network-multitool 135 | ``` 136 | 137 | Create a deployment: 138 | ``` 139 | $ kubectl create deployment multitool --image=praqma/network-multitool 140 | ``` 141 | 142 | Then: 143 | ``` 144 | $ kubectl exec -it pod-name /bin/bash 145 | ``` 146 | 147 | **Note:** You can pass additional parameter `--namespace=` to the above kubectl commands. 148 | 149 | 150 | ### Openshift: 151 | 152 | ``` 153 | $ oc new-project test-project-1 154 | 155 | $ oc new-app praqma/network-multitool:openshift --name multitool-openshift 156 | 157 | $ oc status 158 | 159 | $ oc get pods 160 | 161 | $ oc logs pod-name 162 | 163 | $ oc exec -it pod-name /bin/sh 164 | 165 | $ oc port-forward pod-name 1180:1180 11443:11443 166 | ``` 167 | 168 | 169 | ## How to use this image on **host network** ? 170 | 171 | Sometimes you want to do testing using the **host network**. This can be achieved by running the multitool using host networking. 172 | 173 | 174 | ### Docker: 175 | ``` 176 | $ docker run --network host -d praqma/network-multitool 177 | ``` 178 | 179 | **Note:** If port 80 and/or 443 are already busy on the host, then use pass the extra arguments to multitool, so it can listen on a different port, as shown below: 180 | 181 | ``` 182 | $ docker run --network host -e HTTP_PORT=1180 -e HTTPS_PORT=11443 -d praqma/network-multitool 183 | ``` 184 | 185 | ### Kubernetes: 186 | For Kubernetes, there is YAML/manifest file `multitool-daemonset.yaml` in the `kubernetes` directory, that will run an instance of the multitool on all hosts in the cluster using host networking. 187 | 188 | ``` 189 | $ kubectl apply -f kubernetes/multitool-daemonset.yaml 190 | ``` 191 | 192 | **Notes:** 193 | * You can pass additional parameter `--namespace=` to the above kubectl command. 194 | * Due to a possibility of something (some service) already listening on port 80 and 443 on the worker nodes, the `daemonset` is configured to run multitool on port `1180` and `11443`. You can change this in the YAML file if you want. 195 | 196 | 197 | # Configurable HTTP and HTTPS ports: 198 | There are times when one may want to join this (multitool) container to another container's IP namespace for troubleshooting, or on the host network. This is true for both Docker and Kubernetes platforms. During that time if the container in question is a web server (nginx, apache, etc), or a reverse-proxy (traefik, nginx, haproxy, etc), then network-multitool cannot join it in the same IP namespace on Docker, and similarly it cannot join the same pod on Kubernetes. This happens because network multitool also runs a web server on port 80 (and 443), and this results in port conflict on the same IP address. To help in this sort of troubleshooting, there are two environment variables **HTTP_PORT** and **HTTPS_PORT** , which you can use to provide the values of your choice instead of 80 and 443. When the container starts, it uses the values provided by you/user to listen for incoming connections. Below is an example: 199 | 200 | ``` 201 | $ docker run -e HTTP_PORT=1180 -e HTTPS_PORT=11443 \ 202 | -p 1180:1180 -p 11443:11443 -d local/network-multitool 203 | 4636efd4660c2436b3089ab1a979e5ce3ae23055f9ca5dc9ffbab508f28dfa2a 204 | 205 | 206 | $ docker ps 207 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 208 | 4636efd4660c local/network-multitool "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 80/tcp, 0.0.0.0:1180->1180/tcp, 443/tcp, 0.0.0.0:11443->11443/tcp recursing_nobel 209 | 6e8b6ed8bfa6 nginx "nginx -g 'daemon of…" 56 minutes ago Up 56 minutes 80/tcp nginx 210 | 211 | 212 | $ curl http://localhost:1180 213 | Praqma Network MultiTool (with NGINX) - 4636efd4660c - 172.17.0.3/16 - HTTP: 1180 , HTTPS: 11443 214 | 215 | 216 | $ curl -k https://localhost:11443 217 | Praqma Network MultiTool (with NGINX) - 4636efd4660c - 172.17.0.3/16 - HTTP: 1180 , HTTPS: 11443 218 | ``` 219 | 220 | If these environment variables are absent/not-provided, the container will listen on normal/default ports 80 and 443. 221 | 222 | ------ 223 | 224 | # FAQs 225 | ## Why this multitool runs a web-server? 226 | Well, normally, if a container does not run a daemon/service, then running it (the container) involves using *creative ways / hacks* to keep it alive. If you don't want to suddenly start browsing the internet for "those creative ways", then it is best to run a small web server in the container - as the default process. 227 | 228 | This helps you when you are using Docker. You simply execute: 229 | ``` 230 | $ docker run -d praqma/network-multitool 231 | ``` 232 | 233 | This also helps when you are using kubernetes. You simply execute: 234 | ``` 235 | $ kubectl run multitool --image=praqma/network-multitool 236 | ``` 237 | 238 | 239 | The multitool container starts as web server - so it remains `UP`. Then, you simply connect to it using: 240 | ``` 241 | $ docker exec -it some-silly-container-name /bin/sh 242 | ``` 243 | 244 | Or, on Kubernetes: 245 | ``` 246 | $ kubectl exec -it multitool-3822887632-pwlr1 -- /bin/sh 247 | ``` 248 | 249 | This is why it is good to have a web-server in this tool. Hope this answers the question! Besides, I believe that having a web server in a multitool is like having yet another tool! Personally, I think this is cool! [Henrik](https://www.linkedin.com/in/henrikrenehoegh/) thinks the same! 250 | 251 | 252 | ## I can't find a tool I need for my use-case? 253 | We have tried to put in all the most commonly used tools, while keeping it small and practical. We can't have all the tools under the sun, otherwise it will end up as [something like this](https://www.amazon.ca/Wenger-16999-Swiss-Knife-Giant/dp/B001DZTJRQ). 254 | 255 | However, if you have a special need, for a special tool, for your special use-case, then I would recommend to simply build your own docker image using this one as base image, and expanding it with the tools you need. 256 | 257 | ## Why not use LetsEncrypt for SSL certificates instead of generating your own? 258 | There is absolutely no need to use LetsEncrypt. This is a testing tool, and validity of SSL certificates does not matter. 259 | 260 | ## Why use a daemonset when troubleshooting host networking on Kubernetes? 261 | One could argue that it is possible to simply install the tools on the hosts and get over with it. However, we should keep the infrastructure immutable and not install anything on the hosts. *Ideally* we should never `ssh` to our cluster worker nodes. Some of the reasons are: 262 | 263 | * It is generally cumbersome to install the tools since they might be needed on several hosts. 264 | * New packages may conflict with existing packages, and *may* break some functionality of the host. 265 | * Removing the tools and dependencies after use could be difficult, as it *may* break some functionality of the host. 266 | * By using a `daemonset`, it makes it easier to integrate with other resources. e.g. Use volumes for packet capture files, etc. 267 | * Using the `daemonset` provides a *'cloud native'* approach to provision debugging/testing tools. 268 | * You can `exec` into the `daemonset`, without needing to SSH into the node. 269 | 270 | 271 | ## How to contribute to this project? 272 | Contributions are welcome for packages/tools considered **"absolutely necessary"**, of **"core"** nature, are **"minimal"** in size, and **"have large number of use-cases"**. Remember, the goal is not to create yet another Linux distribution! :) 273 | 274 | -------------------------------------------------------------------------------- /blogpost.md: -------------------------------------------------------------------------------- 1 | # The Network Multitool container image 2 | 3 | ![](leatherman-wave.jpg) 4 | 5 | ## The itch 6 | There was a time ... actually ... many a times, when I needed more than just ping to reach a container running on a particular docker host on a particular docker / container network. 7 | 8 | We know that the idea behind a docker container in general is that it should have just enough software to make sure that the process/service it is supposed to run, is run without any problems; for example: a web server, a java application server, database server, etc. 9 | 10 | Just because these docker (container) images are very minimalistic, (in terms of size too), they have no other tools installed in them, making them very lean in nature. If a container is to run a single process all it's life, why bother filling it up with software which is never going to be used! Great! But since they are lean, troubleshooting them sometimes is difficult. 11 | 12 | Recently I was working on a Kubernetes cluster, and was not able to resolve services' names setup and provided by Kubernetes addon called SkyDNS. I had nginx running as a container, and being minimalistic in nature, it had no tools inside it except ping. It didn't even have nslookup! I installed nslookup in a running container, the usual `apt-get update` and `apt-get install dnsutils`. It is another story that nslookup alone did not prove to be much helpful in this particular case. It was just not giving me enough information about what was happening with name resolution. I was not until I decided to install `dig` that I figured out what was going on! It actually took me many container starts and many times going through the same `apt-get` update and install routines, until I reached a point to install dig instead of nslookup and things got very clear after that. 13 | 14 | This officially was a nasty (or very interesting) itch (depending on how you look at it), and I needed a solution for such situations. 15 | 16 | 17 | ## The solution 18 | Being a big fan and user of the multitools, such as the [Leatherman Wave](https://www.leatherman.com/wave-10.html) that I carry at all times with me as [EDC](https://en.wikipedia.org/wiki/Everyday_carry), I needed a container image which would have all the necessary tools installed in it, which I could use at will - without getting into the apt-* mess. I also wanted the container image to run as a standard (web service) pod too, so I could get two things out of it: 19 | 20 | * I will always have a web service to test my connections - nginx in this case; and, 21 | * I will just 'exec' and 'bash' into it and not have to remember complex kubectl commands to run it in interactive mode. 22 | 23 | So I went ahead and created [praqma/network-multitool](https://hub.docker.com/r/praqma/network-multitool/). I am a RedHat fan, so I based my image on CENTOS:7 . Intially I had Apache as web server, but later I replaced it with nginx - nginx being very light weight and very fast. 24 | 25 | ## Example usage: 26 | This image can be used in any environment which allows you to run containers; docker, docker-swarm, kubernetes, etc. Here are few eamples on how you can run and use this image: 27 | 28 | ### On Docker host: 29 | 30 | First, pull the image, though not entirely necessary. 31 | ``` 32 | [kamran@kworkhorse ~]$ docker pull praqma/network-multitool 33 | Using default tag: latest 34 | Trying to pull repository docker.io/praqma/network-multitool ... 35 | sha256:970b1ef9c12f4368c67b1fdbcaaedf4030861dae8263a410701fe040d59d1317: Pulling from docker.io/praqma/network-multitool 36 | 37 | 93857f76ae30: Already exists 38 | 6c1308705eea: Pull complete 39 | 82a705257117: Pull complete 40 | 1a824777af48: Pull complete 41 | dfe620fcbab4: Pull complete 42 | 417b019d6dbe: Pull complete 43 | 1d9e1b44b10a: Pull complete 44 | 4e922c058a8f: Pull complete 45 | Digest: sha256:970b1ef9c12f4368c67b1fdbcaaedf4030861dae8263a410701fe040d59d1317 46 | Status: Downloaded newer image for docker.io/praqma/network-multitool:latest 47 | [kamran@kworkhorse ~]$ 48 | ``` 49 | 50 | **Interactive:** 51 | ``` 52 | [kamran@kworkhorse ~]$ docker run --rm -it praqma/network-multitool bash 53 | 54 | [root@92288413e051 /]# nslookup yahoo.com 55 | Server: 192.168.100.1 56 | Address: 192.168.100.1#53 57 | 58 | Non-authoritative answer: 59 | Name: yahoo.com 60 | Address: 98.138.253.109 61 | Name: yahoo.com 62 | Address: 98.139.183.24 63 | Name: yahoo.com 64 | Address: 206.190.36.45 65 | 66 | [root@92288413e051 /]# 67 | ``` 68 | 69 | **Datached / Daemon mode:** 70 | ``` 71 | [kamran@kworkhorse ~]$ docker run -P -d praqma/network-multitool 72 | a76d156c674f2b61c9b9fb10f87c645620c4fcbe88a13162546379abc9a87f14 73 | [kamran@kworkhorse ~]$ 74 | ``` 75 | 76 | ``` 77 | [kamran@kworkhorse ~]$ docker ps 78 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 79 | a76d156c674f praqma/network-multitool "/start_nginx.sh" 31 seconds ago Up 30 seconds 0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp silly_franklin 80 | [kamran@kworkhorse ~]$ 81 | ``` 82 | 83 | ``` 84 | [kamran@kworkhorse ~]$ docker exec -it silly_franklin bash 85 | 86 | [root@a76d156c674f /]# curl -I yahoo.com 87 | HTTP/1.1 301 Redirect 88 | Date: Sun, 16 Apr 2017 16:09:20 GMT 89 | Via: https/1.1 ir28.fp.ne1.yahoo.com (ApacheTrafficServer) 90 | Server: ATS 91 | Location: https://www.yahoo.com/ 92 | Content-Type: text/html 93 | Content-Language: en 94 | Cache-Control: no-store, no-cache 95 | Connection: keep-alive 96 | Content-Length: 304 97 | 98 | [root@a76d156c674f /]# 99 | ``` 100 | 101 | ### On Kubernetes cluster: 102 | First run the container image as **deployment (detached/daemon mode)**: 103 | 104 | ``` 105 | [kamran@kworkhorse ~]$ kubectl run multitool --image=praqma/network-multitool 106 | deployment "multitool" created 107 | [kamran@kworkhorse ~]$ 108 | ``` 109 | 110 | Find the pod name and connect to it in **interactive mode**: 111 | ``` 112 | [kamran@kworkhorse ~]$ kubectl get pods 113 | NAME READY STATUS RESTARTS AGE 114 | multitool-2814616439-hd8p6 1/1 Running 0 1m 115 | [kamran@kworkhorse ~]$ 116 | ``` 117 | 118 | ``` 119 | [kamran@kworkhorse ~]$ kubectl exec -it multitool-2814616439-hd8p6 bash 120 | 121 | [root@multitool-2814616439-hd8p6 /]# traceroute google.com 122 | traceroute to google.com (64.233.184.102), 30 hops max, 60 byte packets 123 | 1 gateway (10.112.1.1) 0.044 ms 0.014 ms 0.009 ms 124 | 2 wa-in-f102.1e100.net (64.233.184.102) 0.716 ms 0.701 ms 0.896 ms 125 | [root@multitool-2814616439-hd8p6 /]# exit 126 | exit 127 | [kamran@kworkhorse ~]$ 128 | ``` 129 | 130 | ## Summary: 131 | Creating this network-multitool image has completely soothed this itch. Now, I use it to solve all sorts of problems. Packet capture, telnet , traceroute, mtr, dig, netstat, curl - you name it, it probably has it! I hope you will enjoy using this multitool as much as well as we do at Praqma. 132 | 133 | 134 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | # If the html directory is mounted, it means user has mounted some content in it. 5 | # In that case, we must not over-write the index.html file. 6 | 7 | WEB_ROOT=/usr/share/nginx/html 8 | MOUNT_CHECK=$(mount | grep ${WEB_ROOT}) 9 | HOSTNAME=$(hostname) 10 | 11 | if [ -z "${MOUNT_CHECK}" ] ; then 12 | echo "The directory ${WEB_ROOT} is not mounted." 13 | echo "Therefore, over-writing the default index.html file with some useful information:" 14 | 15 | # CONTAINER_IP=$(ip addr show eth0 | grep -w inet| awk '{print $2}') 16 | # Note: 17 | # CONTAINER IP cannot always be on device 'eth0'. 18 | # It could be something else too, as pointed by @arnaudveron . 19 | # The 'ip -j route' shows JSON output, 20 | # and always shows the default route as the first entry. 21 | # It also shows the correct device name as 'prefsrc', with correct IP address. 22 | CONTAINER_IP=$(ip -j route get 1 | jq -r '.[0] .prefsrc') 23 | 24 | # Reduced the information in just one line. It overwrites the default text. 25 | echo -e "Praqma Network MultiTool (with NGINX) - ${HOSTNAME} - ${CONTAINER_IP} - HTTP: ${HTTP_PORT:-80} , HTTPS: ${HTTPS_PORT:-443}" | tee ${WEB_ROOT}/index.html 26 | cat /root/press-release.html >> ${WEB_ROOT}/index.html 27 | else 28 | echo "The directory ${WEB_ROOT} is a volume mount." 29 | echo "Therefore, will not over-write index.html" 30 | echo "Only logging the container characteristics:" 31 | echo -e "Praqma Network MultiTool (with NGINX) - ${HOSTNAME} - ${CONTAINER_IP} - HTTP: ${HTTP_PORT:-80} , HTTPS: ${HTTPS_PORT:-443}" 32 | 33 | fi 34 | 35 | echo 36 | echo "========================= IMPORTANT ==============================" 37 | echo 38 | cat /root/press-release.md 39 | echo 40 | echo "==================================================================" 41 | echo 42 | 43 | # Custom/user-defined ports: 44 | # ------------------------- 45 | # If the env variables HTTP_PORT and HTTPS_PORT are set, then 46 | # modify/Replace default listening ports 80 and 443 to whatever the user wants. 47 | # If these variables are not defined, then the default ports 80 and 443 are used. 48 | 49 | if [ -n "${HTTP_PORT}" ]; then 50 | echo "Replacing default HTTP port (80) with the value specified by the user - (HTTP_PORT: ${HTTP_PORT})." 51 | sed -i "s/80/${HTTP_PORT}/g" /etc/nginx/nginx.conf 52 | fi 53 | 54 | if [ -n "${HTTPS_PORT}" ]; then 55 | echo "Replacing default HTTPS port (443) with the value specified by the user - (HTTPS_PORT: ${HTTPS_PORT})." 56 | sed -i "s/443/${HTTPS_PORT}/g" /etc/nginx/nginx.conf 57 | fi 58 | 59 | 60 | # Execute the command specified as CMD in Dockerfile: 61 | exec "$@" 62 | 63 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | Praqma Network MultiTool based on nginx:alpine 2 | -------------------------------------------------------------------------------- /kubernetes/README.md: -------------------------------------------------------------------------------- 1 | Running the multitool on Kubernetes using the POD network is as simple as: 2 | 3 | ```shell 4 | $ kubectl run nwtool --image praqma/network-multitool 5 | ``` 6 | 7 | This allows you to use the utilities from the multitool to test on the POD 8 | network, however, sometimes you want to do similar testing using the host 9 | network. This can be achieved by running the multitool using host 10 | networking. The manifest file in this folder contain a daemonset definition that 11 | will run an instance of the multitool on all hosts in the cluster using host 12 | networking. 13 | 14 | Obviously one could simply install the tools on the hosts, however, there are 15 | several reasons why this is not ideal. 16 | 17 | - We should keep the infrastructure immutable and not install anything on the 18 | hosts. Ideally we should never ssh to our cluster hosts. 19 | 20 | - Its cumbersome to install the tools since they might be needed on several hosts 21 | 22 | - Removing the tools and dependencies after use could be difficult 23 | 24 | - Using the tools from a Kubernetes resource allows one to integrate with other 25 | resources like e.g. volumes for packet capture files. 26 | 27 | Using the daemonset provides a 'cloud native' approach to provision 28 | debugging/testing tools. -------------------------------------------------------------------------------- /kubernetes/multiool-daemonset.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: network-multitool 5 | labels: 6 | tier: node 7 | app: network-multitool 8 | spec: 9 | selector: 10 | matchLabels: 11 | tier: node 12 | app: network-multitool 13 | template: 14 | metadata: 15 | labels: 16 | tier: node 17 | app: network-multitool 18 | spec: 19 | hostNetwork: true 20 | tolerations: 21 | - operator: Exists 22 | effect: NoSchedule 23 | containers: 24 | - name: network-multitool 25 | image: praqma/network-multitool 26 | env: 27 | - name: HTTP_PORT 28 | value: "1180" 29 | - name: HTTPS_PORT 30 | value: "11443" 31 | ports: 32 | - containerPort: 1180 33 | name: http-port 34 | - containerPort: 11443 35 | name: https-port 36 | resources: 37 | requests: 38 | cpu: "1m" 39 | memory: "20Mi" 40 | limits: 41 | cpu: "10m" 42 | memory: "20Mi" 43 | securityContext: 44 | runAsUser: 0 45 | capabilities: 46 | add: ["NET_ADMIN"] 47 | -------------------------------------------------------------------------------- /leatherman-wave.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Praqma/Network-MultiTool/c3d4e04c353cdbf8dcbcb8f57190056a66c273c0/leatherman-wave.jpg -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | # This file is copied as /etc/nginx/nginx.conf inside the container image. 2 | 3 | user nginx; 4 | worker_processes 1; 5 | 6 | 7 | # Run in foreground by turning off daemon mode. 8 | # Either set it in this file: 9 | # daemon off; 10 | # 11 | # OR 12 | # 13 | # Pass it as an argument on command line. 14 | # CMD ["/usr/sbin/nginx", "-g", "daemon off;"] 15 | 16 | 17 | # Note: The PID directory needs to exist beforehand. 18 | # Also, the pid file is always created as/by user root. 19 | pid /var/run/nginx.pid; 20 | 21 | # Forward error logs to docker log collector, 22 | # by sending it to stderr instead of a log file. 23 | error_log /dev/stderr warn; 24 | 25 | events { 26 | worker_connections 32; 27 | } 28 | 29 | 30 | http { 31 | include /etc/nginx/mime.types; 32 | default_type application/octet-stream; 33 | 34 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 35 | '$status $body_bytes_sent "$http_referer" ' 36 | '"$http_user_agent" "$http_x_forwarded_for"'; 37 | 38 | # Forward access logs to docker log collector, 39 | # by sending it to stdout instead of a log file. 40 | # This directive must be inside the nginx main 'http' section - not outside. 41 | access_log /dev/stdout main; 42 | 43 | sendfile on; 44 | 45 | keepalive_timeout 65; 46 | 47 | #gzip on; 48 | 49 | server { 50 | listen 80; 51 | server_name localhost; 52 | 53 | location / { 54 | root /usr/share/nginx/html; 55 | index index.html index.htm; 56 | } 57 | } 58 | 59 | server { 60 | listen 443 ssl; 61 | server_name localhost; 62 | 63 | location / { 64 | root /usr/share/nginx/html; 65 | index index.html index.htm; 66 | } 67 | 68 | ssl_certificate /certs/server.crt; 69 | ssl_certificate_key /certs/server.key; 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /press-release.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |

05 Jan 2022 - Press-release: `Praqma/Network-Multitool` is now `wbitt/Network-Multitool`

6 | 7 |

Important note about name/org change:

8 |

9 | Few years ago, I created this tool with Henrik Høegh, as `praqma/network-multitool`. Praqma was bought by another company, and now the "Praqma" brand is being dismantled. This means the network-multitool's git and docker repositories must go. Since, I was the one maintaining the docker image for all these years, it was decided by the current representatives of the company to hand it over to me so I can continue maintaining it. So, apart from a small change in the repository name, nothing has changed.
10 |

11 |

12 | The existing/old/previous container image `praqma/network-multitool` will continue to work and will remain available for **"some time"** - may be for a couple of months - not sure though. 13 |

14 |

15 | - Kamran Azeem https://github.com/KamranAzeem 16 |

17 | 18 |

Some important URLs:

19 | 20 | 25 | 26 |
27 | Or: 28 |
29 | 30 |
31 |   
32 |   docker pull wbitt/network-multitool
33 |   
34 | 
35 | 36 | 37 |
38 | 39 | -------------------------------------------------------------------------------- /press-release.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Press-release: `Praqma/Network-Multitool` is now `wbitt/Network-Multitool` 4 | 5 | ## 05 Jan 2022 - Important note about name/org change: 6 | Few years ago, I created this tool with Henrik Høegh, as `praqma/network-multitool`. Praqma was bought by another company, and now the "Praqma" brand is being dismantled. This means the network-multitool's git and docker repositories must go. Since, I was the one maintaining the docker image for all these years, it was decided by the current representatives of the company to hand it over to me so I can continue maintaining it. So, apart from a small change in the repository name, nothing has changed. 7 | 8 | The existing/old/previous container image `praqma/network-multitool` will continue to work and will remain available for **"some time"** - may be for a couple of months - not sure though. 9 | 10 | - Kamran Azeem [https://github.com/KamranAzeem](https://github.com/KamranAzeem) 11 | 12 | 13 | ## Some important URLs: 14 | * The new official github repository for this tool is: [https://github.com/wbitt/Network-MultiTool](https://github.com/wbitt/Network-MultiTool) 15 | * The docker repository to pull this image is now: [https://hub.docker.com/r/wbitt/network-multitool](https://hub.docker.com/r/wbitt/network-multitool) 16 | 17 | Or: 18 | 19 | ``` 20 | docker pull wbitt/network-multitool 21 | ``` 22 | --------------------------------------------------------------------------------