Automating unifi port forwarding based upon presence detection

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