pfSense Rule Switch

Cross post from /r/homeassistant

For those of you that run pfSense, I’ve made an attempt at writing my first custom component that turns your pfSense firewall rules into a switch for toggling on and off. My goal was to provide a quick and easy way to shutdown down the kids internet on-demand from Home Assistant but I could potentially see a few other use cases.

This is my first component and I can’t remember the last time I was in Python so go easy on me, but I am looking for any feedback.

3 Likes

Looking forward to trying this when I get home. Surprisingly it doesn’t seem like many home assistant users run pfsense

I run pfsense and have seen some of the custom options available using the faux api, however I am wary to utilize them as I don’t want to comprise the security of my pfsense.

Really nice. Works great. One question regarding the permissions for the fauxapi credentials. I tried limiting to rule_* thinking that this would be all that was needed, but that didn’t work. Do you have any idea which minimum permissions are needed?

Thanks again!

Has anyone actually got this working recently?

I can query the fauxapi from my MacBook fine but HomeAssistant will not play ball.

It pulls the rule through as a switch OK, despite multiple entires of

Problem retrieving rule set from pfSense host: X.X.X.X. Likely due to API key or secret.

If you try and do anything with said switch you get this error:

'tracker'
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 130, in handle_call_service
    connection.context(msg),
  File "/usr/src/homeassistant/homeassistant/core.py", line 1260, in async_call
    task.result()
  File "/usr/src/homeassistant/homeassistant/core.py", line 1295, in _execute_service
    await handler.func(service_call)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 209, in handle_service
    self._platforms.values(), func, call, required_features
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 454, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 595, in async_request_call
    await coro
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 485, in _handle_entity_call
    await result
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 621, in async_turn_on
    await self.hass.async_add_executor_job(ft.partial(self.turn_on, **kwargs))
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/pfsense_rule/switch.py", line 126, in turn_on
    self.set_rule_state(True)
  File "/config/custom_components/pfsense_rule/switch.py", line 171, in set_rule_state
    if (rule['tracker'] == self._tracker_id):
KeyError: 'tracker'

Tried with different versions of fauxapi, different versions of pfSense and also tried a mate who also has home assistant and pfSense but he gets the same errors. Tried adding PfsenseFauxapi to the config directory but still no dice.

My Python skills aren’t very good so hoping someone can point me in the right direction.

double check the permissions on the key created in pfsense. If I recall correctly, the key gets added with virtually no permissions.

Also, I assume that you’re not trying to use one of the PFFAExample keys?

Yeah using my own key which works fine if I put it in the string and query Pfsense from my MacBook‘s terminal.
I’ve tried various keys and secrets and various permissions on the key. Tried just rules on its own, tried * and also tried adding everything manually like one of the examples.
It’s almost like HomeAssistant isn’t loading the right Python libraries or something.

I have the same problem, did you solve it @crag-r?

It seems the code tries to retrieve the ‘tracker’ field from the rule dictionary but no such value has been defined (when iterating over the list of rules received from PFSense). I believe the API has changed or maybe theirs something wrong with the data. I don’t know what the tracker-field does…

I found the problem, the code expects all rules to have the tracker property set (is used as a rule-identifier) but not all rules have that property set. For me, it was the “OpenVPN wizard” that created the problem.

To fix it, search for (in switch.py):

for rule in filters['rule']:

And replace the code after with:

for rule in filters['rule']:
            tracker = rule.get('tracker')
            if tracker == None:
                _LOGGER.error("Skipping rule (no tracker_id): " + rule['descr'])
            else:
                if rule_prefix:
                    if (rule['descr'].startswith(rule_prefix)):
                        _LOGGER.debug("Found rule %s", rule['descr'])
                        new_rule = pfSense('pfsense_'+rule['descr'], rule['descr'], tracker, host, api_key, access_token)
                        rules.append(new_rule)
                else:
                    _LOGGER.debug("Found rule %s", rule['descr'])
                    new_rule = pfSense('pfsense_'+rule['descr'], rule['descr'], tracker, host, api_key, access_token)
                    rules.append(new_rule)
            i=i+1

This code simply skips these rules and will also log an error if a rule don’t have the tracker attribute.

Perhaps @dgshue could update the code in the repo?

EDIT:
You also need to replace all:

rule['tracker']

with

rule.get('tracker')
1 Like

I’ll give this a go tomorrow!

Dude -

Thank you so much for this. These components and switches are huge for me and I’ve been migrating to a new pfSense setup this week and yesterday it worked but today it wouldn’t. I couldn’t figure out why. I’d run the OpenVPN wizard late last night. I made the changes you noted and it seems to be working again.

@dgshue As always - thanks for your efforts on these!

On another note, does anyone know if this will work on OPNsense as well?

So looking into it more deeply, it doesn’t look like it would run in OPNsense without some pretty major changes to leverage their API. Since this alone is pretty much keeping me on pfSense vs. OPNsense the next question is - will it work on pfSense 2.5? This is so key for me it’s also the only thing that has kept me from upgrading.

In pfsense once you edit a firewall rule you usually have the clear the system states (Diagnostics->States->Reset States) for it to take effect immediately.

Once you toggle the rule to block access for your kids, does it take effect right away?

Sorry for the late reply but thanks Fredrik your fix is working perfectly

How do you target a specific rule in pfsense with a switch? The rules are not named.

Rules can, and for this integration should, have a description.

Edit: I see now that the github description mentions indeed “rule name”. I guess it refers to rule description.

“ * rule_filter ( Optional ): Used to create switches only on certain rules. Rule name must start with filter to match (ie. HomeAssisant-BlockTraffic1)”

@JeeCee Thx that make it clearer. I am still curious though and would like a response who someone that knows.

For all existing connection (with active states on the firewall), how are those states terminated as to prevent let’s say kids devices from connecting? I understand eventually the states will expire but it may be a while before that happens.

I’ve been struggling with implementing a rule switch using fauxapi, and I can’t for the life of me get it to work.

I just became aware of a new (unofficial) API implementation that is well documented and actively maintained (just out of beta): https://github.com/jaredhendrickson13/pfsense-api

Looks very promising. Has anyone else had a look yet?