Managing secrets

I’d like to see some options to encrypt the credentials that we store in HA.

Right now, if someone leaks a config file by mistake - it could potentially have all sorts of tokens and passwords in it. I’d much rather HA generated a key pair, secured separately from the rest of config, and locked it down as much as possible.

4 Likes

I’m interested in that too… the problem is, it doesn’t work as you stated :slight_smile: Either HA needs to be able to decrypt by itself (which defeats the encryption), or it cannot auto-start and every time it starts you’ll need to enter the decryption key. As a matter of fact, we have those methods available but are now deprecated and pending removal, as while it sounds ideal, it is fairly unusable.

So, if you have can design a way that works like you say, please, contribute it. I’m interested to see that myself (for real).

13 Likes

I may be off-base here, but I understood @dolkensp’s comment to say “if I accidentally share my config files, my passwords are in plain-text”, but if you enter the password into HA and it spits out a new hashed password that it can decrypt on its own, stored separately in a different file structure, then you’d have to accidentally share your config files AND whatever extra directory/file HA uses to keep things “encrypted” (even if it’s more obfuscation than encryption).

@dolkensp does that summarize?

1 Like

I think this depends on the purpose of the encryption.

If the purpose is to prevent someone accidentally posting passwords and such online. You could just generate a key on first startup and store it in the config folder. Any passwords in the config/storage are then encrypted using the key on first run (or just have an option to encrypt the entire config/storage). Provided the key isn’t posted, the contents should be secure.

For added protection against someone posting their entire config directory online, you could protect the key with a passphrase generated from a fingerprint of the machine hardware (e.g. CPU + Motherboard which aren’t often upgraded), and show this to the user somewhere within HA so that they can decrypt/re-encrypt the contents if they need to change machine. This should also protect the key and config against remote access as you would need access to the hardware to generate the fingerprint.

This doesn’t work if the purpose of the encryption is to protect the config files in the event an attacker gains access to the system as they could generate the fingerprint themselves.

Worth pointing out that security is not my field of expertise but I believe this is how some other applications manage it.

Having a key to lock your car doesn’t make it any safer if the key is sitting on the ground right next to your car. You downloading a custom component and running it, will have just as much access to your config and everything else as HA, with a 5000000000 bit key or not as long as the key is sitting right next to the config.

2 Likes

Even though this is a bit off-topic, I don’t think my brainfart deserves a dedicated thread. So here’s my though / idea on how this could be approached. A quick note before I start: I was just about to implement Vault in a similar fashion to the credstash / AWS keystore stuff, but then saw those options to be deprecated. So I cancelled that.

For simplicity I’ll imagine Vault being the default storage for secrets in HA. Here secrets can be accessed by providing a token for authentication + a path. Of course this could be replaced by something internal.

The main problem is (as you already know), that for automatic startup the token to access the secrets needs to be available to HA. By storing it in a file the HA-process can read, an attacker could be able access it in case HA got compromised.

To circumvent this, the token (stored in a file with only root access) could be passed as an environment variable while starting HA. The HA process itself is started as a different user with less privileges, and thereby preventing access to the token-file. During startup HA would fetch the required secrets from Vault (or decrypt them from a custom secrets-storage), and then clear / overwrite the environment-variable once that is done. This way the compromised HA process wouldn’t know the secret anymore, and because the secrets are stored externally (or encrypted), they can’t be accessed (apart from maybe being somewhere in the memory).

The remaining problem in this case would be an integration that’s set up via UI (or something that doesn’t require a restart of HA). After all HA would have to write the secret into the secrets-storage, but it can’t as it’s lacking the required token now. But: it’s UI. So in such a case the user could be queried to provide the token while setting up the integration. Then once HA wrote the secret, it will be forgotten again.

So in pythonic pseudo-code:

$ SECRET_TOKEN="s3cr3t" python3
import os, secretengine
secrets = secretengine.load(os.environ.get('SECERT_TOKEN'))
os.environ['SECRET_TOKEN'] = ''
os.setuid(123)
# Configure integration via UI
secrets.append(app_secret)
secretengine.write(secrets, token_from_ui)

I haven’t put enough thought into this to see the design-flaw in my solution myself (if there is one). At this state it’s really not more than a brainfart. But it might inspire someone to find a solution that’s actually secure.

I don’t know enough about the core to judge if this will fail for other reasons. In my mind this would rely on systemd (which has root privileges) to read the token from disk, pass it via the environment, and start the HA process with the dedicated user. I’m a core User, so that’s why it could work this way. Things could look different though on other installation flavours (which probably won’t have systemd).

Anyways, I just wanted to share this. Maybe it’ll inspire someone to dig deeper and find a solution that provides at least more security for secrets than just having them on the filesystem.

7 Likes

Personally, I wouldn’t mind just entering the decryption key manually every restart. I can’t imagine I’d want HA restarting by itself, so I’d already be restarting it myself and waiting for it to come back up. It’s not much extra to just load the UI and enter the key. (I’m just assuming this is all it would take. I understand it may not be that simple either)

Of course, I know that won’t work for everyone and if there’s a better way then I’m all for it. I’ve never liked all the credentials being so easily accessible, both in secrets.yaml and .storage.

Yup, this pretty much sums it up.

It could be as simple as hashing the passwords against some form of hardware identifier / machine key.

IIS provides similar functionality out of the box, and the default machine key is effectively a hardware/installation ID, but you can manually set it if you want to migrate passwords or run in a cluster, etc.

Alternatively, a separate service account could exist which is configured separately to the rest of HA, and contains the decryption key - that way even if the ha service account is compromised, you can’t just lift the config files and run off with them - you will have to first pass everything through the decryption service, or compromise that service account too.

For another IIS comparison, I’m thinking along the lines of “machine/service account authentication” instead of embedding username/password in your connection strings.

I’d love to be able to set up something that lets me sync my config files with github, but I don’t want my passwords stored in plain text, and I especially don’t want them stored in plain text in a cloud service.

Use secrets.yaml and gitignore for that and the .storage folder. That works…

2 Likes

Custom components have access to HA state and your configs (they need to). So, even if secrets where encrypted, HA (and the components) will still have access to them (unencrypted), since they need to.

My Garmin watch has ‘custom components’ too and they require explicit access to certain features. The same could apply here to HA.

The passwords needs to be sent in plain text, so if they are hashed then they need to be decrypted.
That is something that is generally not possible with the normal hashing types.
That means they would have to build new hashing technique, in a open source project…
You see the issue?
Not to mention when your password is decrypted it’s still possible to be sniffed.

Hopefully you don’t subscribe to anything that doesn’t utilise SSL.

The password is in plain text prior to the SSL encryption.
You can’t send a hashed password in SSL or not SSL, the receiving end needs to understand.
And they won’t do that if it’s encrypted before the “agreed” encryption

Yes of course, but it isn’t “sent” in plaintext.

You are right in that ha needs the unencrypted key to feed to ssl.

Hi,

Can be interesting to have a Vault (https://www.vaultproject.io/) connector integration to manage secret.

So that, just need to push secrets in Vault. When you start HA by example, it request the token (valid for like 10 minutes) or approle credentials in secret mode. In this example, we will see only in config the path of the secret in Vault (like kv/ha/tplink/trololo) (python library can be found here: https://www.vaultproject.io/api-docs/libraries)

Once done, creds are loaded until the next restart (reload) but to check if external component can load also these variables or if it can be like in a jail.

Best regards,

4 Likes

HA needing to be able decrypt secrets.yaml on load is not ideal - but still a big improvement on a plain text file it seems to me.

I posted about potential threats from 3rd party integrations and custom components on the Facebook group page back in early Dec 20. Would be nice to think that post (It generated a lot of interest) is a partial source of this new effort to lock a few sensible things down.
Sandboxing non-curated code from the core makes a lot of sense and certainly improves the general security of HA; there is always more to be done, but this is a great start.

Thanks dev’s for listening (@balloob , @frenck ).
Pete

1 Like

That means they would have to build new hashing technique, in a open source project…
You see the issue?

No? OpenSSL is open source - it’s right there in the name - and it secures half the internet, and is good enough for the government, and the banks. There are numerous DPAPI type projects out there. I even referenced one approach that Microsoft took for years.

Not to mention when your password is decrypted it’s still possible to be sniffed.

Yes, but just because a burglar could break a window, doesn’t stop you putting a lock on your front door. I want the config files to be encrypted by default, so that an accidental misconfiguration, or a sync of configuration up to something like github, or even just sharing a config in a forum, doesn’t put the credentials at immediate risk.

Even RSA-240 has been cracked - with enough compute power. Doesn’t mean it’s useless for every day users to protect themselves against.

Zero Trust Architecture has been a thing for more than a decade now - you can run entire clusters with zero-trust networks. There is a better solution out there than dropping passwords in plaintext yaml files. I don’t care which one we choose, so long as we do choose one.

4 Likes

+1

Given the recent security disclosure about unauthenticated directory traversal, this should be a very high priority. If users get bitten by this, it could do serious damage to Home Assistant’s reputation.

Personally, I’ve held off from integrating a lot of services specifically because of concerns about security. For Home Assistant to reach its full potential, it needs a security audit and obvious things like storing plain text credentials need to be fixed.

6 Likes