feat: Add support for setting the salt-master keys via Docker secrets

This commit is contained in:
Carlos Álvaro
2022-09-19 21:38:33 +02:00
parent 54af1dc7ab
commit aee6f191e6
3 changed files with 198 additions and 43 deletions

View File

@@ -6,8 +6,14 @@ for the list of changes in SaltStack.
**3005_1** **3005_1**
- Add support for setting the `salt-api` user's password via docker secrets using the environment - Add support for setting the `salt-master` keys via Docker secrets using the environment variables:
variable `SALT_API_USER_PASS_FILE`. Has priority over `SALT_API_USER_PASS`. - `SALT_MASTER_KEY_FILE`: The path to the master-key-pair {pem,pub} files without suffixes.
- `SALT_MASTER_SIGN_KEY_FILE`: The path to the signing-key-pair {pem,pub} without suffixes.
- `SALT_MASTER_PUBKEY_SIGNATURE_FILE`: The path of the salt-master public key file with the pre-calculated
signature.
- Add support for setting the `salt-api` user's password via Docker secrets using the environment
variable `SALT_API_USER_PASS_FILE`.
- Note: Has priority over `SALT_API_USER_PASS`.
**3005** **3005**

111
README.md
View File

@@ -170,8 +170,8 @@ autosign_grains:
- domain - domain
``` ```
More info More info at:
at: [Salt Project - Autoaccept Minions From Grains](https://docs.saltproject.io/en/latest/topics/tutorials/autoaccept_grains.html) [Salt Project - Auto accept Minions From Grains](https://docs.saltproject.io/en/latest/topics/tutorials/autoaccept_grains.html)
### Master Signed Keys ### Master Signed Keys
@@ -203,6 +203,65 @@ docker run --name salt_stack -it --rm \
The newly created keys will appear inside `keys/generated/new_master_sign` directory. The newly created keys will appear inside `keys/generated/new_master_sign` directory.
#### Working with secrets
Master keys can be provided via Docker secrets. To do that, you have to set the following environment variable:
- `SALT_MASTER_KEY_FILE`: The path to the master-key-pair {pem,pub} files without suffixes.
Additionally, you can provide the master-sign key pair as well:
- `SALT_MASTER_SIGN_KEY_FILE`: The path to the master-sign-key-pair {pem,pub} files without suffixes.
- `SALT_MASTER_PUBKEY_SIGNATURE_FILE`: The path of the salt-master public key file with the pre-calculated signature.
Here you have a complete docker-compose example
```yml
version: '3.9'
services:
salt-master:
image: cdalvaro/docker-salt-master:latest
ports:
- "4505:4505"
- "4506:4506"
volumes:
- ./config:/home/salt/data/config
secrets:
- source: salt-master-key
target: master.pem
mode: 0400
- source: salt-master-pub
target: master.pub
mode: 0644
- source: salt-master-sign-priv-key
target: master_sign.pem
mode: 0400
- source: salt-master-sign-pub-key
target: master_sign.pub
mode: 0644
- source: salt-master-signature
target: master_pubkey_signature
mode: 0644
environment:
SALT_MASTER_SIGN_PUBKEY: True
SALT_MASTER_KEY_FILE: /run/secrets/master
SALT_MASTER_SIGN_KEY_FILE: /run/secrets/master_sign
SALT_MASTER_PUBKEY_SIGNATURE_FILE: /run/secrets/master_pubkey_signature
secrets:
salt-master-pem-key:
file: ./keys/master.pem
salt-master-pub-key:
file: ./keys/master.pub
salt-master-sign-priv-key:
file: ./keys/master_sign.pem
salt-master-sign-pub-key:
file: ./keys/master_sign.pub
salt-master-signature:
file: ./keys/master_pubkey_signature
```
### Salt API ### Salt API
You can enable `salt-api` service by setting env variable `SALT_API_SERVICE_ENABLED` to `True`. You can enable `salt-api` service by setting env variable `SALT_API_SERVICE_ENABLED` to `True`.
@@ -243,6 +302,10 @@ explicitly setting this variable to an empty string: `SALT_API_USER=''` if you a
As a security measure, if `SALT_API_SERVICE_ENABLED` is set to `True` and you don't disable `SALT_API_USER`, you'll be As a security measure, if `SALT_API_SERVICE_ENABLED` is set to `True` and you don't disable `SALT_API_USER`, you'll be
required to set `SALT_API_USER_PASS`. Otherwise, initialization will fail and your Docker image won't work. required to set `SALT_API_USER_PASS`. Otherwise, initialization will fail and your Docker image won't work.
`SALT_API_USER_PASS_FILE` env variable is available to provide the password via a file. This is useful when using docker
secretes. More info about how to configure secrets can be found in the subsection
[_Working with secrets_](#working-with-secrets).
With all that set, you'll be able to provide your _salt-api_ custom configuration by creating the `salt-api.conf` file With all that set, you'll be able to provide your _salt-api_ custom configuration by creating the `salt-api.conf` file
inside your `conf` directory: inside your `conf` directory:
@@ -352,7 +415,7 @@ gitfs_pubkey: /home/salt/data/keys/gitfs/gitfs_ssh.pub
**Important Note** **Important Note**
This image has been tested with a _ed25519_ ssh key. This image has been tested with an _ed25519_ ssh key.
Alternately, you may create a new RSA key with SHA2 hashing like so: Alternately, you may create a new RSA key with SHA2 hashing like so:
@@ -450,8 +513,7 @@ script: `/usr/local/sbin/healthcheck` (although it is disabled by default). It i
service is alive and responding. service is alive and responding.
If you are running this image under k8s, you can define a _liveness command_ as If you are running this image under k8s, you can define a _liveness command_ as
explained [here](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command) explained [here](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command).
.
If you use `docker-compose` as your container orchestrator, you can add the following entries to your compose file: If you use `docker-compose` as your container orchestrator, you can add the following entries to your compose file:
@@ -534,28 +596,31 @@ Below you can find a list with the available options that can be used to customi
installation. installation.
| Parameter | Description | | Parameter | Description |
|:--------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |:--------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `DEBUG` | Set this to `True` to enable entrypoint debugging. | | `DEBUG` | Set this to `True` to enable entrypoint debugging. |
| `TIMEZONE` / `TZ` | Set the container timezone. Defaults to `UTC`. Values are expected to be in Canonical format. Example: `Europe/Madrid`. See the list of [acceptable values](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). | | `TIMEZONE` / `TZ` | Set the container timezone. Defaults to `UTC`. Values are expected to be in Canonical format. Example: `Europe/Madrid`. See the list of [acceptable values](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). |
| `PUID` | Sets the uid for user `salt` to the specified uid. Default: `1000`. | | `PUID` | Sets the uid for user `salt` to the specified uid. Default: `1000`. |
| `PGID` | Sets the gid for user `salt` to the specified gid. Default: `1000`. | | `PGID` | Sets the gid for user `salt` to the specified gid. Default: `1000`. |
| `SALT_RESTART_MASTER_ON_CONFIG_CHANGE` | Set this to `True` to restart `salt-master` service when configuration files change. Default: `False` | | `SALT_RESTART_MASTER_ON_CONFIG_CHANGE` | Set this to `True` to restart `salt-master` service when configuration files change. Default: `False`. |
| [`SALT_LOG_LEVEL`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#log-level) | The level of messages to send to the console. One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. Default: `warning` | | [`SALT_LOG_LEVEL`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#log-level) | The level of messages to send to the console. One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. Default: `warning`. |
| `SALT_LOG_ROTATE_FREQUENCY` | Logrotate frequency for salt logs. Available options are 'daily', 'weekly', 'monthly', and 'yearly'. Default: `weekly` | | `SALT_LOG_ROTATE_FREQUENCY` | Logrotate frequency for salt logs. Available options are 'daily', 'weekly', 'monthly', and 'yearly'. Default: `weekly`. |
| `SALT_LOG_ROTATE_RETENTION` | Keep x files before deleting old log files. Defaults: `52` | | `SALT_LOG_ROTATE_RETENTION` | Keep x files before deleting old log files. Defaults: `52`. |
| [`SALT_LEVEL_LOGFILE`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#log-level-logfile) | The level of messages to send to the log file. One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. Default: `warning` | | [`SALT_LEVEL_LOGFILE`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#log-level-logfile) | The level of messages to send to the log file. One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. Default: `warning`. |
| [`SALT_API_SERVICE_ENABLED`](https://docs.saltproject.io/en/latest/ref/cli/salt-api.html) | Enable `salt-api` service. Default: `False` | | `SALT_MASTER_KEY_FILE` | The path to the master-key-pair {pem,pub} files without suffixes. Keys will be copied into the pki directory. Useful to load the password from secrets. _Unset_ by default. |
| `SALT_API_USER` | Set username for `salt-api` service. Default: `salt_api` | | [`SALT_API_SERVICE_ENABLED`](https://docs.saltproject.io/en/latest/ref/cli/salt-api.html) | Enable `salt-api` service. Default: `False`. |
| `SALT_API_USER_PASS_FILE` | `SALT_API_USER` password file path. Use this variable to set the path of a file containing the password for the `SALT_API_USER`. Useful to load the password from secrets. Has priority over `SALT_API_USER_PASS`. _Unset_ by default | | `SALT_API_USER` | Set username for `salt-api` service. Default: `salt_api`. |
| `SALT_API_USER_PASS` | `SALT_API_USER` password. Required if `SALT_API_SERVICE_ENBALED` is `True`, `SALT_API_USER` is not empty and `SALT_API_USER_PASS_FILE` is unset. _Unset_ by default | | `SALT_API_USER_PASS_FILE` | `SALT_API_USER` password file path. Use this variable to set the path of a file containing the password for the `SALT_API_USER`. Useful to load the password from secrets. Has priority over `SALT_API_USER_PASS`. _Unset_ by default. |
| `SALT_API_CERT_CN` | Common name in the request. Default: `localhost` | | `SALT_API_USER_PASS` | `SALT_API_USER` password. Required if `SALT_API_SERVICE_ENBALED` is `True`, `SALT_API_USER` is not empty and `SALT_API_USER_PASS_FILE` is unset. _Unset_ by default. |
| [`SALT_MASTER_SIGN_PUBKEY`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-sign-pubkey) | Sign the master auth-replies with a cryptographic signature of the master's public key. Possible values: 'True' or 'False'. Default: `False` | | `SALT_API_CERT_CN` | Common name in the request. Default: `localhost`. |
| [`SALT_MASTER_USE_PUBKEY_SIGNATURE`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-use-pubkey-signature) | Instead of computing the signature for each auth-reply, use a pre-calculated signature. This option requires `SALT_MASTER_SIGN_PUBKEY` set to 'True'. Possible values: 'True' or 'False'. Default: `True` | | [`SALT_MASTER_SIGN_PUBKEY`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-sign-pubkey) | Sign the master auth-replies with a cryptographic signature of the master's public key. Possible values: 'True' or 'False'. Default: `False`. |
| [`SALT_MASTER_SIGN_KEY_NAME`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-sign-key-name) | The customizable name of the signing-key-pair without suffix. Default: `master_sign` | | [`SALT_MASTER_USE_PUBKEY_SIGNATURE`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-use-pubkey-signature) | Instead of computing the signature for each auth-reply, use a pre-calculated signature. This option requires `SALT_MASTER_SIGN_PUBKEY` set to 'True'. Possible values: 'True' or 'False'. Default: `True`. |
| [`SALT_MASTER_PUBKEY_SIGNATURE`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-pubkey-signature) | The name of the file in the master's pki-directory that holds the pre-calculated signature of the master's public-key. Default: `master_pubkey_signature` | | [`SALT_MASTER_SIGN_KEY_NAME`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-sign-key-name) | The customizable name of the signing-key-pair without suffix. Default: `master_sign`. |
| `SALT_MASTER_ROOT_USER` | Forces `salt-master` to be runned as `root` instead of `salt`. Default: `False` | | `SALT_MASTER_SIGN_KEY_FILE` | The path to the signing-key-pair {pem,pub} without suffixes. The pair will be copied into the pki directory if they don't exists previously. Useful to load the password from secrets. _Unset_ by default. |
| `SALT_GITFS_SSH_PRIVATE_KEY` | The name of the ssh private key for gitfs. Default: `gitfs_ssh` | | [`SALT_MASTER_PUBKEY_SIGNATURE`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#master-pubkey-signature) | The name of the file in the master's pki-directory that holds the pre-calculated signature of the master's public-key. Default: `master_pubkey_signature`. |
| `SALT_GITFS_SSH_PUBLIC_KEY` | The name of the ssh public key for gitfs. Default: `gitfs_ssh`.pub` | | `SALT_MASTER_PUBKEY_SIGNATURE_FILE` | The path of the salt-master public key file with the pre-calculated signature. It will be copied inside the pki directory if a file with name `SALT_MASTER_PUBKEY_SIGNATURE` doesn't exist. Useful to load the password from secrets. _Unset_ by default. |
| `SALT_MASTER_ROOT_USER` | Forces `salt-master` to be run as `root` instead of `salt`. Default: `False`. |
| `SALT_GITFS_SSH_PRIVATE_KEY` | The name of the ssh private key for gitfs. Default: `gitfs_ssh`. |
| `SALT_GITFS_SSH_PUBLIC_KEY` | The name of the ssh public key for gitfs. Default: `gitfs_ssh`.pub`. |
| [`SALT_REACTOR_WORKER_THREADS`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#reactor-worker-threads) | The number of workers for the runner/wheel in the reactor. Default: `10`. | | [`SALT_REACTOR_WORKER_THREADS`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#reactor-worker-threads) | The number of workers for the runner/wheel in the reactor. Default: `10`. |
| [`SALT_WORKER_THREADS`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#worker-threads) | The number of threads to start for receiving commands and replies from minions. Default: `5`. | | [`SALT_WORKER_THREADS`](https://docs.saltproject.io/en/latest/ref/configuration/master.html#worker-threads) | The number of threads to start for receiving commands and replies from minions. Default: `5`. |

View File

@@ -156,6 +156,96 @@ function gen_signed_keys()
echo -n "${GENERATED_KEYS_DIR}" echo -n "${GENERATED_KEYS_DIR}"
} }
#--- FUNCTION -------------------------------------------------------------------------------------------------------
# NAME: _setup_master_keys
# DESCRIPTION: Setup salt-master keys.
#----------------------------------------------------------------------------------------------------------------------
function _setup_master_keys()
{
log_info " ==> Setting up master keys ..."
if [ -n "${SALT_MASTER_KEY_FILE}" ]; then
if ! -f "${SALT_MASTER_KEY_FILE}.pem" || ! -f "${SALT_MASTER_KEY_FILE}.pub"; then
log_error "SALT_MASTER_KEY_FILE is set to '${SALT_MASTER_KEY_FILE}' but .pem and .pub don't exist."
return 1
fi
fi
if [ ! -f "${SALT_KEYS_DIR}/master.pem" ]; then
if [ -n "${SALT_MASTER_KEY_FILE}" ]; then
# Copy master keys provided via external files
log_info "Linking '${SALT_MASTER_KEY_FILE}' keys to '${SALT_KEYS_DIR}/master.{pem,pub}' ..."
ln -sfn "${SALT_MASTER_KEY_FILE}.{pem,pub}" "${SALT_KEYS_DIR}/master.{pem,pub}"
else
log_info "Generating master keys ..."
salt-key --gen-keys master --gen-keys-dir "${SALT_KEYS_DIR}"
fi
else
if [ -n "${SALT_MASTER_KEY_FILE}" ]; then
# If a master key is provided via SALT_MASTER_KEY_FILE, check it is the same as the one in the keys directory
if ! cmp -s "${SALT_MASTER_KEY_FILE}.pem" "${SALT_KEYS_DIR}/master.pem" \
|| ! cmp -s "${SALT_MASTER_KEY_FILE}.pub" "${SALT_KEYS_DIR}/master.pub"; then
log_error "SALT_MASTER_KEY_FILE is set to '${SALT_MASTER_KEY_FILE}' but keys don't match the master keys inside '${SALT_KEYS_DIR}'."
return 1
fi
fi
fi
}
#--- FUNCTION -------------------------------------------------------------------------------------------------------
# NAME: _setup_master_sign_keys
# DESCRIPTION: Setup salt-master sign keys.
#----------------------------------------------------------------------------------------------------------------------
function _setup_master_sign_keys()
{
log_info " ==> Setting up master_sign keys ..."
if [ -n "${SALT_MASTER_SIGN_KEY_FILE}" ]; then
if ! -f "${SALT_MASTER_SIGN_KEY_FILE}.pem" || ! -f "${SALT_MASTER_SIGN_KEY_FILE}.pub"; then
log_error "SALT_MASTER_SIGN_KEY_FILE is set to '${SALT_MASTER_SIGN_KEY_FILE}' but .pem and .pub don't exist."
return 1
fi
fi
if [ ! -f "${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.pem" ]; then
if [ -n "${SALT_MASTER_SIGN_KEY_FILE}" ]; then
# Copy master_sign keys provided via external files
log_info "Linking '${SALT_MASTER_SIGN_KEY_FILE}' keys to '${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.{pem,pub}' ..."
ln -sfn "${SALT_MASTER_SIGN_KEY_FILE}.{pem,pub}" "${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.{pem,pub}"
else
log_info "Generating signed keys ..."
salt-key --gen-signature --auto-create --pub "${SALT_KEYS_DIR}/master.pub" --signature-path "${SALT_KEYS_DIR}"
fi
else
if [ -n "${SALT_MASTER_SIGN_KEY_FILE}" ]; then
# If a master_sign key-pair is provided via SALT_MASTER_SIGN_KEY_FILE, check it is the same as the one in the keys directory
if ! cmp -s "${SALT_MASTER_SIGN_KEY_FILE}.pem" "${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.pem" \
|| ! cmp -s "${SALT_MASTER_SIGN_KEY_FILE}.pub" "${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.pub"; then
log_error "SALT_MASTER_SIGN_KEY_FILE is set to '${SALT_MASTER_SIGN_KEY_FILE}' but keys don't match the master_sign keys inside '${SALT_KEYS_DIR}'."
return 1
fi
fi
fi
if [ -n "${SALT_MASTER_PUBKEY_SIGNATURE_FILE}" ]; then
if ! -f "${SALT_MASTER_PUBKEY_SIGNATURE_FILE}"; then
log_error "SALT_MASTER_PUBKEY_SIGNATURE_FILE is set to '${SALT_MASTER_PUBKEY_SIGNATURE_FILE}' but it doesn't exist."
return 1
fi
if [ ! -f "${SALT_KEYS_DIR}/${SALT_MASTER_PUBKEY_SIGNATURE}" ]; then
log_info "Linking '${SALT_MASTER_PUBKEY_SIGNATURE_FILE}' to '${SALT_KEYS_DIR}/${SALT_MASTER_PUBKEY_SIGNATURE}' ..."
ln -sfn "${SALT_MASTER_PUBKEY_SIGNATURE_FILE}" "${SALT_KEYS_DIR}/${SALT_MASTER_PUBKEY_SIGNATURE}"
else
# If a master_pubkey_signature is provided via SALT_MASTER_PUBKEY_SIGNATURE_FILE, check it is the same as the one in the keys directory
if ! cmp -s "${SALT_MASTER_PUBKEY_SIGNATURE_FILE}" "${SALT_KEYS_DIR}/${SALT_MASTER_PUBKEY_SIGNATURE}"; then
log_error "SALT_MASTER_PUBKEY_SIGNATURE_FILE is set to '${SALT_MASTER_PUBKEY_SIGNATURE_FILE}' but it doesn't match the ${SALT_MASTER_PUBKEY_SIGNATURE} inside '${SALT_KEYS_DIR}'."
return 1
fi
fi
fi
}
#--- FUNCTION ------------------------------------------------------------------------------------------------------- #--- FUNCTION -------------------------------------------------------------------------------------------------------
# NAME: setup_salt_keys # NAME: setup_salt_keys
# DESCRIPTION: Repair keys permissions and creates keys if neaded. # DESCRIPTION: Repair keys permissions and creates keys if neaded.
@@ -163,16 +253,10 @@ function gen_signed_keys()
function setup_salt_keys() function setup_salt_keys()
{ {
log_info "Setting up salt keys ..." log_info "Setting up salt keys ..."
if [ ! -f "${SALT_KEYS_DIR}/master.pem" ]; then _setup_master_keys
log_info "Generating keys ..." [ "${SALT_MASTER_SIGN_PUBKEY}" == True ] && _setup_master_sign_keys
salt-key --gen-keys master --gen-keys-dir "${SALT_KEYS_DIR}"
fi
if [ ! -f "${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.pem" ] && [ "${SALT_MASTER_SIGN_PUBKEY}" == True ]; then
log_info "Generating signed keys ..."
salt-key --gen-signature --auto-create --pub "${SALT_KEYS_DIR}/master.pub" --signature-path "${SALT_KEYS_DIR}"
fi
log_info "Setting up salt keys permissions ..."
while IFS= read -r -d '' pub_key while IFS= read -r -d '' pub_key
do do
if [[ "${pub_key}" =~ .*\.pem$ ]]; then if [[ "${pub_key}" =~ .*\.pem$ ]]; then