15.2 Configuration Management with SaltStack

Key Takeaways

  • Salt uses a master/minion architecture: minions initiate persistent outbound connections to the master on TCP ports 4505 and 4506, so no inbound firewall holes are needed on clients
  • A new minion is untrusted until you accept its key with salt-key -a (or -A for all) — salt-key -L lists pending, accepted, and rejected keys
  • Ad-hoc fleet commands use salt '*' cmd.run '...' (or cmd.shell when you need pipes or redirection); test.ping is the canonical connectivity check
  • States are declarative YAML files (.sls) using modules like pkg.installed and service.running, mapped to minions through /srv/salt/top.sls
  • Configuration management beats SSH loops because states are idempotent, scalable to thousands of machines, and give per-host success/failure reporting
Last updated: July 2026

Installing fifty identical Kali boxes is only half the problem — keeping them configured identically is the harder half. Editing /etc/ssh/sshd_config on one machine is trivial; pushing the same change to fifty machines, verifying it applied, and re-applying it when someone drifts is what configuration management exists for. The Kali Linux Revealed book demonstrates this with SaltStack (Salt), a Python-based configuration management and remote execution framework. Expect the exam to test the master/minion model, key acceptance, the salt command syntax, and the shape of a state file.

Master and Minion Architecture

Salt has two roles:

  • The master is the central control node that holds state definitions and issues commands.
  • Each minion is a managed machine. Critically, the minion initiates a persistent outbound connection to the master over TCP ports 4505 (publish) and 4506 (return) using ZeroMQ. Because connections are outbound, minions can sit behind NAT and firewalls with no inbound rules — a major practical advantage.

Install the roles from the Kali repositories:

# on the master
apt update && apt install -y salt-master
# on each minion
apt update && apt install -y salt-minion

Point each minion at the master by editing /etc/salt/minion:

master: 192.168.101.1

Restart salt-minion, and the minion generates a public/private keypair and presents its public key to the master along with its minion ID (the hostname by default — keep hostnames unique or set id: explicitly).

Accepting Keys

Salt refuses to trust a minion until you approve its key — this is the authentication boundary of the whole system, and a classic exam point:

salt-key -L            # list Accepted / Denied / Unaccepted / Rejected keys
salt-key -a websrv01   # accept one minion by ID
salt-key -A            # accept ALL pending keys (fine in a lab, dangerous in production)
salt-key -d websrv01   # delete a key, revoking the minion

Exam trap: blindly running salt-key -A on a shared network accepts anyone's minion, including an attacker's. In production you verify key fingerprints (salt-key -f websrv01) before accepting.

Remote Execution: salt '*' cmd.run

Once keys are accepted, ad-hoc commands fan out instantly:

salt '*' test.ping                 # every minion answers True
salt 'web*' cmd.run 'uname -a'     # glob targeting by minion ID
salt '*' cmd.run 'apt list --upgradable'
salt '*' grains.items              # view per-minion facts (os, cpu, mem)
salt -G 'os:Debian' test.ping      # target by grain value instead of name

The quoting matters: '*' is a glob matched against minion IDs, and cmd.run is the run function of the cmd execution module. Note the distinction the book relies on: cmd.run does not go through a shell by default, so pipes, redirection and && need cmd.shell instead — the book's own examples read salt kali-scratch cmd.shell 'uptime; uname -a'. Other core modules include pkg.install, pkg.refresh_db, service.start, service.restart, and cp.get_file.

State Files: Declaring Desired State

Ad-hoc commands are imperative — "run this now." Salt's real power is states: declarative YAML files that describe how a machine should look, stored by default under /srv/salt (the file_roots) with the .sls extension (SaLt State). A core.sls for Kali lab machines:

kali-tools-top10:
  pkg.installed

openssh-server:
  pkg.installed

ssh:
  service.running:
    - enable: True
    - watch:
      - file: /etc/ssh/sshd_config

/etc/ssh/sshd_config:
  file.managed:
    - source: salt://files/sshd_config
    - user: root
    - group: root
    - mode: 644

Each stanza is an ID, a state module (pkg.installed, service.running, file.managed), and its arguments. service.running with enable: True both starts sshd now and marks it to start at boot — remember that Kali disables network services by default, so enabling one is an explicit, exam-relevant action. watch restarts the service only when the config file actually changes. YAML is whitespace-sensitive: two-space indents, no tabs, colons followed by spaces. A mangled indent is the number-one cause of state failures.

top.sls: Mapping States to Minions

The top file, /srv/salt/top.sls, decides which states apply to which machines:

base:
  '*':
    - core
  'web*':
    - webserver
  'G@os_family:Debian':
    - debian-hardening

base is the default environment; each match line (* glob, or G@ for grain matching) lists states to apply. Run the enforcement with:

salt '*' state.apply            # or the older state.highstate
salt '*' state.apply core       # one state only, for testing
salt websrv01 state.apply test=True   # dry run: show what WOULD change

Salt converges each minion to the declared state and reports exactly which resources changed, which were already correct, and which failed — per host, in color.

Why This Beats a for-Loop over SSH

The naive alternative — for h in $(cat hosts); do ssh $h 'apt install ...'; done — breaks down fast, and the exam wants you to know why:

SSH loopSalt
Runs the same command every time; not idempotentStates check first, change only what's wrong
Stalls or dies on the first unreachable hostMinions connect out when able; master queues work
No structured report of what changedPer-minion success/failure/change summary
Logic lives in throwaway shell scriptsVersion-controlled, reviewable .sls files
Re-run safety is your problemRe-running a highstate converges drifted machines

Idempotency — applying a state twice yields the same result as applying it once — is the single most important property to remember.

Alternatives

Salt is the book's example, not the only option. Ansible is the most common alternative: it is agentless (it pushes over SSH instead of running minions), uses YAML "playbooks," and suits small fleets, though persistent-connection masters scale better. Puppet and Chef fill the same niche with heavier agents. The concepts transfer: a central source of truth, declarative desired state, and idempotent enforcement.

Test Your Knowledge

A freshly installed Salt minion shows up in salt-key -L under Unaccepted but never receives commands. What is the cause and fix?

A
B
C
D
Test Your Knowledge

Which state declaration both starts the SSH service immediately and ensures it launches at boot?

A
B
C
D