|
| 1 | +--- |
| 2 | +title: External Variable Providers |
| 3 | +description: Configure external variable providers for your Spin App. |
| 4 | +date: 2024-07-17 |
| 5 | +categories: [Spin Operator] |
| 6 | +tags: [Tutorials] |
| 7 | +weight: 12 |
| 8 | +--- |
| 9 | + |
| 10 | +In the [Assigning Variables](./assigning-variables.md) guide, you learned how to configure variables on the SpinApp via its [variables](../reference/spin-app.md#spinappspecvariablesindex) section, either by supplying values in-line or via a Kubernetes ConfigMap or Secret. |
| 11 | + |
| 12 | +You can also utilize an external service like [Vault](https://vaultproject.io) or [Azure Key Vault](https://azure.microsoft.com/en-us/products/key-vault) to provide variable values for your application. This guide will show you how to use and configure both services in tandem with corresponding sample applications. |
| 13 | + |
| 14 | +## Prerequisites |
| 15 | + |
| 16 | +To follow along with this tutorial, you'll need: |
| 17 | + |
| 18 | +- A Kubernetes cluster running SpinKube. See the [Installation](../install/_index.md) guides for more information. |
| 19 | +- The [kubectl CLI](https://kubernetes.io/docs/tasks/tools/#kubectl) |
| 20 | +- The [spin CLI](https://developer.fermyon.com/spin/v2/install ) |
| 21 | +- The [kube plugin for Spin](https://github.com/spinkube/spin-plugin-kube?tab=readme-ov-file#install) |
| 22 | + |
| 23 | +## Supported providers |
| 24 | + |
| 25 | +Spin currently supports [Vault](#vault-provider) and [Azure Key Vault](#azure-key-vault-provider) as external variable providers. Configuration is supplied to the application via a [Runtime Configuration file](https://developer.fermyon.com/spin/v2/dynamic-configuration#dynamic-and-runtime-application-configuration). |
| 26 | + |
| 27 | +In SpinKube, this configuration file can be supplied in the form of a Kubernetes secret and linked to a SpinApp via its [runtimeConfig.loadFromSecret](https://www.spinkube.dev/docs/reference/spin-app/#spinappspecruntimeconfig) section. |
| 28 | + |
| 29 | +> Note: `loadFromSecret` takes precedence over any other `runtimeConfig` configuration. Thus, *all* runtime configuration must be contained in the Kubernetes secret, including [SQLite](../reference/spin-app.md#spinappspecruntimeconfigsqlitedatabasesindex), [Key Value](../reference/spin-app.md#spinappspecruntimeconfigkeyvaluestoresindex) and [LLM](../reference/spin-app.md#spinappspecruntimeconfigllmcompute) options that might otherwise be specified via their dedicated specs. |
| 30 | +
|
| 31 | +Let's look at examples utilizing specific provider configuration next. |
| 32 | + |
| 33 | +# Vault provider |
| 34 | + |
| 35 | +[Vault](https://vaultproject.io) is a popular choice for storing secrets and serving as a secure key-value store. |
| 36 | + |
| 37 | +This guide assumes you have: |
| 38 | + |
| 39 | +- A [Vault cluster](https://www.vaultproject.io/) |
| 40 | +- The [vault CLI](https://developer.hashicorp.com/vault/docs/install) |
| 41 | + |
| 42 | +### Build and publish the Spin application |
| 43 | + |
| 44 | +We'll use the [variable explorer app](https://github.com/spinkube/spin-operator/tree/main/apps/variable-explorer) to test this integration. |
| 45 | + |
| 46 | +First, clone the repository locally and navigate to the `variable-explorer` directory: |
| 47 | + |
| 48 | +```bash |
| 49 | +git clone [email protected]:spinkube/spin-operator.git |
| 50 | +cd apps/variable-explorer |
| 51 | +``` |
| 52 | + |
| 53 | +Now, build and push the application to a registry you have access to. Here we'll use [ttl.sh](https://ttl.sh): |
| 54 | + |
| 55 | +```bash |
| 56 | +spin build |
| 57 | +spin registry push ttl.sh/variable-explorer:1h |
| 58 | +``` |
| 59 | + |
| 60 | +### Create the `runtime-config.toml` file |
| 61 | + |
| 62 | +Here's a sample `runtime-config.toml` file containing Vault provider configuration: |
| 63 | + |
| 64 | +```toml |
| 65 | +[[config_provider]] |
| 66 | +type = "vault" |
| 67 | +url = "https://my-vault-server:8200" |
| 68 | +token = "my_token" |
| 69 | +mount = "admin/secret" |
| 70 | +``` |
| 71 | + |
| 72 | +To use this sample, you'll want to update the `url` and `token` fields with values applicable to your Vault cluster. The `mount` value will depend on the Vault namespace and `kv-v2` secrets engine name. In this sample, the namespace is `admin` and the engine is named `secret`, eg by running `vault secrets enable --path=secret kv-v2`. |
| 73 | + |
| 74 | +### Create the secrets in Vault |
| 75 | + |
| 76 | +Create the `log_level`, `platform_name` and `db_password` secrets used by the variable-explorer application in Vault: |
| 77 | + |
| 78 | +```bash |
| 79 | +vault kv put secret/log_level value=INFO |
| 80 | +vault kv put secret/platform_name value=Kubernetes |
| 81 | +vault kv put secret/db_password value=secret_sauce |
| 82 | +``` |
| 83 | + |
| 84 | +### Create the SpinApp and Secret |
| 85 | + |
| 86 | +Next, scaffold the SpinApp and Secret resource (containing the `runtime-config.toml` data) together in one go via the `kube` plugin: |
| 87 | + |
| 88 | +```bash |
| 89 | +spin kube scaffold -f ttl.sh/variable-explorer:1h -c runtime-config.toml -o scaffold.yaml |
| 90 | +``` |
| 91 | + |
| 92 | +### Deploy the application |
| 93 | + |
| 94 | +```bash |
| 95 | +kubectl apply -f scaffold.yaml |
| 96 | +``` |
| 97 | + |
| 98 | +### Test the application |
| 99 | + |
| 100 | +You are now ready to test the application and verify that all variables are passed correctly to the SpinApp from the Vault provider. |
| 101 | + |
| 102 | +Configure port forwarding from your local machine to the corresponding Kubernetes `Service`: |
| 103 | + |
| 104 | +```bash |
| 105 | +kubectl port-forward services/variable-explorer 8080:80 |
| 106 | + |
| 107 | +Forwarding from 127.0.0.1:8080 -> 80 |
| 108 | +Forwarding from [::1]:8080 -> 80 |
| 109 | +``` |
| 110 | + |
| 111 | +When port forwarding is established, you can send an HTTP request to the variable-explorer from within an additional terminal session: |
| 112 | + |
| 113 | +```bash |
| 114 | +curl http://localhost:8080 |
| 115 | +Hello from Kubernetes |
| 116 | +``` |
| 117 | + |
| 118 | +Finally, you can use `kubectl logs` to see all logs produced by the variable-explorer at runtime: |
| 119 | + |
| 120 | +```bash |
| 121 | +kubectl logs -l core.spinoperator.dev/app-name=variable-explorer |
| 122 | + |
| 123 | +# Log Level: INFO |
| 124 | +# Platform Name: Kubernetes |
| 125 | +# DB Password: secret_sauce |
| 126 | +``` |
| 127 | + |
| 128 | +# Azure Key Vault provider |
| 129 | + |
| 130 | +[Azure Key Vault](https://azure.microsoft.com/en-us/products/key-vault) is a secure secret store for distributed applications hosted on the [Azure](https://azure.microsoft.com) platform. |
| 131 | + |
| 132 | +This guide assumes you have: |
| 133 | + |
| 134 | +- An [Azure account](https://azure.microsoft.com/en-us/free/) |
| 135 | +- The [az CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) |
| 136 | + |
| 137 | +### Build and publish the Spin application |
| 138 | + |
| 139 | +We'll use the [Azure Key Vault Provider](https://github.com/fermyon/enterprise-architectures-and-patterns/tree/main/application-variable-providers/azure-key-vault-provider) sample application for this exercise. |
| 140 | + |
| 141 | +First, clone the repository locally and navigate to the `azure-key-vault-provider` directory: |
| 142 | + |
| 143 | +```bash |
| 144 | +git clone [email protected]:fermyon/enterprise-architectures-and-patterns.git |
| 145 | +cd enterprise-architectures-and-patterns/application-variable-providers/azure-key-vault-provider |
| 146 | +``` |
| 147 | + |
| 148 | +Now, build and push the application to a registry you have access to. Here we'll use [ttl.sh](https://ttl.sh): |
| 149 | + |
| 150 | +```bash |
| 151 | +spin build |
| 152 | +spin registry push ttl.sh/azure-key-vault-provider:1h |
| 153 | +``` |
| 154 | + |
| 155 | +The next steps will guide you in creating and configuring an Azure Key Vault and populating the runtime configuration file with connection credentials. |
| 156 | + |
| 157 | +### Deploy Azure Key Vault |
| 158 | + |
| 159 | +```bash |
| 160 | +# Variable Definition |
| 161 | +KV_NAME=spinkube-keyvault |
| 162 | +LOCATION=westus2 |
| 163 | +RG_NAME=rg-spinkube-keyvault |
| 164 | + |
| 165 | +# Create Azure Resource Group and Azure Key Vault |
| 166 | +az group create -n $RG_NAME -l $LOCATION |
| 167 | +az keyvault create -n $KV_NAME \ |
| 168 | + -g $RG_NAME \ |
| 169 | + -l $LOCATION \ |
| 170 | + --enable-rbac-authorization true |
| 171 | + |
| 172 | +# Grab the Azure Resource Identifier of the Azure Key Vault instance |
| 173 | +KV_SCOPE=$(az keyvault show -n $KV_NAME -g $RG_NAME -otsv --query "id") |
| 174 | +``` |
| 175 | + |
| 176 | +### Add a Secret to the Azure Key Vault instance |
| 177 | + |
| 178 | +```bash |
| 179 | +# Grab the ID of the currently signed in user in Azure CLI |
| 180 | +CURRENT_USER_ID=$(az ad signed-in-user show -otsv --query "id") |
| 181 | + |
| 182 | +# Make the currently signed in user a Key Vault Secrets Officer |
| 183 | +# on the scope of the new Azure Key Vault instance |
| 184 | +az role assignment create --assignee $CURRENT_USER_ID \ |
| 185 | + --role "Key Vault Secrets Officer" \ |
| 186 | + --scope $KV_SCOPE |
| 187 | + |
| 188 | +# Create a test secret called 'secret` in the Azure Key Vault instance |
| 189 | +az keyvault secret set -n secret --vault-name $KV_NAME --value secret_value -o none |
| 190 | +``` |
| 191 | + |
| 192 | +### Create a Service Principal and Role Assignment for Spin |
| 193 | + |
| 194 | +```bash |
| 195 | +SP_NAME=sp-spinkube-keyvault |
| 196 | +SP=$(az ad sp create-for-rbac -n $SP_NAME -ojson) |
| 197 | + |
| 198 | +CLIENT_ID=$(echo $SP | jq -r '.appId') |
| 199 | +CLIENT_SECRET=$(echo $SP | jq -r '.password') |
| 200 | +TENANT_ID=$(echo $SP | jq -r '.tenant') |
| 201 | + |
| 202 | +az role assignment create --assignee $CLIENT_ID \ |
| 203 | + --role "Key Vault Secrets User" \ |
| 204 | + --scope $KV_SCOPE |
| 205 | +``` |
| 206 | + |
| 207 | +### Create the `runtime-config.toml` file |
| 208 | + |
| 209 | +Create a `runtime-config.toml` file with the following contents, substituting in the values for `KV_NAME`, `CLIENT_ID`, `CLIENT_SECRET` and `TENANT_ID` from the previous steps. |
| 210 | + |
| 211 | +```toml |
| 212 | +[[config_provider]] |
| 213 | +type = "azure_key_vault" |
| 214 | +vault_url = "https://<$KV_NAME>.vault.azure.net/" |
| 215 | +client_id = "<$CLIENT_ID>" |
| 216 | +client_secret = "<$CLIENT_SECRET>" |
| 217 | +tenant_id = "<$TENANT_ID>" |
| 218 | +authority_host = "AzurePublicCloud" |
| 219 | +``` |
| 220 | + |
| 221 | +### Create the SpinApp and Secret |
| 222 | + |
| 223 | +Scaffold the SpinApp and Secret resource (containing the `runtime-config.toml` data) together in one go via the `kube` plugin: |
| 224 | + |
| 225 | +```bash |
| 226 | +spin kube scaffold -f ttl.sh/azure-key-vault-provider:1h -c runtime-config.toml -o scaffold.yaml |
| 227 | +``` |
| 228 | + |
| 229 | +### Deploy the application |
| 230 | + |
| 231 | +```bash |
| 232 | +kubectl apply -f scaffold.yaml |
| 233 | +``` |
| 234 | + |
| 235 | +### Test the application |
| 236 | + |
| 237 | +Now you are ready to test the application and verify that the secret resolves its value from Azure Key Vault. |
| 238 | + |
| 239 | +Configure port forwarding from your local machine to the corresponding Kubernetes `Service`: |
| 240 | + |
| 241 | +```bash |
| 242 | +kubectl port-forward services/azure-key-vault-provider 8080:80 |
| 243 | + |
| 244 | +Forwarding from 127.0.0.1:8080 -> 80 |
| 245 | +Forwarding from [::1]:8080 -> 80 |
| 246 | +``` |
| 247 | + |
| 248 | +When port forwarding is established, you can send an HTTP request to the azure-key-vault-provider app from within an additional terminal session: |
| 249 | + |
| 250 | +```bash |
| 251 | +curl http://localhost:8080 |
| 252 | +Loaded secret from Azure Key Vault: secret_value |
| 253 | +``` |
0 commit comments