AssumeRoleWithCertificate

The MinIO AIStor Security Token Service (STS) AssumeRoleWithCertificate API endpoint generates temporary access credentials using a client X.509 (TLS) certificate. This page documents the AIStor Server AssumeRoleWithCertificate endpoint. For instructions on implementing STS using an S3-compatible SDK, defer to the documentation for that SDK.

Unlike OpenID Connect or AD/LDAP, certificate-based authentication does not depend on an external identity provider being continuously available. AIStor authenticates the request using only the client certificate, which can reduce operational complexity and improve availability.

Enable certificate-based authentication

The TLS STS API is disabled by default. Enable it by setting the MINIO_IDENTITY_TLS_ENABLE environment variable to on and restarting the deployment.

Because authentication and authorization rely on the client certificate, all AssumeRoleWithCertificate requests must use TLS (HTTPS) and the client must present exactly one leaf client certificate.

Any trusted CA can issue an identity here

AIStor verifies the client certificate against the same set of root certificate authorities it trusts for everything else, not against a certificate authority reserved for client identity. The policy name comes from the certificate itself, either the SPIFFE workload path or the subject common name.

Together, those two facts have one consequence. Any CA in the deployment’s trust store can issue a certificate whose SPIFFE workload path or subject common name matches a policy name, including an administrative policy. That trust store is not limited to the CAs/ directory. It also holds the host’s system root certificates and the public certificates of the server’s own TLS key pairs. Curating CAs/ therefore does not by itself create an allowlist for client identity. Before you enable this API, confirm that every CA the deployment trusts is one you would allow to mint identities.

Configuration

The TLS STS API supports the following environment variables:

Environment variable Description
MINIO_IDENTITY_TLS_ENABLE Set to on to enable X.509 TLS certificate single sign-on. Defaults to off.
MINIO_IDENTITY_TLS_SKIP_VERIFY Set to on to trust client certificates without verifying that they were issued by a trusted Certificate Authority (CA). Defaults to off (verify).

Skipping verification allows any client to obtain temporary credentials with arbitrary policy permissions, including administrative permissions. Only set this to on for debugging or testing.
MINIO_IDENTITY_TLS_SPIFFE_MULTI_DOMAIN Set to on to qualify the policy name derived from a SPIFFE SVID with its trust domain, so that identical workload paths in different trust domains map to different policies. Defaults to off.

See SPIFFE SVID authentication for details.

You can also configure these values using the standard configuration API with the identity_tls subsystem:

mc admin config set ALIAS identity_tls skip_verify="off"

Request endpoint

The AssumeRoleWithCertificate endpoint has the following form:

POST https://aistor.example.net?Action=AssumeRoleWithCertificate[&ARGS]

The following example uses all supported arguments. Replace the aistor.example.net hostname with the appropriate URL for your AIStor Server, and provide the client certificate and private key with the request:

curl -X POST --key private.key --cert public.crt \
  "https://aistor.example.net?Action=AssumeRoleWithCertificate&Version=2011-06-15&DurationSeconds=3600"

Request query parameters

This endpoint supports the following query parameters:

Parameter Type Description
Version string Required

Specify 2011-06-15.
DurationSeconds integer Optional

Specify the number of seconds after which the temporary credentials expire.
Defaults to 3600.

- The minimum value is 900 or 15 minutes.
- The maximum value is 31536000 or 365 days.

The temporary credentials never outlive the client certificate. If the requested duration extends beyond the certificate’s expiration, AIStor reduces the credential lifetime to match the certificate.
TokenRevokeType string Optional

A string to assign to the generated token that allows the token’s access to be revoked before expiration.

A user may have a maximum of 100 unique token revocation types at any time. A token revocation type can be assigned to multiple tokens.

Certificate-to-policy mapping

AIStor maps the client certificate to an S3 policy using the subject common name (CN) of the X.509 certificate. A certificate with the subject CN = consoleAdmin is associated with the policy named consoleAdmin.

The certificate must meet the following requirements:

  • The subject common name (CN) must not be empty and must match an existing policy name.
  • The certificate must include the TLS Web Client Authentication extended key usage. AIStor rejects certificates that cannot be used for client authentication.
  • Unless MINIO_IDENTITY_TLS_SKIP_VERIFY is set to on, the certificate must be issued by a CA that the AIStor server trusts.

The following self-signed certificate is issued for consoleAdmin, so AIStor associates it with the pre-defined consoleAdmin policy:

Certificate:
    Data:
        Version: 3 (0x2)
        Signature Algorithm: ED25519
        Issuer: CN = consoleAdmin
        Validity
            Not Before: Jul 19 15:08:44 2021 GMT
            Not After : Aug 18 15:08:44 2021 GMT
        Subject: CN = consoleAdmin
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE

Group mapping is not possible with standard X.509 certificates.

Authentication flow

Certificate-based authentication proceeds in four steps:

  1. The client sends an HTTP POST request over a TLS connection to the AIStor TLS STS endpoint, presenting its client certificate.
  2. AIStor verifies that the client certificate is valid.
  3. AIStor finds the policy whose name matches the certificate’s subject common name (CN).
  4. AIStor returns temporary S3 credentials associated with the matched policy.

SPIFFE SVID authentication

In addition to classic X.509 common name (CN) authentication, AIStor validates SPIFFE SVIDs presented as client certificates for AssumeRoleWithCertificate. A SPIFFE SVID is an X.509 certificate that carries a SPIFFE ID (for example, spiffe://example.org/myservice) in a URI Subject Alternative Name (SAN).

When the leaf client certificate contains any URI SAN with the spiffe scheme, AIStor treats the certificate as a SPIFFE SVID and validates it accordingly:

  • AIStor validates the certificate as a SPIFFE X.509 SVID using the SPIFFE reference library (github.com/spiffe/go-spiffe/v2). The certificate must contain exactly one valid SPIFFE ID URI SAN.
  • If the SVID is invalid, AIStor rejects the request. There is no fallback to the certificate’s subject common name (CN) for a certificate that presents a spiffe URI.
  • The SPIFFE ID must include a workload path. A SPIFFE ID with an empty path (for example, spiffe://example.org/) is rejected because no policy name can be derived from it.

SVID validation runs in addition to the existing certificate checks. Unless MINIO_IDENTITY_TLS_SKIP_VERIFY is set to on, the certificate must still be issued by a CA that AIStor trusts and must include the TLS Web Client Authentication extended key usage.

SPIFFE-to-policy mapping

AIStor derives the S3 policy name from the workload path of the SPIFFE ID:

  • By default, AIStor uses only the path component of the SPIFFE ID, with the leading / removed. The SPIFFE ID spiffe://example.org/myservice maps to a policy named myservice.
  • When MINIO_IDENTITY_TLS_SPIFFE_MULTI_DOMAIN is set to on, AIStor qualifies the policy name with the trust domain in the form <trust-domain>/<path>. The SPIFFE ID spiffe://example.org/myservice maps to a policy named example.org/myservice. Use this option when identical workload paths exist in different trust domains and must map to different policies.

The derived policy name must match an existing policy exactly. As with CN-based authentication, group mapping is not possible with SPIFFE SVIDs.

Scoping access by workload identity

AIStor selects the policy by name, as described above, so a policy named after each workload path must exist. Substitution does not change that. What it changes is the content of those policies: instead of writing a bespoke document per workload, you write one document that refers to the caller and reuse it unchanged for every workload.

AIStor exposes the workload path as the jwt:sub condition key, and substitutes it into resource paths and conditions when it evaluates a request.

To give every workload its own prefix in a shared bucket, use ${jwt:sub} in the resource:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": ["arn:aws:s3:::shared-bucket/${jwt:sub}/*"]
    }
  ]
}

Create that policy once per workload path, using the same file every time:

mc admin policy create ALIAS billing/api ./workload-policy.json
mc admin policy create ALIAS billing/reports ./workload-policy.json

A certificate for spiffe://example.org/billing/api then reaches only shared-bucket/billing/api/*, and a certificate for spiffe://example.org/billing/reports reaches only its own prefix. Because the text never varies, you can generate these policies from your workload registry, and no workload can be granted another’s prefix by an editing mistake.

To harden a policy against being reused by an identity it was not written for, match jwt:sub in a condition as well:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::telemetry/*"],
      "Condition": {
        "StringLike": { "jwt:sub": "ingest/*" }
      }
    }
  ]
}

The policy then grants nothing unless the caller’s workload path begins with ingest/, even if the policy is later attached to another identity.

Note the following behavior:

  • jwt:sub holds the same value AIStor used to select the policy. When MINIO_IDENTITY_TLS_SPIFFE_MULTI_DOMAIN is set to on, that value includes the trust domain, so spiffe://example.org/billing/api substitutes as example.org/billing/api rather than billing/api. Write both resource paths and condition patterns to match the form you configured. With multi-domain qualification on, the resource pattern becomes arn:aws:s3:::shared-bucket/example.org/billing/api/* and the condition pattern becomes "jwt:sub": "example.org/ingest/*". A pattern written for the unqualified form denies the very workloads it is meant to allow.
  • Substitution uses the first value of a condition key. jwt:sub carries a single value, so the result is unambiguous for workload identities.
  • AIStor substitutes only condition keys it recognizes. If a key is unknown, or its value is empty, AIStor leaves the ${...} text in the pattern unchanged. The resulting pattern matches no object, so a missing identity denies access rather than widening it.
  • Bucket policies do not apply here. AIStor evaluates bucket policies only for anonymous requests, so an authenticated workload is governed by its own policy alone.
Substitution widens what a certificate controls

Without substitution, a client certificate selects a policy that an administrator wrote. With ${jwt:sub} in a resource path, the workload path inside the certificate also selects the object prefix.

Combined with the trust store behavior described at the top of this page, any CA the deployment trusts can mint a certificate for any workload path, and therefore for any prefix the policy covers. Do not rely on prefix substitution alone to separate tenants unless you control every CA in the trust store.

Response elements

MinIO AIStor returns an AssumeRoleWithCertificateResult object, where the Credentials object contains the temporary credentials generated by AIStor:

  • AccessKeyId - The access key applications use for authentication.
  • SecretAccessKey - The secret key applications use for authentication.
  • Expiration - The ISO-8601 date-time after which the credentials expire.
  • SessionToken - The session token applications use for authentication. Some SDKs may require this field when using temporary credentials.

The following example is similar to the response returned by the AIStor Server STS AssumeRoleWithCertificate endpoint:

<?xml version="1.0" encoding="UTF-8"?>
<AssumeRoleWithCertificateResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
   <AssumeRoleWithCertificateResult>
      <Credentials>
         <AccessKeyId>YC12ZBHUVW588BQAE5BM</AccessKeyId>
         <SecretAccessKey>Zgl9+zdE0pZ88+hLqtfh0ocLN+WQTJixHouCkZkW</SecretAccessKey>
         <Expiration>2021-07-19T20:10:45Z</Expiration>
         <SessionToken>eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...</SessionToken>
      </Credentials>
   </AssumeRoleWithCertificateResult>
   <ResponseMetadata>
      <RequestId>169339CD8B3A6948</RequestId>
   </ResponseMetadata>
</AssumeRoleWithCertificateResponse>

Considerations

Applications that use the S3 API directly work without issue. However, interactive users uploading content from a browser (for example, when a browser POSTs to a presigned URL the application generates) may see a prompt to provide a client certificate. The user must cancel that prompt to continue. There is no workaround for this behavior at this time.

Error elements

The XML error response for this API endpoint is similar to the AWS AssumeRoleWithWebIdentity response.