Automating unifi port forwarding based upon presence detection

@jon102034050

Hey mate, still can’t thank you enough for these port forwarding switches. You wouldn’t happen to have a solution for triggering a reboot for a UDM device?

Sad reality is that these still need semi-regular reboots to keep them working at their best and I’d love to schedule this rather than cut the power in times of need.

Cheers :slight_smile:

Linton

glad you’ve found it useful! I personally use a UXG and haven’t really had too many issues with it that require a reboot (totally glossing over the fact that it’s missing a LOT of features that are in older hardware). As I find time, I’ll see if I can work something up though. As a workaround in the meantime, you could always just put it on a smart plug and cycle that via automation

Yeah I’ve got one of those dumb power timers I used to do a similar thing with for my old router. Ubiquiti equipment just feels a bit more delicate to be doing that all the time.

apparently in an SSH terminal you would simply type ‘reboot’ to execute what i’m doing if that helps.

Alrighty, did a little testing and here’s what I came up with:

I’m not entirely sure this will work perfectly for you though as I don’t personally own a UDM device. This works for rebooting an AP for me, so hopefully it’ll just be plug-n-play for a UDM as well.
Let me know how it goes!

Just noticed that with the latest unifi update, they must have changed the case of the x-csrf-token which busted this script :frowning:
This commit fixes that though: fix(unifi): csrf case sensitivity · JonGilmore/ha-personal@76c17a1 · GitHub

Can you explain what the “id from unifi ui” bit means? Pretty sure I have the rest of this sorted, but I’m not sure where to get that. I’m looking at my ui, under “port forwarding”, and I don’t see any id?

Thanks!

Sorry, thats not super clear :smiley: It’s the ID in the URL when you click on edit for the port forward

1 Like

That was my guess, but better safe than sorry. Thank you so much for the speedy reply!!

1 Like

Ok, I’ve been messing with this for hours now, and I just cannot get it to work. I’m still pretty new to Home Assistant, so I’m doing most things in the UI yet, with minimal yaml when absolutely necessary.

Because of that, I couldn’t use the secrets file because you cannot access !secret from within the automations UI - and when I attempted to manually add the automations to the yaml file, it blew up ALL my automations and I got nothing but error 500.

So, I tried typing out all the required fields in the data of the service call in automations, so my automation looks like this:

- id: '164228015xxxx'
  alias: Disable HA Port fwd
  description: ''
  trigger:
  - platform: state
    entity_id: input_boolean.everybody_home
    from: 'off'
    to: 'on'

  condition: []

  action:
  - service: shell_command.unifi_portfwd
    data:
      username: root
      password: password!
      baseurl: 10.x.x.x
      name: HA
      enabled: 'false'
      dst_port: '8123'
      ip: 10.x.x.x
      fwd_port: '8123'
      proto: tcp
      id: 654sfghbsfg6h54sfg6b5th
      unifi_site: default
  mode: single

The script I copied from your git:

#!/bin/sh
cookie=$(mktemp)
headers=$(mktemp)
curl_cmd="curl --silent --output /dev/null --cookie ${cookie} --cookie-jar ${cookie} --insecure"
portfwd() {

  # authenticate against unifi controller
  ${curl_cmd} -H 'Content-Type: application/json' -D ${headers} -d "{\"username\":\"$1\", \"password\":\"$2\"}" https://${3}/api/auth/login

  # grab the `x-csrf-token` and strip the newline (added when upgraded to controller 6.1.26)
  csrf="$(awk -v FS=': ' '/^x-csrf-token/{print $2}' "${headers}" | tr -d '\r')"

  # enable/disable firewall rule
  ${curl_cmd} -k -X PUT https://${3}/proxy/network/api/s/default/rest/portforward/${10} -H "Content-Type: application/json" -H "x-csrf-token: ${csrf}" -d @- <<-EOF
  {
    "name":"$4",
    "enabled":$5,
    "src":"any",
    "dst_port":"$6",
    "fwd":"$7",
    "fwd_port":"$8",
    "proto":"$9",
    "log":false,
    "_id":"$10",
    "site_id":"$11",
    "pfwd_interface":"wan",
    "destination_ip":"any"
  }
EOF
}
"$@"

The line I added to configuration.yaml:

shell_command:
    unifi_portfwd: /bin/bash /config/scripts/unifi.sh portfwd {{ username }} {{ password }} {{ baseurl }} {{ name }} {{ enabled }} {{ dst_port }} {{ ip }} {{ fwd_port }} {{ proto }} {{ id }} {{ unifi_site }}

After a bit more screwing around and trying to do some of this manually to see what’s broken, the result of both curl commands is “Invalid CSRF Token”.

I’m running UniFi OS UDM Pro 1.11.0 and Network 6.5.54 (Build: atag_6.5.54_16676)

Any help you can offer would be GREATLY appreciated!

UPDATE: Got it working!

For anyone else that’s new at this that may bang their head against the wall in the future… The user account that you attempt to authenticate with is very important! The ssh account (root) will NOT work! After much messing around (and LOTS of googling), I created a limited admin LOCAL user, and used those credentials for the automations. Works like a charm! Thank you much, @jon102034050! Awesome!!!

1 Like

Ah, awesome! I probably should have clarified that I do use a local account for auth with this. Glad it’s working!

Hi,

Relative noob here. I’ve been running HA for years but left it at a stable point and only recently started tinkering again having added Unifi and Reolink to the mix. I stumbled across this topic whilst trying to solve turning on and off port forwarding in Unifi and really hope you can help. I’m having an issue at the moment in that when I try and run the service it returns an error code 127. Any ideas what that relates to?

HA Log says:

2023-07-29 22:15:18.204 ERROR (MainThread) [homeassistant.components.shell_command] Error running command: /bin/bash /config/scripts/unifi.sh portfwd {{ username }} {{ password }} {{ baseurl }} {{ name }} {{ enabled }} {{ dst_port }} {{ ip }} {{ fwd_port }} {{ proto }} {{ id }} {{ unifi_site }}, return code: 127

Probably should mention I’m running unifi controller v7.3.83 on a USG3P
HA is 2023.7.1
HA OS is 10.3

Thanks in advance

Mike

Just based off a quick google search, it looks like you might just have a simple error in your pathing or something on the curl command. can you copy/paste everything you’ve put in so far here, or link me to your github repo?

Hi Jon.

Thanks for getting back to me.

Below are my configurations:

configuration.yaml -

shell_command:
  unifi_portfwd: /bin/bash /config/scripts/unifi.sh portfwd {{ username }} {{ password }} {{ baseurl }} {{ name }} {{ enabled }} {{ dst_port }} {{ ip }} {{ fwd_port }} {{ proto }} {{ id }} {{ unifi_site }}

/config/scripts/unifi.sh -

#!/bin/sh

cookie=$(mktemp)
headers=$(mktemp)
curl_cmd="curl --silent --output /dev/null --cookie ${cookie} --cookie-jar ${cookie} --insecure"

portfwd() {
  # authenticate against unifi controller
  ${curl_cmd} -H 'Content-Type: application/json' -D ${headers} -d "{\"username\":\"$1\", \"password\":\"$2\"}" https://${3}/api/auth/login

  # grab the `X-CSRF-Token` and strip the newline (added when upgraded to controller 6.1.26)
  csrf="$(awk -v FS=': ' '/^X-CSRF-Token/{print $2}' "${headers}" | tr -d '\r')"

  # enable/disable firewall rule
  ${curl_cmd} -k -X PUT https://${3}/proxy/network/api/s/default/rest/portforward/${10} -H "Content-Type: application/json" -H "X-CSRF-Token: ${csrf}" -d @- <<-EOF
  {
    "name":"$4",
    "enabled":$5,
    "src":"any",
    "dst_port":"$6",
    "fwd":"$7",
    "fwd_port":"$8",
    "proto":"$9",
    "log":false,
    "_id":"$10",
    "site_id":"$11",
    "pfwd_interface":"wan",
    "destination_ip":"any"
  }
EOF
}

powercycleport() {
  # authenticate against unifi controller
  ${curl_cmd} -d "{\"username\":\"$1\", \"password\":\"$2\"}" https://${3}/api/login

  # cycle unifi port
  ${curl_cmd} -k -X POST https://${3}/api/s/default/cmd/devmgr -H "Content-Type: application/json" -d @- <<-EOF
    {
        "mac":"$4",
        "port_idx":$5,
        "cmd":"$6"
    }
EOF
}

"$@"

Automations.yaml -

- id: RDP portfwd ON
  alias: RDP portfwd ON
  description: ''
  trigger:
    - platform: state
      entity_id: binary_sensor.rdp
      from: "off"
      to: "on"
  action:
    - service: shell_command.unifi_portfwd
      data:
        username: !secret unifi_username
        password: !secret unifi_password
        baseurl: !secret unifi_controller_ip
        name: !secret RDP_name
        enabled: 'true'
        dst_port: !secret RDP_DST_port
        ip: !secret unifi_controller_ip
        fwd_port: !secret RDP_FWD_port
        proto: !secret RDP_proto
        id: !secret RDP_id
        unifi_site: !secret unifi_site
- id: RDP portfwd OFF
  alias: RDP portfwd OFF
  description: ''
  trigger:
    - platform: state
      entity_id: binary_sensor.rdp
      from: "on"
      to: "off"
  action:
    - service: shell_command.unifi_portfwd
      data:
        username: !secret unifi_username
        password: !secret unifi_password
        baseurl: !secret unifi_controller_ip
        name: !secret RDP_name
        enabled: 'false'
        dst_port: !secret RDP_DST_port
        ip: !secret unifi_controller_ip
        fwd_port: !secret RDP_FWD_port
        proto: !secret RDP_proto
        id: !secret RDP_id
        unifi_site: !secret unifi_site

secrets.yaml -

RDP_name: RDP
RDP_DST_port: 3389
RDP_FWD_port: 19402
RDP_proto: Both
RDP_id: 64bee7d325015412dcf789a6
unifi_username: XXXX
unifi_password: yyyyyyyy
unifi_controller_ip: 192.168.0.102
unifi_site: default

Thank you for taking time to look at this.

Mike

Hey @mjd239 - I’d highly recommend you do not use this to port forward RDP. That is a recipe for disaster. Take time to setup Tailscale/WireGuard/etc and use that instead.

If you must use this, I’d recommend you get onto your HA box and run the script manually as it would be ran from your automation. If you’re using docker, you’d run something similar to this to get into the running container: docker exec -it homeassistant bash and then copy/paste each of the values into

/bin/bash /config/scripts/unifi.sh portfwd {{ username }} {{ password }} {{ baseurl }} {{ name }} {{ enabled }} {{ dst_port }} {{ ip }} {{ fwd_port }} {{ proto }} {{ id }} {{ unifi_site }}

Hopefully that’ll help determine whats goign on here, that was what I was able to do to determine when i was getting errors from my automation

Hello! Functionality to support controlling port forwarding from the unifi integration has been posted Unifi add port forward control to switch platform by Kane610 · Pull Request #98309 · home-assistant/core · GitHub

1 Like

@Robban strikes again! I JUST released a YouTube video about manually configuring this. It had been in the works for some time, so I wanted to come here and let people know I made a tutorial - but as with most things computer-related, it appears to be obsolete the day it was released. LOL

Oh well. Maybe next time.

Thanks for your hard work on the Unifi integration, Robban. At some point I’ll be switching away from these shell scripts and just doing all this stuff via the integration. You’re the man! :slight_smile:

1 Like

Haha

Would it be enough to have a button entity to randomise a new password? Or would it need to be a service that can take whatever or randomise if wished?

For me, it would either need to be an event scheduled from within the integration, or it would need to be a callable service - unless there’s a way to programmatically “press” the button? I rotate my guest wifi password automatically every day.

But that’s the other discussion, the one about the wifi password change thing.

This discussion is about the port forward rules. :slight_smile:

1 Like

Too many parallel discussions :joy:

1 Like