├── Makefile ├── Dockerfile ├── LICENSE ├── nsenter1.c └── README.md /Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | docker build -t justincormack/nsenter1 . 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:edge as BUILD 2 | RUN apk update && apk add build-base 3 | COPY nsenter1.c ./ 4 | RUN cc -Wall -static nsenter1.c -o /usr/bin/nsenter1 5 | 6 | FROM scratch 7 | COPY --from=BUILD /usr/bin/nsenter1 /usr/bin/nsenter1 8 | ENTRYPOINT ["/usr/bin/nsenter1"] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016 Justin Cormack 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /nsenter1.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern char **environ; 11 | 12 | // Reassociate with the most important namespaces of pid 1 13 | 14 | int main(int argc, char **argv) { 15 | char *shell = "/bin/sh"; 16 | char *def[] = {shell, NULL}; 17 | char *cmd = shell; 18 | char **args = def; 19 | int fdm = open("/proc/1/ns/mnt", O_RDONLY); 20 | int fdu = open("/proc/1/ns/uts", O_RDONLY); 21 | int fdn = open("/proc/1/ns/net", O_RDONLY); 22 | int fdi = open("/proc/1/ns/ipc", O_RDONLY); 23 | int froot = open("/proc/1/root", O_RDONLY); 24 | 25 | if (fdm == -1 || fdu == -1 || fdn == -1 || fdi == -1 || froot == -1) { 26 | fprintf(stderr, "Failed to open /proc/1 files, are you root?\n"); 27 | exit(1); 28 | } 29 | 30 | if (setns(fdm, 0) == -1) { 31 | perror("setns:mnt"); 32 | exit(1); 33 | } 34 | if (setns(fdu, 0) == -1) { 35 | perror("setns:uts"); 36 | exit(1); 37 | } 38 | if (setns(fdn, 0) == -1) { 39 | perror("setns:net"); 40 | exit(1); 41 | } 42 | if (setns(fdi, 0) == -1) { 43 | perror("setns:ipc"); 44 | exit(1); 45 | } 46 | if (fchdir(froot) == -1) { 47 | perror("fchdir"); 48 | exit(1); 49 | } 50 | if (chroot(".") == -1) { 51 | perror("chroot"); 52 | exit(1); 53 | } 54 | if (argc > 1) { 55 | cmd = argv[1]; 56 | args = argv + 1; 57 | } 58 | if (execve(cmd, args, environ) == -1) { 59 | perror("execve"); 60 | exit(1); 61 | } 62 | exit(0); 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nsenter1 2 | 3 | [![Docker Build Status](https://img.shields.io/docker/pulls/justincormack/nsenter1.svg)](justincormack/nsenter1) 4 | 5 | Minimal image for `nsenter` to namespaces of PID 1 6 | 7 | * mnt 8 | * uts 9 | * net 10 | * ipc 11 | 12 | To achieve the above with the basic alpine image you would enter: 13 | 14 | $ docker run -it --rm --privileged --pid=host alpine:edge nsenter -t 1 -m -u -n -i sh 15 | / # 16 | 17 | Unfortunately, however, there is an [outstanding issue](https://github.com/gliderlabs/docker-alpine/issues/359) 18 | that prevents specifying the target pid. 19 | 20 | With this image, you can simply run the following: 21 | 22 | $ docker run -it --rm --privileged --pid=host justincormack/nsenter1 23 | / # 24 | 25 | ## So what is this good for 26 | 27 | `nsenter` allows you to enter a shell in a running container (technically into the namespaces that provide 28 | a container's isolation and limited access to system resources). The crazy thing is that this image allows 29 | you to run a privileged container that runs nsenter for the process space running as pid 1. How is this useful? 30 | 31 | Well, this is useful when you are running a lightweight, container-optimized Linux distribution such as 32 | [LinuxKit](https://blog.docker.com/2017/04/introducing-linuxkit-container-os-toolkit/). 33 | Here is one simple example: say you want to teach a few people about Docker networking and you want to 34 | show them how to inspect the default bridge network after starting two containers using `ip addr show`; 35 | the problem is if you are demonstrating with Docker for Mac, for example, your containers are not running on 36 | your host directly, but are running instead inside of a minimal Linux OS virtual machine specially built for 37 | running containers, i.e., LinuxKit. But being a lightweight environment, LinuxKit isn't running `sshd`, so 38 | how do you get access to a shell so you can run `nsenter` to inspect the namespaces for the process running as pid 1? 39 | 40 | Well, you could run the following: 41 | 42 | $ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty 43 | 44 | Docker for Mac does expose a screen session to attach to, but it's a bit less than ideal if you're not familiar 45 | with screen. It's not a big deal, but it's not optimal and it's also very specific to Docker for Mac. Since 46 | we're already running Docker the general solution is ideal in this case: 47 | 48 | ``` 49 | $ docker run -it --rm --privileged --pid=host justincormack/nsenter1 50 | / # ip a 51 | 256: vethb72bfa3@if255: mtu 1500 qdisc noqueue master docker0 state UP 52 | link/ether 7a:41:32:02:63:7c brd ff:ff:ff:ff:ff:ff 53 | inet6 fe80::7841:32ff:fe02:637c/64 scope link 54 | valid_lft forever preferred_lft forever 55 | 1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1 56 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 57 | inet 127.0.0.1/8 brd 127.255.255.255 scope host lo 58 | valid_lft forever preferred_lft forever 59 | inet6 ::1/128 scope host 60 | valid_lft forever preferred_lft forever 61 | 2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 62 | link/ether 02:50:00:00:00:01 brd ff:ff:ff:ff:ff:ff 63 | inet 192.168.65.3/24 brd 192.168.65.255 scope global eth0 64 | valid_lft forever preferred_lft forever 65 | inet6 fe80::49e8:1c10:4c64:c980/64 scope link 66 | valid_lft forever preferred_lft forever 67 | ... 68 | ``` 69 | 70 | Have fun! 71 | 72 | --------------------------------------------------------------------------------