commit
e9d5e789f4
|
@ -0,0 +1,12 @@
|
||||||
|
FROM scratch
|
||||||
|
COPY . .
|
||||||
|
ARG ROOT_PW
|
||||||
|
RUN echo -e "${ROOT_PW}\n${ROOT_PW}" | passwd
|
||||||
|
RUN mkdir -p /var/lock
|
||||||
|
RUN opkg update && \
|
||||||
|
opkg install \
|
||||||
|
iperf3 \
|
||||||
|
ip-full && \
|
||||||
|
opkg list-upgradable | awk '{print $1}' | xargs opkg upgrade
|
||||||
|
|
||||||
|
CMD [ "/sbin/init" ]
|
12
Makefile
12
Makefile
|
@ -1,13 +1,17 @@
|
||||||
.PHONY: build run clean install uninstall
|
.PHONY: build build-rpi run clean install uninstall
|
||||||
|
|
||||||
include openwrt.conf
|
include openwrt.conf
|
||||||
|
export
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@docker build \
|
docker build \
|
||||||
--build-arg ROOT_PW=${ROOT_PW} \
|
--build-arg ROOT_PW \
|
||||||
--build-arg OPENWRT_TAG=${OPENWRT_TAG} \
|
--build-arg OPENWRT_TAG \
|
||||||
-t ${BUILD_TAG} .
|
-t ${BUILD_TAG} .
|
||||||
|
|
||||||
|
build-rpi:
|
||||||
|
./build-rpi.sh ${RPI_SOURCE_IMG}
|
||||||
|
|
||||||
run:
|
run:
|
||||||
./run.sh
|
./run.sh
|
||||||
|
|
||||||
|
|
14
README.md
14
README.md
|
@ -1,8 +1,8 @@
|
||||||
# OpenWRT in Docker
|
# OpenWrt in Docker
|
||||||
|
|
||||||
Inspired by other projects that run `hostapd` in a Docker container. This goes one step further and boots a full network OS intended for embedded devices called [OpenWRT](https://openwrt.org/), so you can manage all aspects of your network from a user-friendly web UI.
|
Inspired by other projects that run `hostapd` in a Docker container. This goes one step further and boots a full network OS intended for embedded devices called [OpenWrt](https://openwrt.org/), so you can manage all aspects of your network from a user-friendly web UI.
|
||||||
|
|
||||||
I only tested this on x86_64, but it might work on ARM too with some minor tweaking.
|
For Raspberry Pi-specific build instructions, see [Building on Raspberry Pi](./rpi.md).
|
||||||
|
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
@ -17,13 +17,13 @@ I only tested this on x86_64, but it might work on ARM too with some minor tweak
|
||||||
```
|
```
|
||||||
$ make build
|
$ make build
|
||||||
```
|
```
|
||||||
If you want additional OpenWRT packages to be present in the base image, add them to the Dockerfile. Otherwise you can install them with `opkg` after bringing up the container.
|
If you want additional OpenWrt packages to be present in the base image, add them to the Dockerfile. Otherwise you can install them with `opkg` after bringing up the container.
|
||||||
|
|
||||||
A searchable package list is available on [openwrt.org](https://openwrt.org/packages/table/start).
|
A searchable package list is available on [openwrt.org](https://openwrt.org/packages/table/start).
|
||||||
|
|
||||||
## Configure
|
## Configure
|
||||||
|
|
||||||
Initial configuration is performed using a config file, `openwrt.conf`. Values read from this file at runtime are used to generate OpenWRT format config files.
|
Initial configuration is performed using a config file, `openwrt.conf`. Values read from this file at runtime are used to generate OpenWrt format config files.
|
||||||
|
|
||||||
To add or change the base configuration, modify the config templates in `etc/config/<section>.tpl`.
|
To add or change the base configuration, modify the config templates in `etc/config/<section>.tpl`.
|
||||||
|
|
||||||
|
@ -58,10 +58,8 @@ This will delete the container and all associated Docker networks so you can sta
|
||||||
|
|
||||||
### Hairpinning
|
### Hairpinning
|
||||||
|
|
||||||
This took a couple of tries to get working. The most challenging issue was getting traffic from WLAN clients to reach each other.
|
|
||||||
|
|
||||||
In order for this to work, OpenWRT bridges all interfaces in the LAN zone and sets hairpin mode (aka [reflective relay](https://lwn.net/Articles/347344/)) on the WLAN interface, meaning packets arriving on that interface can be 'reflected' back out through the same interface.
|
In order for WLAN clients to see one another, OpenWrt bridges all interfaces in the LAN zone and sets hairpin mode (aka [reflective relay](https://lwn.net/Articles/347344/)) on the WLAN interface, meaning packets arriving on that interface can be 'reflected' back out through the same interface.
|
||||||
OpenWRT is not able to set this mode from inside the container even with `NET_ADMIN` capabilities, so this must be done from the host.
|
|
||||||
|
|
||||||
`run.sh` tries to handle this, and prints a warning if it fails.
|
`run.sh` tries to handle this, and prints a warning if it fails.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Extracts the rootfs from OpenWRT Raspberry Pi image available from
|
||||||
|
# https://downloads.openwrt.org/releases/19.07.2/targets/brcm2708/bcm2708/
|
||||||
|
# and builds a Docker container out of it
|
||||||
|
#
|
||||||
|
# Refer to https://openwrt.org/toh/raspberry_pi_foundation/raspberry_pi
|
||||||
|
# to choose the right image
|
||||||
|
#
|
||||||
|
# If building on x86, you must have qemu-arm and binfmt-support installed
|
||||||
|
set -e
|
||||||
|
|
||||||
|
IMG=${1:-'x'}
|
||||||
|
|
||||||
|
|
||||||
|
mount_rootfs() {
|
||||||
|
echo "* mounting image"
|
||||||
|
offset=$(sfdisk -d ${IMG} | grep "${IMG}2" | sed -E 's/.*start=\s+([0-9]+).*/\1/g')
|
||||||
|
tmpdir=$(mktemp -u -p .)
|
||||||
|
mkdir -p "${tmpdir}"
|
||||||
|
sudo mount -o loop,offset=$((512 * $offset)) -t ext4 ${IMG} ${tmpdir}
|
||||||
|
}
|
||||||
|
|
||||||
|
docker_build() {
|
||||||
|
echo "* building Docker image"
|
||||||
|
sudo docker build \
|
||||||
|
--build-arg ROOT_PW="${ROOT_PW}" \
|
||||||
|
-t ${BUILD_TAG} -f Dockerfile.rpi ${tmpdir}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
echo "* cleaning up"
|
||||||
|
sudo umount ${tmpdir}
|
||||||
|
rm -rf ${tmpdir}
|
||||||
|
}
|
||||||
|
|
||||||
|
test -f ${IMG} || { echo 'no image file found'; exit 1; }
|
||||||
|
trap cleanup EXIT
|
||||||
|
mount_rootfs
|
||||||
|
docker_build
|
|
@ -1,24 +1,31 @@
|
||||||
### Sample OpenWRT config file ###
|
### Sample OpenWRT config file ###
|
||||||
|
|
||||||
# general
|
## general
|
||||||
|
# source image for Raspberry Pi build target
|
||||||
|
SOURCE_IMG=openwrt-19.07.2-brcm2708-bcm2708-rpi-ext4-factory.img
|
||||||
|
# source tag for build (x86) target
|
||||||
OPENWRT_TAG=x86-64-19.07.2
|
OPENWRT_TAG=x86-64-19.07.2
|
||||||
|
# final tag for built Docker image
|
||||||
BUILD_TAG=openwrt
|
BUILD_TAG=openwrt
|
||||||
|
# container name
|
||||||
CONTAINER=openwrt_1
|
CONTAINER=openwrt_1
|
||||||
ROOT_PW=changeme123
|
ROOT_PW=changeme123
|
||||||
|
|
||||||
# docker network settings
|
# docker network settings
|
||||||
# wan
|
# wan
|
||||||
WAN_NAME=openwrt-wan
|
WAN_NAME=openwrt-wan
|
||||||
WAN_PARENT=enp0s20f0u4
|
# host interface which will provide the WAN link for OpenWRT
|
||||||
|
WAN_PARENT=eth0
|
||||||
UPSTREAM_DNS_SERVER=8.8.8.8
|
UPSTREAM_DNS_SERVER=8.8.8.8
|
||||||
|
|
||||||
# lan
|
# Static IP address configuration for OpenWRT LAN
|
||||||
LAN_NAME=openwrt-lan
|
LAN_NAME=openwrt-lan
|
||||||
LAN_DOMAIN=home
|
LAN_DOMAIN=home
|
||||||
LAN_SUBNET=192.168.16.0/24
|
LAN_SUBNET=192.168.16.0/24
|
||||||
LAN6_SUBNET=fd99:1234::/48
|
LAN6_SUBNET=fd99:1234::/48
|
||||||
|
# Set LAN_ADDR to something other than the first available address
|
||||||
|
# in the subnet - Docker will claim this address for the host
|
||||||
LAN_ADDR=192.168.16.2
|
LAN_ADDR=192.168.16.2
|
||||||
LAN_HOST=192.168.16.1
|
|
||||||
# openwrt doesn't accept CIDR notation; must match LAN_SUBNET
|
# openwrt doesn't accept CIDR notation; must match LAN_SUBNET
|
||||||
LAN_NETMASK=255.255.255.0
|
LAN_NETMASK=255.255.255.0
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Building on Raspberry Pi
|
||||||
|
|
||||||
|
Turn your Pi into a pretty okay-ish travel router (or a very slow main router)!
|
||||||
|
|
||||||
|
OpenWrt officially supports Raspberry Pi hardware if you want to run it as your OS. But running in a container brings many advantages, one of which is not having to re-flash your SD card.
|
||||||
|
|
||||||
|
This has been tested on a Raspberry Pi Zero W running Raspbian Lite, but should work for other versions too. Just make sure you download the right image for your Pi version (refer to the notes in [build-rpi.sh](./build-rpi.sh)).
|
||||||
|
|
||||||
|
|
||||||
|
## IPv6
|
||||||
|
By default Raspbian does not load the kernel module for IPv6 `iptables` on boot.
|
||||||
|
|
||||||
|
Run `sudo modprobe ip6_tables` to load it immediately.
|
||||||
|
|
||||||
|
To persist on reboot, run
|
||||||
|
|
||||||
|
$ echo 'ip6_tables' | sudo tee /etc/modules-load.d/ip6-tables.conf
|
||||||
|
|
||||||
|
---
|
||||||
|
## Build
|
||||||
|
You can build the OpenWRT docker image on the Pi itself, or on your x86 PC with `qemu-arm` installed.
|
||||||
|
|
||||||
|
First download and extract the OpenWRT factory image for your Pi. Refer to the [OpenWrt Table of Hardware](https://openwrt.org/toh/raspberry_pi_foundation/raspberry_pi) to choose the right image. Then run the `make` target.
|
||||||
|
|
||||||
|
The variable `RPI_SOURCE_IMG` can be specified in openwrt.conf or on the command line:
|
||||||
|
```
|
||||||
|
$ https://downloads.openwrt.org/releases/19.07.2/targets/brcm2708/bcm2708/openwrt-19.07.2-brcm2708-bcm2708-rpi-ext4-factory.img.gz
|
||||||
|
$ gzip -d openwrt-*.img.gz
|
||||||
|
$ make build-rpi RPI_SOURCE_IMG=openwrt-19.07.2-brcm2708-bcm2708-rpi-ext4-factory.img
|
||||||
|
```
|
||||||
|
|
||||||
|
If you built the image on your PC, send it to your Raspberry Pi (`$BUILD_TAG` is a config variable):
|
||||||
|
```
|
||||||
|
$ docker save $BUILD_TAG | ssh <your_raspberry_pi_host> docker load
|
20
run.sh
20
run.sh
|
@ -69,7 +69,7 @@ function _set_hairpin() {
|
||||||
for i in {1..10}; do
|
for i in {1..10}; do
|
||||||
echo -n '.'
|
echo -n '.'
|
||||||
sudo ip netns exec $CONTAINER ip link set $WIFI_IFACE type bridge_slave hairpin on 2>/dev/null && { echo 'ok'; break; }
|
sudo ip netns exec $CONTAINER ip link set $WIFI_IFACE type bridge_slave hairpin on 2>/dev/null && { echo 'ok'; break; }
|
||||||
sleep 1
|
sleep 3
|
||||||
done
|
done
|
||||||
if [[ $i -ge 10 ]]; then
|
if [[ $i -ge 10 ]]; then
|
||||||
echo -e "\ncouldn't set hairpin mode, wifi clients will probably be unable to talk to each other"
|
echo -e "\ncouldn't set hairpin mode, wifi clients will probably be unable to talk to each other"
|
||||||
|
@ -78,8 +78,8 @@ function _set_hairpin() {
|
||||||
|
|
||||||
function _create_or_start_container() {
|
function _create_or_start_container() {
|
||||||
docker inspect $BUILD_TAG >/dev/null 2>&1 || { echo "no image '$BUILD_TAG' found, did you forget to run 'make build'?"; exit 1; }
|
docker inspect $BUILD_TAG >/dev/null 2>&1 || { echo "no image '$BUILD_TAG' found, did you forget to run 'make build'?"; exit 1; }
|
||||||
docker inspect $CONTAINER >/dev/null 2>&1
|
|
||||||
if [[ $? -eq 0 ]]; then
|
if docker inspect $CONTAINER >/dev/null 2>&1; then
|
||||||
echo "* starting container '$CONTAINER'"
|
echo "* starting container '$CONTAINER'"
|
||||||
docker start $CONTAINER
|
docker start $CONTAINER
|
||||||
else
|
else
|
||||||
|
@ -103,9 +103,20 @@ function _create_or_start_container() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _reload_fw() {
|
||||||
|
echo "* reloading firewall rules"
|
||||||
|
docker exec -it $CONTAINER sh -c '
|
||||||
|
for iptables in iptables ip6tables; do
|
||||||
|
for table in filter nat mangle; do
|
||||||
|
$iptables -t $table -F
|
||||||
|
done
|
||||||
|
done
|
||||||
|
/sbin/fw3 -q restart'
|
||||||
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
test -z $WIFI_IFACE && _usage
|
test -z $WIFI_IFACE && _usage
|
||||||
cd $SCRIPT_DIR
|
cd "${SCRIPT_DIR}"
|
||||||
_get_phy_from_dev
|
_get_phy_from_dev
|
||||||
_nmcli
|
_nmcli
|
||||||
_create_or_start_container
|
_create_or_start_container
|
||||||
|
@ -124,6 +135,7 @@ function main() {
|
||||||
echo "* getting address via DHCP"
|
echo "* getting address via DHCP"
|
||||||
sudo dhcpcd -q "br-${LAN_ID:0:12}"
|
sudo dhcpcd -q "br-${LAN_ID:0:12}"
|
||||||
|
|
||||||
|
_reload_fw
|
||||||
echo "* ready"
|
echo "* ready"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue