# Check your KMS permissions

Source: https://codeherder.com/docs/self-host-kms-check/

Prove with real AWS KMS calls that your self-hosted server can seal secrets, that a wrong context fails, and that an unauthorised principal is denied.

A self-hosted server seals each secret with a data key from AWS KMS. The server needs a reachable KMS key and the right permissions. Run these checks once in your chosen region, before you store real secrets. Use a test key. Do not use production data.

You need the AWS CLI and two principals:

- The **app role**. The server runs as this role. It can use the key.
- A **denied principal**. It has no `kms:Decrypt` permission on the key.

Set these values first:

```
export KEY_ID=<your test key ID or ARN>
export AWS_REGION=<your chosen region>
```

## What the server sends to KMS

The server sends an encryption context of two values, `scope` and `name`. KMS binds the data key to that context. A decrypt call with a different context must fail.

## Run the checks

Run the first three commands as the app role.

1. Confirm the key is reachable. The server makes this call at boot. `aws kms describe-key --key-id "$KEY_ID " --query KeyMetadata.KeyState` Expect `"Enabled"`.
2. Make a data key with a context, and keep the wrapped copy. `aws kms generate-data-key --key-id "$KEY_ID " --key-spec AES_256 \ --encryption-context scope=probe,name=probe \ --query CiphertextBlob --output text | base64 -d > wrapped.bin`
3. Decrypt with the same context. Then decrypt with a changed context. `aws kms decrypt --ciphertext-blob fileb://wrapped.bin \ --encryption-context scope=probe,name=probe --query KeyId --output text aws kms decrypt --ciphertext-blob fileb://wrapped.bin \ --encryption-context scope=probe,name=other --query KeyId --output text` The first call prints the key ARN. The second call fails with `InvalidCiphertextException`.
4. Switch to the denied principal. Decrypt with the correct context. `aws kms decrypt --ciphertext-blob fileb://wrapped.bin \ --encryption-context scope=probe,name=probe` Expect `AccessDeniedException`. If the call succeeds, fix the key policy and IAM policies before you go live.

Delete `wrapped.bin` when you finish.

## Record the evidence

Copy this table. Fill in one row for each check.

| Command | Principal | Expected | Actual | Date |
| --- | --- | --- | --- | --- |
| `describe-key` | App role | `Enabled` |  |  |
| `generate-data-key` | App role | Succeeds |  |  |
| `decrypt`, same context | App role | Succeeds |  |  |
| `decrypt`, changed context | App role | `InvalidCiphertextException` |  |  |
| `decrypt`, same context | Denied principal | `AccessDeniedException` |  |  |

If an error code differs from the table, record the real code. Do not mark the row as passed.

## If you cannot reach KMS

The server fails closed. It keeps no plaintext key and no fallback. If a check fails, fix the key policy or the role. Then run the check again.
