In message <https://marc.info/?l=openbsd-misc&m=162550822403762&w=1>
(date 2021-07-05) I wrote:
> Has anyone used an OpenBSD firewall (pf) to protect an Ooma Telo VOIP
> phone system from internet attacks?  If so, how did you do it?  More
> generally, how do people protect VOIP phone systems (regardless of brand)
> from internet attacks?

There were various helpful replies in that thread, but I wasn't able
to complete my firewall upgrade at that time.  I've recently returned
to this project, and after a bit of fiddling around I'm please to report
a successful outcome.  For the benefit of anyone else trying to protect
a similar VOIP system, here's a summary of what I've done.

My network topology is this:

                     +-------------------+
  (internet) --------| ISP-provided ADSL |
                     | modem/router      |
                     +-------------------+
                        |
                        |
               +----------+        +-----------+
               | OpenBSD  |--------| Omma Telo |.......... analog
               | firewall |        | VOIP box  |           telephones
               +----------+        +-----------+
                 |      |
  +--------+     |      |
  | Wifi   |-----+      +------ wired client
  | access |                    (or network switch for
  | point  |                     multiple wired clients)
  +--------+

One of my overall goals in trying to design this system is to not trust
either the ISP-provided ADSL modem/router (the "ISP box") or the Ooma Telo
VOIP box any more than necessary -- they're both probably running out-of-date
embedded Linux systems, and could well be hacked at some point.  Notably,
I'd rather not trust the ISP box's DNS service, and I'd like to prevent
either the ISP box or the VOIP box from being able to probe or attack
my other local computers.

Therefore, the OpenBSD firewall (a PC Engines apu4d4) has separate physical
network interfaces to talk to the ISP box, the VOIP box, a wifi access point
(for other local computers that want internet access) and a wired client
(another local computer or computers that wants internet access).

The firewall gets a dynamic address on its "outside" interface via DHCP
from the ISP box.  The firewall assigns distinct /26 subnets of the
192.168.*.* address space to clients connecting via its three internal
interfaces ("wired", "wifi", and "voip").  The firewall runs dhcpd to
assign dynamic IP addresses within those subnets, and to advertise itself
as a DNS server to all the local clients and the VOIP box.  The firewall
also runs unbound to provide caching DNS service to the VOIP box and the
local computers, and to do secure DNS-over-TCP to an upstream DNSSEC
provider.  (That way I don't need to trust the ISP box's DNS service.)

The Ooma VOIP documentation says it uses the following ports:
  outgoing UDP/TCP 53, 1194, 1294
  outgoing TCP 80, 110, 443
  outgoing UDP 67, 123, 3480
  incoming UDP 10000 to 30000
but doesn't have much to say about NAT-vs-dynamically-chosen-ports issues.
I was pleasantly surprised to find that it works fine through the firewall's
NAT.

I give the relevant parts of the firewall's /etc/pf.conf below.  This
doesn't give perfect protection (e.g., the ISP box could still insert
nastygram packets into non-encrypted connections), but it does offer fairly
good protection, hopefully enough to protect me from typical "mass attacks".

Unless the ISP box meddles in the traffic quite heavily, the OpenBSD
firewall's NAT and "modulate state" should ensure that all traffic to/from
the outside world has high-entropy initial TCP sequence numbers and ports
(for improved resistance to TCP-sequence-guessing attacks).


--- begin firewall /etc/pf.conf ---
# uncomment one of the following two lines
# to configure logging for the main wired/wifi subnets
MAYBE_LOG_MAIN  = ""            # uncomment for no logging
#MAYBE_LOG_MAIN = "log"         # uncomment for logging

# uncomment one of the following two lines
# to configure logging for the voip subnet
MAYBE_LOG_VOIP  = ""            # uncomment for no logging
#MAYBE_LOG_VOIP = "log"         # uncomment for logging

# uncomment one of the following two lines
# to configure logging for the default block rule
MAYBE_LOG_BLOCK = ""            # uncomment for no logging
#MAYBE_LOG_BLOCK        = "log"         # uncomment for logging

################################################################################

if_outside      = "em0"
if_wired        = "em1"
if_wifi         = "em2"
if_voip         = "em3"
if_internal     = "{"             $if_wired $if_wifi $if_voip "}"
if_all          = "{" $if_outside $if_wired $if_wifi $if_voip "}"

# last byte of ip address:
#   /25 /26 /27 /28 /29 /30 /31 /32
#   128  64  32  16   8   4   2   1
# so a /26 has a netmask of 255.255.255.192 = 0xffffffc0
subnet_wired            = "192.168.144.0/26"    # .0 to .63
subnet_wifi             = "192.168.144.64/26"   # .64 to .127
subnet_wired_or_wifi    = "192.168.144.0/25"    # .0 to .127
subnet_voip             = "192.168.144.128/26"  # .128 to .191
subnet_internal         = "192.168.144.0/24"    # .0 to .255

################################################################################

set skip on lo
block return $MAYBE_LOG_BLOCK

# allow incoming ipv4 connections from any local machine
# to the firewall itself (localhost or any of the firewall's internal addresses)
# ... this is used for for dns lookups
#     and for ssh from local machines to the firewall
pass in  $MAYBE_LOG_MAIN quick on $if_wired inet from $subnet_wired     \
         to { localhost $if_internal }
pass in  $MAYBE_LOG_MAIN quick on $if_wifi  inet from $subnet_wifi      \
         to { localhost $if_internal }
pass in  $MAYBE_LOG_VOIP quick on $if_voip  inet from $subnet_voip      \
         to { localhost $if_internal }

# allow outgoing ipv4 connections from the firewall itself to any address
# ... traffic from the firewall itself may appear to come from localhost
#     or from an interface address
pass out $MAYBE_LOG_MAIN quick on $if_internal inet                     \
         from { localhost  $if_internal  }
pass out $MAYBE_LOG_MAIN quick on $if_outside  inet                     \
         from { localhost ($if_outside ) }

##
## firewall rules to pass traffic from/to the wired/wifi subnets omitted here
##

# allow ipv4 connections from the voip subnet
# (but only the protocols/ports documented for our Ooma voip box)
# to/from the outside world and NAT these
pass in  $MAYBE_LOG_VOIP quick on $if_voip    inet                      \
         proto udp                                                      \
         from $subnet_voip to !$subnet_internal                         \
         port { 53 1194 1294 67 123 3480 10000:20000 }
pass in  $MAYBE_LOG_VOIP quick on $if_voip    inet                      \
         proto tcp                                                      \
         from $subnet_voip to !$subnet_internal                         \
         port { 53 1194 1294 80 110 443 }
pass out $MAYBE_LOG_VOIP quick on $if_voip    inet                      \
         proto udp                                                      \
         to $subnet_voip                                                \
         port 49000:50000
pass out $MAYBE_LOG_VOIP quick on $if_outside inet                      \
         from $subnet_voip to !$subnet_internal                         \
         nat-to ($if_outside) modulate state
--- end firewall /etc/pf.conf ---


-- 
-- "Jonathan Thornburg [remove -color to reply]" <jthorn4...@gmail-pink.com>
   on the west coast of Canada, eh?
   "Why would we install sewers in London?  Everyone keeps getting cholera
    again and again so there's obviously no reason to install sewers.  We
    just need to get used to this as the new normal."
                             -- 2022-07-25 tweet by "Neoliberal John Snow"

Reply via email to