saltstack / saltstack/salt

[BUG] Vault token is not refreshed when using salt proxy minions

Open
#61,498 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug needs-triage Proxy-Minion
Dominant language
Python
Stars
15.7k
Forks
5.6k
Avg merge
2d 44m
Merged PRs (30d)
80

Description

Description

We have two salt setups - one using regular minions to provision servers, which we have been doing for over a year and another using proxy minions to provision our network devices, which we have recently started using. We use Hashicorp Vault for secrets.

I am using the same configuration and utilising vault in the same way between the two setups. It works fine on the server side with regular minions. However, on the network side with proxy minions, something strange is going on with regard to refreshing tokens.

Using state.highstate seems to work fine. However, if I use other functions like: net.load_template or slsutil.renderer, it will work fine for the TTL of the token, then i'll get an error like:

    salt.exceptions.SaltRenderError: Problem running salt function in Jinja template: Failed to read secret! HTTPError: 403 Client Error: Forbidden for url: https://myhost/v1/path/to/secret; line 5
    ---
    [snip]
    {%- set headers = { 'X-API-Key': salt['vault'].read_secret('/path/to/secret').key } -%}    <======================

It looks like it's using an old token which has now expired!

Strangely, if I do a state.highstate, that'll then work fine - but going back to net.load_template or slsutil.renderer, they'll still error. So... state.highstate appears to be doing the right thing - but strangely not triggering a token refresh (to buy another 5 mins) globally.

The only way I can find of fixing it for these other commands is to either wait (trying again later on and it might work again - as if the old token eventually expires and causes a refresh) or to restart the proxy minion. Then it'll work fine again for $TTL and break again.

Setup

We are running our own Salt masters, on Debian 11 (bullseye) VMs.

Using the latest salt versions from the Saltstack Debian repos.

root@network-master:~# dpkg -l |grep salt
ii  salt-common                    3004+ds-1                      all          shared libraries that salt requires for all packages
ii  salt-master                    3004+ds-1                      all          remote manager to administer servers via salt
ii  salt-minion                    3004+ds-1                      all          client package for salt, the distributed remote execution system

We are configuring the salt master with:

vault:
  url: https://<URL>
  auth:
    method: token
    token: <REDACTED>
    uses: 30
    ttl: 300
  policies:
    - something
    - something2

peer_run:
  .*:
    - vault.generate_token

I then do things like this in jinja templates:

{{ salt['vault'].read_secret('/path/to/secret').password }}

Expected behavior

On the server/minion side, if I do:

root@salt-master:~# salt <minion> vault.read_secret auth/token/lookup-self

... on each run I get a different token - each with an expiry of +5 mins, as you'd expect.

Steps to Reproduce the behavior

On the network/proxy minion side, if I do:

root@network-master:~# salt <proxy minion> vault.read_secret auth/token/lookup-self

... on every run it's the same token - so it appears that instead of going to vault and obtaining a token on every run as it should, it's deciding to get one sometimes and re-using it...and continuing to try and use it even after it's expires... until some point later, when it decides to go and get another one.

Versions Report

root@network-master:~# salt --versions-report
Salt Version:
          Salt: 3004

Dependency Versions:
          cffi: Not Installed
      cherrypy: Not Installed
      dateutil: 2.8.1
     docker-py: Not Installed
         gitdb: Not Installed
     gitpython: Not Installed
        Jinja2: 2.11.3
       libgit2: 1.1.0
      M2Crypto: 0.37.1
          Mako: Not Installed
       msgpack: 1.0.0
  msgpack-pure: Not Installed
  mysql-python: Not Installed
     pycparser: 2.20
      pycrypto: Not Installed
  pycryptodome: 3.9.7
        pygit2: 1.4.0
        Python: 3.9.2 (default, Feb 28 2021, 17:03:44)
  python-gnupg: Not Installed
        PyYAML: 5.3.1
         PyZMQ: 20.0.0
         smmap: Not Installed
       timelib: Not Installed
       Tornado: 4.5.3
           ZMQ: 4.3.4

System Versions:
          dist: debian 11 bullseye
        locale: utf-8
       machine: x86_64
       release: 5.10.0-9-amd64
        system: Linux
       version: Debian GNU/Linux 11 bullseye

Additional context

If I set a higher TTL and uses, then it makes it work for longer, as there is longer before the token expires.... but then it still happens when it does expire.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by comparing vault.read_secret through regular and proxy minions, then contrast state.highstate with net.load_template and slsutil.renderer using the reported 3004 setup. Trace when the configured token is reused or refreshed for proxy-minion calls; done means expired tokens are refreshed consistently across these entry points without requiring a proxy-minion restart.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.