pyinfra-dev / pyinfra

pyinfra turns Python code into shell commands and runs them on your servers. Execute ad-hoc commands and write declarative operations. Target SSH servers, local machine and Docker containers. Fast and scales from one server to thousands.
https://pyinfra.com
MIT License
3.91k stars 382 forks source link

User-friendly but still somehow secure sudo-password saved in system KeyChain (via python-keyring) #1117

Open mpasternak opened 5 months ago

mpasternak commented 5 months ago

Hi,

I'd like to contribute this small piece of code, which, when imported: 1) checks system keyring library (via python-keyring) for the presence of a key for ("pyinfra", "base64-encoded-hostname") 2) if the passwords exists, it gets used for sudo, 3) if the password does not exist, user is prompted for it.

This could be added to docs as a somehow opinionated, but still safe & simple solution.

Is your feature request related to a problem? Please describe

Yes, I think docs don't give a clear (opinionated, but still clear) solution to the problem of sudo password stored somewhere. Either you store it in inventory (plain-text, unsafe) or get asked about it every time. My small piece of code saves those to the system keyring (KeyChain on Apple for example) which should be somehow more secure than plaintext passwords in the inventory to the all of the hosts you maintain.

Describe the solution you'd like

I'd like somebody read it, review it and give me a green light so I can prepare a patch for the documentation. Or, tell me to get lost :-)

"""
use_sudo_password_from_keychain.py

This module, when imported in your pyinfra actions (deployment) file
will try to read sudo password for a given host from system keyring library
(KeyChain on macOS, for example). If the password does not exist in the keychain,
the user gets a prompt and a chance to enter it.

Before you use this library, you must install keyring library
<https://pypi.org/project/keyring/> for python via ``pip install keyring``.
"""

import base64, keyring, getpass

from pyinfra import host

sudo_password = host.data.get("_use_sudo_password")

if sudo_password is None:
    b64hostname = str(base64.b64encode(host.name.encode("utf-8")))
    sudo_password = keyring.get_password("pyinfra", b64hostname)

    if sudo_password is None:
        sudo_password = getpass.getpass("sudo(8) password for " + host.name + "> ")
        keyring.set_password("pyinfra", b64hostname, sudo_password)

    host.data.override_datas["_use_sudo_password"] = sudo_password
mpasternak commented 5 months ago

Unable to attach .py files, oh well. Should I create GitHub gist for it?

mpasternak commented 5 months ago

Oh well, just noticed it has to be wrapped in a function and this function must be called each time in the deployment module. But you should get the idea. Is the docs/FAQ section suitable for this code? Do you have any "contrib" directory for such modules?

Fizzadar commented 5 months ago

This is awesome! I think this would be really great as an example in the examples repo: https://github.com/pyinfra-dev/pyinfra-examples

The docs will (v.soon) inline the examples list directly, we can add a FAQ too since this is a highly requested feature/addon 👍

mpasternak commented 5 months ago

Hi Nick, I'm glad you like it.

Let's keep this issue open so it won't get lost.

This kind of stuff would not be possible with other tools I think, unless you'd write some extension module for those. But as we using Python all the time, that's basically a large one-liner :-)