Web clicks simulator for rebooting disabled SSH access router

I have two ASUS access points, one Ubiquiti UAP-AC-PRO and one TP-Link outdoor AP, alongside my ISP router (wich is an ASKEY router). From time to time, I like to reboot all devices for the whole network to be optimized. I can automate such task for all devices but the ISP router. For the APs, I can SSH to them through shell_command (SSH + id_rsa keys) and reboot them both with automations or with a button.

However, my ISP does not allow to SSH into their router. Such device can be rebooted through the web interface. Is there any way I can build a script or something supported by HA so I can simulate the web access calls to reboot it? How can I “capture” such web interface calls in appropriate order, including pauses?

Normally one does something like use NodeRed and the web request node. Make the first call to the login page, and ensure you save the cookies to re-use later in the flow. The next call would then be a POST to the URL that is called when you tell it to reboot.

We get the URLs by opening developer tools in Chrome or Firefox, and switching to the Network Tab to see what pages are being requested.

I don’t have NodeRed installed. I’d rather to achieve this without it, as I don’t want to install it for this specific need only. Will keep checking the thread just in case I get another solution. Thanks for your feedback anyway!

1 Like

Just as long as you know that whatever the solution is, whether it is a python script or something else, it is not something that can be done natively in Home Assistant. It’s going to involve a third party of some description.

Finally, I was able to make it. I used Web Developer Tools in Chrome as you advised and could build two cURL commands: One to log in to the router and to get the cookie with session ID and then another command to reboot it, using the session ID I got earlier.

However, now I am stuck on the easiest part, which is the automation one. For whatever reason, if I call both cURL shell commands, one after another in the Developer Tools Service, the calls work and the router is rebooted. However, if I run the commands (inserting a 5 seconds wait between commands) in an automation, the router is not rebooted.

shell_command:
  reiniciar_movistar_login: "curl -c /config/tmp/cookie-jar.txt 'http://[ROUTER_IP]/te_acceso_router.cgi' -H 'Connection: keep-alive' -H 'Cache-Control: max-age=0' -H 'Origin: http://[ROUTER_IP]' -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'Content-Type: application/x-www-form-urlencoded' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' -H 'Referer: http://[ROUTER_IP]/te_acceso_router.html' -H 'Accept-Language: en-US,en;q=0.9,es;q=0.8' -H 'Cookie: sessionID=' --data-raw 'loginPassword=PASSWORD' --compressed --insecure"
  reiniciar_movistar_reboot: "curl -b /config/tmp/cookie-jar.txt 'http://[ROUTER_IP]/rebootinfo.cgi?sessionKey=' -H 'Connection: keep-alive' -H 'Upgrade-Insecure-Requests: 1' -H 'DNT: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' -H 'Referer: http://[ROUTER_IP]/resetrouter.html' -H 'Accept-Language: en-US,en;q=0.9,es;q=0.8' --compressed --insecure"

And this is just a simple automation I created to check it:

alias: Reiniciar Routers
description: ''
trigger:
  - platform: time
    at: '04:15'
condition: []
action:
  - service: shell_command.reiniciar_movistar_login
  - delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
  - service: shell_command.reiniciar_movistar_reboot
mode: single

I am guessing that the automation is perhaps opening two shells in the background, so the remote host (router) is generating two different session IDs, although I am not sure. I will try to figure it out tomorrow, but I am just saying in case you or someone else has any suggestion.

Any idea on how to concatenate both commands in one cURL line just in case it works that way?

The easiest way is to put them in to a shell script in the config folder, and then just tell Home Assistant to run the shell script.
If you have the core_ssh addon installed (SSH and Web) then pop in to there after you have created and saved the file and make sure you chmod +x the file to give Home Assistant permission to execute the file. I don’t know if Home Assistant will run it as is, or if you will have to invoke bash before the filename.

As for your curl commands:

curl -c /config/tmp/cookie-jar.txt 'http://[ROUTER_IP]/te_acceso_router.cgi' -H 'Origin: http://[ROUTER_IP]' -H 'Content-Type: application/x-www-form-urlencoded' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' -H 'Referer: http://[ROUTER_IP]/te_acceso_router.html' -H 'Cookie: sessionID=' --data-raw 'loginPassword=PASSWORD' --compressed --insecure

is enough for your first command and

curl -b /config/tmp/cookie-jar.txt 'http://[ROUTER_IP]/rebootinfo.cgi' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' -H 'Referer: http://[ROUTER_IP]/resetrouter.html' --compressed --insecure

Should be enough for your second command. I don’t believe you need the sessionkey in the URL, since the point is that the sessionkey should be stored in the cookie after you logged in, that was the whole point of -H 'Cookie: sessionID=' to clear the stored sessionkey

Thank you for your suggestion about deleting the string right after rebootinfo.cgi. It worked, and it is better to have the code as clean as possible. I will also try the rest of the headers regarding browser and MIME types, as I think it is not necessary for them to be so detailed.

I also merged everything in a .sh file script and it works for the ISP router (movistar). :slight_smile:

However, I have another issue with the TP-Link Outdoor AP I mentioned at the beginning of this thread. For this specific device, I have chosen to connect to the device through ssh together with sshpass, because this device has not any place in the backend to provide a public RSA key. It is happening something similar than for the ISP router (the script works if I execute it from HA Terminal command line), but it does not work if I execute it from the Developer Tools section. Here you go:

#!/bin/bash
ssh-keygen -R [TPLINK_IP]
sshpass -p 'PASSWORD' ssh -o StrictHostKeyChecking=no username@[TPLINK_IP] -t 'restart'

If I don’t manage to do it this way, I will try the cURL option, but I’d rather to do it with sshpass.

Apart from this, I am wondering where is the most appropriate place to put these script files, as I have some security concerns. For shell_commands, I put them in the secrets.yaml file. But for the .sh files, should I put them in some sort of non-accesible folder? How can I do that?

Create a folder that starts with a . for example /config/.scripts by default folders that start with a full stop are hidden unless you turn on show hidden files in whatever file browser you are using.

As for your TPlink - can you not use ssh-copy-id ?
The reason it does not work from inside Home Assistant is because when you run ssh-keygen it is being run INSIDE the HA Terminal which is a docker container, so it has no access to the outside system, except for the /config directory. Which is why when ssh-keygen asks you where you want to store the key, you should ideally have created /config/.ssh and store the keys in there. Then when you run the command in Home Assistant it should be something like:

ssh -i /config/.ssh/tplink_rsa username@TPLINKIP restart

and if you can indeed use ssh-copy-id to copy your key to the TPLink, then make sure you specify the file:

ssh-copy-id -i /config/.ssh/tplink_rsa username@TPLINKIP

Hope that helps send you in the right direction.

Hi Andrew,

I actually have my scripts in a folder named .commands, but I wasn’t sure whether it is safe enough for the purpose. Ideally, it should be a folder with specific permissions which don’t allow access except from local network. I guess it should be something similar to whatever it is done with secrets.yaml.

For the ssh-copy-id, unfortunately, the TP-Link does not allow it. The device returns the following:

sh: can't create /dev/null: Permission denied
sh: can't create /etc/dropbear/authorized_keys: Read-only file system

Still trying to find an option which works from an automation for the TP-Link, but so far at least I managed to do this with the Asus, Ubiquity and ISP devices. I guess I will achieve to reboot the TP-Link at some point. :slight_smile: Will update the thread when done. Thanks for your help so far!

FInally I could make it! What I did was to setup a shell_command to install sshpass. Doing this, the sshpass command is installed into the HA core instead of the host. Actually, the command is not available from the HA Terminal, so now I understand that they are different “universes”. But the point here is that the TP-Link shell_command now works, which is what I was trying to achieve. :slight_smile: I guess the error 127 I was getting before with related to the fact that sshpass was not available for the core when I installed it from the Terminal (host).

What I am not sure now is whether the sshpass installation will be persistent or I will need to install it every time the system is updated… Any idea?

That I am not sure about. Updating Core - pulls a new docker image and destroys the existing container and creates a new one from the new image. Anything that was installed in the old container will be lost. That’s why installing something like sshpass in the ssh addon should be done by adding it to the addon config under apks:
change:

apks: []

to:

apks:
  - sshpass

and then restart the addon and it will ALWAYS be available inside that container, even if it is updated.

As for how you use it - you use the addon_stdin service:

service: hassio.addon_stdin
data:
  addon: core_ssh
  input: /bin/sshpass all of your command here

Use which sshpass inside the ssh terminal to find out the proper path for sshpass