Skip to content

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-----
Ensure indentation is preserved. The certificate block must remain indented beneath 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 %}
The state checks the certificate thumbprint before importing to ensure idempotency.

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