diff --git a/README.md b/README.md index 790dde3..da6677e 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Please add - **netmask** (**required**): Subnet mask of the network - **broadcast** (**required**): Broadcast address of the network - **interface** (_optional_): Which wlan card to use. Default: wlan0 +- **virtual_interface** (_optional_): If set (e.g. to wlan1) a new virtual interface will be used as access point, leaving the wlan0 interface working as station. +- **isolation** (_optional_): Enable or disable network isolation. 0 = disable, 1 = enable. Defaults to disabled. Note: If the DNS server is provided by a local router, it will not be accessible inside the AP network. In that case you should set the _client_dns_override_ option to an external DNS, e.g. 8.8.8.8. - **hide_ssid** (_optional_): Whether SSID is visible or hidden. 0 = visible, 1 = hidden. Defaults to visible - **dhcp** (_optional_): Enable or disable DHCP server. 0 = disable, 1 = enable. Defaults to disabled - **dhcp_start_addr** (_optional_): Start address for DHCP range. Required if DHCP enabled diff --git a/hassio-access-point/config.json b/hassio-access-point/config.json index b3702d5..41ea063 100644 --- a/hassio-access-point/config.json +++ b/hassio-access-point/config.json @@ -22,6 +22,8 @@ "netmask": "255.255.255.0", "broadcast": "192.168.99.255", "interface": "wlan0", + "virtual_interface": "", + "isolation": "0", "hide_ssid": "0", "dhcp": "0", "dhcp_start_addr": "192.168.99.10", @@ -42,6 +44,8 @@ "netmask": "str", "broadcast": "str", "interface": "str", + "isolation": "str", + "virtual_interface": "str", "hide_ssid": "int", "dhcp": "int", "dhcp_start_addr": "str", diff --git a/hassio-access-point/run.sh b/hassio-access-point/run.sh index 56e3433..8ae6844 100644 --- a/hassio-access-point/run.sh +++ b/hassio-access-point/run.sh @@ -6,9 +6,36 @@ term_handler(){ ifdown $INTERFACE ip link set $INTERFACE down ip addr flush dev $INTERFACE + if [ ${#VINTERFACE} -ne 0 ]; then + iw dev $INTERFACE del + fi + cleanup_iptables exit 0 } + +function cleanup_iptables() { + if [ ${#VINTERFACE} -ne 0 ]; then + iptables -t nat -D POSTROUTING -o $BASE_INTERFACE -j MASQUERADE + fi + iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE + iptables -t nat -D PREROUTING -i $INTERFACE -j ACCEPT + iptables -D INPUT -i $INTERFACE -j DROP + iptables -D INPUT -i $INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT + if [ ${#VINTERFACE} -ne 0 ]; then + wifi_net=$(ip addr show $BASE_INTERFACE | grep inet | awk '{print $2}') + if [ ${#wifi_net} -ne 0 ]; then + iptables -D FORWARD -i $INTERFACE -o $BASE_INTERFACE -d ${wifi_net} -j DROP + fi + fi + eth_net=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}') + if [ ${#eth_net} -ne 0 ]; then + iptables -D FORWARD -i $INTERFACE -o eth0 -d ${eth_net} -j DROP + fi + iptables -D INPUT -p udp -i $INTERFACE --dport 67 -j ACCEPT +} + + # Logging function to set verbosity of output to addon log logger(){ msg=$1 @@ -27,11 +54,12 @@ ADDRESS=$(jq --raw-output ".address" $CONFIG_PATH) NETMASK=$(jq --raw-output ".netmask" $CONFIG_PATH) BROADCAST=$(jq --raw-output ".broadcast" $CONFIG_PATH) INTERFACE=$(jq --raw-output ".interface" $CONFIG_PATH) +VINTERFACE=$(jq --raw-output ".virtual_interface" $CONFIG_PATH) +ISOLATION=$(jq --raw-output ".isolation" $CONFIG_PATH) HIDE_SSID=$(jq --raw-output ".hide_ssid" $CONFIG_PATH) DHCP=$(jq --raw-output ".dhcp" $CONFIG_PATH) DHCP_START_ADDR=$(jq --raw-output ".dhcp_start_addr" $CONFIG_PATH) DHCP_END_ADDR=$(jq --raw-output ".dhcp_end_addr" $CONFIG_PATH) -DNSMASQ_CONFIG_OVERRIDE=$(jq --raw-output '.dnsmasq_config_override | join(" ")' $CONFIG_PATH) ALLOW_MAC_ADDRESSES=$(jq --raw-output '.allow_mac_addresses | join(" ")' $CONFIG_PATH) DENY_MAC_ADDRESSES=$(jq --raw-output '.deny_mac_addresses | join(" ")' $CONFIG_PATH) DEBUG=$(jq --raw-output '.debug' $CONFIG_PATH) @@ -45,6 +73,12 @@ if [ ${#INTERFACE} -eq 0 ]; then INTERFACE="wlan0" fi +# If we use a virtual interface, INTERFACE points to the base interface +if [ ${#VINTERFACE} -ne 0 ]; then + BASE_INTERFACE="${INTERFACE}" + INTERFACE="${VINTERFACE}" +fi + # Set debug as 0 if not specified in config if [ ${#DEBUG} -eq 0 ]; then DEBUG=0 @@ -58,6 +92,15 @@ logger "Add to /etc/network/interfaces: iface $INTERFACE inet static" 1 # Create and add our interface to interfaces file echo "iface $INTERFACE inet static"$'\n' >> /etc/network/interfaces +# Create virtual interface if needed +if [ ${#VINTERFACE} -ne 0 ]; then + # If using virtual interface, channel must be the same as the base interface + CHANNEL=$(iw dev $BASE_INTERFACE info | grep channel | awk '{print $2}') + # Create virtual interface + logger "Run command: iw dev $BASE_INTERFACE interface add $INTERFACE type __ap" 1 + iw dev $BASE_INTERFACE interface add $INTERFACE type __ap +fi + logger "Run command: nmcli dev set $INTERFACE managed no" 1 nmcli dev set $INTERFACE managed no @@ -96,6 +139,11 @@ if [ $DHCP -ne 1 ]; then DHCP=0 fi +# Sanitise config value for isolation +if [ $ISOLATION -ne 1 ]; then + ISOLATION=0 +fi + if [[ -n $error ]]; then exit 1 fi @@ -197,7 +245,6 @@ if [ $DHCP -eq 1 ]; then echo "$dns_string"$'\n' >> /dnsmasq.conf logger "Add DNS: $dns_string" 0 fi - fi # Append override options to dnsmasq.conf @@ -209,25 +256,44 @@ if [ $DHCP -eq 1 ]; then logger "Add to dnsmasq.conf: $override" 0 done fi - - # Setup Client Internet Access - if [ $CLIENT_INTERNET_ACCESS -eq 1 ]; then - - ## Route traffic - iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE - iptables -P FORWARD ACCEPT - iptables -F FORWARD - fi + else logger "# DHCP not enabled. Skipping dnsmasq" 1 - # Setup Client Internet Access ## No DHCP == No DNS. Must be set manually on client. - ## Step 1: Routing - if [ $CLIENT_INTERNET_ACCESS -eq 1 ]; then - iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE - iptables -P FORWARD ACCEPT - iptables -F FORWARD +fi + +# Setup Client Internet Access +if [ $CLIENT_INTERNET_ACCESS -eq 1 ]; then + ## Route traffic + if [ ${#VINTERFACE} -ne 0 ]; then + iptables -t nat -A POSTROUTING -o $BASE_INTERFACE -j MASQUERADE + fi + iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE + iptables -P FORWARD ACCEPT + iptables -F FORWARD +fi + +# Setup network isolation +if [ $ISOLATION -eq 1 ]; then + # Do not pass packets coming from AP to docker + iptables -t nat -I PREROUTING -i $INTERFACE -j ACCEPT + # Accept only locally-initiated traffic + iptables -I INPUT -i $INTERFACE -j DROP + iptables -I INPUT -i $INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT + if [ ${#VINTERFACE} -ne 0 ]; then + # Prevent access to local wifi network from AP network + wifi_net=$(ip addr show $BASE_INTERFACE | grep inet | awk '{print $2}') + if [ ${#wifi_net} -ne 0 ]; then + iptables -I FORWARD -i $INTERFACE -o $BASE_INTERFACE -d ${wifi_net} -j DROP + fi + fi + # Prevent access to local eth network from AP network + eth_net=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}') + if [ ${#eth_net} -ne 0 ]; then + iptables -I FORWARD -i $INTERFACE -o eth0 -d ${eth_net} -j DROP fi + # Allow access to local DHCP server + iptables -I INPUT -p udp -i $INTERFACE --dport 67 -j ACCEPT fi # Start dnsmasq if DHCP is enabled in config @@ -236,6 +302,11 @@ if [ $DHCP -eq 1 ]; then killall -q dnsmasq; dnsmasq -C /dnsmasq.conf fi +if [ ${#VINTERFACE} -ne 0 ]; then + # Don't know why it is needed, but hostapd fails later on without this delay + sleep 10 +fi + logger "## Starting hostapd daemon" 1 # If debug level is greater than 1, start hostapd in debug mode if [ $DEBUG -gt 1 ]; then @@ -243,3 +314,5 @@ if [ $DEBUG -gt 1 ]; then else killall -q hostapd; hostapd /hostapd.conf & wait ${!} fi + +term_handler