# Salt-Minion for Automation

> Salt-Minion on VyOS - centralized configuration management, deployment automation, and remote command execution for network devices

Source: https://opennix.org/en/docs/vyos/services/vyos-salt-minion/


Salt-Minion integrates VyOS with SaltStack for centralized configuration management and automation of network devices.

## Overview

### What is SaltStack

**SaltStack** is a configuration management and orchestration system:
- IT infrastructure automation
- Remote command execution
- Configuration management
- Infrastructure as Code approach
- Event-driven architecture
- Python-based solution

### Salt Architecture

**Salt Master**:
- Central management server
- Stores states and pillars
- Manages minions
- Performs orchestration

**Salt Minion**:
- Agent on the managed device
- Executes commands from the Master
- Sends data and events
- Applies configurations

**Salt Proxy Minion**:
- For devices that cannot run an agent
- Uses SSH/NETCONF/API
- Support for network devices
- Netmiko for VyOS

### Capabilities for VyOS

- **Operational commands** - running show commands
- **Configuration management** - applying configurations
- **Batch operations** - bulk changes
- **Event monitoring** - tracking events
- **Inventory management** - hardware accounting
- **Compliance checking** - verifying compliance

## Installing Salt Master

### Installation on the Management Server

**Ubuntu/Debian:**
```bash
# Add SaltStack repository
curl -fsSL https://packages.broadcom.com/artifactory/api/gpg/key/public | sudo tee /etc/apt/keyrings/salt-archive-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/salt-archive-keyring.gpg arch=amd64] https://packages.broadcom.com/artifactory/saltproject-deb/ stable main" | sudo tee /etc/apt/sources.list.d/salt.list

# Install Salt Master
sudo apt update
sudo apt install salt-master salt-minion salt-ssh

# Start service
sudo systemctl enable salt-master
sudo systemctl start salt-master
```

**CentOS/RHEL:**
```bash
# Add repository
sudo yum install https://repo.saltproject.io/salt/py3/redhat/8/x86_64/latest/salt-repo-latest.el8.noarch.rpm

# Install
sudo yum install salt-master salt-minion

# Start
sudo systemctl enable salt-master
sudo systemctl start salt-master
```

### Basic Master Configuration

`/etc/salt/master`:
```yaml
interface: 0.0.0.0
auto_accept: False
file_roots:
  base:
    - /srv/salt/states
pillar_roots:
  base:
    - /srv/salt/pillars
```

Creating the directory structure:
```bash
sudo mkdir -p /srv/salt/{states,pillars}
sudo systemctl restart salt-master
```

## Configuring Salt-Minion on VyOS

### Basic Setup

Minimal configuration:

```
set service salt-minion id 'vyos-router-01'
set service salt-minion master '192.0.2.250'
commit
save
```

Parameters:
- `id` - unique minion identifier
- `master` - IP or hostname of the Salt Master

### Advanced Configuration

```
set service salt-minion id 'vyos-edge-router'
set service salt-minion master '192.0.2.250'
set service salt-minion hash md5
set service salt-minion interval 60
commit
save
```

Parameters:
- `hash` - hashing algorithm (md5, sha256)
- `interval` - Master check interval (seconds)

### Multiple Masters

```
set service salt-minion id 'vyos-router-01'
set service salt-minion master '192.0.2.250'
set service salt-minion master '192.0.2.251'
commit
save
```

## Accepting Keys on the Master

### Viewing Pending Keys

On the Salt Master:
```bash
sudo salt-key -L
```

Output:
```
Accepted Keys:
Denied Keys:
Unaccepted Keys:
vyos-router-01
Rejected Keys:
```

### Accepting a Key

Accept a specific key:
```bash
sudo salt-key -a vyos-router-01
```

Accept all keys (use with caution!):
```bash
sudo salt-key -A
```

### Removing a Key

```bash
sudo salt-key -d vyos-router-01
```

### Verifying the Connection

```bash
sudo salt 'vyos-router-01' test.ping
```

Output:
```
vyos-router-01:
    True
```

## Salt Proxy Minion for VyOS

### What is a Proxy Minion

A Proxy Minion is used when:
- An agent cannot be installed on the device
- Management via SSH/API is required
- Network devices lack Salt support

### Configuring the Netmiko Proxy

**Installing Netmiko on the Salt Master:**
```bash
sudo pip3 install netmiko
```

**Creating a Pillar for the device:**

`/srv/salt/pillars/vyos-proxy.sls`:
```yaml
proxy:
  proxytype: netmiko
  device_type: vyos
  host: 192.0.2.10
  username: vyos
  password: vyos_password
  secret: enable_password
```

**Top file for Pillars:**

`/srv/salt/pillars/top.sls`:
```yaml
base:
  'vyos-proxy-01':
    - vyos-proxy
```

### Starting the Proxy Minion

```bash
sudo salt-proxy --proxyid=vyos-proxy-01 -d
```

Or via systemd:

`/etc/systemd/system/salt-proxy@.service`:
```ini
[Unit]
Description=Salt Proxy Minion %i
After=network.target

[Service]
Type=notify
NotifyAccess=all
ExecStart=/usr/bin/salt-proxy --proxyid=%i
Restart=always

[Install]
WantedBy=multi-user.target
```

Startup:
```bash
sudo systemctl enable salt-proxy@vyos-proxy-01
sudo systemctl start salt-proxy@vyos-proxy-01
```

### Accepting the Proxy Minion Key

```bash
sudo salt-key -a vyos-proxy-01
```

## Basic Operations

### Verifying the Connection

All minions:
```bash
sudo salt '*' test.ping
```

A specific minion:
```bash
sudo salt 'vyos-router-01' test.ping
```

By glob pattern:
```bash
sudo salt 'vyos-*' test.ping
```

### Retrieving Information

System version:
```bash
sudo salt 'vyos-router-01' test.version
```

Network interfaces:
```bash
sudo salt 'vyos-router-01' network.interfaces
```

Interface details:
```bash
sudo salt 'vyos-router-01' network.interface eth0
```

### Executing Commands

Shell commands:
```bash
sudo salt 'vyos-router-01' cmd.run 'show version'
```

Multiple commands:
```bash
sudo salt 'vyos-router-01' cmd.run 'show interfaces; show system uptime'
```

## Managing VyOS Configuration

### Via Proxy Minion with Netmiko

**Sending a configuration command:**
```bash
sudo salt 'vyos-proxy-01' netmiko.send_config config_commands="['set interfaces ethernet eth0 description WAN_Link']" commit=True
```

**Multiple commands:**
```bash
sudo salt 'vyos-proxy-01' netmiko.send_config \
  config_commands="['set system host-name vyos-edge', 'set system domain-name example.com']" \
  commit=True
```

### States for Configuration

**Creating a State file:**

`/srv/salt/states/vyos-basic.sls`:
```yaml
configure_hostname:
  netmiko.send_config:
    - config_commands:
        - set system host-name vyos-router-01
        - set system domain-name corp.local
    - commit: True

configure_ntp:
  netmiko.send_config:
    - config_commands:
        - set system ntp server 0.pool.ntp.org
        - set system ntp server 1.pool.ntp.org
    - commit: True

configure_dns:
  netmiko.send_config:
    - config_commands:
        - set system name-server 8.8.8.8
        - set system name-server 8.8.4.4
    - commit: True
```

**Applying the State:**
```bash
sudo salt 'vyos-proxy-01' state.apply vyos-basic
```

### Batch Configuration

**File with commands:**

`/srv/salt/states/commands.txt`:
```
set interfaces ethernet eth1 description LAN
set interfaces ethernet eth1 address 192.168.1.1/24
set service ssh port 22
set service ssh listen-address 0.0.0.0
```

**State to apply it:**

`/srv/salt/states/vyos-config.sls`:
```yaml
apply_config_from_file:
  netmiko.send_config:
    - config_file: salt://commands.txt
    - commit: True
```

**Application:**
```bash
sudo salt 'vyos-proxy-01' state.apply vyos-config
```

## Automation Examples

### Example 1: Basic System Setup

`/srv/salt/states/vyos-system.sls`:
```yaml
system_hostname:
  netmiko.send_config:
    - config_commands:
        - set system host-name {{ pillar['hostname'] }}
    - commit: True

system_timezone:
  netmiko.send_config:
    - config_commands:
        - set system time-zone {{ pillar['timezone'] }}
    - commit: True

system_users:
  netmiko.send_config:
    - config_commands:
        - set system login user admin authentication plaintext-password {{ pillar['admin_password'] }}
        - set system login user admin level admin
    - commit: True
```

`/srv/salt/pillars/vyos-router-01.sls`:
```yaml
hostname: vyos-edge-01
timezone: Europe/Moscow
admin_password: SecurePassword123!
```

### Example 2: Interface Configuration

`/srv/salt/states/vyos-interfaces.sls`:
```yaml
{% for iface, config in pillar['interfaces'].items() %}
configure_{{ iface }}:
  netmiko.send_config:
    - config_commands:
        - set interfaces ethernet {{ iface }} description '{{ config.description }}'
        {% if config.address %}
        - set interfaces ethernet {{ iface }} address {{ config.address }}
        {% endif %}
        {% if config.dhcp %}
        - set interfaces ethernet {{ iface }} address dhcp
        {% endif %}
    - commit: True
{% endfor %}
```

`/srv/salt/pillars/vyos-router-01.sls`:
```yaml
interfaces:
  eth0:
    description: "WAN Interface"
    dhcp: true
  eth1:
    description: "LAN Interface"
    address: "192.168.1.1/24"
  eth2:
    description: "DMZ Interface"
    address: "192.168.100.1/24"
```

### Example 3: Firewall Configuration

`/srv/salt/states/vyos-firewall.sls`:
```yaml
firewall_wan_local:
  netmiko.send_config:
    - config_commands:
        - set firewall ipv4 name WAN_LOCAL default-action drop
        - set firewall ipv4 name WAN_LOCAL rule 10 action accept
        - set firewall ipv4 name WAN_LOCAL rule 10 state established
        - set firewall ipv4 name WAN_LOCAL rule 10 state related
        - set firewall ipv4 name WAN_LOCAL rule 20 action drop
        - set firewall ipv4 name WAN_LOCAL rule 20 state invalid
        - set firewall ipv4 name WAN_LOCAL rule 30 action accept
        - set firewall ipv4 name WAN_LOCAL rule 30 protocol icmp
        - set interfaces ethernet eth0 firewall in name WAN_LOCAL
    - commit: True
```

### Example 4: NAT Deployment

`/srv/salt/states/vyos-nat.sls`:
```yaml
configure_nat:
  netmiko.send_config:
    - config_commands:
        - set nat source rule 100 outbound-interface name eth0
        - set nat source rule 100 source address 192.168.1.0/24
        - set nat source rule 100 translation address masquerade
    - commit: True
```

### Example 5: DHCP Server

`/srv/salt/states/vyos-dhcp.sls`:
```yaml
configure_dhcp_server:
  netmiko.send_config:
    - config_commands:
        - set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option default-router 192.168.1.1
        - set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 option name-server 192.168.1.1
        - set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 range 0 start 192.168.1.100
        - set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 range 0 stop 192.168.1.200
        - set service dhcp-server shared-network-name LAN subnet 192.168.1.0/24 subnet-id 1
    - commit: True
```

## Retrieving Information from VyOS

### Operational Commands

Via proxy minion:
```bash
# Show version
sudo salt 'vyos-proxy-01' netmiko.send_command command="show version"

# Show configuration
sudo salt 'vyos-proxy-01' netmiko.send_command command="show configuration"

# Show interfaces
sudo salt 'vyos-proxy-01' netmiko.send_command command="show interfaces"

# Show route table
sudo salt 'vyos-proxy-01' netmiko.send_command command="show ip route"
```

### Grains (Facts System)

Retrieving all grains:
```bash
sudo salt 'vyos-router-01' grains.items
```

A specific grain:
```bash
sudo salt 'vyos-router-01' grains.get os
```

## Monitoring and Events

### Event Bus

Subscribing to events:
```bash
sudo salt-run state.event pretty=True
```

### Reactor System

Automatic actions in response to events:

`/etc/salt/master.d/reactor.conf`:
```yaml
reactor:
  - 'salt/minion/*/start':
    - /srv/salt/reactors/minion-start.sls
```

`/srv/salt/reactors/minion-start.sls`:
```yaml
send_notification:
  local.cmd.run:
    - tgt: salt-master
    - arg:
      - echo "Minion {{ data['id'] }} started" | mail -s "Minion Start" admin@example.com
```

## Operational Commands

### On VyOS

Checking salt-minion status:
```
show service salt-minion
```

Restarting salt-minion:
```
restart salt-minion
```

### On the Salt Master

List of all keys:
```bash
sudo salt-key -L
```

List of minions:
```bash
sudo salt '*' test.ping
```

Applying a state:
```bash
sudo salt 'vyos-router-01' state.apply
```

Highstate (all states):
```bash
sudo salt 'vyos-router-01' state.highstate
```

## Troubleshooting

### Minion Does Not Connect

**Problem**: The minion does not appear in the key list.

**Diagnostics:**

On VyOS:
```
show service salt-minion
show log | grep salt
```

On the Master:
```bash
sudo systemctl status salt-master
sudo tail -f /var/log/salt/master
```

**Solution:**
```bash
# Check Master reachability
ping 192.0.2.250

# Check ports (4505, 4506)
telnet 192.0.2.250 4505
telnet 192.0.2.250 4506

# Firewall on the Master
sudo firewall-cmd --add-port=4505-4506/tcp --permanent
sudo firewall-cmd --reload
```

### Key Is Not Accepted

**Problem**: salt-key does not show the minion.

**Solution:**

Restart the minion on VyOS:
```
restart salt-minion
```

On the Master:
```bash
# Remove the old key
sudo salt-key -d vyos-router-01

# Wait for the new one
sudo salt-key -L
```

### Commands Do Not Run

**Problem**: `salt 'minion' test.ping` returns no result.

**Diagnostics:**
```bash
# Check that the key is accepted
sudo salt-key -L

# Verbose mode
sudo salt 'vyos-router-01' test.ping -l debug
```

**Solution:**
```bash
# Restart the master
sudo systemctl restart salt-master

# Restart the minion
restart salt-minion  # on VyOS
```

### Proxy Minion Issues

**Problem**: The proxy minion does not connect to VyOS.

**Diagnostics:**
```bash
# Check the logs
sudo journalctl -u salt-proxy@vyos-proxy-01 -f

# Test SSH
ssh vyos@192.0.2.10
```

**Solution:**
```bash
# Check the pillar
sudo salt 'vyos-proxy-01' pillar.items

# Verify the credentials in the pillar
# Make sure netmiko is installed
sudo pip3 install --upgrade netmiko
```

### Netmiko Errors

**Problem**: "Authentication failed" or "Connection timeout".

**Solution:**

Check the Pillar:
```bash
sudo salt 'vyos-proxy-01' pillar.get proxy
```

Update the credentials:
```yaml
# /srv/salt/pillars/vyos-proxy.sls
proxy:
  proxytype: netmiko
  device_type: vyos
  host: 192.0.2.10
  username: correct_user
  password: correct_password
```

Refresh the pillar:
```bash
sudo salt 'vyos-proxy-01' saltutil.refresh_pillar
```

## Security

### Security Recommendations

1. **Protecting the Master**:
```yaml
# /etc/salt/master
auto_accept: False
client_acl:
  admin:
    - .*
publisher_acl:
  admin:
    - .*
```

2. **Firewall on the Master**:
```bash
# Only from known networks
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.0.2.0/24" port protocol="tcp" port="4505-4506" accept' --permanent
sudo firewall-cmd --reload
```

3. **Encrypting Pillars**:
```bash
# Generate the key
sudo salt-key --gen-keys=master

# /etc/salt/master
pillar_opts: False
gpg_keydir: /etc/salt/gpgkeys
```

4. **Key Rotation**:
```bash
# Remove the old key
sudo salt-key -d vyos-router-01

# The minion will generate a new one on restart
restart salt-minion
```

5. **Action Auditing**:
```yaml
# /etc/salt/master
return_job: True
job_cache: True
keep_jobs: 24
```

6. **Restricting Commands**:
```yaml
# /etc/salt/master
publisher_acl:
  operator:
    - test.ping
    - network.interface
  admin:
    - .*
```

## Best Practices

1. **Use Pillars for secrets**:
   - Store passwords in pillars
   - Encrypt sensitive data
   - Restrict access to pillars

2. **Version control for States**:
   - Keep states in Git
   - Code review changes
   - Test before applying

3. **Testing before production**:
```bash
# Test mode
sudo salt 'vyos-router-01' state.apply test=True

# Single minion first
sudo salt 'vyos-router-01' state.apply vyos-config

# Then batch
sudo salt 'vyos-*' state.apply vyos-config
```

4. **Backup before changes**:
```bash
# Save config before applying a state
sudo salt 'vyos-proxy-01' netmiko.send_command command="save /config/backup-$(date +%Y%m%d).config"
```

5. **Monitoring results**:
```bash
# Check job results
sudo salt-run jobs.list_jobs

# Specific job
sudo salt-run jobs.lookup_jid <job_id>
```

6. **Documentation**:
   - Describe your states
   - Comment your pillars
   - Maintain a changelog

7. **File organization**:
```
/srv/salt/
├── states/
│   ├── base/
│   │   ├── init.sls
│   │   └── packages.sls
│   ├── network/
│   │   ├── vyos-interfaces.sls
│   │   ├── vyos-firewall.sls
│   │   └── vyos-nat.sls
│   └── services/
│       ├── vyos-dhcp.sls
│       └── vyos-dns.sls
└── pillars/
    ├── top.sls
    ├── network/
    │   └── vyos-devices.sls
    └── secrets/
        └── credentials.sls
```

8. **Regular updates**:
   - Update the Salt Master
   - Update the Minions
   - Watch for security bulletins

## Integration with Other Systems

### Gitlab CI/CD

`.gitlab-ci.yml`:
```yaml
stages:
  - test
  - deploy

test_state:
  stage: test
  script:
    - salt 'vyos-test' state.apply vyos-config test=True

deploy_production:
  stage: deploy
  script:
    - salt 'vyos-prod-*' state.apply vyos-config
  only:
    - main
  when: manual
```

### Ansible + Salt

Using Salt as an execution engine for Ansible:
```yaml
# playbook.yml
- hosts: localhost
  tasks:
    - name: Apply Salt state to VyOS
      command: salt 'vyos-*' state.apply vyos-config
```

### Prometheus Monitoring

Salt for collecting metrics:
```bash
# Custom grain for metrics
sudo salt 'vyos-*' grains.set monitoring:enabled True
```

## Conclusion

Salt-Minion provides powerful capabilities for automating VyOS:

- **Centralized management** - a single point of control
- **Configuration as Code** - configuration versioning
- **Scalability** - managing many devices at once
- **Flexibility** - support for both direct minion and proxy minion
- **Event-driven** - event-based automation
- **Integration** - integration with the CI/CD pipeline

Key use cases:
- Bulk configuration deployment
- Standardizing settings
- Automating routine tasks
- Compliance checking
- Centralized management of network infrastructure

A proper Salt setup enables efficient management of VyOS routers at enterprise scale and simplifies operations in cloud environments.

