Automating unifi port forwarding based upon presence detection

So I got it working and it was amazing! Thank you.

But, I had to update to use my new U6-Lite AP and now it won’t :frowning:

Seems it was a large update to the unifi controller.
The config URL port changed to 443 (well, there is no longer a port so I assume 443)

I’ve tried changing the string in the script to :443 and removing it completely.

I’ve also tried putting in raw data instead of secrets.

Unifi controller ver is now 6.0.43
UDM firmware 1.8.6.2969

Can this be debugged?

Thank you

Linton

Here’s a link to my commit where I fix the issue you’re describing. I probably should update my OP with this as well: https://github.com/JonGilmore/ha-personal/commit/65b99934b10bbd138d98539e62d7785adcf12631

the tl;dr of it is the X-CSRF-Token was breaking stuff, and it seems unifi changed the behavior of this.

WORKING!!! :smiley: :smiley:

thanks mate

nice! I spent WAY too long trying to figure out what the hell broke when I updated as well, hopefully I saved you some time, hah!

Seems to be a common theme in the Unifi world :stuck_out_tongue:

@jon102034050 thanks for the workaround.

I have created a feature request to make controlling Port Forwarding part of the Unifi integration: Unifi Integration: Ability to control Port Forwarding . Feel free to vote for it to get some attention to this feature.

1 Like

@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