diff --git a/crates/common/src/fastly_storage.rs b/crates/common/src/fastly_storage.rs index c4a8241..d39170d 100644 --- a/crates/common/src/fastly_storage.rs +++ b/crates/common/src/fastly_storage.rs @@ -6,6 +6,8 @@ use http::StatusCode; use crate::backend::ensure_backend_from_url; use crate::error::TrustedServerError; +const FASTLY_API_HOST: &str = "https://api.fastly.com"; + pub struct FastlyConfigStore { store_name: String, } @@ -75,7 +77,8 @@ impl FastlySecretStore { pub struct FastlyApiClient { api_key: Vec, - base_url: String, + base_url: &'static str, + backend_name: String, } impl FastlyApiClient { @@ -84,7 +87,7 @@ impl FastlyApiClient { } pub fn from_secret_store(store_name: &str, key_name: &str) -> Result { - ensure_backend_from_url("https://api.fastly.com").map_err(|e| { + let backend_name = ensure_backend_from_url(FASTLY_API_HOST).map_err(|e| { TrustedServerError::Configuration { message: format!("Failed to ensure API backend: {}", e), } @@ -93,9 +96,12 @@ impl FastlyApiClient { let secret_store = FastlySecretStore::new(store_name); let api_key = secret_store.get(key_name)?; + log::debug!("FastlyApiClient initialized with backend: {}", backend_name); + Ok(Self { api_key, - base_url: "https://api.fastly.com".to_string(), + base_url: FASTLY_API_HOST, + backend_name, }) } @@ -132,11 +138,11 @@ impl FastlyApiClient { .with_body(body_content); } - request.send("backend_https_api_fastly_com").map_err(|e| { - TrustedServerError::Configuration { + request + .send(&self.backend_name) + .map_err(|e| TrustedServerError::Configuration { message: format!("Failed to send API request: {}", e), - } - }) + }) } pub fn update_config_item( diff --git a/docs/guide/key-rotation.md b/docs/guide/key-rotation.md index c191fb9..738852e 100644 --- a/docs/guide/key-rotation.md +++ b/docs/guide/key-rotation.md @@ -26,6 +26,174 @@ Key rotation is the process of generating new signing keys and transitioning fro - **Incident-based**: Immediately if compromise suspected - **Before major releases**: Ensure fresh keys for new deployments +## Prerequisites + +Before you can rotate keys, you need to set up the required Fastly stores and API credentials. + +### Required Stores + +Key rotation requires three Fastly stores: + +1. **Config Store** (`jwks_store`) - Stores public JWKs and metadata + - `current-kid` - The active key identifier + - `active-kids` - Comma-separated list of valid key IDs + - Individual JWKs keyed by their `kid` + +2. **Secret Store** (`signing_keys`) - Stores private signing keys + - Each key stored with its `kid` as the key name + - Values are base64-encoded Ed25519 private keys + +3. **Secret Store** (`api-keys`) - Stores Fastly API credentials + - `api_key` - Fastly API token for managing stores + +### Creating Stores + +#### 1. Create Config Store + +```bash +# Create the config store +fastly config-store create --name=jwks_store + +# Get the store ID (you'll need this for configuration) +fastly config-store list +``` + +Note the Config Store ID from the output. + +#### 2. Create Secret Stores + +```bash +# Create secret store for signing keys +fastly secret-store create --name=signing_keys + +# Create secret store for API credentials +fastly secret-store create --name=api-keys + +# Get the store IDs +fastly secret-store list +``` + +Note both Secret Store IDs from the output. + +::: tip Dashboard Alternative +You can also create stores via the Fastly dashboard, but CLI commands are recommended for automation and reproducibility. +::: + +### Creating Fastly API Key + +Key rotation uses the Fastly API to manage store contents. You need to create an API token: + +#### Step 1: Generate API Token + +1. Log in to the [Fastly Dashboard](https://manage.fastly.com) +2. Navigate to **Account → API Tokens → Personal Tokens** +3. Click **Create Token** +4. Configure the token: + - **Name**: `trusted-server-key-rotation` + - **Scope**: `global:read`, `global:write` (or scope to your specific service) + - **Expiration**: Set according to your security policy + +#### Step 2: Store API Token + +Store the API token in the `api-keys` secret store: + +```bash +# Store the API key +fastly secret-store-entry create \ + --store-id= \ + --name=api_key \ + --secret= +``` + +::: warning Keep Your API Token Secure +- Never commit API tokens to version control +- Store them only in Fastly Secret Store +- Rotate API tokens according to your security policy +- Use minimal required permissions +::: + +### Linking Stores to Service + +Stores must be linked to your Compute service to be accessible at runtime. + +#### Production (CLI) + +```bash +# Link config store +fastly service-version compute config-store create \ + --version= \ + --config-store-id= \ + --name=jwks_store + +# Link signing keys secret store +fastly service-version compute secret-store create \ + --version= \ + --secret-store-id= \ + --name=signing_keys + +# Link API keys secret store +fastly service-version compute secret-store create \ + --version= \ + --secret-store-id= \ + --name=api-keys +``` + +::: tip Dashboard Linking +You can also link stores via the Fastly dashboard under your service's **Resources** section. +::: + +#### Local Development + +For local testing, configure stores in `fastly.toml`: + +```toml +[local_server.config_stores] + [local_server.config_stores.jwks_store] + format = "inline-toml" + [local_server.config_stores.jwks_store.contents] + ts-2025-01-01 = "{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"kid\":\"ts-2025-01-01\",\"use\":\"sig\",\"x\":\"...\"}" + current-kid = "ts-2025-01-01" + active-kids = "ts-2025-01-01" + +[local_server.secret_stores] + [[local_server.secret_stores.signing_keys]] + key = "ts-2025-01-01" + data = "" + + [[local_server.secret_stores.api-keys]] + key = "api_key" + env = "FASTLY_KEY" # Load from environment variable +``` + +### Configuration in trusted-server.toml + +Update `trusted-server.toml` with your store IDs: + +```toml +[request_signing] +enabled = true +config_store_id = "" # Your jwks_store ID +secret_store_id = "