Managing secrets

So I understand that even if the secrets are encrypted on disk that HA is going to need a way to use them unencrypted. But I feel like it’s still a pro if the only way to get decrypted secrets is through HA APIs at runtime instead of having them hanging out unencrypted on disk, right?

For example, what if you could provide an encryption key on a USB drive connected to the host device? Then HA OS could put that key somewhere it has access to but supervisor specifically cannot access. It would then provide that key as a config value to supervisor giving it what it needs to decrypt secrets. But the secrets themselves always remain encrypted at rest, secrets are only available to integrations via APIs that HA carefully manages and controls and no one can get to the encryption key itself from within supervisor. Since supervisor doesn’t let anyone access it’s config value and the file itself is completely inaccessible to anything other then the host OS (which by default provides no mechanism for accessing it directly).

I mean look, I’m not an expert here at all. So absolutely feel free to tell me I’m a naive idiot and dont know what I’m talking about. It just seems like there must be a way. I mean look at node reds handling of credentials which I think seems to work pretty well. Secret value provided via config used to encrypt the credentials on disk with APIs for carefully managed decrypted access only for component developers (and only for the creds they requested in that component).

It depends on the threat model. In any case, to protect everything we only need to keep one secret.

Threat: non-technical wife/kids/friends
Security level: obfuscation
Implementation: hard coded encryption
Caveats: is it worth it?

Threat: Add-On developers stealing your stuff
Security level: general, per-addon encryption
Implementation: each container asks the main container to decrypt secrets
Caveats: need a place to store that one code and keep it safe, like a special file in a container instance. Likely will need to keep two files, one with user password and one with system.

Threat: hackers/government agencies
Security level: per-instance/process implementation
Implementation: Trusted Execution environment processing, RPMB storage, Trusted Execution handles storage.
Caveats: not supported on RPI. RPI does not have a RPMB. No backup on x86, no way to transport by swapping disks.

I think the first thing is to lay out the user stories.
Then we can figure out what’s technically possible
What do we want to get out of encryption? That’s @frenck’s job because what he wants is what is best for the users.

2 Likes

As a senior embedded systems security engineer, I do have a lot of experience in protecting secrets on embedded systems. I’m at your service @frenck.

6 Likes

This is as good as obfuscation when you’ve got access to the machine.

After reading all concerns, I’d suggest a good compromise between extreme lockdown and usability might be a built-in password manager. It would be owned by the ha instance and never show the password in the UI once entered. It would have access granted per-requester based on stack trace. It would not be available outside python Api without an API key for the password manager. Depending on the system, it could utilize platform resources such as RPMB/Trusted Execution Environment, or other. Since owned by HA, it could also be backed up and the backup could be protect via key.

  1. User enters password and assigns key and which addon/platform is being used
  2. Platform request key password or addon/API request key with token
  3. Authorization is verified by token or stack trace (this could be better).
  4. Password is returned

This would protect against posting, addon devs, friends and hackers/government entities depending on the platform.

10 Likes

I like @adamoutler’s suggestion a lot. If implemented and audited, I’d consider using integrations that rely on sensitive credentials. There are so many integrations that I’ve been holding off on, and, for that matter, I’ve been writing a bunch of integrations (e.g. car, home security, etc.) that are now tabled due to this security issue.

Shut up and take my money

1 Like

Admittedly I mostly use secrets as variables for just about anything that gets used in multiple places in my config, like MAC and IP Addresses so that if they change, I can update them in one place without trying to remember everywhere I use that bit of info, more than I use it for passwords… and I’m sure I’m not the only one, cause it works really well for that!
So hopefully if this was followed on with and implemented, it would either allow usage like that as well, or secrets.yaml could be also kept in place (maybe renamed to identify its for not so sensitive info that is used in a lot of places). :crossed_fingers:

But either way, I would love to see something like that implemented :slight_smile:

Had a thought on this today. I noticed the LinuxServer Home Assistant image doesn’t run HA as root but rather as a user in the container. Doing that seems like it could be a solution here. Something like this:

  1. Map a new volume to the home assistant container, call it /etc/secrets for sake of argument
  2. There’s one file in there for now, secrets.yaml
  3. S6 reads in these secrets and provides them as arguments to HA when launching it in run
  4. When reading in the YAML config, HA first looks in the secret store provided via arguments. On fail to find it then looks in /config/secrets.yaml for backwards compatibility
  5. S6 runs HA as a non-root user. This user has no access to /etc/secrets

This solution should work on all 4 installation types although some additional work required for supervisor:

  1. HA Core - User modifies HA launch to include secrets arguments
  2. HA Container - User maps folder to /etc/secrets when launching container
  3. HA OS/Supervised - 2 ways:
    • (Quick way) Allow addons to map /etc/secrets as a volume. Update the Samba/SSH addons with this mapping. Discourage all other addons from using this since it hopefully is a temporary fix in favor of…
    • Add a “secrets” tab in supervisor panel. This UI lets users add and update secret values. The values become masked after entry and can only be replaced, they cannot be seen again (like Github secrets). Supervisor updates /etc/secrets/secrets.yaml with what is entered here. Supervisor and HA share the /etc/secrets volume, no other addon or HA container can.

Note that I’m imagining the file itself remains unencrypted and we rely on user controls to prevent access to it. Encrypting the file is trickier because to Frenck’s point way above, you have to get the encryption key in without letting others see it (hence it can’t be anywhere the root user can see it or you didn’t change anything). And have a solution that supports all 4 installation methods which becomes tricky. This seems like it could work in the meantime?

Of note there would be one downside here. Any change along these lines would mean that updates to secrets would require a restart of HA. HA wouldn’t be able to reload parts of its config with fresh secret values like it does today because it cannot access the secrets file. I feel like that’s an acceptable cost personally but perhaps others think differently.

1 Like

Did you ever get a chance to review the proposals in this thread @frenck?

You’re right in that anyone that has breached the filesystem of the device can realistically crack anything as the project is open source, but there are many reasons that the /config directory of a HA install may end up exposed to the internet, and there’s no reason that the key material securing any encrypted/sensitive values needs to be in that same location.

Any level of security is nothing more than obfuscation to someone with physical access, but this isn’t about physical access, this is about making it easy for end users to protect their credentials instead of storing them in plain text.

2 Likes

Hoping this gets picked up again…someone has this up and running now, via kubernetes. Using Hashicorp Vault to store secrets

1 Like

I am running Home Assistant onto Kubernetes and using SealedSecret from bitnami to securely store secrets.yaml inside my github repo.


---
kind: SealedSecret
apiVersion: bitnami.com/v1alpha1
metadata:
  name: secrets
  namespace: home-automation
spec:
  encryptedData:
    home_city: AgDUbLU7L334basktEdTU5hynRJUhT7u/850mkvKP/EXIgtzpZTwkQCnaFQRQAhFKVwjvlcCqaHxGCVfjg3F2kGivIamRxlDKt2FF8kVQHm1cYuqn708CjOrPOvlUm/Yd+GBNE30T7c5I2YoEizKhLsZjf8zaSNsv9BEscgE/dqRCSDgK9xcurdOK1TY1foxh+1UuWjpg++H4GHnMsuyJsKE2HnyrZ1vcG54l2usRpd7d37rkcmihtywcHs8PSBLqydIjQxRMrYtnTHwtC+ZoxLoqPWjjpBZ0ndNlGcBKpkt2e6ypR0vgCmHO2RE5H3eeAS1PqbWBfTCRsPeCCKiPDLx2IZlCcIdWomBgJ2tk+UzPWRaYKYwt3Iv+3+AB78kIiycPsAQNTpJgs0JWhVjx3kA42eVI71rNAndbVffofs8CYihhQSuK9ZEnna9gKPoDM80HWqQyRX4rgdwwS4xsuil1sWXWF87tn9EMmxh/ox2sBbJWxo3FnTe4z/gBD7k3zzAhOxbPh1noJCRvOBg1N4tJ5fz/8JAkwzF0tkI1AIsnqoiOyBlvA4wzAFrUf+EJFYnArpvj2P2W9sajoTvizIG2scYaLCl5hYdextwzmLki+G9+PPGVoWmHT8x9jGjeuZj4eHMRS4syc9Hx2tkMdPBqq07G6wzdy7cWAPGMUR8zJpUABgWXC01WX2dBbXZSXcUyso=
    home_latitude: AgAXfKHiBS0dIHRoODyLtSaYGwXSw1RfuDeUepGyrI5VaMiboPOo6pOLCspv0ObQMA1rV2N6UZR0IaP0KdFZiWC1dYv18PKchb3KROjpYtoL2MxAlcQ+Ua2MaUUqLM6rwt5Sism5A3g2WO6HqB2PK7DTp3+6XhhzAajcgl/dM1TZCo7fACZGlEMzSSXP0ZnubGjqT4iBoyWAO/jtQ16oyICOWr9VMMAmBbEMTf2ljkRQJRPW18PXSHSUwmV1LucYZxrZyxUrt9qSXLzNedbVd868+T9DOdYFA/1MxPCIt6BaS87rTtCykNYY0jClwNRT8u3TbOrjt9rLcR75Pez6p25SUwkYDMPAoedSQ=
    home_longitude: AgDHcGDimGbOFt4MiFLfRMVcapPaELs5ltNKz4H/KhmppaezkxbhCoCOsSqpXlZ1GN0mofznsfapMdlJxCojOl3D7AL9NgZmE3UFnEje46Lu8LCUdLbHvzXKUY9X/o8yGS7bzNrNIpae8fRuy8o/ccVXwTukTRLpPVUmTIwVnUXqIT0XhYspFfkefjIkjInfuGNjjdjeBHV+rOPDQpw94LMu0/xky2+y/BI63yPvlWkvw7yOidl332mDk3peg42uWvjA==
    home_number: B3QlPHgsTdwsFxA0BmhDwVSPeng/v1rTy+8QeCKdt2p8J2
    home_zipcode: /kfSQX2w4+iOqOh9+bm/qrei+m6PrVJV/C9Oy52Wv3g2RJ19BSAFDtbfsQkDTK8rWwXAmHucPiFzxVwmHKGwkySMF+jtOAW2SYdga+WqZpbYgBKzUKxWVfNDyrXoxDE+wZn7x+2MLReayS20=
    mysql-password: AgApe2KjSZhVniU1L9W1tiISU5p1vx/gn49iScakiCzguKYeGseWhJjFvSqvvgoVoUzfAOYdBzTxiSvEMWeKvzfcStv8ETBS4MH6g7/hopzn86KDXuw8LfQGTOkVj+tBkZjGfXRBacBsZxnPiOM8Sxc/gSFTI1Ah9zbbPM28JrCH0NBP7+2eiwpzfkkjPYZLpYjWZRuIEbj+cPVKFHX/hdzuU/cUEe2J97BkXX9uC7tTk+I5oiTzBC9rHWQRkKdUq27lFTOLke9w3xlygPBbNIZHQaMBoEKD7CPqvCACu1voxPypix/w6oG7E4bBv7KCfJkR9NM38km6VFYvkdEzsZPGdjwZuP/VmdaXdqR5gWUmyzXre+bN3+HDuXCbY9p3uD8dKYdiRxG8V8qyTh3oExLsvNGXVQsxooYOk2t15iTlIY1T+1LXup30GweShoJvv3YNi3l//BxHDHeDY5P2kBU/HhAAGTCZg+x8AM2nhDjd9LJZCLsWUPuD67EewrTNVQaBWK64l0smVxrlglgIrTUcR2bugwWiyOTA+6Yw5mEU/8whbnh7EBYrwlE3ymTtbaOH2Y+1cMwYK3JU9sGo8eIPgsC0DJHgXCJsyzwW34kSwjapbhVqE59FyQZkr3s4pmhKWMafr2FZYwEy0DtQX6wXpjwiMzX31wisPrtiFD0B/sv3enafac9SZ6MV7Q0QuF6bDlxIrlEQ2k/mVtZuhVHiUZdcW/Fn
    mysql-username: AgAu6EdT0IakH5Ns4TiSUI1WaXVzo1BPa+dvw/N+f3WSnr6oNUV/XIA/xkkFYJ8nsVqcelYIX7KqBzIDwVtJZAe9Ts4KocRjAACX+itbuCf90T844GJInxV/Iap7LJPi4XVP2ZFpOy/Ed465X49sSm1E8L+YhxxCHr0XNG9rtTRRFV8Rh+n3lC7rECziZ7u+xfSCBV0XhHl6ZvOEMTiqLdgpsbtyxEclTe/n0jKRQvo+BpqMNwL8MT+52h4WyoPWGcehxzFLcZ82CHPIOZERVl7AGlyo39s16LuwSLRQaSGzkBPh8V4MHXZkKaMQQqOp4UGkxRQm9aYr/7JT3dxogb1RQkB6biw0l7JElVkLysXj8Vmw9qhBAJdnnmY8TIn1WLKb1xnTwBZ3K6UQ6FdN9RMeuJ+/zeQz1JJKoafvWzlWGrZopCfKdwudAaPSbQ0rxuIsP9wFgMsLalJzMyBfy3pA4VE477mPeJTVaTiKNs2toVRyK1gDyL/LzuOSaijixrnJxp+fuTLpzHqLdmsbyFspesB4E88gSqdEAbxaXmHiij1ac0tGwj2jiSRhRM65hpTTGGT/jUtvkdF3SAulPuk9GgUMBxIOk6B/ZAT1P/wOeD13sSVFDi76JWO1uUd0imHo6zQW31qLWtcCVtQJ8aTnlKPYD8NOEoSFltvKcEUMy/orLIpSJJZBDQyTkEMhXYi/8Bv+QJKkeFB0yWoSjw==
    xiaomi_cloud_password: AgAJcv9vZvcmF3dYX/xaHB7WIAyFjvaGjVsVykyvZSVTk+peiCGMPUmEZ9ydNnBvPAB+YZG9x3eyqP0Yn3wOI88Qph0rC3wH++D485kEPesBPsrhcVSVIGlAUQ+oGOk7JUDoq3T3GS0gfP071064yZAp8kH0Mi/UZNC8pKWEScQhBo061Naq+UI8OQY/2yKx4JHDTIWorv/61mObaflxw8v1rnyR1hPyzXmDC0/VM3SoRhTZY+wHLHCl0n/f5QU+tU2+TZaHIbuKYfBEXoJniGnNlI74236aZ4UdnX/PX7p2yF8pox4fic46bn7OhEPpRIhSZVZgoIJcQFhShqMuUMdyoM7N+Hh8bsRxiZL4QSwHJWZTQeXAsngWjnzgC7Gh4s9JJxMnlbZGL2WXKiNzBrD/plpQeOiShQGf2VsF2gUREtpH5ZPwUd+H3JgDz010Xv451PRi3sTzWAcN86ScqNPmYNC6FF8q5bu4+YmxomN9iVBr1Xm0NMIVzHjer/TlZgQca8aSgye+TUtjkoqcW9qhnhXqlEqAR2/HYMhZ384kCv0lN3CVjFQLGV6BCKWkQAJ5R9xHG1ZX0kH+v71why2CIZcJ0dCRayd0RdTvmLVDOgNguR72VWXXmj0rKRNAs+eLgqOa2T7YCGxoEXiLoaRu3HvLwh18HxnSsHSjGLCCAF+UoF0zCLbGWJIQIcEnbFtLLeD3VDOAiuUPoAzkH8ix
    xiaomi_cloud_username: AgAbhK9jR6q/0/v0NnqwMcm8dhUOwbpI5qDXpnSNfAyHEWO9CjOSOKjhMfMjpUc7xMlm/6cjaZiHVWx6DvjbKJXDj78LGYVF+klYVVCB478Z/kdv0bqTaqtzWp87IrPHB6p0KPwtHW3Gqk1pTALR9BDt7pCp5ZWWAAOp6tDLbo+evT7yMY3hpZeB2YR9L0irDr4LXWdSn0Cueb0IPTNF9tzsH9a1kPSxE5huQlOgjLxLqqb8i0Xk7MJe3r2qsXGigSjA2uPn3QrpPfkrPZGqgu19oPO/+3QOc23b92z/mE88v9JProkbK9vQ71aoTUDB9SQ9cKaXruR5jFIXCzJf2Od4O/ER5j6QpA4Ty/roebVurPsizudUQ5J8y3a/IYVBvm2HLg+m6Oisd6rh/SWKjzrJMMFnmxCLAL3Op1TiED2bb+GJAdmZMQBN+3O13PsHjWQvBLRgo+uAtOOP4c1qCp1udRgdzwsisLYHqAjr4gdbqG9H4eR0mU1CBJnHKnweymD702ONhBUwa5Gsji9vQk2/AoYoYj/QZJ1MGTdcZJgl2ZFGp7icrzWqSRW+mhjDJo3eXURSX2yJmzYw+QMpkBxb3FNbW0Kl/FFB4qwfreY+DDH4jZSV+UpLi8Zr5xXj3CFa8l/rK2r3EChYmPvONACuRZrzN13Je9DmjY9fvZYrVgbrPtpttPXPf6+lhDa5DEUp2ajwLqKCVeCRwZwcPGX5
    xiaomi_vacuum_s6_token: AgAcm9eDmKZBcDhP1X0vRxJnKIoc8a8vxfDFHqRgtWKdPukqOl0lgKuaR7fhc3zwc04bGg6FwKgxNWNtqmisoqIKXSI4BCQfu6qkyWNH26pbxDmfPuC9BEKp5oLQgdtld+1RKI8RN/0TFUi9oto2qNK0L3MuYHbSuSeQxwrTb0BrGssqPSvflVHzizaK3mlnDM1uyZnSSbaL4Cnvgpf4ZruO4Ucq2dJUGh8A/fK3NIk+JxedvzBV5bOvKsz3emlFykmqSeP3++lVQU7q8VYEmFQyz3UWNrinMz3FFAdcMFt1xpY50vgJbbiNlb3LMEBPv5Ym8fZlna9LVviNeRXHd5xC0qL7G+mvOXhPoEV4GNxr8P+ltyrSC0jSB/txOKN52ZM/yoGOKWQsecwibmoY5/BaHDjzzXE0LHMjeaWrBTRvGHsFDN/2rdLw4+Y2MOYQc33xJ7F4RHbBotpfP9vS3p76ElFxN3ZE7eGkB5uB58A7rN66OxSNGIImtgqOxAQ+IOYQnrtFT4yCO1IF2oR9ahV6Dr2097z9FRGEv4/uj35LRFL5JD6teWIYApLQaiNM1LeksccAxTxoH3lIr7wox1ZigUHUU/s8Kb8TpHq+ySdtlX9hEkWUboplyVPklq/g5p9L47qPXqAHKLwA06Pe0sBJyig5qqiDy3G7m3FObeHgko3aULEm2fOL7Ha5A7gPzBrHbDX22ASXaA60Oj2IsQxOLUaTeq+1AATJffWcx8XCXA==
  template:
    data:
      secrets.yaml: |-
        db_url: mysql://{{ index . "mysql-username" }}:{{ index . "mysql-password" }}@home-assistant-mysql.home-automation.svc.cluster.local/home-assistant?charset=utf8mb4
        home_zipcode: {{ index . "home_zipcode" }}
        home_number: {{ index . "home_number" }}
        home_city: {{ index . "home_city" }}
        home_latitude: {{ index . "home_latitude" }}
        home_longitude: {{ index . "home_longitude" }}
        nvidia_shield_ip: 192.168.30.60
        xiaomi_cloud_password: {{ index . "xiaomi_cloud_password" }}
        xiaomi_cloud_username: {{ index . "xiaomi_cloud_username" }}
        xiaomi_vacuum_s6_token: {{ index . "xiaomi_vacuum_s6_token" }}
        proxmox_host: test
        proxmox_username: test
        proxmox_password: test
    metadata:
      labels:
        app.kubernetes.io/name: home-assistant
      name: secrets
      namespace: home-automation
1 Like

node-red does it very nicely. It uses a root secret which is then used to encrypt all other secrets. It would be nice to have HA use similar concept. The idea is to limit the blast radius in case of breach. While that single secret could be used to decrypt other secrets, it reduces the risk a bit since attacker would have to compromise both the files and security algorithm used.

2 Likes