SSH'ing from a command line sensor or shell command

It was probably trying to remove it from a different known_hosts file. I have edited my previous post to add the -f argument to ssh-keygen.

2 Likes

Small update - removed the Portainer alternative. The portainer addon has been deprecated and unavailable in the community addons repo for some time so I don’t want to advertise an alternative option that almost no one can follow anymore.

I’ll copy the instructions in here in case someone still has the addon or maintains their own fork or something.

Portainer alternative (deprecated)

For those using the official SSH add-on or without protection mode disabled on their SSH add-on you won’t be able to use docker exec like I suggested above. Instead open the Portainer add-on from the left side nav of HA. From there click on ‘containers’
Screen Shot 2020-12-22 at 10.38.27 AM
Then find the ‘homeassistant’ container in the list and click the little icon for ‘exec console’ like this:


Then in this screen just click “connect” to launch a terminal window on this container as root:
Screen Shot 2020-12-22 at 10.42.39 AM
Now enter your command in the terminal that appears and run it like so:

Running it this way runs it in exactly the same way that HA does so now I have confirmed my command is working and can be put in a command line sensor.

There’s also another option depending what you’re going to, you can disable the host key checking as part of the SSH options. The risk is you won’t notice if you somehow end up logging into a MITM spoofed box, but that isn’t always important based on what you are doing.

Here’s what I use in a SSH command to run speed-tests on my router (vs on my home assistant box)
/usr/bin/ssh -oStrictHostKeyChecking=no -i /config/speedtest-net/pfsense_speedtest_id_rsa [email protected]

Breakdown:
/usr/bin/ssh → this calls the SSH command
-oStrictHostKeyChecking=no → this disables host validation, sidestepping the “accept key”
-i /config/speedtest-net/pfsense_speedtest_id_rsa → the path on Home Assistant to where my SSH key is stored
[email protected] → username @ host that we are logging into

Its “generally bad practice” to disable host key checking, but also worth considering how sensitive is what you’re doing in reality?

In my case, I consider it very low, as I’m simply doing a query sending no information of value, and the received payload is simply a number on a graph to tell me “hey this is out of range” so the risk is someone could confuse me slightly why a graph is unusually high/low and make me walk down to check my modem.

In my case also since the target is a router, the host key changes if I reload the OS to do upgrades or fix issues and would be a pain to maintain.

The other risk is what if someone got your private key and logged in as your automation. This is mitigated by on the server, then in authorized_keys you can restrict down to exactly 1 command that is allowed to be run, so even if someone gets hold of your SSH private key they can’t do much damage - just run lots of internet speed tests so I consider this another reason that I’m unconcerned about this particular “security risk” of not validating what I’m connecting to.

Here’s the authorized_keys line that is on my router (in my example):
command="/usr/local/bin/speedtest --accept-license --format=json" ssh-rsa <hassio public key data here>

using the command="something" prefix for the authorized_keys public-key line means when the client SSH connects it will only be allowed to run that 1 command and nothing else. Generally a good thing for security, as the home automation sensor should never be doing anything BUT that one command its fetching data about.

If you’re doing more sensitive things, you may want to keep the host key checking enabled, but it is worth considering what the worst-case damage is that can be done before declaring its “good” or “unimportant”.

Another possibility that might be easier, there is an option flag to specify a CUSTOM known_hosts file and avoid having to hack around in containers that may or may not stick.
-o UserKnownHostsFile=/path/to/my_temporary_known_host ssh_host
Using this, you could specify a custom known_hosts file to validate the host against, and store it in the /config area with your custom public/private keypair. This should simplify things considerably.

1 Like

I’m a bit confused. My post that you appear to be replying to with this was just letting people know that I updated the wiki at the top to remove something which used to be there. I’m not having any issues here. I am well aware of both -o StrictHostKeyChecking=no and -o UserKnownHostsFile since I wrote the guide at the top that covers both options, what they do and where it is safe to put your known hosts file so it survives restarts and upgrades.

Did you mean to reply to a diferent post?

Missed where you mentioned relocating the host key (instead of messing with trying to get into the hassio core container) as an option, I only saw the no host checking.

Also wanted to add the security tip to configure authorized_keys so it only allows the approved single command on the server for extra security.

Just trying to add a couple other tips based on what I found in the original guide.

1 Like

Ah ok fair enough.

That is a good tip on the authorized keys restriction, you’re right I didn’t know that part. I’m going to add a task to do that myself.

I’ll work that into the guide when I get a chance. Or you can if you want, posts in the community guides section are actually wikis so anyone can edit them.

Happy if you want to work it into yours, I don’t see a point in writing an entire guide about it.

Also note you can have only one specific command to a SSH public/private key pair when you limit it in the authorized_keys file like that, and I believe you have to put all the “parameters” in the authorized_keys as it will ignore whatever the client sends.

But if you want multiple commands, you could have it call a script to interact with, or just have one separate public/private key pair per command you run on that server. I’ve not pursued that because I’m just polling for data from a remote server. There’s various ideas out on the web, but those thoughts should point you in a useful direction.

1 Like

Hi @ondras12345

I am facing the same problem again, this time with two devices.

Here’s the steps I am taking (assuming the knwon_hosts file in in /config/.ssh/known_hosts

Created a new keys pair

ssh-keygen -t rsa

Was asked where to save the file:

/config/.ssh/id_rsa

Did NOT add a passphrase. Then tried to copy the public key to the Rpi:

docker exec -it homeassistant bash
ssh-copy-id -i /config/.ssh/id_rsa.pub [email protected]

and I got the same error message. So I thought, let’s remove all keys and start again. I opened the known_hosts file:

nano /config/.ssh/known_hosts

Removed all keys in there leaving it blank, closed and saved the file. Then reopened it to confirm it is empty, and it was. Then started again by creating new keys again.

ssh -o UserKnownHostsFile=/config/.ssh/known_hosts
ssh-keygen -t rsa

Again I am asked where to save the file:

/config/.ssh/id_rsa

Again I did NOT add a passphrase. Then tried to copy the public key to the 3 devices (out of which one is not changed, the other two rebuilt today). The command for the first one went fine:

ssh-copy-id -i /config/.ssh/id_rsa.pub [email protected]

I was asked for pi’s password and the key was saved.
The second command though displayed the same error:

ssh-copy-id -i /config/.ssh/id_rsa.pub [email protected]

and so did the third:

ssh-copy-id -i /config/.ssh/id_rsa.pub [email protected]

I confirmed that the user names are correct. Nevertheless, the keys won’t be copied. Now here’s the weird part. Since the first device’s command went through, the key should be appearing in the known_hosts file, right? So I open it:

nano /config/.ssh/known_hosts

And it’s empty!

Now I have no idea what to do. Any help would be greatly appreciated. What am I doing wrong?

This command doesn’t say anything about which known hosts file to use since it doesn’t include this bit:

-o UserKnownHostsFile=/config/.ssh/known_hosts

Therefore it will look for known hosts in the default location, ~/.ssh/known_hosts. I’m guessing if you open that file you’ll find entries for your machines with the old fingerprints since it doesn’t sound like you changed those.

Remember ssh-copy-id is just a script that calls ssh. So either add all the same parameters to it that you add to your ssh command or make sure hosts file in the default location works.

Either is fine for ssh-copy-id since you’d never call that from a shell command or command line sensor. Generally you only call it interactively so it doesnt matter if the known hosts data it’s using gets erased by an update.

2 Likes

Sorry Mike, I don’t understand.

When I try the following command in the HA terminal:

sudo nano ~/.ssh/known_hosts

the file comes up completely empty.

In the OP you mention that:

do not store your key file in the default location. The default location for ssh-keygen is ~/.ssh which translates to /root/.ssh.

Instead I’d recommend telling ssh-keygen to store its generated ID file in /config/.ssh. You don’t have to put it in a folder called .ssh if you don’t want to, I just like the consistency. But you should put it in /config so it is preserved over updates.

I am trying to follow this practice as I agree with you re consistency. So, what would be the correct syntax for the ssh-copy-id command if I was to use the /config/.ssh folder instead?

So first, the quick answer. Do this:

ssh-copy-id -i /config/.ssh/id_rsa.pub -o UserKnownHostsFile=/config/.ssh/known_hosts [email protected]

The longer answer: you’re mixing and matching a lot of things in ways that’s a bit hard to follow.

When you say “HA Terminal” here, which terminal are you referring to exactly? Do you mean the one you get into simply by SSH’ing to HA? Or do you mean the one you get to by doing this after SSH’ing?

Since the HA container does not include sudo or nano I assume you mean the first one. Which means you are trying to edit an entirely different known_hosts file since you said you got the error when you did this:

This command goes into the HA container and runs ssh-copy-id from there. The HA container and the ssh container do not share /root.

Fair although just keep in mind the guide is really about stuff you run from HA using command line sensors and shell commands and such. Those are the commands that must work when run non-interactively. If a prompt appears that says “I don’t know this host, will you accept its fingerprint?” then that command has failed because there is no way to accept the fingerprint when running a command from a shell_command.

That doesn’t apply to ssh-copy-id. ssh-copy-id is generally only run interactively. It’s just a tool to move the text in one file into another file over ssh. If you have to accept a host’s fingerprint to run it that’s fine, you’re running it interactively so that’s not an issue. You also don’t need to use ssh-copy-id at all, you can just copy and paste the key from one file to another manually if you want. That’s literally all its doing.

2 Likes

Now it makes perfect sense, thank you.

By “HA Terminal” I mean the " SSH & Web Terminal" AddOn, not the one after SSH’ing.
The info that The HA container and the ssh container do not share /root. was really what cleared the picture for me, thank you!

Also thank you for the clarification about shell commands and sensors. I knew this bit but you cleared it up nicely.

Thanks again, it’s all working now!

1 Like

Having followd this guide and several other troubleshooting steps, I still can’t get it to work properly. The commands I’m testing work on the SSH AddOn, but neither on a command line sensor nor a switch, throwing the same error.

I’ll elaborate a bit. One of the sensors that I want to have is one to monitor the state of a service on another machine, for example:

  - platform: command_line
    name: Test Sensor
    unique_id: test_sensor
    scan_interval: 360
    command: "ssh -vvv -i /config/testfolder/id_ed25519 -o UserKnownHostsFile=/config/testfolder/known_hosts [email protected] 'pgrep cron' 2> /config/sensor.log"
    value_template: '{{int(value) > 0}}'

And it works perfectly on the Terminal Addon, however it fails when running on the background. And when looking through the sensor.log file, I can see this:

debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 60
debug1: Server accepts key: /config/ KEY 1 (Commented the key out)
debug3: sign_and_send_pubkey: KEY 1
debug3: sign_and_send_pubkey: signing using KEY 1
debug1: read_passphrase: can't open /dev/tty: No such device or address
debug2: no passphrase given, try next key
debug2: we did not send a packet, disable method
debug3: authmethod_lookup password
debug3: remaining preferred: ,password
debug3: authmethod_is_enabled password
debug1: Next authentication method: password
debug1: read_passphrase: can't open /dev/tty: No such device or address
debug3: send packet: type 50
debug2: we sent a password packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password
Permission denied, please try again.
debug1: read_passphrase: can't open /dev/tty: No such device or address
debug3: send packet: type 50
debug2: we sent a password packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password
Permission denied, please try again.
debug1: read_passphrase: can't open /dev/tty: No such device or address
debug3: send packet: type 50
debug2: we sent a password packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password
debug2: we did not send a packet, disable method
debug1: No more authentication methods to try.
[email protected]: Permission denied (publickey,password).

What I did so far (HA is on a VM, if that makes a difference):

  • Checked permissions for /dev/tty on both machines, and they return ( crw-rw-rw- 1 root tty)
  • Checked permissions for the HA folder (drwxr-xr-x 2 root root)
  • Checked permissions for the HA files Private Key (-rw------- 1 root root), Public Key (-rw-r–r-- 1 root root) and Known Hosts (-rw-r–r-- 1 root root)
  • Checked permissions for target machine .ssh folder (drwx------ 2 test test)
  • Checked permissions for target machine authorized_keys file (-rw-r–r–)

Also I can simply login in the target machine via the terminal with a simple ssh -i /config/testfolder/id_ed25519 -o UserKnownHostsFile=/config/testfolder/known_hosts [email protected]. So I don’t know why it doesn’t work via sensor.

Also, I’m not even sure if my template {{int(value) > 0}} works. What I had in mind was that if pgrep returns a number, the service is running, but I’m not sure how to handle empty responses in the template (and there’s an error with ValueError: Template error: int got invalid input 'None' when rendering template about it on the HA log).

Apologies if this is obvious…
I have the “terminal and ssh” addon installed
I can bring up an SSH session either via the browser or via the web interface

However, typing “docker” gives me:

[core-ssh config]$ docker
-bash: docker: command not found

I can run:

ha docker

But then the -it flag is not recognised…?

You installed the wrong ssh addon.

Note: community addons, not core/official addons.

1 Like

Hi All, I wrote an article on this a number of years back explaining how to set this up. I have just finished revamping the article and getting it up to date using the latest version of Home Assistant.

It is a complete step by step guide, might be of use to some newcomers, questions and comments welcome: Home Assistant Shell Integration: Local & SSH Linux Control

In the tutorial I show you how to connect Home Assistant to a remote Linux machine and execute a simple shell script. Home Assistant also monitors the time and date that the script was last run and displays it in an entities card using SSH to fetch the data.

Home Assistant is running supervised on Mac OS using UTM. The remote machine is a second instance of UTM running Debian. Although the method is identical for entirely separate machines, RasPi for example. :slightly_smiling_face:

Your guide just recommends people disable host key checking by adding -o 'StrictHostKeyChecking=no'. Which obviously works but isn’t good security practice. You don’t have to do that, you can just move the known hosts file to a persistent location like the key. See Handling the known_hosts file at the top of this thread.

Also you might want to mention techniques for debugging when things go wrong like in here. Can be tough to figure out what broke when you can’t see stdout and stderr.

Thanks for the tips @CentralCommand ! Yes a couple of minor things there, should be easy enough to add :slightly_smiling_face:

1 Like

Hi all,

If someone has the problem:

[core-ssh ~]$ docker exec -it homeassistant bash
-bash: docker: command not found
[core-ssh ~]$

It means that you have installed the standard SSH addon, not the community one as suggested here. Installing the “docker” as package didn´t help me much, it fails differently, probably due to the protection ( I am a docker newbie).

Anyway, I managed to call SSH with the default SSH addon (not the community one), without doing the “docker” part to add the known_hosts, using the alternative that is also mentioned here somewhere:

/usr/bin/ssh [email protected] -o "StrictHostKeyChecking no" -i /config/.ssh_keys/id_rsa touch /tmp/test1.txt
1 Like

I’m not good at reading instructions and most likely I do not understand what I just red :slight_smile:
So I’m just going to ask simply question.
If I want Home Assistant to ssh Unifi device and run “reboot” command… is this possible by following these instructions? And I mean…can I build automation for it too?