Salt Command-Line Reference

Salt Command-Line Reference

Salt provides a command-line interface (Salt CLI) with commands for creating the attestation document from the inputs, interacting with the attestations registry, and verifying an attestation with respect to the referenced software product.

salt CMD, with CMD = attestation | keygen | registry | contract | verify.

The Salt CLI has two levels (command + subcommand), and -h | --help will show the help at the command or subcommand level.

The main commands are:

  • salt keygen: Generate key pairs for signing

  • salt attestation init|add|run|status|commit|reset: Incremental attestation build

  • salt attestation provenance: Provenance for pipeline, in a single shot

  • salt registry search | get | put: Operations with Attestation Registry

  • salt verify: Verify contract / attestation for SW artifact

  • salt contract create | from-build | upload | download: Contract handling

Software attestations are typically created in the same pipeline that builds and/or deploys the software. One or more steps can run Salt CLI commands to either build the attestation either in one shot (salt attestation provenance) or incrementally at different steps along the pipeline (salt attestation init|add|run|commit).

These commands may be invoked from CI/CD-specific plugins (like GitHub Custom Action or Jenkins Step), instead of calling directly the command executable.

The attestation is always a JSON file following a standard format, that could be distributed along with the software products, or registered in a specific Attestation Registry.

The -h | --help could be used with any command / subcommand:

$ salt -h
                    oooo      .
                    `888    .o8
  .oooo.o  .oooo.    888  .o888oo
 d88(  "8 `P  )88b   888    888
 `"Y88b.   .oP"888   888    888
 o.  )88b d8(  888   888    888 .
 8""888P' `Y888""8o o888o   "888"

Software Attestations Layer for Trust, by Xygeni Security.
Usage:

salt [-hqvV] [--config=<config>] [@<filename>...] [COMMAND]

Parameters:
      [@<filename>...]    One or more argument files containing options.
  -v, --verbose           Verbose output?
  -q, --quiet             Quiet mode: do not generate output at console
      --config=<config>   config file (default $HOME/.salt/salt.yaml or
                            $SALT_HOME/conf/salt.yaml)
  -h, --help              Show this help message and exit.
  -V, --version           Print version information and exit.

Commands:
  attestation, at
                       Create software attestation: incrementally using init / add / run
                         / commit|reset, or SLSA build provenance in a single shot.

  keygen, kg           Generate and save signing keys to disk. These keys may be used
                         later in attestation commands.
  registry, reg        Operations on Attestations Registry (store, retrieve, search)
  verify, v, check     download and verify an attestation for an artifact
  generate-completion  Generate bash/zsh completion script for salt.

Key Generation

Keys need to be generated before generating attestations (unless you use the keyless mode with --keyless). You may use your own tooling like openssl

$ salt --no-banner keygen -h

Usage:

salt keygen [-hV] [-b=<bits>] [--config=<config>] [-d=<path>] [-f=NAME] [-t=<type>]
            [@<filename>...]

Generate and save signing keys to disk.
These keys may be used later in attestation commands.

Parameters:
      [@<filename>...]     One or more argument files containing options.
  -f, --file-prefix=NAME   Name for generated .key / .pub files (default: salt).
  -t, --type=<type>        key type, one of rsa, ed25519, ecdsa (default: rsa).
  -b, --bits=<bits>        key size, in bits.
                           RSA defaults to 3072. Use values between 2048 and 4096.
                           ECDSA curve will be P-192 up to P-521 depending on bits
                             (defaults to 384).
                           Not applicable to Ed25519.
  -d, --basedir=<path>     directory for key files.
                           Default is current working dir.
  -h, --help               Show this help message and exit.
  -V, --version            Print version information and exit.
      --config=<config>    config file (default $HOME/.salt/salt.yaml or
                             $SALT_HOME/conf/salt.yaml)

Examples:
  # generate key-pair and write to salt.key and salt.pub files
  salt keygen

  # generate key-pair and write to my-signer.key and my-signer.pub files
  salt keygen --file-prefix my-signer

NOTE:
  The command interactively prompts for a password.
  You may use SALT_PRIVATE_KEY_PASSWORD environment variable to provide one.

Example: To generate an Ed25519 keypair and store in my_key.pub (public key) and my_key.key (private key), use the following:

$ salt -nb kg -t ed25519 -f my_key
# with long options:
# salt --no-banner keygen --type ed25519 --file my_key

Enter password for private key:
Repeat password:
2023-11-29 05:15:15 INFO  Salt - Generating ed25519 key-pair...
2023-11-29 05:15:15 INFO  Salt - Private key stored in my_key.key
2023-11-29 05:15:15 INFO  Salt - Public key stored in my_key.pub

The command generates the keypair of the requested type and stores its parts in two files. Please note that, unless the password should be written from the console unless it is passed in the SALT_PRIVATE_KEY_PASSWORD env var.

For generating on-the-fly, single-use keys, and if you have an OIDC provider available, it could be more convenient to use keyless signing.

Protect the private key and private key password as important secrets, otherwise the digital signature has no value, losing its ability for source authentication and for protecting the integrity of the signed attestations.

Remember that doing encryption right is tough, managing secrets is even harder if doing it yourself. You may use CI/CD secrets for storing the private key and the private key password. You may use instead a Secrets Vault.

Notes on the supported cryptographic algorithms:

(1) Salt supports RSA, ECDSA and Ed25519 key pairs.

(2) Signature formats currently supported are raw signatures (DER-encoded). Keys are often encoded in PEM (key content in base64 between PEM delimiters).

(3) Signing with external tools like openssl or PGP is not currently supported. This is planned for future versions.

(4) Cloud-provided Key Management Systems (KMS) will be also supported in future versions.

Create attestations

Creating and verifying software attestations is the core of the SALT framework. Attestations in SALT follow the in-toto Attestations Framework. See Attestation Format for more details.

The command salt attestation is used for creating software attestations:

$ salt attestation --help

Usage: salt attestation [-hV] [--never-fail] [--config=<config>] -p=<pipeline>
                        [@<filename>...] CMD

Create software attestation: incrementally using init / add / run / commit|reset, or SLSA
build provenance in a single shot.

      [@<filename>...]    One or more argument files containing options.
  -p, --pipeline=<pipeline>
                          name of the pipeline/workflow being run.
      --never-fail        Always return 0, even with errors, to keep the pipeline
                            running. Default: false
  -h, --help              Show this help message and exit.
  -V, --version           Print version information and exit.
      --config=<config>   config file (default $HOME/.salt/salt.yaml or
                            $SALT_HOME/conf/salt.yaml)

Commands:
  init              start attestation drafting process
  run               run a command and add attestation for the command execution
  add               add element (material, subject, product or statement) to draft
                      attestation
  status            show the current draft attestation
  commit            complete and store the attestation in draft
  reset             reset attestation in draft
  provenance, slsa  generate and sign SLSA provenance

There are two modes: incremental (init …​ commit) and one-shot (provenance).


Notes:

(1) The working directory used for resolving file / directory paths is the -d | --basedir option, or the current working directory when not given.

(2) Key material is typically encoded in PEM format, and can be passed as an environment variable, local file path, or the PEM value itself. You may use also prefix-based syntax like env:YOUR_VARNAME_HERE or file:YOUR_PATH_HERE. File paths are resolved against the current working directory.

Please note that using env:VAR_NAME could be more convenient than passing $VAR_NAME substitution to the command option directly, as the value will not be shown in logfiles.

Generate Provenance

For generating attestation during the build of software in a single shot, you may use the attestation provenance command (alias: attestation slsa). SLSA Provenance is a common attestation format.

Usage:

salt attestation provenance [-hV] [--config=<config>] [-d=<basedir>] -p=<pipeline>
                            ([-n=<name>] (-v=<value> | -f=<file> | -i=<image> |
                            --digest=<digest>))... [[-k=<key>] [-pk=<publicKey>]
                            [-cert=<certificate>] [-kpass=<keyPassword>]
                            [--pki-format=pkiFormat] [--keyless]] [[-o=<output>]
                            [--output-unsigned=<outputStatement>] [--pretty-print]
                            [--project=<project>] [--[no-]upload] [--[no-]result-upload]]
                            [@<filename>...]


generate and sign SLSA provenance

Parameters:
      [@<filename>...]       One or more argument files containing options.
  -d, --basedir=<basedir>    base directory where source files are located
  -h, --help                 Show this help message and exit.
  -V, --version              Print version information and exit.
  -p, --pipeline=<pipeline>  name of the pipeline/workflow being run.
      --config=<config>      config file (default $HOME/.salt/salt.yaml or
                               $SALT_HOME/conf/salt.yaml)

Subject content, choose one:
  -v, --value=<value>        value (string) to add to attestation
  -f, --file=<file>          file to add to attestation
  -i, --image=<image>        published container image, [REG/]NAME[:TAG]
                             Example: index.docker.io/my_org/my_image:latest or
                               my_org/my_image
      --digest=<digest>      digest, prefixed by the digest function
                             Example: sha256:d82938...8e62

Signer configuration:
  -k, --key=<key>            reference (path, env-var name) to the private key for
                               signing the attestation.
                             Alternatively uses SALT_PRIVATE_KEY env-var when provided.
      -pk, --public-key=<publicKey>
                             reference (path, env-var name or value) to the public key
                               for signing the attestation.
                             Alternatively uses SALT_PUBLIC_KEY env-var when provided.
      -cert, --certificate=<certificate>
                             optional reference (path, env-var name or value) to the
                               signer's certificate.
                             Alternatively uses SALT_CERTIFICATE env-var when provided.
      -kpass, --key-password=<keyPassword>
                             reference (path or env-var name) to the private key password
                             Alternatively uses SALT_PRIVATE_KEY_PASSWORD env-var when
                               provided.
                             If '-', the password will be prompted (DO NOT USE in CI/CD)
      --pki-format=pkiFormat format for signature and private key: One of pgp, x509,
                               minisign, ssh, pkcs7, tuf (default null)
      --keyless              Sign using an ephemeral keypair and a short-lived
                               certificate with OIDC identity.
                             Uses OIDC identity token to obtain a 10' public-key
                               certificate from Fulcio CA.

Attestation options:
  -o, --output=<output>      file for the signed attestation. Use '-' for stdout.
      --output-unsigned=<outputStatement>
                             file for the unsigned attestation. Use '-' for stdout.
      --pretty-print         pretty-print the attestation. Default: false.
      --project=<project>    The software name (project), default: git repository.
      --[no-]upload          Do not upload the attestation, useful for testing or using
                               your own storage.
      --[no-]result-upload   Do not upload the result of the attestation forging to
                               Xygeni.
Subjects to add:
  -n, --name=<name>          name for the subject (if under contract, use the contract's
                               field name)

Example: Generate SLSA provenance for a container image.

With REPO_DIR = path to the software repository, and OUT_DIR = directory where the output files will be written.

$ salt -nb attestation provenance --basedir $REPO_DIR --pipeline my_pipeline \
       --name sources --file src \
       --name my_tool_cli --file build/my_cli \
       --image myorg/myimage:latest \
       --keyless --certificate=$OUT_DIR/ephemeral_cert.crt \
       -o $OUT_DIR/provenance.signed.json --output-unsigned=$OUT_DIR/provenance.json \
       --pretty-print

Generating ephemeral ecdsa keypair of 256 bits
Authenticating using OIDC provider https://oauth2.sigstore.dev/auth
Opening in existing browser session.
Retrieving ephemeral certificate chain from fulcio
Ephemeral fulcio certificate:
-----BEGIN CERTIFICATE-----
MIIC0TCCAligAwIBAgIUJcAbnJyHfrPM7CHQvXZjuEOoyKwwCgYIKoZIzj0EAwMw
...
2nhi3BU=
-----END CERTIFICATE-----

IntotoEnvelope written to provenance.signed.json
Statement https://slsa.dev/provenance/v1 written to provenance.json
Ephemeral certificate saved in file ephemeral_cert.crt
Chain (length 2) for ephemeral certificate saved in file ephemeral_cert.crt.chain
Created entry in the attestations registry with id: 8a...c5b0
Entry may be downloaded from: https://salt.xygeni.io/download/8a...c5b0

The above command will:

  • compute the gitoid:sha256 digest on the $REPO_DIR/src directory as a subject named 'sources', the sha256 digest on the $REPO_DIR/build/my_cli executable (one product of the build), and the docker sha256 digest on the container image (as a second product).

  • use keyless signing to generate and certify on-the-fly keys,

  • produce a signed SLSA v1 Provenance in $OUT_DIR/provenance.signed.json and the unsigned in-toto Statement (easier to see the attestation contents) into $OUT_DIR/provenance.json.

  • upload the signed attestation to the configured attestations registry, and the result from the build and attestation drafting to Xygeni platform.

Computing the relevant image digest needs the docker client installed locally.You may pass a full image name, including the reference to the OCI container where it was pushed.The default image registry is the Docker Hub (docker.io), but you may use another, including a cloud, SCM or private registry.Just use the qualified image name, like registry/namespace/image:tag, as with any docker / podman command.

You may pass the --no-upload to do not upload signed provenance to the configured attestations registry, and --no-result-upload to do not upload build and provenance results to Xygeni platform. The generated signed attestation could be uploaded to your registry of choice.

Generate Custom Attestation

As an alternative to the attestation provenance command, you may incrementally generate a fully customized attestation using the attestation init | add | run | commit/reset subcommands. If you need to create a customized attestation.

You may consider different attestation formats, for different use cases:

  • "Know your container" - Feed into automated policy engines, like Google Cloud’s Binary Authorization.

  • Generate SBOM (Cyclone DX or SPDX formats) and include it as part of a software attestation for protecting the integrity and authenticate source of origin. SPDX or CycloneDX can be inserted as predicates for an In-Toto Statement.

  • Add statements about the current security position of the software: Typically, the statement reads like "I hereby certify that, to the best of my knowledge, the software does not have any critical security flaw…​"

    The idea is to integrate the usual software product security reports like static analysis' detected flaws, software composition and vulnerabilities in components, results from dynamic vulnerability scans. Formats like SCAI or the Vulnerabilities Predicate allow for this.

Attestation drafting

Attestation could be built incrementally, using multiple steps kept in a context, and a final step that signs the attestation and publishes it. This incremental process is called attestation drafting. The drafting process follows the commands in the developer’s possibly favorite tool, git: init / add / commit. An attestation run subcommand is added to capture the execution of a build command.

Example of attestation drafting: Assume that PIPELINE is the reference to the running pipeline where the commands are inserted, and that REPO_DIR points to the software repo directory.

# Initialize attestation
salt -nb attestation init -p $PIPELINE -d $REPO_DIR \
     --attestor git --attestor environment

# Add material (src directory) and products
# (command-line executable tool built, and OCI image)
salt -nb attestation add -p $PIPELINE -d $REPO_DIR \
     --type material --file src
     --type product --file path/to/cli_tool  \
     --type product --image myorg/my_image:my_tag

# Add an attestation predicate,
# for example issues from your favorite Xygeni scan
# in the in-toto Vulnerabilities predicate format:
salt -nb attestatation add -p $PIPELINE -d $REPO_DIR \
     -n xygeni-scan -t predicate -f path/to/xygeni_scan.json \
     --predicate-type https://in-toto.io/attestation/vulns

# Capture a build step to be included in the attestation
# In the example the current Go package is built and installed:
salt -nb attestation run -p $PIPELINE -d $REPO_DIR \
     --step compile -- \
     go install

# ... more attestation add commands may follow ...

salt -nb attestation commit -p $PIPELINE -d $REPO_DIR \
     -k env:MY_KEY -kpass env:MY_KEY_PASS -pk env:MY_PUB \
     -o attestation.json --output-unsigned

Use your CI/CD variables for creating a value for --pipeline that it is unique to the pipeline run. For example, $GITHUB_WORKFLOW_REF/$GITHUB_RUN_ID could be used in GitHub Actions.

  • attestation init creates the initial draft with initial information (like system environment and git data) extracted by one or more predefined 'attestors'. Available attestors are currently git and environment. Materials (inputs) may also be added as files or directories.

  • attestation add adds elements (material, subject, product or statement) to the current draft attestation. Typically, a digest (SHA-256) is computed on the element. Container images may be added.

    The image digest is the 'official' from the manifest, as extracted from docker manifest inspect IMAGE -v or gcrane digest IMAGE

  • attestation run runs a command and add an attestation predicate for the command execution. Cryptographic digests for input artifacts (materials) will be computed before running the command, and for output artifacts (by-products) at the end of the command. This could be used to link the inputs and outputs of the step with other steps in the pipeline. The predicate has a type URI of xygeni.io/attestations/command-run/v1, and includes the following information:

{
  "name" : "pipeline@compile",
  "cmd" : [ "go", "install" ],
  "exitcode" : 0,
  "startTime" : "2023-07-29T21:03:59.519225Z",
  "endTime" : "2023-07-29T21:05:01.244904Z",
  "stdout": "base64-encoded, truncated standard output",
  "stderr": "base64-encoded, truncated standard error",
  "materials": {
    "source_code": {
      "name": "source_code",
      "uri": "./src",
      "digest": { "gitoid:sha256": "938cd...34af" }
    },
    ...
  },
  "products": { ... }
}

  • attestation commit builds the final attestation as in-toto Statement, serializes it as JSON, signs it with the passed key material, creates an in-toto Envelope with the statement as payload, the signature and the reference for the signing key, and publishes it in the attestation registry

    The final statement is composed using the added predicates, input artifacts ('materials'), run command predicates, init attestors, and output artifacts ('products'), and then signed using the key materials passed as options.

    As multiple predicates are typically added, a special collection predicate (with type URI xygeni.io/attestations/collection-predicates/v1) is used to compound multiple predicates in the single predicate that goes as the predicate for the in-toto statement to be signed.

Publishing in the configured Attestations Registry, and report upload to Xygeni platform is done by default. They may be disabled with the --no-upload and --no-result-upload options, respectively.

For full command syntax, use the --help | -h option on the attestation subcommand:

salt attestation init -h
salt attestation add -h
salt attestation run -h
salt attestation commit -h

Attestation Status

You may use the attestation status subcommand in the drafting process to show the current context for the drafted attestation. Example:

$ salt -nb attestation status -p pipe01 --full

Pipeline 'pipe01' has potential attestations.
Has 1 materials and 1 products
Material #1: path/to/cli={
  "name" : "path/to/cli",
  "digest" : {
    "gitoid:sha256" : "426271ff2486829a7b7774309c490fe7ebb51fd207b711a067de3f9397a18fbf"
  }
}
Product #1: myorg/myimage:latest={
  "name" : "myorg/myimage:latest",
  "uri" : "myorg/myimage:latest",
  "digest" : {
    "sha256" : "0c8a2fb6a0b3c8db482e841ddb5be69ff48d418ca7e585065e49d14ac6df3a0f"
  }
}
Has 3 pending attestations:
Attestation #1: predicate with type URI 'xygeni.io/attestations/git/v1'
{
  "gitInfo" : {
    "commitHash" : {
      "sha1" : "a984aa1e224b0e399dceda0f17b782e10abb9499"
    },
    "author" : "John Doe",
    "authorEmail" : "john.doe@xygeni.io",
    "commitDate" : "2023-07-28T10:29:51+01:00",
    "commitMessage" : "[Bug] Fix the thing",
    "signature" : {
      "status" : "good",
      "signer" : "John Doe <john.doe@xygeni.io>",
      "signerKey" : "758e48...95328",
      "signerPrimaryKey" : "8690454...0152b"
    },
    "treeHash" : "2bc830...62de9",
    "parentHashes" : [ "e236e...4cc092" ],
    "refs" : [ "refs/heads/feature/myfeat" ]
  },
  "timestamp" : "2023-07-29T07:56:20.325872Z"
}
...

Attestation Reset

If the attestation drafting process should be aborted, due to an failure ini the build, the attestation reset command should be invoked for cleaning up the attestation context:

salt -nb attestation reset -p $PIPELINE \
     --cause failure --reason "Image deployment failed"

Run salt attestation reset -h for full command reference.

Verify Attestation

Attestation verification can be performed by the user of the software the attestation refers to. The verification process:

(1) Computes the digests of the subjects that are passed to the command, following the same syntax as with the attestation add | provenance commands. Emits a verification failure if any digest do not match with the digests present in the attestation. This could help with detecting post-build tampering.

(2) Verifies the signature of the attestation payload (statement included in the attestation envelope) with the public key or certificate passed.

(3) If certificate available, verifies the certificate and certificate chain. For ephemeral certificates used with --keyless, it checks that the certificate was valid at the moment when the attestation was signed, and checks that certificate issuance was registered in the transparency log at the given timestamp.

The attestation verify performs this validation:

$ salt -nb verify \
       --image=myorg/myimage:my_tag \
       --attestation=attestation.json \
       --public-key=ephemeral_cert.crt

Verifying in-toto envelope with type = https://in-toto.io/Statement/v1
Attestations passed OK

When there is a failure in validation, the result shows what failed:

$ salt -nb verify \
       --image=myorg/myimage:my_tag \
       --attestation=attestation.json \
       --public-key=ephemeral_cert.crt

WARN Salt - Attestations do not pass: {
  "subjects" : [ {
    "name" : "xygeni/xygeni_scanner:latest",
    "digest" : {
      "sha256" : "0c8a2...f3a0f"
    }
  } ],
  "errors" : [ {
    "state" : "signature_failed",
    "reason" : "Invalid signature for key e083...32a7f",
    "publicKeyId" : "e083a...2a7f"
  } ],
  "contract" : "",
  "pass" : false,
  "unverifiedSubjects" : [ ]
}

Use salt verify -h to display the full syntax of the command.

Further 'semantic' validation is not supported in the current version. In future releases, additional checks based on a contract (sort of policy specifying the steps in the build process and the expected results for each step), signed by the attestor, will be added to salt.

Registry Operations

Attestation generation and verification may connect with the Attestations Registry to fetch the attestation. The registry command allows you to upload, search for, and download attestations.

Attestation entries in the registry have a digest (64 hexadecimal digits) for downloading.

Search typically use the digest value of a subject in the attestation to list the attestations that refer to it (there could be multiple).

salt -nb registry search --digest $IMAGE_SHA

29 attestations found containing digest sha256:0c8a...df3a0f:
---
Attestation #1: 3a7062b...d6a3
Download URL: https://salt.xygeni.io/download/3a7062b...d6a3
Payload type: application/vnd.in-toto+json
Signature #1: MEUCI...+tE=
Public Key ID #1: d0a3e...b63
---
Attestation #2: 5f6b....3c89
...

Use salt reg -h for the full syntax of the commands.

$ salt reg -h

Usage: salt registry [-hV] [--never-fail] [--config=<config>] [--format=<format>]
                     [@<filename>...] CMD
Operations on Attestations Registry (store, retrieve, search)
      [@<filename>...]    One or more argument files containing options.
      --format=<format>   Output format, one of json, text (default: text)
      --never-fail        Always return 0, even with errors, to keep the pipeline running.
                            Default: false
  -h, --help              Show this help message and exit.
  -V, --version           Print version information and exit.
      --config=<config>   config file (default $HOME/.salt/salt.yaml or
                            $SALT_HOME/conf/salt.yaml)

Commands:
  store, put, upload       store an attestation in the registry
  retrieve, get, download  get an attestation from registry
  search, find, query      search for attestations in the registry

Last updated