From b8f3d1bc659095d0e4b30f81468ab7945d865259 Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Tue, 30 Apr 2024 19:15:26 +0800 Subject: [PATCH 1/7] feature: add documentation --- README.md | 163 +++++++++++++++++++++++++++++++++++++++++++++- example/README.md | 7 ++ 2 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 example/README.md diff --git a/README.md b/README.md index 70f8101..20e0c50 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,161 @@ -# saltstack-age-renderer +# Saltstack renderer for age-encrypted secrets -Prototype of an [age](https://github.com/FiloSottile/age) renderer for -[Saltstack](https://saltproject.io/). +This project introduces a [SaltStack](https://saltproject.io/) renderer +integrated with [age](https://age-encryption.org/), +a modern and simple encryption tool. +SaltStack is an open-source configuration management system that allows you to +automate the setup, deployment, and management of your infrastructure. +Integrating age encryption enhances SaltStack by providing a secure method to +handle secrets. + +By using age, this renderer allows you to securely store encrypted secrets +directly in your source control. +This is particularly useful for environments where security and privacy are +paramount. +Only Salt masters (or masterless minions) configured with the appropriate age +identity or passphrase can decrypt these secrets, ensuring that sensitive +information remains protected even if source control is compromised. + +The typical use case for this extension involves encrypting secrets stored in +Salt's pillar data, +enhancing security without sacrificing convenience or functionality. + +## Requirements + +This package has been tested with Saltstack 3007.0 on Ubuntu 22.04.4 LTS +(Jammy Jellyfish). + +## Installation + +If you use the [official Saltstack package](https://repo.saltproject.io/), +you can simply install it using: + +```sh +sudo salt-pip install saltstack-age +``` + +## Configuration + +age can be used to encrypt data using either a passphrase or an identity file. +This extension supports both, and they can be defined either in the Saltstack +daemon configuration file, or in the daemon environment. + +| Type | Configuration directive | Environment variable | Expected value | +| ------------ | ----------------------- | -------------------- | ---------------------------- | +| identity | `age_identity_file` | `AGE_IDENTITY_FILE` | Path of an age identity file | +| passphrase | `age_passphrase` | `AGE_PASSPHRASE` | An age passphrase | + +You can check this [example configuration](./example/config/minion). + +## Secret encryption + +Encrypted secrets are formatted as `ENC[age-passphrase,CIPHERTEXT]` or +`ENC[age-identity,CIPHERTEXT]`, depending on the encryption type. +`CIPHERTEXT` is the age-encrypted value, encoded with base64. + +This package provides a handy CLI tool to make it easier: + +```sh +$ saltstack-age -P secret-passphrase enc secret-value +ENC[age-passphrase,YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCB1QndwT3dJejhaSEtZZlIxeFEvZk5RIDIwCmhrcm9OY0tTOWdwNkhWbDdadlNIOHRFYmFLdkpZSjhLTktTWXhZVHFHKzgKLS0tIHFJWVRNc0JzTkpKNHJ1TFBuZ2tybWt0WWVQR0wrbjVnMmlZYzRaWVlBbFkKPWQu4lawaAu1owDXPDwwmj9/tN9/5NF/Avd4jPrLoy/ugUb0ciqm8H5My44=] +``` + +> [!CAUTION] +> While it is convenient to pass all arguments to the command-line, +> be careful to not leak credentials while doing it. + +The tool exposes multiple options to provide the passphrase and identity files. +You can see them in details by reading its help: `saltstack-age --help`. + +## Pillar data formatting + +The renderer must be specified on the first line of the pillar data files that +contain encrypted values: + +```yaml +#!yaml|age +``` + +Then you can define your secret values as: + +```yaml +#!yaml|age +secret: ENC[age-passphrase,YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCB1QndwT3dJejhaSEtZZlIxeFEvZk5RIDIwCmhrcm9OY0tTOWdwNkhWbDdadlNIOHRFYmFLdkpZSjhLTktTWXhZVHFHKzgKLS0tIHFJWVRNc0JzTkpKNHJ1TFBuZ2tybWt0WWVQR0wrbjVnMmlZYzRaWVlBbFkKPWQu4lawaAu1owDXPDwwmj9/tN9/5NF/Avd4jPrLoy/ugUb0ciqm8H5My44=] +``` + +For reference, you can read this [example](./example/). + +## FAQ + +### Why did you write this extension? + +As a fan of GPG, I explored the +[GPG renderer](https://docs.saltproject.io/en/latest/ref/renderers/all/salt.renderers.gpg.html) +offered by SaltStack. +While GPG is robust, I found it somewhat cumbersome for smaller projects. +The simplicity and effectiveness of age encryption inspired me to develop this +extension, +providing a straightforward solution for managing secrets in Salt environments. + +### Do I need to install age separately? + +No, there's no need to install age separately. +This extension utilizes [pyrage](https://github.com/woodruffw/pyrage), +a Python wrapper that embeds [rage](https://github.com/str4d/rage), +a Rust implementation of age. +It simplifies the installation process by embedding all necessary functionality +within the extension itself. + +### How do I ensure my secrets are secure when using this extension? + +To maximize security: +- Always use secure channels for transferring sensitive information, + including age identities and passphrases. +- Store your age identities and passphrases securely, using environment + variables or secure files that are not checked into source control. +- Be cautious with logging and command-line usage as these can inadvertently + expose sensitive information if not handled properly. + +To ensure calls to the `saltstack-age` command are never logged in your +bash history, add it to your +[HISTIGNORE](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-HISTIGNORE) +variable. + +### What should I do if I encounter errors during encryption or decryption? + +First, verify that your age identities and passphrases are correctly configured +and accessible to the Salt master or minion. Check for typos or incorrect paths +in your configuration. +If the issue persists, refer to the detailed error messages provided by Salt and +age for further troubleshooting. +You can also seek help from the Salt community +or the issue tracker for this project. +Please provide your entire configuration so we can reproduce the error +(and use throwaway credentials to do so). + +### Where can I find more resources? + +For more detailed guidance on using age, +visit the official [age documentation](https://age-encryption.org/). +For SaltStack, consult the +[SaltStack documentation](https://docs.saltproject.io/) and community forums. +These resources offer comprehensive information and community-driven support +that can help you effectively utilize age encryption in your SaltStack projects. + +## Development + +* Environment is managed with [rye](https://rye-up.com/) +* Create a virtualenv: `rye sync` +* Check typing: `rye run basedpyright` +* Check formatting with ruff: `rye fmt -- --check` +* Check linting with ruff: `rye check` +* Run tests: `rye run pytest` + +See [workflow](./.github/workflows/build.yaml) for reference. + +## Release + +* Build package: `rye build --clean --wheel` +* Publish package: `rye publish` + +See [workflow](./.github/workflows/release.yaml) for reference. diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..d850b8c --- /dev/null +++ b/example/README.md @@ -0,0 +1,7 @@ +# saltstack-age example + +This is a working example of a saltstack-age configuration. + +You can run it with `salt-call state.apply`. + +It is used in [integration tests](../tests/integration/). From ef7b5978c856c7542025e09dca4200e01bcfd184 Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Tue, 30 Apr 2024 19:24:16 +0800 Subject: [PATCH 2/7] chore: bump version to 0.2.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3477a5d..d607493 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "saltstack-age" -version = "0.2.1" +version = "0.2.2" description = "age renderer for Saltstack" authors = [{ name = "Philippe Muller" }] dependencies = [ From 3b2c9a1e3380e68dc531dd1030e2b2d427dc7046 Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Tue, 30 Apr 2024 19:41:42 +0800 Subject: [PATCH 3/7] feat: document URLs --- CHANGELOG.md | 1 + pyproject.toml | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4704eed --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +# saltstack-age change log diff --git a/pyproject.toml b/pyproject.toml index d607493..38adf94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,12 @@ dependencies = [ readme = "README.md" requires-python = ">= 3.8" +[project.urls] +Homepage = "https://github.com/pmuller/saltstack-age" +Repository = "https://github.com/pmuller/saltstack-age.git" +Issues = "https://github.com/pmuller/saltstack-age/issues" +Changelog = "https://github.com/pmuller/saltstack-age/blob/main/CHANGELOG.md" + [tool.rye] managed = true dev-dependencies = [ From ade93248a481c872c84bc9c84f53a5c97ff3ba50 Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Tue, 30 Apr 2024 19:44:55 +0800 Subject: [PATCH 4/7] feat: use MIT license --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 38adf94..626b9f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ ] readme = "README.md" requires-python = ">= 3.8" +license = {text = "MIT License"} [project.urls] Homepage = "https://github.com/pmuller/saltstack-age" From 32da88f60edcf2954c0ec5936e095decb73d822e Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Tue, 30 Apr 2024 19:45:50 +0800 Subject: [PATCH 5/7] feat: add PyPI keywords --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 626b9f7..3b811cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ readme = "README.md" requires-python = ">= 3.8" license = {text = "MIT License"} +keywords = ["saltstack", "salt", "pillar", "age", "encryption", "security"] [project.urls] Homepage = "https://github.com/pmuller/saltstack-age" From f021277d27a3dda79d89667171b03d78d5d86024 Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Tue, 30 Apr 2024 19:49:18 +0800 Subject: [PATCH 6/7] feat: add project classifiers --- pyproject.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 3b811cd..df0f28d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,17 @@ readme = "README.md" requires-python = ">= 3.8" license = {text = "MIT License"} keywords = ["saltstack", "salt", "pillar", "age", "encryption", "security"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Security", + "Topic :: System :: Systems Administration", +] [project.urls] Homepage = "https://github.com/pmuller/saltstack-age" From b537f09cddaaaff86aceef56d10b8f0951c5c5ac Mon Sep 17 00:00:00 2001 From: Philippe Muller Date: Tue, 30 Apr 2024 19:50:34 +0800 Subject: [PATCH 7/7] chore: bump version to 0.2.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index df0f28d..8a16a07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "saltstack-age" -version = "0.2.2" +version = "0.2.3" description = "age renderer for Saltstack" authors = [{ name = "Philippe Muller" }] dependencies = [