Skip to content

For a platform the catalogue does not cover, you write the script yourselves and upload it as a custom agent under Agents › Catalogue. The appliance signs it with its own key, and it runs only on the target server, never on the appliance.

  • Only an Owner can turn on custom agents and upload them.
  • The licence decides how many custom agents the appliance can have: Free 1, Basic 3, Professional and Enterprise unlimited. A later version of a custom agent you already have does not take another slot.
  • A custom agent cannot call an API from the appliance. All actions run on the target server.
  1. Package the agent as a zip file: agent.yml and scripts (.sh, .ps1 or .py). See agent.yml and the examples for Windows and Linux.

  2. Open Agents › Catalogue and turn on Allow custom agents under Custom agents.

  3. Choose the file under Zip file with the agent and click Upload and approve.

  4. Use the agent in a rule: in the rule’s Summary step, each service has an Agent chain for …. Which service to choose on a server where discovery does not know the application is described in Servers sslbrain does not know. Click Add agent, choose your custom agent under Eligible agents, and choose the action. The parameters from agent.yml are filled in on that step.

  5. If the target is a server with a service agent, the agent must be allowed to run custom scripts (script policy).

If you turn off Allow custom agents, no more can be uploaded, but the custom agents already approved keep running.

The zip file may be at most 1 MB, with 64 files, 512 KB per file and 4 MB unpacked. The allowed file types are yml, yaml, sh, ps1, py, json, md and txt. Hidden files, symbolic links, binary files, absolute paths and .. are refused.

agent.yml describes the agent and its actions. The appliance refuses the agent if any of these requirements is not met:

  • agent.channel is set: push-winrm for Windows and push-ssh for Linux. On a Windows server the script runs through the service agent, whatever the channel is called.
  • agent.tier is custom.
  • agent.name is not used by a package from FairSSL or by a built-in package.
  • Each action has runtime.location: target, a runtime.interpreter and a runtime.delivery.mode that matches the channel: winrm-stream for push-winrm, stdin or scp-tmp for push-ssh.
  • Each action’s script is a .sh, .ps1 or .py file in the zip file.
FieldMeaning
agent.channelpush-winrm for Windows, push-ssh for Linux
actions.<name>The action, for example deploy
runtime.interpreterpowershell or bash
runtime.delivery.modewinrm-stream for Windows, stdin for Linux
categorywrite for an action that changes something, read for one that only reads
timeoutSeconds the action may run
cert_formatpem or pfx_base64: the form the certificate is delivered in (variables)
parametersYour own fields, filled in on the rule

The script runs in PowerShell on the server, through the service agent. The example imports the certificate into the computer’s certificate store and restarts a service.

agent.yml:

agent:
name: intranet-app-windows
display_name: "Intranet-app"
version: "1.0.0"
tier: custom
description: "Installs the certificate for the intranet app"
channel: push-winrm
actions:
deploy:
description: "Import the certificate and restart the service"
script: deploy.ps1
runtime:
location: target
interpreter: powershell
delivery:
mode: winrm-stream
category: write
timeout: 180
cert_format: pfx_base64

deploy.ps1:

Terminal window
$ErrorActionPreference = 'Stop'
try {
$flags = [Security.Cryptography.X509Certificates.X509KeyStorageFlags]'MachineKeySet,PersistKeySet'
$cert = [Security.Cryptography.X509Certificates.X509Certificate2]::new(
[Convert]::FromBase64String($PFX_BASE64), $PFX_PASSWORD, $flags)
$store = [Security.Cryptography.X509Certificates.X509Store]::new('My', 'LocalMachine')
$store.Open('ReadWrite'); $store.Add($cert); $store.Close()
# Bind the certificate to the service here, for example with $cert.Thumbprint
Restart-Service -Name 'IntranetApp'
@{ status = 'success'; message = "Installed $($cert.Thumbprint)" } | ConvertTo-Json -Compress
} catch {
@{ status = 'error'; message = $_.Exception.Message } | ConvertTo-Json -Compress
exit 1
}

The script runs in bash on the server. The example writes the certificate and key to files and reloads a service.

agent.yml:

agent:
name: intranet-app-linux
display_name: "Intranet-app"
version: "1.0.0"
tier: custom
description: "Installs the certificate for the intranet app"
channel: push-ssh
actions:
deploy:
description: "Write the certificate and key, and reload the service"
script: deploy.sh
runtime:
location: target
interpreter: bash
delivery:
mode: stdin
category: write
timeout: 120
cert_format: pem

deploy.sh:

#!/usr/bin/env bash
set -euo pipefail
trap 'echo "{\"status\":\"error\",\"message\":\"deploy failed\"}"; exit 1' ERR
SUDO=""
[ "${SSLBRAIN_ELEVATE:-}" = "sudo" ] && SUDO="sudo -n"
umask 077
$SUDO mkdir -p /etc/intranet-app/tls
printf '%s\n' "$CERT_PEM" | $SUDO tee /etc/intranet-app/tls/cert.pem >/dev/null
printf '%s\n' "$KEY_PEM" | $SUDO tee /etc/intranet-app/tls/key.pem >/dev/null
$SUDO systemctl reload intranet-app
echo '{"status":"success"}'

SSLBRAIN_ELEVATE is set to sudo when the credential has Elevate via sudo turned on.

The certificate is delivered in the form the action’s cert_format names:

cert_formatVariables
pemCERT_PEM, KEY_PEM, and CHAIN_PEM when the chain is delivered separately
pfx_base64PFX_BASE64, PFX_PASSWORD, PFX_ENCRYPTION and CERT_PEM

An action that declares the parameters CERT_PEM, KEY_PEM or CHAIN_PEM without cert_format gets the certificate as PEM.

The script also always gets:

VariablesContent
HOSTNAME, HOST, SERVER_IP, SERVER_OS_TYPEThe server
CREDENTIAL_USER, CREDENTIAL_SECRETThe credential on the server, when it has one
SSLBRAIN_ELEVATEsudo, when the credential has Elevate via sudo turned on
CERTIFICATE_CN, CERTIFICATE_SERIAL, CERTIFICATE_FINGERPRINT_SHA256, CERTIFICATE_NOT_AFTER, CERTIFICATE_ISSUER, NEW_THUMBPRINT, NEW_FRIENDLY_NAMEThe certificate being installed
OLD_THUMBPRINT, OLD_FRIENDLY_NAME, OLD_FINGERPRINT_SHA256, OLD_CERTIFICATE_SERIALThe certificate being replaced
ENDPOINT_NAME, ENDPOINT_AGENT_TYPE, DEPLOYMENT_MODEThe service in the rule
CERT_PATH, KEY_PATH, CHAIN_PATH, RELOAD_COMMAND, INIT_SYSTEM, SOFTWARE_VERSIONWhat discovery found on the service, when it found it

A parameter with source: certificate.pem, certificate.key, certificate.chain, certificate.pfx_base64, certificate.pfx_password, certificate.thumbprint, certificate.previous_thumbprint or certificate.fingerprint_sha256 gets the value under the parameter’s own name. That is useful in PowerShell, where the variables are also bound to param() by name.

In bash the values are environment variables. In PowerShell they are session variables, for example $PFX_BASE64.

Result: the script writes a JSON object with status set to success or error, and optionally message. The last object with a status counts. error makes the action fail even if the exit code is 0, and an exit code other than 0 makes it fail. Always write the JSON result.

The service agent on the server decides for itself which scripts it runs. The default is FairSSL’s packages and the community packages, not custom agents. The appliance cannot change this.

The policy is a number, the sum of what the agent may run:

ValueMay run
1FairSSL packages that read
2FairSSL packages that write
4Community packages that read
8Community packages that write
16Custom agents

The default is 15. To run custom agents, the value must be 31. On a Windows server it is set in one of two ways:

  • At installation, with the MSI property SCRIPTPOLICY=31 (silent install).
  • With Group Policy: the value ScriptPolicy (REG_DWORD, 31) under HKLM\SOFTWARE\Policies\SSLBrain. It takes precedence over the value from the installation. The agent rereads its configuration during its cycle; restart the SSLBrainAgent service if the value should apply at once.

Without a service agent there is no script policy on the server. The Policy and audit › Script policy page on the appliance does not decide what the agents run (policy).

Every package from FairSSL has a signed manifest with the SHA-384 of each file and its category, type and path. The signature is ECDSA P-384 and is made on a hardware key at FairSSL, never in a build pipeline. The appliance trusts only FairSSL’s fixed production keys.

A custom agent is signed by the appliance with its own key when it is uploaded. The service agents get the appliance’s key when they register, and trust it for custom agents.

Agents › Catalogue shows for each version whether the signature is Verified, Invalid or Unsigned.