Skip to content

microdevops-com/salt-project-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

529 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

About

Project Template for Salt Masters /srv.

Prepare the repository

Create empty Git repo:

mkdir example-salt
cd example-salt
git init 

Add this repo as Git Submodule to a project:

git submodule add --name .salt-project-template -b master -- https://github.com/microdevops-com/salt-project-template .salt-project-template

Copy example template_install.sh from template to the repo:

cp .salt-project-template/template_install.sh.example template_install.sh

Edit template_install.sh depending on your needs.

Run template install:

./template_install.sh

Fill the repo with some additional data:

  • README.md
  • pillar/top_sls files (see pillar/top_sls/srv1.example.com.example)
  • pillar/bootstrap files (see pillar/bootstrap/.../srv1_example_com.example)
  • pillar/users/example/admins.sls
  • pillar/ip/example/example.sls (see pillar/ip/example/example.sls.example)
  • pillar/ufw_simple/vars.jinja (see pillar/ufw_simple/vars.jinja.example) or pillar/ufw/vars.jinja (see pillar/ufw/vars.jinja.example)
  • pillar/hosts/example.sls (see https://github.com/microdevops-com/microdevops-formula/blob/master/hosts/pillar.example - static hosts file, recommended to distribute heartbeat_receivers, alerta hosts here)

For Salt-SSH:

  • etc/salt/roster (see roster.example in .salt-project-template)

Secrets with Vault (vault_salt_sdb)

This template ships a custom SDB driver (salt/extmods/sdb/vault_salt_sdb.py) that reads secrets from HashiCorp Vault (KV v2) instead of keeping them in plaintext pillar. The driver, its extmods.conf and the minion.d wiring are always installed but stay dormant until a profile is configured, so repos that do not use Vault are unaffected.

Enable at install time

Set the three vars in template_install.sh (presence of VAULT_SALT_SDB_URL turns the feature on):

VAULT_SALT_SDB_URL=https://vault.example.com \
VAULT_SALT_SDB_PREFIX=iac/example \
VAULT_SALT_SDB_JWT_ROLE=salt-ci-example \
  • VAULT_SALT_SDB_URL - Vault address. If unset, install.sh strips out the profile, the macro and the CI OIDC lines (the driver itself stays installed, dormant).
  • VAULT_SALT_SDB_PREFIX - per-repo KV path prefix, e.g. iac/<project>. Required when the URL is set.
  • VAULT_SALT_SDB_JWT_ROLE - Vault jwt auth role name for CI (see CI auth below). Required when the URL is set. Baked into the profile's inline auth: block.

This generates etc/salt/master.d/vault_salt_sdb.conf (mirrored into minion.d via symlink) and un-comments the #vault# OIDC lines in .gitlab-ci.yml.

Runtime auth

The driver authenticates two ways from one profile, chosen by whether an auth_file is present:

CI (keyless, GitLab OIDC). The generated .gitlab-ci.yml gives every pillar-rendering job an id_tokens: block that mints a short-lived JWT as $VAULT_ID_TOKEN; the profile's inline auth: {method: jwt, role: <VAULT_SALT_SDB_JWT_ROLE>} exchanges it for a short-lived Vault token. No long-lived secret is stored on the runner. Configure Vault once:

vault auth enable jwt
vault write auth/jwt/config oidc_discovery_url="https://gitlab.example.com" \
                            bound_issuer="https://gitlab.example.com"
vault policy write salt-ci-example - <<'EOF'
path "iac/data/example/*" { capabilities = ["read"] }
EOF
vault write auth/jwt/role/salt-ci-example - <<'EOF'
{ "role_type": "jwt", "user_claim": "project_path",
  "bound_audiences": ["https://vault.example.com"],
  "bound_claims": {"project_path": "group/subgroup/example"},
  "token_policies": ["salt-ci-example"], "token_ttl": "5m", "token_max_ttl": "10m",
  "token_no_default_policy": true }
EOF

The role name must match VAULT_SALT_SDB_JWT_ROLE; the KV read path is <mount>/data/<project>/*; bound_audiences must equal the aud in .gitlab-ci.yml (the Vault URL). Vault must be able to reach the GitLab OIDC discovery URL. Missing/mis-set Vault config fails the pillar check closed.

Salt Masters & local dev (AppRole). Drop an auth.conf at /root/.config/vault_salt_sdb/auth.conf (kept OUT of the repo); when present it OVERRIDES the inline JWT auth, so persistent masters and local drun (which bind-mounts it automatically, see .docker-misc.bash) use AppRole instead:

mkdir -p ~/.config/vault_salt_sdb
cat > ~/.config/vault_salt_sdb/auth.conf <<'EOF'
method: approle
role_id: <role-id>
secret_id: <secret-id>
EOF
chmod 600 ~/.config/vault_salt_sdb/auth.conf

Secret values are cached at /root/.cache/vault_salt_sdb/cache.json (mode 0600) for a short freshness window and as an outage fallback; tune via cache_* keys in the profile.

Using secrets in pillar

Import the macro and reference a secret by its path under the prefix:

{% from 'vault_salt_sdb.jinja' import secret %}

myapp:
  db_password: "{{ secret('app/db/password') }}"

secret('app/db/password') expands to sdb://vault_salt_sdb/<VAULT_SALT_SDB_PREFIX>/app/db/password. The URI is <mount>/<path>/<key>: the first segment is the KV mount, the last is the field inside the secret, the middle is the secret path. The macro keeps the per-repo prefix in one place so secret references stay copy-paste identical across repos. You can also call the driver directly: {{ salt['sdb.get']('sdb://vault_salt_sdb/iac/example/app/db/password') }}.

Use the repository

Either push to GitLab and pipeline should deploy depo code to Salt Masters or build the docker image Then use Gitlab Pipelines to run salt/salt-ssh.

Or build and run locally for Salt-SSH with SSH Agent:

docker build --pull -t example-salt:latest .
docker run -it --rm -v $SSH_AUTH_SOCK:/root/.ssh-agent -e SSH_AUTH_SOCK=/root/.ssh-agent example-salt:latest
salt-ssh srv1.example.com test.ping

About

Salt Project Template

Topics

Resources

License

Stars

Watchers

Forks

Contributors