blog/content/posts/2023-03-29-shell-in-docker-compose-container-without-shell.md

77 lines
2.4 KiB
Markdown
Raw Normal View History

2023-03-30 04:33:59 +00:00
+++
title = "Getting a shell in a Docker Compose container without any shells"
date = 2023-03-29
tags = ["docker", "linux"]
+++
Here is a rather dumb way of entering a Docker Compose container that didn't
have a shell. In this specific case, I was trying to enter a Woodpecker CI
container without exiting it. These are my specific steps for running it, please
replace the paths and container names with the ones relevant to your specific
use-case.
At first, I tried an approach following [this][1] document. But once I got to
actually running commands within the namespace, I realized that this exposes the
exact same interface as Docker compose; if there was no shell available from the
Docker container entirely, then I couldn't run something from outside. I would
need to get some shell into the container.
[1]: https://www.redhat.com/sysadmin/container-namespaces-nsenter
Fortunately, there's a software that contains a lot of handy tools in one
binary: [busybox][2]. It's a GPL software that contains a small implementation
of a bunch of typical Unix utilities in a single static binary.
[2]: https://busybox.net/
I grabbed a copy of the busybox tool using:
```
$ nix build nixpkgs#pkgsStatic.busybox
```
(if you are not using [Nix][3], you may want to grab one of the pre-built
binaries from their website)
[3]: https://nixos.org/
To make sure it's statically linked (this means it doesn't depend on any
libraries already existing on your system, which may not be available within the
container), run this:
```
$ ldd ./result/bin/busybox
not a dynamic executable
```
If it doesn't say "not a dynamic executable" exactly, you're not using a static
build. If you're downloading off the website, make sure you look for something
that says [`musl`][4], which is a static version of libc.
[4]: https://musl.libc.org/
Now it's just a matter of getting this binary into the container.
```
$ docker compose cp ./result/bin/busybox woodpecker-server:/
```
Now, I can run a shell, using the busybox program:
```
$ docker compose exec woodpecker-server /busybox sh
/ #
```
If you just ran busybox without the `sh` param, then it would list all the
busybox utilities that were built into your static binary. At this point, I also
ran:
```
/ # /busybox --install -s /bin
```
within the container. This installs symlinks to busybox so it acts as each of
the individual programs it emulates. Now I can type `ls` instead of having to
run `busybox ls` every single time.