mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 09:40:20 +00:00
Make the pass renderer more configurable
1. Allow us to make the pass renderer fail during pillar rendering when a secret corresponding with a pass path cannot be fetched. For this we add a master config variable pass_strict_fetch. 2. Allow to have prefix for variables that should be processed with the pass renderer. For this we add a master config variable pass_variable_prefix. 3. Allow us to configure pass' GNUPGHOME and PASSWORD_STORE_DIR environmental variables. For this we add master config variables pass_gnupghome and pass_dir.
This commit is contained in:
parent
b327996cf5
commit
bcf5dd82ed
3 changed files with 82 additions and 3 deletions
4
changelog/62120.added
Normal file
4
changelog/62120.added
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Config option pass_variable_prefix allows to distinguish variables that contain paths to pass secrets.
|
||||||
|
Config option pass_strict_fetch allows to error out when a secret cannot be fetched from pass.
|
||||||
|
Config option pass_dir allows setting the PASSWORD_STORE_DIR env for pass.
|
||||||
|
Config option pass_gnupghome allows setting the $GNUPGHOME env for pass.
|
|
@ -964,6 +964,14 @@ VALID_OPTS = immutabletypes.freeze(
|
||||||
# The port to be used when checking if a master is connected to a
|
# The port to be used when checking if a master is connected to a
|
||||||
# minion
|
# minion
|
||||||
"remote_minions_port": int,
|
"remote_minions_port": int,
|
||||||
|
# pass renderer: Fetch secrets only for the template variables matching the prefix
|
||||||
|
"pass_variable_prefix": str,
|
||||||
|
# pass renderer: Whether to error out when unable to fetch a secret
|
||||||
|
"pass_strict_fetch": bool,
|
||||||
|
# pass renderer: Set GNUPGHOME env for Pass
|
||||||
|
"pass_gnupghome": str,
|
||||||
|
# pass renderer: Set PASSWORD_STORE_DIR env for Pass
|
||||||
|
"pass_dir": str,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1604,6 +1612,10 @@ DEFAULT_MASTER_OPTS = immutabletypes.freeze(
|
||||||
"fips_mode": False,
|
"fips_mode": False,
|
||||||
"detect_remote_minions": False,
|
"detect_remote_minions": False,
|
||||||
"remote_minions_port": 22,
|
"remote_minions_port": 22,
|
||||||
|
"pass_variable_prefix": "",
|
||||||
|
"pass_strict_fetch": False,
|
||||||
|
"pass_gnupghome": "",
|
||||||
|
"pass_dir": "",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,34 @@ Install pass binary
|
||||||
|
|
||||||
pass:
|
pass:
|
||||||
pkg.installed
|
pkg.installed
|
||||||
|
|
||||||
|
Salt master configuration options
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
# If the prefix is *not* set (default behavior), all template variables are
|
||||||
|
# considered for fetching secrets from Pass. Those that cannot be resolved
|
||||||
|
# to a secret are passed through.
|
||||||
|
#
|
||||||
|
# If the prefix is set, only the template variables with matching prefix are
|
||||||
|
# considered for fetching the secrets, other variables are passed through.
|
||||||
|
#
|
||||||
|
# For ease of use it is recommended to set the following options as well:
|
||||||
|
# renderer: 'jinja|yaml|pass'
|
||||||
|
# pass_strict_fetch: true
|
||||||
|
#
|
||||||
|
pass_variable_prefix: 'pass:'
|
||||||
|
|
||||||
|
# If set to 'true', error out when unable to fetch a secret for a template variable.
|
||||||
|
pass_strict_fetch: true
|
||||||
|
|
||||||
|
# Set GNUPGHOME env for Pass.
|
||||||
|
# Defaults to: ~/.gnupg
|
||||||
|
pass_gnupghome: <path>
|
||||||
|
|
||||||
|
# Set PASSWORD_STORE_DIR env for Pass.
|
||||||
|
# Defaults to: ~/.password-store
|
||||||
|
pass_dir: <path>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +82,7 @@ from os.path import expanduser
|
||||||
from subprocess import PIPE, Popen
|
from subprocess import PIPE, Popen
|
||||||
|
|
||||||
import salt.utils.path
|
import salt.utils.path
|
||||||
from salt.exceptions import SaltRenderError
|
from salt.exceptions import SaltConfigurationError, SaltRenderError
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -80,6 +108,23 @@ def _fetch_secret(pass_path):
|
||||||
# Make a backup in case we want to return the original value without stripped whitespaces
|
# Make a backup in case we want to return the original value without stripped whitespaces
|
||||||
original_pass_path = pass_path
|
original_pass_path = pass_path
|
||||||
|
|
||||||
|
# Remove the optional prefix from pass path
|
||||||
|
pass_prefix = __opts__["pass_variable_prefix"]
|
||||||
|
if pass_prefix:
|
||||||
|
# If we do not see our prefix we do not want to process this variable
|
||||||
|
# and we return the unmodified pass path
|
||||||
|
if not pass_path.startswith(pass_prefix):
|
||||||
|
return pass_path
|
||||||
|
|
||||||
|
# strip the prefix from the start of the string
|
||||||
|
pass_path = pass_path[len(pass_prefix) :]
|
||||||
|
|
||||||
|
# The pass_strict_fetch option must be used with pass_variable_prefix
|
||||||
|
pass_strict_fetch = __opts__["pass_strict_fetch"]
|
||||||
|
if pass_strict_fetch and not pass_prefix:
|
||||||
|
msg = "The 'pass_strict_fetch' option requires 'pass_variable_prefix' option enabled"
|
||||||
|
raise SaltConfigurationError(msg)
|
||||||
|
|
||||||
# Remove whitespaces from the pass_path
|
# Remove whitespaces from the pass_path
|
||||||
pass_path = pass_path.strip()
|
pass_path = pass_path.strip()
|
||||||
|
|
||||||
|
@ -91,14 +136,32 @@ def _fetch_secret(pass_path):
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["HOME"] = expanduser("~")
|
env["HOME"] = expanduser("~")
|
||||||
|
|
||||||
|
pass_dir = __opts__["pass_dir"]
|
||||||
|
if pass_dir:
|
||||||
|
env["PASSWORD_STORE_DIR"] = pass_dir
|
||||||
|
|
||||||
|
pass_gnupghome = __opts__["pass_gnupghome"]
|
||||||
|
if pass_gnupghome:
|
||||||
|
env["GNUPGHOME"] = pass_gnupghome
|
||||||
|
|
||||||
proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env)
|
proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env)
|
||||||
pass_data, pass_error = proc.communicate()
|
pass_data, pass_error = proc.communicate()
|
||||||
|
|
||||||
# The version of pass used during development sent output to
|
# The version of pass used during development sent output to
|
||||||
# stdout instead of stderr even though its returncode was non zero.
|
# stdout instead of stderr even though its returncode was non zero.
|
||||||
if proc.returncode or not pass_data:
|
if proc.returncode or not pass_data:
|
||||||
log.warning("Could not fetch secret: %s %s", pass_data, pass_error)
|
try:
|
||||||
return original_pass_path
|
pass_error = pass_error.decode("utf-8")
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
pass
|
||||||
|
msg = "Could not fetch secret '{}' from the password store: {}".format(
|
||||||
|
pass_path, pass_error
|
||||||
|
)
|
||||||
|
if pass_strict_fetch:
|
||||||
|
raise SaltRenderError(msg)
|
||||||
|
else:
|
||||||
|
log.warning(msg)
|
||||||
|
return original_pass_path
|
||||||
return pass_data.rstrip("\r\n")
|
return pass_data.rstrip("\r\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue