Reliable and updatable access point

Hello! there may be a way to configure the access point so it always resolve to a specific domain. On the config.json your hostname will generate a local URL (mdns) (e.g. your-hostname.local) exposing port 80. So that would enable your users to connect to that URL once connected to the access point WiFi and get all the instructions about how to connect. Having said that, it would resolve to a local IP (potentially different for every access point) not a static IP address.

Hi @mpous,

Thanks for your answer. I’ve heard about the .local domains, but it’s not really what I’m trying to accomplish. I’d like to browse to, for example: access-point.mydomain.com, and when connected to the AP, it resolves to its own IP address (so 10.42.0.1 for example). I know this can be done by using dnsmasq, but running my own dnsmasq instance in a container adds some more maintenance and probably another container. But because the HostOS already uses it, it’d be better if I can configure that dnsmasq instance from within my container.

I’ve followed Connecting the device to a wifi behind a captive, and the DBus is enabled. But if I understand @majorz, I can’t really use that dnsmasq instance, which is too bad. But please correct me if I’m wrong :slight_smile:

Hey, if I’m understanding the query correctly I think that the extra_hosts parameter should help?

Hi,

It’s not from within my container that I’d like to set access-point.mydomain.com to a specific host, but from the client that’s connected to the AP created by my balena device. So I’ve to use some DNS service like dnsmasq probably. But thanks for the link!

Ah, my mistake. Yes it does sound like dnsmasq would help here.

bversluijs, did you find a solution for this?

Hi,

I don’t know what you mean by ‘this’ exactly. The DNS service or the whole updatable access point?
I’m happy to share some details about my access point application :slight_smile: .

Sorry for the ambiguity :slight_smile:
Most critically for me, when a client is connected to the WiFi access point, how to route their request to some.domain.com to a localhost:port destination running inside balenaOS.

No problem!

When my wlan0 is initiated as an access point (because it can’t connect to a Wi-Fi network), it also starts a dnsmasq instance in the background (spawn in Node.js) with the following config parameters (credits to wifi-connect)

dnsmasq \
      --address=/#/10.42.0.1 \
      --dhcp-range=10.42.0.1,10.42.0.254 \
      --dhcp-option=option:router,10.42.0.1 \
      --interface=wlan0 \
      --keep-in-foreground \
      --bind-interfaces \
      --except-interface=lo \
      --conf-file \
      --no-hosts \
      --log-facility=-

Change device, gateway, route and dhcp according to your needs.

But what this does is the following:
It routes everything to 10.42.0.1. You can’t forward it to a port, because when a user does an HTTP request, it uses port 80, so DNSMASQ routes it to 10.42.0.1:80. HTTPS is the same, it routes to 10.42.0.1:443. So you can’t change the port, only the address (I’d recommend to just use this ports for your web application, because it’s the standard).

It also only binds to wlan0, so your ethernet can still be connected to the internet for example.
Because everything routes to 10.42.0.1, most devices will show a ‘hotspot’ interface, because it can’t reach it’s hotspot check (e.g. Apple checks for http://captive.apple.com/hotspot-detect.html).

When wlan0 is connected to a Wi-Fi client, or is trying to connect, my app kills the dnsmasq instance and tries to connect via NetworkManager. When it fails, it sets up the AP again and thus dnsmasq. But when it’s successful, it stays a Wi-Fi client.

Good to know
Most HTTPS websites set a HSTS-header. This means that your browser will “always” know, that when connecting to, for example, https://google.com, it uses HTTPS with a (valid) certificate. So when you’re connected to the AP, and you’re trying to go to https://google.com (with your webserver listening on port 443), it’ll give an HTTPS error, because the certificate is invalid (it’s not the Google certificate it expected).

We’re working around that by using a subdomain (local.domain.com) which doesn’t use HTTPS but just HTTP. That’ll work because the HSTS-header is not set for that domain. So we instruct users to connect to the AP, and if the hotspot interface doesn’t come up, go to local.domain.com, and it’ll “redirect” them to 10.42.0.1.

Hope you get the idea, if you’ve any other questions, please ask them here!
I’ve got this working by using Google and open-source projects, so I’m happy to share some knowledge!

2 Likes

Thanks bversluijs, very helpful. Combining that with some info above…

/mnt/boot/system-connections/balena-hotspot

[connection]
id=balena-hotspot
uuid=36060c57-aebd-4ccf-aba4-ef75121b5f77
type=wifi
autoconnect=true
interface-name=wlan0
permissions=
secondaries=

[wifi]
band=bg
mac-address-blacklist=
mac-address-randomization=0
mode=ap
seen-bssids=
ssid=MY_HOTSPOT_SSID

[wifi-security]
group=
key-mgmt=wpa-psk
pairwise=
proto=rsn
psk=MY_HOTSPOT_PASSWORD

[ipv4]
dns-search=
method=shared

[ipv6]
addr-gen-mode=stable-privacy
dns-search=
method=auto

docker-compose.yaml

version: "2.1"
services:
  dnsmasq:
    build: ./dnsmasq
    container_name: dnsmasq
    restart: always
    network_mode: host
    entrypoint:
      - sh
      - ./start.sh
    cap_add:
      - NET_ADMIN

Dockerfile

FROM balenalib/debian:stretch

RUN apt-get update
RUN apt-get install -y dnsmasq
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

COPY dnsmasq.conf /etc/dnsmasq.conf
COPY start.sh start.sh

start.sh

echo "Starting dnsmasq - $(date)"

dnsmasq \
  --address=/#/10.42.0.1 \
  --dhcp-range=10.42.0.1,10.42.0.254 \
  --dhcp-option=option:router,10.42.0.1 \
  --interface=wlan0 \
  --keep-in-foreground \
  --bind-interfaces \
  --except-interface=lo \
  --conf-file \
  --no-hosts \
  --log-facility=-

tail -f /dev/null

Some issues

When the entrypoint initialises, it says:

dnsmasq: unknown interface wlan0

Running nmcli on balena root suggests this does indeed exist. Maybe an execution order issue…

# nmcli
wlan0: connected to balena-hotspot
        "Ralink n"
        wifi (rt2800usb), 9C:EF:D5:FB:C1:D4, hw, mtu 1500
        inet4 10.42.0.1/24
        route4 10.42.0.0/24
        inet6 fe80::4da7:5750:80d8:2c99/64
        route6 fe80::/64
        route6 ff00::/8
...

If I run the dnsmasq command again it will says:

dnsmasq: failed to create listening socket for 10.42.0.1: Address already in use

In any case, its not quite working. Visiting a domain, eg) thing.local does not route to localhost:80 on the balena device.

I have tried changing the [ipv4] hotspot settings to method=manual but if I do that, the hotspot does not seem to be initialised (I can’t see it on my client device).

Any ideas what the issue may be?

EDIT:

I have been able to make it work by delaying the dnsmasq command a few seconds to avoid unknown interface wlan0 and also changing the IPV4 config:

[ipv4]
address1=10.42.0.1/24
dns=127.0.0.1;1.1.1.1;
dns-search=
method=manual

Thanks for your help!

Hi,

Settings the method to manual and adding address1=10.42.0.1/24 should be enough indeed.
Glad you could make it work!

1 Like

Hi, me again :slight_smile: ,

For the security of the access point, I’m using the following:

[wifi-security]
group=
key-mgmt=wpa-psk
pairwise=
proto=rsn
psk=MY_HOTSPOT_PASSWORD

However, when I connect to it via my iPhone or Mac, it says that the security is weak (probably WPA1/WPA2 (TKIP)), but the most secure at the moment (afaik) is WPA2 (AES). I’ve read that proto=rsn means it can use WPA2, but doesn’t force it.

Is there a way to force WPA2 (AES)? I’ve read the NetworkManager documentation about this, and it’s probably the pairwise or group setting, because that can be set to ccmp, which is AES(?). But I want it to remain stable and working on most devices, so that’s why I wanted to ask this first.

Thanks in advance!


I’ve added the following:

group=ccmp;
pairwise=ccmp;

This removes the warning on my Apple devices that it’s insecure. However, I’m still curious if this is the best / most stable solution.