Running OpenWrt in a Docker container

Hi,

I've been working on running OpenWRT itself (not the build environment) inside a container as my primary home router / wifi AP.
You can find the repo here with intructions: https://github.com/oofnikj/docker-openwrt

Currently I have it running on an Intel NUC with Ubuntu 16.04 as the host OS and seems stable.

And a write-up on my blog that explains why someone might want to do this: https://badgateway.qc.to/contain-your-router/

Hope someone finds this interesting / useful.

15 Likes

Thanks allot for this post - that way I became aware of Oracle abandoning PCI passthrough functionality.
(Which in my case unfortunately leads to changing my hypervisor, as that functionality is crucial for me - I will try out running my server with windows 10 and hyper-v).

1 Like

Update: Just merged the Raspberry Pi branch to master.
Tested on a RPi Zero W with a USB Ethernet dongle. Wi-Fi bandwidth maxes out around 30 Mbps.

This could make a great travel router.

2 Likes

Great work @oofnik, thanks a lot for it, and other awesome scripts you've made, like your UEFI OpenWrt image generator!
I'm experementing with OpenWrt in a docker container under Debian Stretch (an offcial image from https://hub.docker.com/r/openwrtorg/rootfs). Using sudo docker run -it openwrtorg/rootfs to run it. What I've noticed is that hitting Ctrl+C from inside the container, it gets terminated and I get bailed out to the host's shell. That happens if I run for example "ping -t 8.8.8.8" or "top" inside OpenWrt and use Ctrl+C to stop them. It doesn't happen if I just hit Ctrl+C form inside the container's bash itself.
Is there anyway to prevent Ctrl+C from terminating the container like that? Are you experiencing the same with your docker environment? Thanks!

What do you think should happen if the ping command terminates?
Spinning up a shell or there like?

The container terminates if the main process terminates.
As bash doesn't terminate on a strg+c the container also doesn't terminate.

You can try for example: docker run -it openwrtorg/rootfs /bin/bash ping -t 8.8.8.8

Or only execute the bash and pipe your desired command through stdin to bash inside the container.

1 Like

What do you think should happen if the ping command terminates?

I'm new to this, so please bear with me, but I think it should take me back to OpenWrt's shell, which runs inside the container, and keep the container running.

I'd expect that because when I run OpenWrt via docker, the container asks me to "Please press Enter to activate this console." Only after I press Enter, it starts BusyBox. Then when I type "exit", it doesn't terminate the container. Instead I see "Please press Enter to activate this console" again. Note I simply start the container as sudo docker run --rm -it openwrtorg/rootfs, and what its docker file invokes is CMD ["/sbin/init"].

So why terminating "ping", which I started manually from BusyBox's shell inside the OpenWrt container, would also terminate the shell, and then the container itself?

PS. I've just tried the same with Alpine ("sudo docker run -it alpine") and it worked as I'd expect, i.e., terminating ping with Ctrl+C doesn't kill the container.

1 Like

Thanks @noseratio!

I've noticed that too.

It happens (I think) because Docker traps certain signals (including SIGINT) and sends them to PID 1 inside the OpenWrt container, not to the process that's currently attached to the shell.

In the case of Openwrt, PID 1 is procd, which dies upon receiving SIGINT, along with all of its child processes.
This can be mitigated by running the container in the background and issuing a subsequent docker exec command:

$ docker run -it --name openwrt openwrtorg/rootfs:latest
0963b6be70540448b1d932ecc45b1bbbe3bbed98d72946bad87769011323483d
$ docker exec -it openwrt sh -l

This will execute an interactive login shell inside the container that is not a child process of procd, thus the SIGINT won't get passed up to procd and shut down the container.

The reason this doesn't happen with other Docker images (Alpine, Debian, Ubuntu, etc.) is because they are not running an init system inside the container - only a single process. SIGINT (Crtl+C) goes to the shell, which is PID 1, and does what SIGINT would normally do in any interactive shell.

2 Likes

It happens (I think) because Docker traps certain signals (including SIGINT) and sends them to PID 1 inside the OpenWrt container...

Thank you @oofnik, this nails it down, what a great explanation and a workaround!

1 Like

Hi,

I have trying to find a way to build a setup with OpenWRT, OpenMediaVault and NextCloud all on one physical machine. I think your guide to run OpenWRT as a Docker Container is the gold ticket to what I'm trying to achieve here. My USB Wireless Dongle is working on the host machine. My question: Does OpenWRT running as a Docker container needs the drivers of the USB Wireless Dongle for it to work?

Here is my idea setup:
Host machine running OpenMediaVault with Docker
Container 1: OpenWRT
Container 2: NexCloud

Thanks in advance!

Hi @v01ded,
Sounds like this could work in your case. My setup is similar to what you're trying to do (media center + network router / wireless AP in one machine).

Running OpenWrt in Docker means, like any container, that it is running on the host kernel, using the host kernel's drivers. As long as your wireless driver uses the mac80211 subsystem (most but not all do) and supports AP mode, you should be all set.

If you try to run the script and see some errors like "RTNETLINK answers: Operation not supported", it probably means your wireless card driver doesn't support network namespaces, and you won't be able to use it in Docker.

The only other alternative to achieve what you're looking for would be to run OpenWrt in a VM, and pass through the wireless device to the guest.

Give it a shot - I'd love to hear if it works (or doesn't) for you.

1 Like

Hi @oofnik and All,

Reporting back my progress. I have setup OMV5 as the host machine and ran OpenWRT as a container. I'm getting the following error when I tried to move the Namespace of my wifi card. I guess my WIFI card driver does not support setting of namespace.. :frowning:

# iw phy $PHY set netns $PID
command failed: Operation not supported (-95)

Here is details of my setup:

  • (Host) OpenMediaVault v5.3.9-1 with Kernel 5.4.0.0
  • (Wireless) RTL8821CU USB Wi-Fi adapter self compiled driver here
  • (Docker Image)Openwrtorg/rootfs:x86-64

Technically it is possibl and since I'm compiling the wifi driver, maybe I can compile the drive to support setting of namespace? But I do not let know how can it be done.. Any suggestion? :slightly_smiling_face:

Ciao!

let me to say thanks you for all information and for the article https://badgateway.qc.to/contain-your-router/

I am able to run openwrt in a container but it is blocked at init start.

Here i am able to pass from my host to the openwrt container 2 nics and my wifi pcie card (asus ac88).

sudo docker run --rm -it -d --name openwrt openwrtorg/rootfs:x86-64-19.07.2

sudo docker inspect --format='{{ .State.Pid }}' openwrt
sudo ip link set eno3 netns 15019
sudo ip link set eno4 netns 15019

sudo docker inspect -f '{{.State.Pid}}' openwrt
sudo iw phy phy0 set netns 15019

sudo docker exec openwrt ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: eno3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether ac:1f:6b:b0:06:36 brd ff:ff:ff:ff:ff:ff
5: eno4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether ac:1f:6b:b0:06:37 brd ff:ff:ff:ff:ff:ff
6: wlp2s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 2c:fd:a1:ce:33:9d brd ff:ff:ff:ff:ff:ff
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff


sudo docker exec -i openwrt passwd

but from the logs i got

fabri@hostgateway:~$ sudo docker logs --tail all openwrt
Failed to resize receive buffer: Operation not permitted
/etc/preinit: line 6: can't create /sys/devices/system/cpu/microcode/reload: Read-only file system
ip: RTNETLINK answers: Operation not permitted
Press the [f] key and hit [enter] to enter failsafe mode
Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level
ip: can't send flush request: Operation not permitted
ip: SIOCSIFFLAGS: Operation not permitted
Please press Enter to activate this console.

and if i try to run by hand the init process i got:

fabri@hostgateway:~$ sudo docker exec -it openwrt /bin/init
OCI runtime exec failed: exec failed: container_linux.go:345: starting container process caused "exec: \"/bin/init\": stat /bin/init: no such file or directory": unknown

Do you have some idea on how to start correctly openwrt and LUCI web page?

Try this instead

sudo docker exec -it openwrt /bin/sh

after that you will have to install LUCI from the shell to access the web interface..
:slightly_smiling_face:

1 Like

ho... thanks a lot man...

I assumed that everything was ready ... :slight_smile:
It is was so easy...

but does it all work even if I have those errors?

UPDATE ---------------------------------------------------------

i have install
root@03c70b13a3ff:/# opkg install luci-ssl-nginx

but whit a laptop i am not able to got IP from eno3 interface...

4: eno3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether ac:1f:6b:b0:06:36 brd ff:ff:ff:ff:ff:ff
sudo docker exec openwrt ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: eno3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether ac:1f:6b:b0:06:36 brd ff:ff:ff:ff:ff:ff
5: eno4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether ac:1f:6b:b0:06:37 brd ff:ff:ff:ff:ff:ff
6: wlp2s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 2c:fd:a1:ce:33:9d brd ff:ff:ff:ff:ff:ff
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff

and ifconfig doesn't provide any configuration of eno3

root@03c70b13a3ff:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1943 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2260 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:4654311 (4.4 MiB)  TX bytes:241876 (236.2 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

i have also tried to reload network configurationand editing /etc/config/network without success

config interface 'loopback'
    option ifname 'lo'
    option proto 'static'
    option ipaddr '127.0.0.1'
    option netmask '255.0.0.0'

config interface 'wan'
    option ifname 'eth0'
    option proto 'dhcp'

config interface 'wan6'
    option ifname 'eth0'
    option proto 'dhcp6'

can you kindly tell me how obtain ip address from my eno3 interface and i can access to luci web page?

another input... seems that i am not able to turn no the eno3 interface...

root@03c70b13a3ff:/# ifconfig eno3 up
ifconfig: SIOCSIFFLAGS: Operation not permitted
root@03c70b13a3ff:/# ip link set eno3 up
ip: SIOCSIFFLAGS: Operation not permitted

Hi fabri,

I'm not able to change the name space of my nic card so I cannot comment much. What I did is to map the OpenWRT Container port 80 to my host 8080 and access LUCI through the host IP with port 8080 ( http://your.server.ip:8080)

To do that, start your OpenWRT container with the following command:

docker run -it --name openwrt openwrtorg/rootfs:latest -p 8080:80

You can read out more about port mapping in Docker here.

1 Like

doesn't working if i am using -p option...

root@hostgateway:~# sudo docker run --rm -it -d --name openwrt openwrtorg/rootfs:x86-64-19.07.2 -p 8080:80
3a29aabc1124b8f6edeb6081769da4ea896cc61808424d580321e8aa56cee924
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"-p\": executable file not found in $PATH": unknown.

same for latest version

root@hostgateway:~# docker container ls -all
CONTAINER ID        IMAGE                      COMMAND             CREATED             STATUS              PORTS                     NAMES
37f9bb846422        openwrtorg/rootfs:latest   "-p 8080:80"        15 seconds ago      Created             22/tcp, 80/tcp, 443/tcp   openwrt
root@hostgateway:~#
root@hostgateway:~# docker start -i openwrt
Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"-p\": executable file not found in $PATH": unknown
1 Like

If you're running the OpenWrt image from the command-line, there are a couple of additional parameters that you need to pass to Docker in order for OpenWrt to be able to manage your network interfaces.

Take a look at the command line options in the docker-openwrt project main script.

Without --cap-add NET_ADMIN, for example, Docker won't allow OpenWrt to add, remove, or change IP addresses, making it fairly useless.

1 Like

Well, it would require a lot of work by a kernel driver developer.
I tried and gave up with a similar project.
The issue is that these out-of-tree drivers from old Realtek source code do not use the mac80211 wireless subsystem where a lot of the network namespace code lives. Refactoring these drivers to use that code is probably not worth the effort.

Luckily for me, I own a device with the RTL8822BU chipset, and there is a fork of the modern, completely re-written rtw88 driver which adds support for USB. I worked with the developer on this driver who got it to a point of good performance and stability.

1 Like

Hi offnik,

I found another USB Wifi dongle laying around that support Namespace change. I tried to add the NET_ADMIN option and now my OpenWRT could not connect to internet. Not sure why.. Can you advise what other option do we need to get OpenWRT container working with a USB Wifi dongle connected on the host? So far, my OpenWRT contain can already see the USB Wifi dongle but I cannot get the Wireless configuration working. There is also no Wireless configuration optionin LUCI. Banging my head on my keyboard for the past days trying to figure this out...