|
| 1 | +# Bitbucket - Web Identity Federation |
| 2 | + |
| 3 | +In this guide, you'll learn how to deploy from Bitbucket Pipelines using OpenFaaS's IAM support and Web Identity Federation. |
| 4 | + |
| 5 | +You'll need to create YAML files for an Issuer, a Policy and a Role. These need to be applied through kubectl, Helm or a GitOps tool. |
| 6 | + |
| 7 | +Your build will need to be adapted in order to receive an *id_token* from Bitbucket, which will be exchanged for an OpenFaaS access token. |
| 8 | + |
| 9 | +## Pre-requisites |
| 10 | + |
| 11 | +### OIDC identity provider |
| 12 | + |
| 13 | +You need to retrieve your workspace's OIDC identity provider URL and audience from Bitbucket: |
| 14 | + |
| 15 | +1. Navigate to your repository in Bitbucket Cloud. |
| 16 | +2. Select **Repository settings** on the left navigation sidebar. |
| 17 | +3. Select **OpenID Connect** under **Pipelines** on the left sidebar. |
| 18 | +4. Copy the **Identity provider URL** and **Audience** values. |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +You'll need both values when configuring the `JwtIssuer` resource below. |
| 23 | + |
| 24 | +See also: [Integrate Pipelines with resource servers using OIDC](https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/) |
| 25 | + |
| 26 | +### faas-cli pro license |
| 27 | + |
| 28 | +The `faas-cli pro` plugin requires a valid CLI license. The plugin looks for the license in the `OPENFAAS_LICENSE` environment variable. |
| 29 | + |
| 30 | +Add `OPENFAAS_LICENSE` as a repository variable under **Repository settings > Pipelines > Repository variables**, or as a workspace variable under **Workspace settings > Pipelines > Workspace variables**. Mark it as **Secured** so its value is masked in build logs. Workspace and repository variables are automatically available as environment variables in all pipeline steps. |
| 31 | + |
| 32 | +See: [Variables and secrets](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/) |
| 33 | + |
| 34 | +> If you don't have a CLI license yet, contact your account representative at OpenFaaS to receive one. |
| 35 | +
|
| 36 | +## Define an Issuer for Bitbucket |
| 37 | + |
| 38 | +First define a new JwtIssuer resource, setting the `iss` field to the Identity provider URL retrieved from your workspace's OIDC settings. Set the `aud` field to the URL of your OpenFaaS gateway. |
| 39 | + |
| 40 | +```yaml |
| 41 | +apiVersion: iam.openfaas.com/v1 |
| 42 | +kind: JwtIssuer |
| 43 | +metadata: |
| 44 | + name: bitbucket.com |
| 45 | + namespace: openfaas |
| 46 | +spec: |
| 47 | + iss: https://api.bitbucket.org/2.0/workspaces/WORKSPACE/pipelines-config/identity/oidc |
| 48 | + aud: |
| 49 | + - https://gw.example.com |
| 50 | + tokenExpiry: 30m |
| 51 | +``` |
| 52 | +
|
| 53 | +Replace `WORKSPACE` with the name of your Bitbucket workspace. |
| 54 | + |
| 55 | +The `tokenExpiry` field controls how long the OpenFaaS access token is valid. A short expiry such as `30m` is recommended to reduce the window in which a leaked token could be used, but it can be increased for longer-running CI jobs. |
| 56 | + |
| 57 | +The `aud` field must match the custom audience configured in your Bitbucket pipeline. By adding the gateway URL as a [custom audience](#custom-audience) in the pipeline step, it is included in the token's `aud` claim so that OpenFaaS can validate the token. |
| 58 | + |
| 59 | +If you have repositories across multiple workspaces that need to access OpenFaaS, you will need to create a separate issuer for each workspace. |
| 60 | + |
| 61 | +Apply the Issuer with `kubectl apply`. |
| 62 | + |
| 63 | +## Bitbucket OIDC token claims |
| 64 | + |
| 65 | +IAM Roles and Policies support conditions to restrict access based on claims from the Bitbucket OIDC token. For example, you can limit a Role so it only applies to a specific repository or branch by matching the `repositoryUuid` or `branchName` claims. Here is an overview of the claims available within a Bitbucket Pipelines `id_token`: |
| 66 | + |
| 67 | +```json |
| 68 | +{ |
| 69 | + "sub": "{repositoryUuid}:{stepUuid}", |
| 70 | + "stepUuid": "{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx}", |
| 71 | + "iss": "https://api.bitbucket.org/2.0/workspaces/WORKSPACE/pipelines-config/identity/oidc", |
| 72 | + "branchName": "xxxxxxxxx", |
| 73 | + "workspaceUuid": "{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx}", |
| 74 | + "pipelineRunUuid": "{xxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxxx}", |
| 75 | + "aud": [ |
| 76 | + "ari:cloud:bitbucket::workspace/WORKSPACE", |
| 77 | + "https://gw.example.com" |
| 78 | + ], |
| 79 | + "repositoryUuid": "{xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx}", |
| 80 | + "exp": "xxxxxxxxxx", |
| 81 | + "iat": "xxxxxxxxxx", |
| 82 | + "pipelineUuid": "{xxxxx-xxxxx-xxxx-xxxx-xxxxxxxxxxx}" |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +You can view the example payload for your repository on the **OpenID Connect** page under **Repository settings > Pipelines** in Bitbucket Cloud. |
| 87 | + |
| 88 | +When a [custom audience](#custom-audience) is configured in the pipeline, it will appear alongside the default workspace audience in the `aud` array. |
| 89 | + |
| 90 | +## Custom audience |
| 91 | + |
| 92 | +By default, the Bitbucket OIDC token only includes the workspace audience (e.g. `ari:cloud:bitbucket::workspace/WORKSPACE_UUID`). We recommend adding the OpenFaaS gateway URL as a custom audience. This is done by adding an `oidc` block with an `audiences` list to your pipeline step. The custom audience is appended to the default workspace audience in the token's `aud` claim. |
| 93 | + |
| 94 | +```yaml |
| 95 | +oidc: |
| 96 | + audiences: |
| 97 | + - https://gw.example.com |
| 98 | +``` |
| 99 | + |
| 100 | +When setting a custom audience in the pipeline, the `aud` field in the `JwtIssuer` resource must be set to the same gateway URL so that OpenFaaS can validate the token. |
| 101 | + |
| 102 | +You can set custom audiences at the step level or globally using `options`. See the [Bitbucket documentation](https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/) for more details. |
| 103 | + |
| 104 | +## Deploy functions to a specific namespace |
| 105 | + |
| 106 | +In this example, a pipeline deploys or updates functions in the `etl` namespace. The Policy grants deploy, and update permissions for any function within that namespace, and the Role locks it down to a specific repository and branch. |
| 107 | + |
| 108 | +### Create a Policy |
| 109 | + |
| 110 | +The `etl-deployer` policy grants permission to create or update any function within `etl`: |
| 111 | + |
| 112 | +```yaml |
| 113 | +apiVersion: iam.openfaas.com/v1 |
| 114 | +kind: Policy |
| 115 | +metadata: |
| 116 | + name: etl-deployer |
| 117 | + namespace: openfaas |
| 118 | +spec: |
| 119 | + statement: |
| 120 | + - sid: 1-rw-etl |
| 121 | + action: |
| 122 | + - Function:Create |
| 123 | + - Function:Update |
| 124 | + effect: Allow |
| 125 | + resource: ["etl:*"] |
| 126 | +``` |
| 127 | + |
| 128 | +### Bind the Policy to a Role |
| 129 | + |
| 130 | +Create a Role that binds the `etl-deployer` policy and uses conditions to restrict access to a specific repository and branch: |
| 131 | + |
| 132 | +```yaml |
| 133 | +apiVersion: iam.openfaas.com/v1 |
| 134 | +kind: Role |
| 135 | +metadata: |
| 136 | + name: etl-ci-deployer |
| 137 | + namespace: openfaas |
| 138 | +spec: |
| 139 | + policy: |
| 140 | + - etl-deployer |
| 141 | + condition: |
| 142 | + StringEqual: |
| 143 | + jwt:iss: ["https://api.bitbucket.org/2.0/workspaces/WORKSPACE/pipelines-config/identity/oidc"] |
| 144 | + jwt:repositoryUuid: ["{REPO_UUID}"] |
| 145 | + jwt:branchName: ["main"] |
| 146 | +``` |
| 147 | + |
| 148 | +Replace `WORKSPACE` with your Bitbucket workspace name and `{REPO_UUID}` with the UUID of the repository. The example restricts deployments to the `main` branch. You could restrict this further by matching additional claims such as `workspaceUuid` or `pipelineUuid`. |
| 149 | + |
| 150 | +Apply the Policy and Role with `kubectl apply`. |
| 151 | + |
| 152 | +### Create the pipeline |
| 153 | + |
| 154 | +Create a `bitbucket-pipelines.yml` file in the root of your repository: |
| 155 | + |
| 156 | +```yaml |
| 157 | +image: atlassian/default-image:3 |
| 158 | +
|
| 159 | +pipelines: |
| 160 | + default: |
| 161 | + - step: |
| 162 | + name: Deploy to OpenFaaS |
| 163 | + oidc: |
| 164 | + audiences: |
| 165 | + - https://gw.example.com |
| 166 | + script: |
| 167 | + - export OPENFAAS_URL="https://gw.example.com" |
| 168 | +
|
| 169 | + # Install faas-cli and the pro plugin |
| 170 | + - curl -sLS https://cli.openfaas.com | sh |
| 171 | + - faas-cli plugin get pro |
| 172 | +
|
| 173 | + # Exchange the Bitbucket OIDC token for an OpenFaaS access token |
| 174 | + - faas-cli pro auth --token=$BITBUCKET_STEP_OIDC_TOKEN |
| 175 | +
|
| 176 | + # Deploy functions defined in stack.yml |
| 177 | + - faas-cli template pull stack |
| 178 | + - faas-cli deploy --tag=sha |
| 179 | +``` |
| 180 | + |
| 181 | +The pipeline assumes a `stack.yml` file exists in the repository root defining the functions to deploy. In this example we are using the stack.yaml from [CI/CD with Bitbucket Pipelines](/reference/cicd/bitbucket/#create-a-function). Add a `namespace` field to each function so it is deployed into the namespace allowed by the policy: |
| 182 | + |
| 183 | +```diff |
| 184 | + functions: |
| 185 | + import-csv: |
| 186 | ++ namespace: etl |
| 187 | + lang: python3-http |
| 188 | +``` |
| 189 | + |
| 190 | +The `oidc.audiences` field adds the OpenFaaS gateway URL as a [custom audience](#custom-audience) to the OIDC token. Bitbucket makes the token available through the `BITBUCKET_STEP_OIDC_TOKEN` environment variable when `oidc` is configured on the step. |
| 191 | + |
| 192 | +The faas-cli pro plugin exchanges the Bitbucket OIDC token for an OpenFaaS access token and saves it. After that, faas-cli can be used to deploy functions to the gateway. |
| 193 | + |
| 194 | +## Invoke an authenticated function |
| 195 | + |
| 196 | +In this example, a pipeline invokes a single function called `import-csv` in the `etl` namespace. The Policy restricts access to only invoking that function, and the Role ensures only a specific repository and branch can use it. |
| 197 | + |
| 198 | +Note that the function must have authentication enabled. See [Function Authentication](/openfaas-pro/iam/function-authentication/) for how to enable it. |
| 199 | + |
| 200 | +### Create a Policy |
| 201 | + |
| 202 | +The `etl-invoker` policy restricts access to invoking a single function by name: |
| 203 | + |
| 204 | +```yaml |
| 205 | +apiVersion: iam.openfaas.com/v1 |
| 206 | +kind: Policy |
| 207 | +metadata: |
| 208 | + name: etl-invoker |
| 209 | + namespace: openfaas |
| 210 | +spec: |
| 211 | + statement: |
| 212 | + - sid: 1-invoke-etl |
| 213 | + action: |
| 214 | + - Function:Invoke |
| 215 | + effect: Allow |
| 216 | + resource: ["etl:import-csv"] |
| 217 | +``` |
| 218 | + |
| 219 | +The resource list can be expanded to allow invoking multiple functions: |
| 220 | + |
| 221 | +```yaml |
| 222 | + resource: ["etl:import-csv", "etl:transform-data", "etl:export-report"] |
| 223 | +``` |
| 224 | + |
| 225 | +Or use a wildcard to allow invoking any function in the namespace: |
| 226 | + |
| 227 | +```yaml |
| 228 | + resource: ["etl:*"] |
| 229 | +``` |
| 230 | + |
| 231 | +### Bind the Policy to a Role |
| 232 | + |
| 233 | +Create a Role that binds the `etl-invoker` policy and uses conditions to restrict access to a specific repository and branch: |
| 234 | + |
| 235 | +```yaml |
| 236 | +apiVersion: iam.openfaas.com/v1 |
| 237 | +kind: Role |
| 238 | +metadata: |
| 239 | + name: etl-ci-invoker |
| 240 | + namespace: openfaas |
| 241 | +spec: |
| 242 | + policy: |
| 243 | + - etl-invoker |
| 244 | + condition: |
| 245 | + StringEqual: |
| 246 | + jwt:iss: ["https://api.bitbucket.org/2.0/workspaces/WORKSPACE/pipelines-config/identity/oidc"] |
| 247 | + jwt:repositoryUuid: ["{REPO_UUID}"] |
| 248 | + jwt:branchName: ["main"] |
| 249 | +``` |
| 250 | + |
| 251 | +Replace `WORKSPACE` with your Bitbucket workspace name and `{REPO_UUID}` with the UUID of the repository. The example restricts invocations to the `main` branch. You could restrict this further by matching additional claims such as `workspaceUuid` or `pipelineUuid`. |
| 252 | + |
| 253 | +Apply the Policy and Role with `kubectl apply`. |
| 254 | + |
| 255 | +### Create the pipeline |
| 256 | + |
| 257 | +Create a `bitbucket-pipelines.yml` file in the root of your repository: |
| 258 | + |
| 259 | +```yaml |
| 260 | +image: atlassian/default-image:3 |
| 261 | +
|
| 262 | +pipelines: |
| 263 | + default: |
| 264 | + - step: |
| 265 | + name: Invoke function |
| 266 | + oidc: |
| 267 | + audiences: |
| 268 | + - https://gw.example.com |
| 269 | + script: |
| 270 | + - export OPENFAAS_URL="https://gw.example.com" |
| 271 | +
|
| 272 | + # Install faas-cli and the pro plugin |
| 273 | + - curl -sLS https://cli.openfaas.com | sh |
| 274 | + - faas-cli plugin get pro |
| 275 | +
|
| 276 | + # Exchange the Bitbucket OIDC token for an OpenFaaS access token |
| 277 | + - faas-cli pro auth --token=$BITBUCKET_STEP_OIDC_TOKEN |
| 278 | +
|
| 279 | + # Invoke the function with a sample CSV payload |
| 280 | + - >- |
| 281 | + printf 'name,email\nalice,alice@example.com\nbob,bob@example.com\n' |
| 282 | + > data.csv |
| 283 | + - faas-cli invoke -n etl import-csv < data.csv |
| 284 | +``` |
| 285 | + |
| 286 | +The `oidc.audiences` field adds the OpenFaaS gateway URL as a [custom audience](#custom-audience) to the OIDC token. Bitbucket makes the token available through the `BITBUCKET_STEP_OIDC_TOKEN` environment variable when `oidc` is configured on the step. |
| 287 | + |
| 288 | +The faas-cli pro plugin exchanges the Bitbucket OIDC token for an OpenFaaS access token and saves it. After that, faas-cli can be used to invoke functions. |
0 commit comments