0

We're using a forked version of pi-gen (https://github.com/RPi-Distro/pi-gen) to build custom RPi images. As part of the build, we're trying to set the RPi up as an Access point as WiFi router/repeater with additional WiFi-dongle (thanks @Ingo), and have added this step to one of our stages in RPi:

#!/bin/bash -e

# Disable classic networking
sudo systemctl mask networking.service dhcpcd.service
sudo mv /etc/network/interfaces /etc/network/interfaces~
sudo sed -i '1i resolvconf=NO' /etc/resolvconf.conf

# Enable systemd-networkd
sudo systemctl enable systemd-networkd.service systemd-resolved.service
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

# Configure wpa_supplicant for wlan0 as access point
sudo cat > /etc/wpa_supplicant/wpa_supplicant-wlan0.conf <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="${AP_WPA_ESSID}"
    mode=2
    key_mgmt=WPA-PSK
    psk="${AP_WPA_PASSWORD}"
    frequency=2412
}
EOF

sudo chmod 600 /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
sudo systemctl disable wpa_supplicant.service
sudo systemctl enable wpa_supplicant@wlan0.service

# Configure wpa_supplicant for wlan1 as client
sudo cat > /etc/wpa_supplicant/wpa_supplicant-wlan1.conf <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="${CLIENT_WPA_ESSID}"
    psk="${CLIENT_WPA_PASSWORD}"
}
EOF

sudo chmod 600 /etc/wpa_supplicant/wpa_supplicant-wlan1.conf
sudo systemctl enable wpa_supplicant@wlan1.service

env SYSTEMD_EDITOR=tee sudo -E systemctl edit --system wpa_supplicant@wlan1.service <<EOF
[Service]
ExecStartPre=/sbin/iptables -t nat -A POSTROUTING -o wlan1 -j MASQUERADE
ExecStopPost=-/sbin/iptables -t nat -D POSTROUTING -o wlan1 -j MASQUERADE
EOF

# Configure interfaces
sudo cat > /etc/systemd/network/08-wlan0.network <<EOF
[Match]
Name=wlan0
[Network]
Address=192.168.4.1/24
IPForward=yes
DHCPServer=yes
[DHCPServer]
DNS=1.1.1.1 8.8.8.8
EOF

sudo cat > /etc/systemd/network/12-wlan1.network <<EOF
[Match]
Name=wlan1
[Network]
DHCP=yes
EOF

When we run the new image, we're getting the following error during the boot:

[ TIME ] Timed out waiting for device /subsystem/net/devices/wlan1.
[DEPEND] Dependency failed for WPA … (interface-specific version).

What dependency are we missing?

UPDATE 1

Following the pi-gen convention, I'm now "installing" the systemd files:

#!/bin/bash -e

install -v -m 600 files/wpa_supplicant-wlan0.conf       "${ROOTFS_DIR}/etc/wpa_supplicant/"
install -v -m 600 files/wpa_supplicant-wlan1.conf       "${ROOTFS_DIR}/etc/wpa_supplicant/"

install -v -m 600 files/08-wlan0.network                "${ROOTFS_DIR}/etc/systemd/network/"
install -v -m 600 files/12-wlan1.network                "${ROOTFS_DIR}/etc/systemd/network/"

# Disable classic networking
on_chroot << EOF
    sudo systemctl mask networking.service dhcpcd.service
    sudo mv /etc/network/interfaces /etc/network/interfaces~
    sudo sed -i '1i resolvconf=NO' /etc/resolvconf.conf

    # Enable systemd-networkd
    sudo systemctl enable systemd-networkd.service systemd-resolved.service
    sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

    sudo systemctl disable wpa_supplicant.service
    sudo systemctl enable wpa_supplicant@wlan0.service
    sudo systemctl enable wpa_supplicant@wlan1.service
EOF

....but I'm not sure what to do with the systemctl edit steps.

UPDATE 2

Made the switch to using instructions in Access point as WiFi router/repeater, optional with bridge and have updated the pi-gen scripts as follows:

00-run.sh:

#!/bin/bash -e

install -v -m 644 files/hostapd.service                 "${ROOTFS_DIR}/etc/systemd/system/"

on_chroot << EOF
    systemctl unmask hostapd
    systemctl enable hostapd

    # Disable classic networking
    systemctl mask networking.service dhcpcd.service
    mv /etc/network/interfaces /etc/network/interfaces~
    sed -i '1i resolvconf=NO' /etc/resolvconf.conf

    # Enable systemd-networkd
    systemctl enable systemd-networkd.service systemd-resolved.service
    ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
EOF

install -v -m 644 files/hostapd.conf                    "${ROOTFS_DIR}/etc/hostapd/"

on_chroot << EOF
    sed -i 's/^#DAEMON_CONF=.*$/DAEMON_CONF="\/etc\/hostapd\/hostapd.conf"/' /etc/default/hostapd
EOF

install -v -m 644 files/wpa_supplicant-wlan0.conf       "${ROOTFS_DIR}/etc/wpa_supplicant/"

on_chroot << EOF
    systemctl disable wpa_supplicant.service
    systemctl enable wpa_supplicant@wlan0.service
EOF

install -d                                              "${ROOTFS_DIR}/etc/systemd/system/wpa_supplicant@wlan0.service.d"
install -v -m 644 files/override.conf                   "${ROOTFS_DIR}/etc/systemd/system/wpa_supplicant@wlan0.service.d/"

install -v -m 644 files/08-wlan0.network                "${ROOTFS_DIR}/etc/systemd/network/"
install -v -m 644 files/12-ap0.network                  "${ROOTFS_DIR}/etc/systemd/network/"

files/hostapd.service:

[Unit]
Description=Advanced IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator
#After=network.target
Wants=wpa_supplicant@wlan0.service

[Service]
Type=forking
PIDFile=/run/hostapd.pid
Restart=on-failure
RestartSec=2
Environment=DAEMON_CONF=/etc/hostapd/hostapd.conf
EnvironmentFile=-/etc/default/hostapd
ExecStartPre=/sbin/iw dev wlan0 interface add ap0 type __ap
ExecStart=/usr/sbin/hostapd -B -P /run/hostapd.pid -B $DAEMON_OPTS ${DAEMON_CONF}
ExecStopPost=-/sbin/iw dev ap0 del

[Install]
WantedBy=multi-user.target

files/hostapd.conf:

interface=ap0
driver=nl80211
ssid=RPiWiFi
country_code=GB
hw_mode=g
channel=1
auth_algs=1
wpa=2
wpa_passphrase=pass1234
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP

files/wpa_supplicant-wlan0.conf:

country=GB
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="OfficeWiFi"
    psk="1234pass"
    key_mgmt=WPA-PSK
}

files/override.conf:

[Unit]
BindsTo=hostapd.service
After=hostapd.service

[Service]
ExecStartPost=/sbin/iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
ExecStopPost=-/sbin/iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE

files/08-wlan0.network:

[Match]
Name=wlan0
[Network]
IPForward=yes
DHCP=yes

files/12-ap0.network:

[Match]
Name=ap0
[Network]
Address=192.168.4.1/24
DHCPServer=yes
[DHCPServer]
DNS=1.1.1.1 8.8.8.8

...but we're getting the following error with wpa_supplicant@wlan0.service

Nov 28 12:52:10 raspberrypi wpa_supplicant[350]: Could not set interface wlan0 flags (UP): Name not unique on network
Nov 28 12:52:10 raspberrypi wpa_supplicant[350]: nl80211: Could not set interface 'wlan0' UP
Nov 28 12:52:10 raspberrypi wpa_supplicant[350]: nl80211: deinit ifname=wlan0 disabled_11b_rates=0
Nov 28 12:52:10 raspberrypi wpa_supplicant[350]: Could not set interface wlan0 flags (UP): Name not unique on network
Nov 28 12:52:10 raspberrypi wpa_supplicant[350]: WEXT: Could not set interface 'wlan0' UP
Nov 28 12:52:10 raspberrypi wpa_supplicant[350]: wlan0: Failed to initialize driver interface
Nov 28 12:52:10 raspberrypi systemd[1]: wpa_supplicant@wlan0.service: Main process exited, code=exited, status=255/EXCEPTION
Nov 28 12:52:10 raspberrypi systemd[1]: wpa_supplicant@wlan0.service: Failed with result 'exit-code'.
Nov 28 12:52:10 raspberrypi systemd[1]: Failed to start WPA supplicant daemon (interface-specific version).

UPDATE 3

Had another chance to take a look at this. The above error appears to be because both the wlan0 and ap0 network interfaces have the same MAC address:

$ ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 74:da:38:c9:b1:15 brd ff:ff:ff:ff:ff:ff
3: ap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 74:da:38:c9:b1:15 brd ff:ff:ff:ff:ff:ff

Can I set the hwaddress for each network interface ahead of time? Or is it an issue with the USB/WiFi dongle? What USB/WiFi dongle is best for this setup?

AdminBee
  • 244
  • 1
  • 15
timborden
  • 113
  • 4

1 Answers1

2

The problem is that your script does not run with root rights. You took the commands as given in the tutorial and where needed you prepend them with sudo. This does not work with redirection > and << because redirection then is related to sudo and not to cat as intended. If you look at the tutorial you will find as very first command

rpi ~$ sudo -Es

This opens a shell running with root rights. All following commands on the command line now are executed as root. Check with whoami.

The solution is to remove all sudo calls within the script and run the script itself with sudo, for example:

rpi ~$ sudo setup-ap.sh

If this is not possible then you have to ensure that all commands within the script that need root rights, run in its own environment. There are some technics to ensure it. You can use sudo bash -c '<commands>', e.g.:

rpi ~$ sudo bash -c 'echo "hello world" > out.tst'

or practical:

rpi ~$ sudo bash -c 'cat > /etc/systemd/network/12-wlan1.network <<EOF
[Match]
Name=wlan1
[Network]
DHCP=yes
EOF'

Have attention to single and double quotes with nesting. They have different meanings in bash. And please note that every command opens a separate shell with complete environment so for performance it is much better to execute the whole script as root.

Using systemctl edit as in

rpi ~# systemctl edit wpa_supplicant@wlan1.service

will create a so called drop in file that extends the original service. It is documented in man systemd.unit with Example 2. Overriding vendor settings at the end. The example creates the drop in file direct without using systemctl edit. You can just do it the same way with scripting. If unsure about the additional directories and files to create you can just create them one time with systemctl edit and show what it has done with

rpi ~$ systemctl cat wpa_supplicant@wlan1.service
Ingo
  • 40,606
  • 15
  • 76
  • 189
  • Thanks for the quick response! ...really appreciate it. `pi-gen` has a way to copy/install systemd files (https://github.com/RPi-Distro/pi-gen/blob/master/stage2/02-net-tweaks/01-run.sh) that should help with the permissions issue. I've updated my script to use that approach, but I'm not sure what to do with the `systemctl edit` steps. Can the contents of that EOF be included in a file that I can install using `pi-gen`? – timborden Nov 06 '19 at 16:16
  • @timborden I have updated the answer at the end about scripting `systemctl edit`. About your update: I don't know `pi-gen` but I see you are still using sudo? Is it really needed within `on_chroot`? – Ingo Nov 06 '19 at 17:04
  • Thanks again for taking the time. I've been through a few iterations with the pi-gen scripts and are very close (the access point is broadcasting, but the RPi is not connecting to the office WiFi). I've updated the question, if you've got time to take a look, it would be greatly appreciated. – timborden Nov 28 '19 at 13:53
  • I had a look at it but cannot say much because I don't know `pi-gen` and its installation routines. At a first glance it doesn't look bad but I don't oversee its dynamic: when is what installed and started. The setup [Access point as WiFi router/repeater, optional with bridge](https://raspberrypi.stackexchange.com/a/89804/110874) have to meet a very important condition: starting the services for **ap0**, **hostapd** and **wpa_supplicant** strict in this order as described in section "♦ Details". Typical are conflicts with interfaces if it isn't given. You should check the starting order. – Ingo Nov 28 '19 at 19:09
  • Thanks for taking a look, appreciate it. I'll double check the order. – timborden Nov 29 '19 at 16:08
  • I had a chance to review the start order (timestamps in `journalctl`) and the services appear to be starting in the desired order. I was also able to understand the error from the `wpa_supplicant@wlan0.service` (I've updated the question) If you've got time to take another look, it would be greatly appreciated. – timborden Dec 13 '19 at 16:18
  • @timborden With your second setup you use physical interface **wlan0** and virtual iinterface **ap0** bind to **wlan0**. It is normal that the virtual interface has the MAC address from its physical interface. You can try to [change the mac address](https://wiki.archlinux.org/index.php/MAC_address_spoofing#Automatically). With your third Update you asked: "*Or is it an issue with the USB/WiFi dongle?*". When using **ap0** there is no need to have a USB/WiFi dongle, so I don't understand what you mean. – Ingo Dec 13 '19 at 20:00
  • Finally figured it out. We were using Edimax EW-7811Un USB WiFi dongle and it doesn't support the nl80211 driver. When I switched dongles, it worked as expected. – timborden Dec 17 '19 at 12:38
  • @timborden You can try to use the older background driver **wext** instead. – Ingo Dec 17 '19 at 15:45