Skip to content

Instantly share code, notes, and snippets.

@FlorianHeigl
Last active June 7, 2025 16:07
Show Gist options
  • Save FlorianHeigl/d127ec26266f3d4fd95193f432fbc1ae to your computer and use it in GitHub Desktop.
Save FlorianHeigl/d127ec26266f3d4fd95193f432fbc1ae to your computer and use it in GitHub Desktop.
bavarian wifi
# dont execute hook if dhcp req failed
if [ $1 != 0 ]; then
return 0
fi
# get bayernwlan interface name
interface=$( ifconfig | grep -B1 "BAYERNWLAN" | grep -v description: | awk -F: '{print $1}' )
# some validation might be [a-z]+[0-9]
# get bayernwlan interface address
ip=$( ifconfig vtnet2 | grep -w inet | awk '{print $2}' )
# some validation might be a classic ip address regex
# make sure the resulting vars are passed on
export ip interface
# trigger api request
/usr/local/bin/by.py
#!/usr/bin/python3
# Logs into BayernWLAN automatically
# Created on Alpine Linux - I'll make a prod grade version for use on pfSense clusters.
# Likely this is also a nice workaround for OSX clients where the login button is broken all the time.
# Source: https://github.com/asdil12/wifi_autologin/blob/master/wifi_autologin/networks/bayernwlan.py
# Source: https://gist.githubusercontent.com/alexhorn/f1a7f32430e79f835b7dbc180b9ee727/raw/584ba5b79ffc20b9674cabcaded9c9f33c733193/login.py
# Extended to bind to the correct nic ip address when POST'ing to the captive portal
import os
import requests
from requests_toolbelt.adapters import source
ip = os.environ.get('ip')
interface = os.environ.get('interface')
print("will bind to ip %s of %s and try to login to bayernwlan" % (ip, interface))
# per $1 reingeben und als linkup script nehmen
source = source.SourceAddressAdapter(ip)
with requests.Session() as session:
session.mount('https://', source)
sid = session.get('https://hotspot.vodafone.de/api/v4/session').json()['session']
r = session.post('https://hotspot.vodafone.de/api/v4/login?sessionID=%s&action=redirect&portal=bayern' % sid, data={
'loginProfile': '6',
'accessType': 'termsOnly',
'session': sid})
if r.ok:
print("got positive confirmation from portal")
else:
print(r.text)
#!/usr/bin/env bash
# do we need to create a hardlink to post-renew also?
if [[ ${interface} = "wlan0" ]]; then
/usr/local/bin/by.py ${ip}
fi
/etc/udhcpc # ./post-renew
will bind to ip 100.xx.xx.xx of wlan0 and try to login to bayernwlan
got positive confirmation from portal
@FlorianHeigl
Copy link
Author

FlorianHeigl commented Nov 28, 2024

for pfSense I recommend going with the dhcp exit hook as shown here.

reason:

  • there's nothing in pfSense shell land that provides useful access to the information we need.
  • doing it this way has no need for interaction with the whole firewall stack
  • error handling is wrapped in dhclient and it will tell us if it didn't have luck getting a lease
  • we can even report back to dhclient if we managed to unlock access
  • we can rely on the "overwrite dns with dhcp one" which in reality just adds this dns as primary, so we're not exposed to a large amount of problems on that end. we can fall back to google dns etc. for the firewalls local resolution. client resolution can be kept separate.

it'll be a very easy spot if you want to replace the python stack with something else, i.e. these

bash
freifunk-openwrt
travelmate

@FlorianHeigl
Copy link
Author

FlorianHeigl commented Jun 7, 2025

OSX

tested on OSX now, you can use this whenever the "Verbinden" ('connect') button on the captive portal isn't working.
(and that's an ever that's also a given, blocking a little canal of it's own...)

You'll just need your wifi device name, and if you know how to do the wifi connect from cli you could fully automate it.

sanitized history / commands

python setup crap

$  cd bayernwlan
$  python3 -m venv .venv
$  . .venv/bin/activate
$  pip3 install requests
$  pip3 install requests_toolbelt

connection

Ok, now connect to the WiFi via the GUI icon
Then you need the ip address they assigned to you:

$ ifconfig en0 < checked my assigned ip address

use that info and setup env for script, and call script


$  export ip=100.xxxxxxx
$  export interface=en0
$  ./by.py 

result / example

(.venv) [utopia.bayernwlan]% ./by.py               
will bind to ip 100.xxxxx of en0 and try to login to bayernwlan
got positive confirmation from portal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment