Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with OS Keychains #201

Open
CMCDragonkai opened this issue Jul 5, 2021 · 3 comments
Open

Integration with OS Keychains #201

CMCDragonkai opened this issue Jul 5, 2021 · 3 comments
Labels
enhancement New feature or request r&d:polykey:core activity 1 Secret Vault Sharing and Secret History Management research Requires research

Comments

@CMCDragonkai
Copy link
Member

CMCDragonkai commented Jul 5, 2021

Is your feature request related to a problem? Please describe.

Applications often delegate credential storage to a third party program on the OS rather than writing it themselves. Since Polykey is not a well-known credential storage, then PK won't immediately be used as a credential storage system. Therefore this limits the UX of using Polykey. It's an integration problem.

Browsers maintain their own credentials, and every other system has their own as well.

These third party programs include:

For example Docker is capable of integrating into these keychains as you can see here: https://github.com/docker/docker-credential-helpers

By default, Docker looks for the native binary on each of the platforms, i.e. “osxkeychain” on macOS, “wincred” on windows, and “pass” on Linux. A special case is that on Linux, Docker will fall back to the “secretservice” binary if it cannot find the “pass” binary. If none of these binaries are present, it stores the credentials (i.e. password) in base64 encoding in the config files described above.
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

This shows you that Docker login credentials are not even encrypted at rest.

Android and iOS are also important here. In fact Android and iOS integration I believe actually supersedes browser integration. So you don't really create a "browser plugin" for the mobile phone browsers, but a password manager can be integrated to the OS. For example: https://developer.android.com/guide/topics/text/autofill and https://developer.apple.com/documentation/security/password_autofill/

Describe the solution you'd like

It would be nice for Polykey to capable of performing secret injection into the OS keychain to use these as potential integration points.

Describe alternatives you've considered

A generic integration point on desktop OSes is using the clipboard or performing autofill.

For autofill/autotype on desktop, we should look at this:

I think desktops don't have a direct autofill/autotype feature builtin unlike mobile systems. But this would be quite useful for desktop applications.

Additional context

@CMCDragonkai CMCDragonkai added the enhancement New feature or request label Jul 5, 2021
@CMCDragonkai
Copy link
Member Author

This is new feature request, and it requires review into our product vision, and if accepted, we are meant to create a design issue for this.

@CMCDragonkai CMCDragonkai added the research Requires research label Jul 5, 2021
@CMCDragonkai CMCDragonkai added the r&d:polykey:core activity 1 Secret Vault Sharing and Secret History Management label Jul 24, 2022
@CMCDragonkai CMCDragonkai self-assigned this Jul 10, 2023
@CMCDragonkai
Copy link
Member Author

CMCDragonkai commented Jan 11, 2024

Some recent experiments on running a grafana-agent due to #599 was interesting in showcasing how systemd works to manage credentials especially on NixOS.

This has some relation with #169.

The grafana-agent service config looks like this in NixOS:

{
  enable = true;
  settings = {
    integrations = {
      prometheus_remote_write = [
        {
          basic_auth = {
            username = "\${METRICS_USERNAME}";
            password = "\${METRICS_PASSWORD}";
          };
          url = "\${METRICS_URL}";
        }
      ];
    };
    logs = {
      configs = [
        {
          clients = [
            {
              basic_auth = {
                username = "\${LOGS_USERNAME}";
                password = "\${LOGS_PASSWORD}";
              };
              url = "\${LOGS_URL}";
            }
          ];
        }
      ];
    };
    metrics = {
      configs = [
        {
          name = "integrations";
          remote_write = [
            {
              basic_auth = {
                username = "\${METRICS_USERNAME}";
                password = "\${METRICS_PASSWORD}";
              };
              url = "\${METRICS_URL}";
            }
          ];
        }
      ];
      wal_directory = "\${STATE_DIRECTORY}";
    };
  };
  credentials = {
    METRICS_URL = "/run/keys/grafana_agent/metrics_url";
    METRICS_USERNAME = "/run/keys/grafana_agent/metrics_username";
    METRICS_PASSWORD = "/run/keys/grafana_agent/metrics_password";
    LOGS_URL = "/run/keys/grafana_agent/logs_url";
    LOGS_USERNAME = "/run/keys/grafana_agent/logs_username";
    LOGS_PASSWORD = "/run/keys/grafana_agent/logs_password";
  };
}

What's interesting here is that the credentials specially managed by the Nix derivation for the systemd unit.

Here you can see that they are key values where the value is a path to a file. The file path is in /run/keys which is supposed to be owned by the root user. By keeping as files instead of directly embedding them in the config, we avoid loading the credentials directly into the evaluated Nix which would be globally readable in the /nix/store.

This is then used here: https://github.com/NixOS/nixpkgs/blob/3dc440faeee9e889fe2d1b4d25ad0f430d449356/nixos/modules/services/monitoring/grafana-agent.nix#L127C1-L161C7

The 2 key things are:

LoadCredential = lib.mapAttrsToList (key: value: "${key}:${value}") cfg.credentials;

Here you can see how Nix is used as a templating language for arbitrary configuration. This systemd directive basically reads the file paths and puts them into a special location called the CREDENTIALS_DIRECTORY.

This is all documented here: https://systemd.io/CREDENTIALS/

Later inside the script that actually gets executed, it does this:

# Load all credentials into env if they are in UPPER_SNAKE form.
if [[ -n "${CREDENTIALS_DIRECTORY:-}" ]]; then
  for file in "$CREDENTIALS_DIRECTORY"/*; do
    key=$(basename "$file")
    if [[ $key =~ ^[A-Z0-9_]+$ ]]; then
      echo "Environ $key"
      export "$key=$(< "$file")"
    fi
  done
fi

Thus ensuring that all the files under CREDENTIALS_DIRECTORY gets loaded as an environment variable.

By default systemd doesn't do this, because environment variables are not as isolated as files as per:

Access to credentials is restricted to the service’s user. Unlike environment variables the credential data is not propagated down the process tree. Instead each time a credential is accessed an access check is enforced by the kernel. If the service is using file system namespacing the loaded credential data is invisible to all other services.

Basically this is the problem of the confused deputy. If there other processes whether they are sibling processes or child processes with the same ambient environment, they may be able to access those credentials by looking at the environment of a given process. File systems then have more fine grained access control that can be applied to a given situation. But with script it just gives some flexibility in using environment variables.


Note that I have some problem with it reloading credentials: systemd/systemd#21099

This ends up requiring a restart of the service atm.

@CMCDragonkai
Copy link
Member Author

CMCDragonkai commented Aug 17, 2024

The PAM framework on Linux allows one to automatically unlock the OS keychain using the same password that is used during login of the user session. This allows one to open the keychain (containing various necessary secrets) and allow the keychain to start injection operations.

In this sense, the usage of a keychain here is an upgrade from sprawling plaintext secrets over the home directory. The OS user-session system gates access to the home directory normally, and most programs still expect the ability to load secrets from a file path. An OS keychain like PK at the very least enables security-at-rest compared to plaintext secrets in the home directory.

In this sense it can help solve the secret zero problem of user-level background service of PK by pushing the buck to the OS login session.

@CMCDragonkai CMCDragonkai removed their assignment Sep 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request r&d:polykey:core activity 1 Secret Vault Sharing and Secret History Management research Requires research
Development

No branches or pull requests

1 participant