Chromcast firewall rules

Does anyone have an idea how to handle connection tracking for Chromecast for the ip firewall?

Setup

  • Mikrotik Chateau series, latest ROS
  • Two bridges configured. Full access between all bridge ports
  • One bridge is for “trusted” hosts: work machines etc, other bridge is for “untrusted” hosts: appliances and Chromecast
  • mDNS setup and working correctly. Forwarding allowed between bridge interfaces
  • IP firewalls configured to allow “trusted” to connect to “untrusted” on destination port 8008-8009.
  • Both bridges have internet access

What’s working

  • Devices on “trusted” can see the Chromecast on “untrusted”
  • Devices can start streaming to the Chromecast in a mode where it is streaming from YouTube etc. I.e. The control message is sent via 8008/8009 and the Chromecast streams from the internet.

What’s not working

  • Local lan streaming (i.e. streaming from VLC)

In this mode, The streaming host opens a connection to the Chromecast control port on 8008/8009. The Chromecast then creates a return connection from the Chromecast to the streaming host on port 8010. If I allow this port back on the firewall rules, the streaming works correctly.

Firewall rules
I would like to have the router firewall rules to only allow traffic back to the streaming host if a request is made to the control port.

i.e.

  1. Streaming host on “trusted” creates a connection to the Chromecast on port 8008/8009
  2. Router allows Chromecast on “untrusted” traffic back only to the streaming host on port 8010

I had a few ideas, some of which might a combination, but not sure on the direction.

  • A connection tracking helper. This is similar to TFTP in nature. I’m not sure if it’s even possible to load a custom connection tracking helper.
  • Using the mangle filters to mark the connection somehow.
  • Putting the address in an address list using a firewall rule and then referencing that later. Only thing with this approach is that I’d like the connection close to remove the address from the address list, which doesn’t seem possible. I know a timeout is possible, but then the timeout just depends on the length of the media being watched.
  • Transforming the packet on via masquerade if the transformed packet matches the connection, allow the original (not sure if this is possible).

Ideally I’d like to only match the SYN packet, since after the connection is established, the normal connection tracking mechanism can handle the packets.

Hi @kanwhoa and welcome to the Mikrotik user forum!

Thanks for the detailed post but a few things are a bit unclear though:

You mention “full access between all bridge ports”, but also describe firewall filtering between trusted and untrusted bridges. That sounds contradictory unless you mean full access within each bridge only, and routing/firewall between them.

It would really help if you could clarify the setup with:

  • A simple network topology diagram or description (ascii, hand-drawn on a napkin etc)
  • A sanitized full /export (remove sensitive stuff like passwords, crypto keys, serial number public IPs etc.). Check this how-to guide.

That way it’s easier to understand exactly how traffic is flowing and where connection tracking might break down.

Hi, sure no problem.

Firstly, yes, that’s what I meant. There is no filtering between devices on the same bridge. Between bridges the IP firewalls are in place.

Here is a picture:
routernet.png
Here is the relevant sections from the config file. I have removed things that are not entirely relevant.

#
### GENERAL ###
### WIFI ###
#
# Teardown
/interface wifi
remove [ find where master=no ]
reset [ find where master=yes ]
/interface wifi channel
remove [ find ]
/interface wifi configuration
remove [ find ]
/interface wifi security
remove [ find ]
#
# Wifi security profiles
/interface wifi security
add name=wsecprof0 authentication-types=wpa2-psk,wpa3-psk wps=disable ft=yes ft-over-ds=yes
add name=wsecprof1 authentication-types=wpa3-psk          wps=disable ft=yes ft-over-ds=yes
add name=wsecprof2 authentication-types=wpa2-psk,wpa3-psk wps=disable ft=yes ft-over-ds=yes
add name=wsecprof3 authentication-types=wpa2-psk,wpa3-psk wps=disable ft=yes ft-over-ds=yes
#
# WiFi configuration profiles
/interface wifi configuration
add name=wconfprof0 security=wsecprof0 max-clients=50 mode=ap hide-ssid=no  ssid=default
add name=wconfprof1 security=wsecprof1 max-clients=50 mode=ap hide-ssid=yes ssid=trusted
add name=wconfprof2 security=wsecprof2 max-clients=50 mode=ap hide-ssid=no  ssid=guest
add name=wconfprof3 security=wsecprof3 max-clients=50 mode=ap hide-ssid=no  ssid=untrusted
#
# Wifi setup
/interface wifi
remove [ find master=no ]
reset [ find master ]
set [ find default-name=wifi1 ] channel=ch5 configuration=wconfprof0 disabled=no
set [ find default-name=wifi2 ] channel=ch2 configuration=wconfprof0 disabled=no
add configuration=wconfprof1 disabled=no mac-address=XX:XX:XX:XX:XX:XX master-interface=wifi1 name=wifi3
add configuration=wconfprof1 disabled=no mac-address=XX:XX:XX:XX:XX:XX master-interface=wifi2 name=wifi4
add configuration=wconfprof2 disabled=no mac-address=XX:XX:XX:XX:XX:XX master-interface=wifi1 name=wifi5 datapath.client-isolation=yes
add configuration=wconfprof2 disabled=no mac-address=XX:XX:XX:XX:XX:XX master-interface=wifi2 name=wifi6 datapath.client-isolation=yes
add configuration=wconfprof3 disabled=no mac-address=XX:XX:XX:XX:XX:XX master-interface=wifi1 name=wifi7 datapath.client-isolation=yes
add configuration=wconfprof3 disabled=no mac-address=XX:XX:XX:XX:XX:XX master-interface=wifi2 name=wifi8 datapath.client-isolation=yes
#
### BRIDGES ###
#
# Bridge networks
/interface bridge
remove [ find ]
add admin-mac=XX:XX:XX:XX:XX:XX auto-mac=no name=bridge0 comment=default
add admin-mac=XX:XX:XX:XX:XX:XX auto-mac=no name=bridge1 comment=trusted
add admin-mac=XX:XX:XX:XX:XX:XX auto-mac=no name=bridge2 comment=guest
add admin-mac=XX:XX:XX:XX:XX:XX auto-mac=no name=bridge3 comment=untrusted
#
# Bridge network members
/interface bridge port
add bridge=bridge0 interface=ether2
add bridge=bridge0 interface=wifi1
add bridge=bridge0 interface=wifi2
add bridge=bridge1 interface=ether3
add bridge=bridge1 interface=wifi3
add bridge=bridge1 interface=wifi4
add bridge=bridge2 interface=ether4
add bridge=bridge2 interface=wifi5
add bridge=bridge2 interface=wifi6
add bridge=bridge3 interface=ether5
add bridge=bridge3 interface=wifi7
add bridge=bridge3 interface=wifi8
#
### INTERFACE LISTS ###
#
# Create a list of interfaces
/interface list
remove [ find builtin=no ]
add comment=wan     name=WAN
add comment=default name=DEFAULT
add comment=trusted name=TRUSTED
add comment=guest   name=GUEST
add comment=untrusted name=UNTRUSTED
add comment=devices name=LOCAL   include=DEFAULT,TRUSTED,GUEST,UNTRUSTED
# Add members to the interface lists
/interface list member
add list=WAN     interface=ether1
add list=DEFAULT interface=bridge0
add list=TRUSTED interface=bridge1
add list=GUEST   interface=bridge2
add list=UNTRUSTED interface=bridge3
#
### DHCP ###
#
# DHCP server address pools
/ip pool
add name=dhcpspool0 ranges=192.168.0.50-192.168.0.254
add name=dhcpspool1 ranges=192.168.1.50-192.168.1.254
add name=dhcpspool2 ranges=192.168.2.50-192.168.2.254
add name=dhcpspool3 ranges=192.168.3.50-192.168.3.254
# DHCP server assignment
/ip dhcp-client
add interface=ether1 use-peer-ntp=no comment="WAN DHCP client"
/ip dhcp-server
add address-pool=dhcpspool0 disabled=no interface=bridge0 name=dhcps0
add address-pool=dhcpspool1 disabled=no interface=bridge1 name=dhcps1
add address-pool=dhcpspool2 disabled=no interface=bridge2 name=dhcps2
add address-pool=dhcpspool3 disabled=no interface=bridge3 name=dhcps3
# DHCP server network config
/ip dhcp-server network
remove [ find ]
add address=192.168.0.0/24 dns-server=192.168.0.1 gateway=192.168.0.1 netmask=24 ntp-server=192.168.0.1 dhcp-option-set=dhcpos0 domain=local
add address=192.168.1.0/24 dns-server=192.168.1.1 gateway=192.168.1.1 netmask=24 ntp-server=192.168.1.1 dhcp-option-set=dhcpos1 domain=local
add address=192.168.2.0/24 dns-server=192.168.2.1 gateway=192.168.2.1 netmask=24 ntp-server=192.168.2.1 dhcp-option-set=dhcpos2 domain=local
add address=192.168.3.0/24 dns-server=192.168.3.1 gateway=192.168.3.1 netmask=24 ntp-server=192.168.3.1 dhcp-option-set=dhcpos3 domain=local
# DHCP client for WAN
/ip dhcp-client
remove [ find ]
add interface=ether1 use-peer-dns=yes use-peer-ntp=no comment="WAN DHCP client"
#
### DNS ###
# 
# Allow DNS
/ip dns
set allow-remote-requests=yes cache-size=20480
set mdns-repeat-ifaces=[ :local bs [:toarray ""]; :foreach b in=[/interface/bridge/print as-value proplist=name] do={:set bs ($bs,($b->"name"))}; $bs ]
# 
### ROUTER ADDRESSES ###
#
/ip address
add address=192.168.0.1/24 interface=bridge0 network=192.168.0.0
add address=192.168.1.1/24 interface=bridge1 network=192.168.1.0
add address=192.168.2.1/24 interface=bridge2 network=192.168.2.0
add address=192.168.3.1/24 interface=bridge3 network=192.168.3.0
#
### FILTERING/FIREWALL RULES ###
#
# Bridge - controls low level filtering on the bridges. input represents input to the bridge, forward represents a forward between bridge interfaces,
# output is output from a bridge interface.
/interface bridge filter
remove [ find dynamic=no ]
#
# IP
/
:global getBroadcast do={
  :local addr $1
  :local remmaskbits $2

  :local mask ""
  :for i from=1 to=4 do={
    :local octetbits 0
    :if ($remmaskbits >= 8) do={
      :set octetbits 8
      :set remmaskbits ($remmaskbits - 8)
    } else={
      :set octetbits $remmaskbits
      :set remmaskbits 0
    }
    :if ([:len $mask] > 0) do={ :set mask "$mask." }
    :local octetbyte ((0xff >> $octetbits) & 0xff)
    :set mask "$mask$octetbyte"
  }
  :return ($addr|[:toip $mask])
}
#
/ip firewall filter
remove [ find where dynamic=no ]
# High priority rules
add action=fasttrack-connection chain=forward connection-state=established,related hw-offload=yes         comment="accept established via fasttrack"
add action=accept  chain=forward connection-state=established,related,untracked                           comment="accept established and related"
add action=drop    chain=forward connection-state=invalid                                                 comment="drop invalid"
add action=accept  chain=input   connection-state=established,related,untracked comment="accept established,related,untracked"
add action=drop    chain=input   connection-state=invalid                       comment="drop invalid"
# Forward
add action=accept  chain=forward in-interface-list=LOCAL out-interface-list=WAN connection-state=new      comment="accept new connections from local networks to external"
add action=accept  chain=forward in-interface-list=LOCAL out-interface-list=UNTRUSTED protocol=tcp dst-port=8008-8009 comment="accept tcp/Chromecast from LOCAL to UNTRUSTED"
#add action=accept  chain=forward in-interface-list=UNTRUSTED out-interface-list=LOCAL protocol=tcp dst-port=8010 comment="accept tcp/Chromecast return from UNTRUSTED to LOCAL. THIS LINE IS THE PROBLEM TO BE SOLVED"
add action=drop    chain=forward in-interface-list=LOCAL out-interface-list=LOCAL                         comment="drop all between local networks"
add action=drop    chain=forward connection-nat-state=!dstnat connection-state=new in-interface-list=WAN  comment="drop all from WAN not DSTNATed"
add action=log     chain=forward                                                                          comment="drop all"
add action=drop    chain=forward                                                                          comment="drop all"
# Input
add action=accept  chain=input   in-interface=lo                                comment="accept loopback"
:foreach addr in=[ /ip address find dynamic=no ] do={
  :local thisif [/ip/address/get $addr interface]
  :local thisnw [/ip/address/get $addr network as-string-value]
  :local thisgw [/ip/address/get $addr address]
  :local thisbm [:pick $thisgw ([:find $thisgw "/"]+1) [:len $thisgw]]
  :local thisgw [:pick $thisgw 0 [:find $thisgw "/"]]
  :local thisbc [$getBroadcast $thisnw $thisbm]
  add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=icmp                comment="accept ICMP to $thisgw via $thisif from $thisnw/$thisbm"
  add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=udp  dst-port=53    comment="accept udp/DNS to $thisgw via $thisif from $thisnw/$thisbm"
  add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=tcp  dst-port=53    comment="accept tcp/DNS to $thisgw via $thisif from $thisnw/$thisbm"
  add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=udp  dst-port=123   comment="accept udp/NTP to $thisgw via $thisif from $thisnw/$thisbm"
  add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=udp  dst-port=67    comment="accept udp/DHCP to $thisgw via $thisif from $thisnw/$thisbm"
  add action=accept  chain=input   in-interface=$thisif dst-address=255.255.255.255 protocol=udp  dst-port=67    comment="accept udp/DHCP to $thisgw via $thisif from $thisnw/$thisbm"
  add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=udp  dst-port=5353  comment="accept udp/mDNS to $thisgw via $thisif from $thisnw/$thisbm"
  :if ([:len [/interface/list/member/find where (list=TRUSTED && interface=$thisif)]] = 1) do={
    add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=tcp  dst-port=22   comment="accept tcp/SSH to $thisgw via $thisif from $thisnw/$thisbm"
    add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=tcp  dst-port=80   comment="accept tcp/HTTP to $thisgw via $thisif from $thisnw/$thisbm"
    add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=tcp  dst-port=443  comment="accept tcp/HTTPS to $thisgw via $thisif from $thisnw/$thisbm"
    add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=udp  dst-port=137  comment="accept udp/NetBIOS to $thisgw via $thisif from $thisnw/$thisbm"
    add action=accept  chain=input   in-interface=$thisif dst-address=$thisbc         protocol=udp  dst-port=137  comment="accept udp/NetBIOS to $thisgw via $thisif from $thisnw/$thisbm"
    add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=tcp  dst-port=445  comment="accept tcp/SMB to $thisgw via $thisif from $thisnw/$thisbm"
    add action=accept  chain=input   in-interface=$thisif dst-address=$thisgw         protocol=tcp  dst-port=8291 comment="accept tcp/WinBox to $thisgw via $thisif from $thisnw/$thisbm"
  }
}
add action=accept  chain=input   in-interface-list=LOCAL dst-address=224.0.0.251 protocol=udp dst-port=5353 comment="accept udp/mDNS to multicast via LOCAL"
add action=accept  chain=input   in-interface-list=WAN dst-address=255.255.255.255 protocol=udp dst-port=67 comment="accept udp/DHCP to broadcast via WAN"
add action=log  chain=input
add action=drop chain=input
# NAT
/ip firewall nat
remove [ find where dynamic=no ]
add action=masquerade chain=srcnat ipsec-policy=out,none out-interface-list=WAN comment="masquerade"
#

You will have to somehow allow the reverse connection obviously.

You can make the thing a bit more secure by using something a bit like port knocking:

  • add a dst-address-list criterion to your rule for port 8010
  • populate this address list based on packets flowing to your ChromeCast control ports 8008-8009 with action=add-src-to-address-list with some appropriate timeout (w.g. 30s)
  • you probably want this add-to-address list thingy to renew the timeout for each control packet (not just new connections) so you will probably want to place it before your established/related rule; also if you do this, you will probably want to selectively disable fasttrack for the control connections

Just a thought, and maybe I’m missing something, but can’t you give full access just between the Chromecast and Streaming Host 1?

Thanks, this is along the lines of what I was thinking. Will have a play to see if it works.

Btw: @Larsa The reason for not giving full access is firstly, that’s a DHCP client, and while I could statically define a client, I have used this as an example. There are many clients on the trusted network that can initiate streaming, but I’d rather not allow full access from untrusted, even if it is limited to one port. The key reason I have these devices is on untrusted because I don’t trust them. Things like set top boxes and appliances that are put into the field and are not patched/updated regularly.