Configuring Windows Minions to Trust Artifactory SSL Certificate
Introduction¶
This guide describes a pattern for distributing and trusting an internal Artifactory SSL certificate on Windows Salt minions.
Rather than embedding certificate data in state files, we store the certificate chain in pillar and deploy it via an idempotent state. This keeps configuration data separate from state logic and allows for controlled certificate rotation across environments.
1. Get the Servers SSL cert in PEM format¶
Run the following command to get the cert for the Artifactory server (run this on the salt master). This will output the full certificate chain presented by the server.
echo | openssl s_client -showcerts -connect artifactory.example.com:443 -servername artifactory.example.com 2>/dev/null \
| sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p'
Note:
-
If using an internal CA, include only the root and intermediate certificates.
-
If the certificate is self-signed, include the entire output.
2. Create a pillar file with this cert¶
We use a pillar because this data may change over time and we do not want config values in our state files.
Create an artifactory.sls in your pillar repo with the following contents:
artifactory:
cert_pem: |
-----BEGIN CERTIFICATE-----
<INSERT output from step #1 here>
-----END CERTIFICATE-----
cert_pem.
3. Update the top.sls to push this file out to all windows minions¶
Add the following to the pillar top.sls to expose this pillar data to Windows minions:
Replace base with what ever branch you want this data to be available to.
base:
'os:Windows':
- match: grain
- artifactory
You can verify the pillar data is available with the following commands:
# Refresh pillar on a test Windows minion called 'win-test'
salt 'win-test' saltutil.refresh_pillar
# Verify pillar is present
salt 'win-test' pillar.get artifactory
4. Create a state file to push this cert into the salt-minion's local certificate trust store¶
Create the following state file: windows/artifactory/trust_cert_pillar.sls
{% set cert_pillar = 'artifactory:cert_pem' %}
{% set pem = salt['pillar.get'](cert_pillar, '') %}
{% if not pem %}
missing_artifactory_cert_pillar:
test.fail_without_changes:
- name: "Missing pillar value {{ cert_pillar }}"
{% else %}
{% set cert_dst = 'C:\\ProgramData\\Salt\\certs\\artifactory-ca.crt' %}
{% set store = 'Root' %}
artifactory_cert_dir:
file.directory:
- name: 'C:\ProgramData\Salt\certs'
- makedirs: True
artifactory_cert_file_from_pillar:
file.managed:
- encoding: utf-8
- name: '{{ cert_dst }}'
- makedirs: True
- contents_pillar: {{ cert_pillar }}
- require:
- file: artifactory_cert_dir
artifactory_import_cert:
cmd.run:
- name: |
$p='{{ cert_dst }}'
$c = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($p)
$tp = $c.Thumbprint.Replace(' ','').ToUpper()
$exists = Get-ChildItem Cert:\LocalMachine\{{ store }} | Where-Object { $_.Thumbprint -eq $tp }
if (-not $exists) {
certutil -f -addstore {{ store }} "$p" | Out-Host
exit $LASTEXITCODE
}
exit 0
- shell: powershell
- require:
- file: artifactory_cert_file_from_pillar
artifactory_verify_cert:
cmd.run:
- name: |
$p='{{ cert_dst }}'
$c = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($p)
$tp = $c.Thumbprint.Replace(' ','').ToUpper()
$exists = Get-ChildItem Cert:\LocalMachine\{{ store }} | Where-Object { $_.Thumbprint -eq $tp }
if ($exists) {
Write-Output "Artifactory cert present in LocalMachine\{{ store }}: $tp"
exit 0
}
Write-Error "Artifactory cert NOT present in LocalMachine\{{ store }}: $tp"
exit 1
- shell: powershell
- require:
- cmd: artifactory_import_cert
{% endif %}
5. Apply the file to your windows minions¶
We can use the -G (grain) flag to state.apply to target minions that have their os grain set to windows to apply this state file to:
salt -G 'os:Windows' state.apply windows.artifactory.trust_cert_pillar test=True
Remove the test=True when you are happy with the statefile output.
Why use Pillar for Certificates?¶
Storing certificates in pillar ensures:
-
Centralised management
-
Easy rotation
-
Separation of configuration from state logic