Introduction:

This guide explains how to rotate Tink encryption keys in CircleCI Server. Tink key rotation lets you introduce a new encryption key for fresh data while retaining old keys for decrypting previously stored information.

This process involves adding a new key to your keyset JSON, making it the primary key, and keeping the old key(s) until they are no longer needed.

This process is useful if your Tink key is leaked or otherwise compromised and needs to be replaced to ensure the security of your context vars.

Prerequisites:

  • Access to the CircleCI Server Helm values (values.yaml)

  • Access to the Tink keyset (JSON), either from tink.keyset in values.yaml or via the tink Kubernetes secret

  • Administrative access to the deployment environment.

  • Familiarity with base64 encoding/decoding and JSON editing.

Instructions:

  1. Locate the current keyset

    1. Retrieve your current Tink keyset from values.yaml or the tink secret

    2. If retreiving from the K8S secret, it'll need to be base64 decoded:

      kubectl -n <circle-ns> get secret tink -o jsonpath='{.data.keyset}'|base64 -d|jq > keyset.json
  2. Generate a new key

    1. Use Tink CLI or library to generate a key for the same primitive (e.g., AES-GCM).

    2. Example (CLI, pseudo-code):

      ./tinkey create-keyset --key-template XCHACHA20_POLY1305 > new_keyset.json
  3. Merge the new key into the existing keyset

    1. Open both keyset.json (existing) and new_keyset.json (new key).

    2. Copy the new key object from new_keyset.json into the key array of keyset.json.

      {
        "primaryKeyId": 3921291120,
        "key": [
          {
            "keyData": {
              "typeUrl": "type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key",
              "value": "GiD7e53Wz1SE71MZ8twnancdqS+48lYJFMwoLkHcZQX65g==",
              "keyMaterialType": "SYMMETRIC"
            },
            "status": "ENABLED",
            "keyId": 3921291120,
            "outputPrefixType": "TINK"
          },
          {
            "keyData": {
              "typeUrl": "type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key",
              "value": "GiARW2q2hwUqOMIAeL7T2R/+NKDIiaOYchhIIMLLVG6HMw==",
              "keyMaterialType": "SYMMETRIC"
            },
            "status": "ENABLED",
            "keyId": 2483191459,
            "outputPrefixType": "TINK"
          }
        ]
      }

      Tip: You can pipe the single line json into jq with no args to get the "pretty" formatted version for editing then run it through jq -c post editing to obtain the oneline compact form.

  4. Make the new key the primary

    1. Update the primaryKeyId in keyset.json to match the keyId of the new key.

    2. Keep the old key’s status as "ENABLED" to ensure old data can still be decrypted.

  5. Update CircleCI Server with the rotated keyset

    1. Encode the updated JSON as base64 and create the new tink secret:

      kubectl create secret generic tink --from-file=keyset=keyset.json


      OR
       

    2. Update values.yaml , encapsulating the full json blob in single quotes under tink.keyset

      helm upgrade
  6. Phase out old keys (optional)

    1. Once all data encrypted with the old key has been re-encrypted or is no longer needed, change its status to "DISABLED" or "DESTROYED".

    2. Apply the updated keyset to CircleCI Server again.

Outcome:

After following these steps, CircleCI Server will encrypt all new data with the newly created key while still being able to decrypt old data using the previous keys. Over time, old keys can be disabled or destroyed to complete the rotation process.

Additional Notes:

  • Tink stores the key ID in the ciphertext prefix, allowing it to automatically select the correct key during decryption.

  • Key rotation does not automatically re-encrypt existing data — you must handle that manually if required.

Additional Resources:

Was this article helpful?
1 out of 1 found this helpful

Comments

0 comments

Article is closed for comments.