From 3ed4444422d75404778494a0a10b66eb2bf07870 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 13:15:33 +0100 Subject: [PATCH 01/21] feat: bicep templates for azure resources --- bicep/main.bicep | 86 ++++++++++++++++++++++++++++++++++++++ bicep/main.test.bicepparam | 3 ++ bicep/modules/id.bicep | 36 ++++++++++++++++ bicep/modules/kv.bicep | 72 +++++++++++++++++++++++++++++++ bicep/modules/law.bicep | 24 +++++++++++ 5 files changed, 221 insertions(+) create mode 100644 bicep/main.bicep create mode 100644 bicep/main.test.bicepparam create mode 100644 bicep/modules/id.bicep create mode 100644 bicep/modules/kv.bicep create mode 100644 bicep/modules/law.bicep diff --git a/bicep/main.bicep b/bicep/main.bicep new file mode 100644 index 0000000..b6c1eca --- /dev/null +++ b/bicep/main.bicep @@ -0,0 +1,86 @@ +targetScope = 'subscription' + +/* + Parameters +*/ + +@allowed([ + 'D' // Development + 'T' // Test + 'A' // Acceptance + 'P' // Production +]) +param environment string +param location string = 'westeurope' +param name object = { + tenantId: 'BVDB' + projectId: 'KEYWEAVE' + region: 'WEU' +} + +/* + Variables +*/ + +var tags = { + project: 'keyweave' +} +var nameFormat = '${name.tenantId}-${name.projectId}-${environment}-${name.region}-{0}-{1:N0}' + +/* + Resource Group +*/ + +resource ResourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: format(nameFormat, 'RG', 1) + location: location + tags: tags +} + +/* + Module for Log Analytics Workspace +*/ + +module LogAnalyticsWorkspace 'modules/law.bicep' = { + name: 'LogAnalyticsWorkspace' + scope: ResourceGroup + params: { + nameFormat: nameFormat + location: location + tags: tags + } +} + +/* + Module for Managed Identities +*/ + +module ManagedIdentities 'modules/id.bicep' = { + name: 'ManagedIdentities' + scope: ResourceGroup + params: { + nameFormat: nameFormat + location: location + tags: tags + } +} + +/* + Module for KeyVault +*/ + +module KeyVault 'modules/kv.bicep' = { + name: 'KeyVault' + scope: ResourceGroup + dependsOn: [ + LogAnalyticsWorkspace + ] + params: { + nameFormat: nameFormat + location: location + tags: tags + + getPrincipalIds: ManagedIdentities.outputs.getPrincipalIds + listPrincipalIds: ManagedIdentities.outputs.listPrincipalIds + } +} diff --git a/bicep/main.test.bicepparam b/bicep/main.test.bicepparam new file mode 100644 index 0000000..5c7b6fa --- /dev/null +++ b/bicep/main.test.bicepparam @@ -0,0 +1,3 @@ +using 'main.bicep' + +param environment = 'T' diff --git a/bicep/modules/id.bicep b/bicep/modules/id.bicep new file mode 100644 index 0000000..4d2d19b --- /dev/null +++ b/bicep/modules/id.bicep @@ -0,0 +1,36 @@ +param nameFormat string +param location string +param tags object + +resource managedIdentityNone 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: format(nameFormat, 'ID', 1) + location: location + tags: tags +} + +resource managedIdentityGet 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: format(nameFormat, 'ID', 2) + location: location + tags: tags +} + +resource managedIdentityList 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: format(nameFormat, 'ID', 3) + location: location + tags: tags +} + +resource managedIdentityGetList 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: format(nameFormat, 'ID', 4) + location: location + tags: tags +} + +output getPrincipalIds array = [ + managedIdentityGet.properties.principalId + managedIdentityGetList.properties.principalId +] +output listPrincipalIds array = [ + managedIdentityList.properties.principalId + managedIdentityGetList.properties.principalId +] diff --git a/bicep/modules/kv.bicep b/bicep/modules/kv.bicep new file mode 100644 index 0000000..52978a2 --- /dev/null +++ b/bicep/modules/kv.bicep @@ -0,0 +1,72 @@ +param nameFormat string +param location string +param tags object + +param getPrincipalIds array +param listPrincipalIds array + +var accessPolicies = [for id in union(getPrincipalIds, listPrincipalIds): { + tenantId: tenant().tenantId + objectId: id + permissions: { + secrets: contains(getPrincipalIds, id) && contains(listPrincipalIds, id) ? ['Get', 'List'] : contains(listPrincipalIds, id) ? ['List'] : ['Get'] + } +}] + +/* + Log Analytics Workspace (existing) +*/ + +resource _logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: format(nameFormat, 'LAW', 1) +} + +/* + Key Vault +*/ + +resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { + name: replace(toLower(format(nameFormat, 'KVT', 1)), '-', '') + location: location + tags: tags + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enableSoftDelete: true + enablePurgeProtection: true + accessPolicies: accessPolicies + } + resource testSecret 'secrets' = { + name: 'testSecret' + properties: { + value: 'testSecretValue' + } + } + resource filterTestSecret 'secrets' = { + name: 'filterTestSecret' + properties: { + value: 'filterTestSecretValue' + } + } +} + +/* + Diagnostic Settings for Key Vault +*/ + +resource keyVaultDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: 'keyVaultLogging' + scope: keyVault + properties: { + workspaceId: _logAnalyticsWorkspace.id + logs: [ + { + category: 'AuditEvent' + enabled: true + } + ] + } +} diff --git a/bicep/modules/law.bicep b/bicep/modules/law.bicep new file mode 100644 index 0000000..451519c --- /dev/null +++ b/bicep/modules/law.bicep @@ -0,0 +1,24 @@ +param nameFormat string +param location string +param tags object + +/* + Log Analytics Workspace +*/ + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: format(nameFormat, 'LAW', 1) + location: location + tags: tags + properties: { + sku: { + name: 'PerGB2018' + } + features: { + enableLogAccessUsingOnlyResourcePermissions: true + } + workspaceCapping: { + dailyQuotaGb: json('0.025') + } + } +} From 2f4dace719ab78c903f12bc329e062d3a537c2bc Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 13:15:49 +0100 Subject: [PATCH 02/21] feat: end-to-end test workflow --- .github/workflows/e2e.yml | 109 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 .github/workflows/e2e.yml diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..ad429a4 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,109 @@ +name: Checks + +permissions: + id-token: write + contents: read + +on: + push: + branches: [ main ] + paths: [ 'bicep/**', 'src/**', 'Cargo.toml', 'Cargo.lock', '.github/workflows/e2e.yml' ] + pull_request: + branches: [ main ] + paths: [ 'bicep/**', 'src/**', 'Cargo.toml', 'Cargo.lock', '.github/workflows/e2e.yml' ] + +jobs: + bicep: + name: Deploy Azure resources + environment: bicep + runs-on: ubuntu-latest + env: + LOCATION: eastus + DEPLOYMENT_NAME: keyweave-${{ github.run_id }} + steps: + - uses: actions/checkout@v3 + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Validate Bicep template + uses: azure/arm-deploy@v1 + with: + scope: subscription + region: ${{ env.LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/main.params.json + deploymentName: ${{ env.DEPLOYMENT_NAME }} + additionalArguments: "--what-if" + + - name: Deploy Bicep template + if: github.ref == 'refs/heads/main' + uses: azure/arm-deploy@v1 + with: + scope: subscription + region: ${{ env.LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/main.params.json + deploymentName: ${{ env.DEPLOYMENT_NAME }} + none-test: + needs: bicep + runs-on: ubuntu-latest + environment: 1-none + steps: + - uses: actions/checkout@v4 + - name: Log into Azure + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - uses: dtolnay/rust-toolchain@stable + - name: Use Keyweave with No Access Policies + run: cargo run -- --vault-name bvdbkeyweavetweukvt1 + get-test: + needs: bicep + runs-on: ubuntu-latest + environment: 2-get + steps: + - uses: actions/checkout@v4 + - name: Log into Azure + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - uses: dtolnay/rust-toolchain@stable + - name: Use Keyweave with Only Get Access Policy + run: cargo run -- --vault-name bvdbkeyweavetweukvt1 + list-test: + needs: bicep + runs-on: ubuntu-latest + environment: 3-list + steps: + - uses: actions/checkout@v4 + - name: Log into Azure + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - uses: dtolnay/rust-toolchain@stable + - name: Use Keyweave with Only List Access Policy + run: cargo run -- --vault-name bvdbkeyweavetweukvt1 + get-list-test: + needs: bicep + runs-on: ubuntu-latest + environment: 4-get-list + steps: + - uses: actions/checkout@v4 + - name: Log into Azure + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - uses: dtolnay/rust-toolchain@stable + - name: Use Keyweave with both Get and List Access Policies + run: cargo run -- --vault-name bvdbkeyweavetweukvt1 From dd3b46951c1476bcfad3f0b98e5fc2b2cf5370ce Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 13:16:06 +0100 Subject: [PATCH 03/21] chore: remove unnecessary comments --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index af80d3e..18e55b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,7 @@ async fn fetch_secrets_from_key_vault( Ok(p) => p, Err(err) => { error!("Failed to fetch secrets page: {}", err); - return Err(err.into()); // Convert the error into anyhow::Error + return Err(err.into()); } }; secret_values @@ -158,7 +158,7 @@ mod tests { vec!["SECRET_KEY=secret_value1", "API_KEY=secret_value2",] ); - fs::remove_file(test_file)?; // Clean up the test file + fs::remove_file(test_file)?; Ok(()) } } From 84832c5ead8b81e29f7f42cb3b5ea5c6a28ba6a2 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 13:21:15 +0100 Subject: [PATCH 04/21] fix: remove what-if and correct path, displaynames --- .github/workflows/e2e.yml | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ad429a4..31bbae5 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -27,27 +27,16 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - - name: Validate Bicep template - uses: azure/arm-deploy@v1 - with: - scope: subscription - region: ${{ env.LOCATION }} - template: infra/bicep/main.bicep - parameters: infra/bicep/main.params.json - deploymentName: ${{ env.DEPLOYMENT_NAME }} - additionalArguments: "--what-if" - - name: Deploy Bicep template - if: github.ref == 'refs/heads/main' uses: azure/arm-deploy@v1 with: scope: subscription region: ${{ env.LOCATION }} - template: infra/bicep/main.bicep - parameters: infra/bicep/main.params.json + template: bicep/main.bicep + parameters: bicep/main.params.json deploymentName: ${{ env.DEPLOYMENT_NAME }} none-test: + name: Tests without access needs: bicep runs-on: ubuntu-latest environment: 1-none @@ -63,6 +52,7 @@ jobs: - name: Use Keyweave with No Access Policies run: cargo run -- --vault-name bvdbkeyweavetweukvt1 get-test: + name: Tests with Get access needs: bicep runs-on: ubuntu-latest environment: 2-get @@ -78,6 +68,7 @@ jobs: - name: Use Keyweave with Only Get Access Policy run: cargo run -- --vault-name bvdbkeyweavetweukvt1 list-test: + name: Tests with List access needs: bicep runs-on: ubuntu-latest environment: 3-list @@ -93,6 +84,7 @@ jobs: - name: Use Keyweave with Only List Access Policy run: cargo run -- --vault-name bvdbkeyweavetweukvt1 get-list-test: + name: Tests with Get and List access needs: bicep runs-on: ubuntu-latest environment: 4-get-list From 4acd2a2e36ad61f2028be649b9e03f78db507236 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 14:42:04 +0100 Subject: [PATCH 05/21] feat: use build and shortened creds --- .github/workflows/e2e.yml | 87 ++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 31bbae5..f2496d0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -4,6 +4,9 @@ permissions: id-token: write contents: read +env: + VAULT_NAME: bvdbkeyweavetweukvt1 + on: push: branches: [ main ] @@ -13,6 +16,19 @@ on: paths: [ 'bicep/**', 'src/**', 'Cargo.toml', 'Cargo.lock', '.github/workflows/e2e.yml' ] jobs: + build: + name: Build Keyweave + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Build project + run: cargo build --all --release + - name: Archive binary artifact + uses: actions/upload-artifact@v3.1.3 + with: + path: target/release/keyweave + bicep: name: Deploy Azure resources environment: bicep @@ -24,9 +40,7 @@ jobs: - uses: actions/checkout@v3 - uses: azure/login@v1 with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Deploy Bicep template uses: azure/arm-deploy@v1 with: @@ -35,67 +49,54 @@ jobs: template: bicep/main.bicep parameters: bicep/main.params.json deploymentName: ${{ env.DEPLOYMENT_NAME }} + none-test: name: Tests without access - needs: bicep + needs: [build, bicep] runs-on: ubuntu-latest - environment: 1-none + environment: none steps: - - uses: actions/checkout@v4 - - name: Log into Azure - uses: azure/login@v1 + - uses: actions/download-artifact@v3.0.2 + - uses: azure/login@v1 with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - uses: dtolnay/rust-toolchain@stable + creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Use Keyweave with No Access Policies - run: cargo run -- --vault-name bvdbkeyweavetweukvt1 + run: ./keyweave --vault-name ${{ env.VAULT_NAME}} + get-test: name: Tests with Get access - needs: bicep + needs: [build, bicep] runs-on: ubuntu-latest - environment: 2-get + environment: get steps: - - uses: actions/checkout@v4 - - name: Log into Azure - uses: azure/login@v1 + - uses: actions/download-artifact@v3.0.2 + - uses: azure/login@v1 with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - uses: dtolnay/rust-toolchain@stable + creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Use Keyweave with Only Get Access Policy - run: cargo run -- --vault-name bvdbkeyweavetweukvt1 + run: ./keyweave --vault-name ${{ env.VAULT_NAME}} + list-test: name: Tests with List access - needs: bicep + needs: [build, bicep] runs-on: ubuntu-latest - environment: 3-list + environment: list steps: - - uses: actions/checkout@v4 - - name: Log into Azure - uses: azure/login@v1 + - uses: actions/download-artifact@v3.0.2 + - uses: azure/login@v1 with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - uses: dtolnay/rust-toolchain@stable + creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Use Keyweave with Only List Access Policy - run: cargo run -- --vault-name bvdbkeyweavetweukvt1 + run: ./keyweave --vault-name ${{ env.VAULT_NAME}} get-list-test: name: Tests with Get and List access - needs: bicep + needs: [build, bicep] runs-on: ubuntu-latest - environment: 4-get-list + environment: getlist steps: - - uses: actions/checkout@v4 - - name: Log into Azure - uses: azure/login@v1 + - uses: actions/download-artifact@v3.0.2 + - uses: azure/login@v1 with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - uses: dtolnay/rust-toolchain@stable + creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Use Keyweave with both Get and List Access Policies - run: cargo run -- --vault-name bvdbkeyweavetweukvt1 + run: ./keyweave --vault-name ${{ env.VAULT_NAME}} From ea0f452eba60b65ef6f76551f43519086761d6dc Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 14:50:36 +0100 Subject: [PATCH 06/21] fix: return to use oidc secrets --- .github/workflows/e2e.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index f2496d0..af05643 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -40,7 +40,9 @@ jobs: - uses: actions/checkout@v3 - uses: azure/login@v1 with: - creds: ${{ secrets.AZURE_CREDENTIALS }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Deploy Bicep template uses: azure/arm-deploy@v1 with: @@ -59,7 +61,9 @@ jobs: - uses: actions/download-artifact@v3.0.2 - uses: azure/login@v1 with: - creds: ${{ secrets.AZURE_CREDENTIALS }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with No Access Policies run: ./keyweave --vault-name ${{ env.VAULT_NAME}} @@ -72,7 +76,9 @@ jobs: - uses: actions/download-artifact@v3.0.2 - uses: azure/login@v1 with: - creds: ${{ secrets.AZURE_CREDENTIALS }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with Only Get Access Policy run: ./keyweave --vault-name ${{ env.VAULT_NAME}} @@ -85,7 +91,9 @@ jobs: - uses: actions/download-artifact@v3.0.2 - uses: azure/login@v1 with: - creds: ${{ secrets.AZURE_CREDENTIALS }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with Only List Access Policy run: ./keyweave --vault-name ${{ env.VAULT_NAME}} get-list-test: @@ -97,6 +105,8 @@ jobs: - uses: actions/download-artifact@v3.0.2 - uses: azure/login@v1 with: - creds: ${{ secrets.AZURE_CREDENTIALS }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with both Get and List Access Policies run: ./keyweave --vault-name ${{ env.VAULT_NAME}} From 0c30fec375cf38fecbcace997b96d5fa2d61bba6 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 14:54:22 +0100 Subject: [PATCH 07/21] feat: use bicepparams file --- .github/workflows/e2e.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index af05643..ff4ced9 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -10,10 +10,8 @@ env: on: push: branches: [ main ] - paths: [ 'bicep/**', 'src/**', 'Cargo.toml', 'Cargo.lock', '.github/workflows/e2e.yml' ] pull_request: branches: [ main ] - paths: [ 'bicep/**', 'src/**', 'Cargo.toml', 'Cargo.lock', '.github/workflows/e2e.yml' ] jobs: build: @@ -49,7 +47,7 @@ jobs: scope: subscription region: ${{ env.LOCATION }} template: bicep/main.bicep - parameters: bicep/main.params.json + parameters: bicep/main.test.bicepparams deploymentName: ${{ env.DEPLOYMENT_NAME }} none-test: From dca1e14be4ec536b78d60ebe17f53e093b562f25 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 14:56:03 +0100 Subject: [PATCH 08/21] fix: don't use param file --- .github/workflows/e2e.yml | 1 - bicep/main.bicep | 2 +- bicep/main.test.bicepparam | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 bicep/main.test.bicepparam diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ff4ced9..b62d294 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -47,7 +47,6 @@ jobs: scope: subscription region: ${{ env.LOCATION }} template: bicep/main.bicep - parameters: bicep/main.test.bicepparams deploymentName: ${{ env.DEPLOYMENT_NAME }} none-test: diff --git a/bicep/main.bicep b/bicep/main.bicep index b6c1eca..cc6cbe3 100644 --- a/bicep/main.bicep +++ b/bicep/main.bicep @@ -10,7 +10,7 @@ targetScope = 'subscription' 'A' // Acceptance 'P' // Production ]) -param environment string +param environment string = 'T' param location string = 'westeurope' param name object = { tenantId: 'BVDB' diff --git a/bicep/main.test.bicepparam b/bicep/main.test.bicepparam deleted file mode 100644 index 5c7b6fa..0000000 --- a/bicep/main.test.bicepparam +++ /dev/null @@ -1,3 +0,0 @@ -using 'main.bicep' - -param environment = 'T' From 61ee03a3d619cd5b54fc15bb3f193c69a21def25 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 16:53:20 +0100 Subject: [PATCH 09/21] chore: debug fs --- .github/workflows/e2e.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index b62d294..acbd824 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -22,8 +22,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - name: Build project run: cargo build --all --release - - name: Archive binary artifact - uses: actions/upload-artifact@v3.1.3 + - uses: actions/upload-artifact@v3.1.3 with: path: target/release/keyweave @@ -62,7 +61,10 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with No Access Policies - run: ./keyweave --vault-name ${{ env.VAULT_NAME}} + run: | + ls -la + tree + ./keyweave --vault-name ${{ env.VAULT_NAME}} get-test: name: Tests with Get access From c272558f4a948c20906745e020eeb6136e4d6ac4 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 17:02:17 +0100 Subject: [PATCH 10/21] fix: use correct artifact path --- .github/workflows/e2e.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index acbd824..d8ea05a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -62,9 +62,7 @@ jobs: subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with No Access Policies run: | - ls -la - tree - ./keyweave --vault-name ${{ env.VAULT_NAME}} + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} get-test: name: Tests with Get access @@ -79,7 +77,7 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with Only Get Access Policy - run: ./keyweave --vault-name ${{ env.VAULT_NAME}} + run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} list-test: name: Tests with List access @@ -94,7 +92,8 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with Only List Access Policy - run: ./keyweave --vault-name ${{ env.VAULT_NAME}} + run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + get-list-test: name: Tests with Get and List access needs: [build, bicep] @@ -108,4 +107,4 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with both Get and List Access Policies - run: ./keyweave --vault-name ${{ env.VAULT_NAME}} + run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} From 3fd2ad2f7c450ea4b305b16b4c12eba54d5eadec Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 17:15:08 +0100 Subject: [PATCH 11/21] fix: give execution permissions --- .github/workflows/e2e.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d8ea05a..9db0f02 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -62,6 +62,7 @@ jobs: subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with No Access Policies run: | + chmod +x ./artifact/keyweave ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} get-test: @@ -77,7 +78,9 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with Only Get Access Policy - run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + run: | + chmod +x ./artifact/keyweave + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} list-test: name: Tests with List access @@ -92,7 +95,9 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with Only List Access Policy - run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + run: | + chmod +x ./artifact/keyweave + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} get-list-test: name: Tests with Get and List access @@ -107,4 +112,6 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Use Keyweave with both Get and List Access Policies - run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + run: | + chmod +x ./artifact/keyweave + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} From bfb45cefa00a7c32a937e69c4715e1c8801ef8cf Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 18:51:26 +0100 Subject: [PATCH 12/21] feat: add federated logins --- bicep/main.bicep | 3 +-- bicep/modules/id.bicep | 59 ++++++++++++++++++++---------------------- bicep/modules/kv.bicep | 9 +++---- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/bicep/main.bicep b/bicep/main.bicep index cc6cbe3..9aa1534 100644 --- a/bicep/main.bicep +++ b/bicep/main.bicep @@ -80,7 +80,6 @@ module KeyVault 'modules/kv.bicep' = { location: location tags: tags - getPrincipalIds: ManagedIdentities.outputs.getPrincipalIds - listPrincipalIds: ManagedIdentities.outputs.listPrincipalIds + identities: ManagedIdentities.outputs.identities } } diff --git a/bicep/modules/id.bicep b/bicep/modules/id.bicep index 4d2d19b..91e6b21 100644 --- a/bicep/modules/id.bicep +++ b/bicep/modules/id.bicep @@ -2,35 +2,32 @@ param nameFormat string param location string param tags object -resource managedIdentityNone 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: format(nameFormat, 'ID', 1) - location: location - tags: tags -} - -resource managedIdentityGet 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: format(nameFormat, 'ID', 2) - location: location - tags: tags -} - -resource managedIdentityList 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: format(nameFormat, 'ID', 3) - location: location - tags: tags -} - -resource managedIdentityGetList 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: format(nameFormat, 'ID', 4) - location: location - tags: tags -} - -output getPrincipalIds array = [ - managedIdentityGet.properties.principalId - managedIdentityGetList.properties.principalId -] -output listPrincipalIds array = [ - managedIdentityList.properties.principalId - managedIdentityGetList.properties.principalId +param identityEnvironments array = [ + 'none' + 'get' + 'list' + 'getlist' ] + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = [for (environment, index) in identityEnvironments: { + name: format(nameFormat, 'ID', index+1) + location: location + tags: tags +}] + +resource federatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = [for (environment, index) in identityEnvironments: { + name: environment + parent: managedIdentity[index+1] + properties: { + issuer: 'https://token.actions.githubusercontent.com' + subject: 'repo:bartvdbraak/keyweave:environment:${environment}' + audiences: [ + 'api://AzureADTokenExchange' + ] + } +}] + +output identities array = [for (environment, index) in identityEnvironments: { + name: environment + id: managedIdentity[index+1].properties.principalId +}] diff --git a/bicep/modules/kv.bicep b/bicep/modules/kv.bicep index 52978a2..8a31e5e 100644 --- a/bicep/modules/kv.bicep +++ b/bicep/modules/kv.bicep @@ -2,14 +2,13 @@ param nameFormat string param location string param tags object -param getPrincipalIds array -param listPrincipalIds array +param identities array -var accessPolicies = [for id in union(getPrincipalIds, listPrincipalIds): { +var accessPolicies = [for identity in identities: { tenantId: tenant().tenantId - objectId: id + objectId: identity.id permissions: { - secrets: contains(getPrincipalIds, id) && contains(listPrincipalIds, id) ? ['Get', 'List'] : contains(listPrincipalIds, id) ? ['List'] : ['Get'] + secrets: contains(identity.name, 'get') && contains(identity.name, 'list') ? ['Get', 'List'] : contains(identity.name, 'get') ? ['Get'] : ['List'] } }] From d9522cb9af0d8cd56f10115fadea3c3b2f066f46 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 18:51:56 +0100 Subject: [PATCH 13/21] feat: additional e2e tests --- .github/workflows/e2e.yml | 41 +++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 9db0f02..b9b3e9d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -63,7 +63,7 @@ jobs: - name: Use Keyweave with No Access Policies run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} get-test: name: Tests with Get access @@ -80,7 +80,7 @@ jobs: - name: Use Keyweave with Only Get Access Policy run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} list-test: name: Tests with List access @@ -97,7 +97,7 @@ jobs: - name: Use Keyweave with Only List Access Policy run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} get-list-test: name: Tests with Get and List access @@ -114,4 +114,37 @@ jobs: - name: Use Keyweave with both Get and List Access Policies run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME}} + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} + + - name: Use Keyweave with a filter + run: | + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} --filter "filter" + + - name: Use Keyweave with a complex file path + run: | + mkdir -p "user/projects/project 1/src/lib" + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} --output "user/projects/project 1/src/lib/.env" + + - name: Use Keyweave with a non-existent Key Vault + run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }}1234 + + - name: Use Keyweave with a no permissions + run: | + mkdir -p "user/projects/project 1/src/lib" + ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} --output "/.env" + + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.OTHER_SUBSCRIPTION_ID }} + - name: Use Keyweave while logged into other Subscription + run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} + + # - uses: azure/login@v1 + # with: + # client-id: ${{ secrets.AZURE_CLIENT_ID }} + # tenant-id: ${{ secrets.OTHER_TENANT_ID }} + # subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + # - name: Use Keyweave while logged into other Azure Tenant + # run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} \ No newline at end of file From c885abd540c878efdf9bef3f4880d47b6ca0f20d Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Tue, 21 Nov 2023 18:55:35 +0100 Subject: [PATCH 14/21] fix: index was incorrect --- bicep/modules/id.bicep | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bicep/modules/id.bicep b/bicep/modules/id.bicep index 91e6b21..cfc65fa 100644 --- a/bicep/modules/id.bicep +++ b/bicep/modules/id.bicep @@ -17,7 +17,7 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023- resource federatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = [for (environment, index) in identityEnvironments: { name: environment - parent: managedIdentity[index+1] + parent: managedIdentity[index] properties: { issuer: 'https://token.actions.githubusercontent.com' subject: 'repo:bartvdbraak/keyweave:environment:${environment}' @@ -29,5 +29,5 @@ resource federatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/f output identities array = [for (environment, index) in identityEnvironments: { name: environment - id: managedIdentity[index+1].properties.principalId + id: managedIdentity[index].properties.principalId }] From cde1d2207c7a2fdd2292a2ca367693a15a73f9bf Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Wed, 22 Nov 2023 02:18:20 +0100 Subject: [PATCH 15/21] feat: e2e test for firewalled kv --- .github/workflows/e2e.yml | 25 ++++++++++--------- bicep/modules/kv.bicep | 52 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index b9b3e9d..762b80c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -5,7 +5,7 @@ permissions: contents: read env: - VAULT_NAME: bvdbkeyweavetweukvt1 + VAULT_NAME: bvdbkeyweavetweukvt{0} on: push: @@ -63,7 +63,7 @@ jobs: - name: Use Keyweave with No Access Policies run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} + ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} get-test: name: Tests with Get access @@ -80,7 +80,7 @@ jobs: - name: Use Keyweave with Only Get Access Policy run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} + ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} list-test: name: Tests with List access @@ -97,7 +97,7 @@ jobs: - name: Use Keyweave with Only List Access Policy run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} + ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} get-list-test: name: Tests with Get and List access @@ -114,24 +114,27 @@ jobs: - name: Use Keyweave with both Get and List Access Policies run: | chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} + ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} - name: Use Keyweave with a filter run: | - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} --filter "filter" + ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} --filter "filter" - name: Use Keyweave with a complex file path run: | mkdir -p "user/projects/project 1/src/lib" - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} --output "user/projects/project 1/src/lib/.env" + ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} --output "user/projects/project 1/src/lib/.env" - name: Use Keyweave with a non-existent Key Vault - run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }}1234 + run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }}1234 + + - name: Use Keyweave with a firewalled Key Vault + run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '2') }} - name: Use Keyweave with a no permissions run: | mkdir -p "user/projects/project 1/src/lib" - ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} --output "/.env" + ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} --output "/.env" - uses: azure/login@v1 with: @@ -139,7 +142,7 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.OTHER_SUBSCRIPTION_ID }} - name: Use Keyweave while logged into other Subscription - run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} + run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} # - uses: azure/login@v1 # with: @@ -147,4 +150,4 @@ jobs: # tenant-id: ${{ secrets.OTHER_TENANT_ID }} # subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} # - name: Use Keyweave while logged into other Azure Tenant - # run: ./artifact/keyweave --vault-name ${{ env.VAULT_NAME }} \ No newline at end of file + # run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} \ No newline at end of file diff --git a/bicep/modules/kv.bicep b/bicep/modules/kv.bicep index 8a31e5e..a0bd707 100644 --- a/bicep/modules/kv.bicep +++ b/bicep/modules/kv.bicep @@ -53,7 +53,43 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { } /* - Diagnostic Settings for Key Vault + Key Vault +*/ + +resource keyVaultWithFirewall 'Microsoft.KeyVault/vaults@2023-02-01' = { + name: replace(toLower(format(nameFormat, 'KVT', 2)), '-', '') + location: location + tags: tags + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enableSoftDelete: true + enablePurgeProtection: true + accessPolicies: accessPolicies + networkAcls: { + defaultAction: 'Deny' + ipRules: [] + } + } + resource testSecret 'secrets' = { + name: 'testSecret' + properties: { + value: 'testSecretValue' + } + } + resource filterTestSecret 'secrets' = { + name: 'filterTestSecret' + properties: { + value: 'filterTestSecretValue' + } + } +} + +/* + Diagnostic Settings for Key Vaults */ resource keyVaultDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { @@ -69,3 +105,17 @@ resource keyVaultDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021- ] } } + +resource keyVaultWithFirewallDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: 'keyVaultLogging' + scope: keyVaultWithFirewall + properties: { + workspaceId: _logAnalyticsWorkspace.id + logs: [ + { + category: 'AuditEvent' + enabled: true + } + ] + } +} From 7b40a0ae170b26b3dabb8e1d66570ad0b55b909d Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Wed, 22 Nov 2023 02:18:44 +0100 Subject: [PATCH 16/21] feat: add dns check to see if vault exists --- src/main.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main.rs b/src/main.rs index 18e55b8..6aea81a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use azure_security_keyvault::prelude::KeyVaultGetSecretsResponse; use azure_security_keyvault::KeyvaultClient; use clap::Parser; use futures::stream::StreamExt; +use paris::{info, log}; use paris::{error, Logger}; use std::fs::File; use std::io::Write; @@ -27,6 +28,24 @@ struct Opts { filter: Option, } +async fn check_vault_dns(vault_name: &str) -> Result<()> { + let vault_host = format!("{}.vault.azure.net", vault_name); + + let lookup_result = { + tokio::net::lookup_host((vault_host.as_str(), 443)).await + }; + + match lookup_result { + Ok(_) => Ok(()), + Err(err) => { + error!("DNS lookup failed for Key Vault: {}", vault_name); + info!("Please check that the Key Vault exists or that you have no connectivity issues."); + Err(err.into()) + } + } +} + + async fn fetch_secrets_from_key_vault( client: &KeyvaultClient, filter: Option<&str>, @@ -38,6 +57,7 @@ async fn fetch_secrets_from_key_vault( let page = match page { Ok(p) => p, Err(err) => { + log!("\n"); error!("Failed to fetch secrets page: {}", err); return Err(err.into()); } @@ -181,6 +201,8 @@ async fn main() -> Result<()> { }; log.success("Detected credentials."); + check_vault_dns(&opts.vault_name).await?; + log.loading(format!( "Fetching secrets from Key Vault: {}", opts.vault_name From 38a15a3bcd00ea3d6ca19251193a51a0a8ff911f Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Wed, 22 Nov 2023 05:17:04 +0100 Subject: [PATCH 17/21] feat: e2e tests within rust instead --- .github/workflows/checks.yml | 2 +- .github/workflows/e2e.yml | 153 ------------------ .github/workflows/tests.yml | 45 ++++++ Cargo.lock | 297 ++++++++++++++++++++++++++++++++++- Cargo.toml | 7 + bicep/modules/id.bicep | 2 +- src/main.rs | 12 ++ tests/e2e.rs | 188 ++++++++++++++++++++++ 8 files changed, 550 insertions(+), 156 deletions(-) delete mode 100644 .github/workflows/e2e.yml create mode 100644 .github/workflows/tests.yml create mode 100644 tests/e2e.rs diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index df67ff3..cf465d3 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -40,7 +40,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Run tests - run: cargo test --all + run: cargo test --bins build: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index 762b80c..0000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,153 +0,0 @@ -name: Checks - -permissions: - id-token: write - contents: read - -env: - VAULT_NAME: bvdbkeyweavetweukvt{0} - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - name: Build Keyweave - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - name: Build project - run: cargo build --all --release - - uses: actions/upload-artifact@v3.1.3 - with: - path: target/release/keyweave - - bicep: - name: Deploy Azure resources - environment: bicep - runs-on: ubuntu-latest - env: - LOCATION: eastus - DEPLOYMENT_NAME: keyweave-${{ github.run_id }} - steps: - - uses: actions/checkout@v3 - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Deploy Bicep template - uses: azure/arm-deploy@v1 - with: - scope: subscription - region: ${{ env.LOCATION }} - template: bicep/main.bicep - deploymentName: ${{ env.DEPLOYMENT_NAME }} - - none-test: - name: Tests without access - needs: [build, bicep] - runs-on: ubuntu-latest - environment: none - steps: - - uses: actions/download-artifact@v3.0.2 - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Use Keyweave with No Access Policies - run: | - chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} - - get-test: - name: Tests with Get access - needs: [build, bicep] - runs-on: ubuntu-latest - environment: get - steps: - - uses: actions/download-artifact@v3.0.2 - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Use Keyweave with Only Get Access Policy - run: | - chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} - - list-test: - name: Tests with List access - needs: [build, bicep] - runs-on: ubuntu-latest - environment: list - steps: - - uses: actions/download-artifact@v3.0.2 - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Use Keyweave with Only List Access Policy - run: | - chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} - - get-list-test: - name: Tests with Get and List access - needs: [build, bicep] - runs-on: ubuntu-latest - environment: getlist - steps: - - uses: actions/download-artifact@v3.0.2 - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Use Keyweave with both Get and List Access Policies - run: | - chmod +x ./artifact/keyweave - ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} - - - name: Use Keyweave with a filter - run: | - ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} --filter "filter" - - - name: Use Keyweave with a complex file path - run: | - mkdir -p "user/projects/project 1/src/lib" - ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} --output "user/projects/project 1/src/lib/.env" - - - name: Use Keyweave with a non-existent Key Vault - run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }}1234 - - - name: Use Keyweave with a firewalled Key Vault - run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '2') }} - - - name: Use Keyweave with a no permissions - run: | - mkdir -p "user/projects/project 1/src/lib" - ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} --output "/.env" - - - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.OTHER_SUBSCRIPTION_ID }} - - name: Use Keyweave while logged into other Subscription - run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} - - # - uses: azure/login@v1 - # with: - # client-id: ${{ secrets.AZURE_CLIENT_ID }} - # tenant-id: ${{ secrets.OTHER_TENANT_ID }} - # subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - # - name: Use Keyweave while logged into other Azure Tenant - # run: ./artifact/keyweave --vault-name ${{ format(env.VAULT_NAME, '1') }} \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..f2ce024 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,45 @@ +name: Tests + +permissions: + id-token: write + contents: read + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + bicep: + name: Deploy Azure resources + environment: bicep + runs-on: ubuntu-latest + env: + LOCATION: eastus + DEPLOYMENT_NAME: keyweave-${{ github.run_id }} + steps: + - uses: actions/checkout@v3 + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Deploy Bicep template + uses: azure/arm-deploy@v1 + with: + scope: subscription + region: ${{ env.LOCATION }} + template: bicep/main.bicep + deploymentName: ${{ env.DEPLOYMENT_NAME }} + + test: + name: Tests + needs: bicep + runs-on: ubuntu-latest + environment: test + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Run all tests + run: cargo test --all diff --git a/Cargo.lock b/Cargo.lock index d58f8b6..26084b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -86,6 +95,36 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "assert_cmd" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_fs" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f070617a68e5c2ed5d06ee8dd620ee18fb72b99f6c094bed34cf8ab07c875b48" +dependencies = [ + "anstyle", + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -235,6 +274,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -382,6 +432,19 @@ dependencies = [ "typenum", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.2", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "deranged" version = "0.3.9" @@ -392,6 +455,12 @@ dependencies = [ "serde", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -402,12 +471,24 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dyn-clone" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -469,6 +550,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -643,6 +733,30 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "h2" version = "0.3.21" @@ -668,6 +782,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + [[package]] name = "heck" version = "0.4.1" @@ -804,6 +924,23 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -811,7 +948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -835,6 +972,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -855,12 +1001,17 @@ name = "keyweave" version = "0.2.3" dependencies = [ "anyhow", + "assert_cmd", + "assert_fs", + "azure_core", "azure_identity", "azure_security_keyvault", "clap", "futures", "openssl", "paris", + "predicates", + "serial_test", "tokio", ] @@ -948,6 +1099,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-traits" version = "0.2.17" @@ -1161,6 +1318,37 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "predicates" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro2" version = "1.0.69" @@ -1259,6 +1447,35 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "reqwest" version = "0.11.22" @@ -1333,6 +1550,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.22" @@ -1441,6 +1667,31 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1547,6 +1798,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "thiserror" version = "1.0.50" @@ -1567,6 +1824,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.30" @@ -1774,12 +2041,31 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "waker-fn" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -1906,6 +2192,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 57e9320..b1ddc77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/bartvdbraak/keyweave/" [dependencies] anyhow = "1.0.75" +azure_core = "0.17.0" azure_identity = "0.17.0" azure_security_keyvault = "0.17.0" clap = { version = "4.4.8", features = ["derive"] } @@ -20,3 +21,9 @@ tokio = {version = "1.34.0", features = ["full"]} [target.'cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "arm", target_arch = "aarch64")))'.dependencies] openssl = { version = "0.10", features = ["vendored"] } + +[dev-dependencies] +assert_cmd = "2.0.12" +assert_fs = "1.0.13" +predicates = "3.0.4" +serial_test = "2.0.0" diff --git a/bicep/modules/id.bicep b/bicep/modules/id.bicep index cfc65fa..3f95431 100644 --- a/bicep/modules/id.bicep +++ b/bicep/modules/id.bicep @@ -20,7 +20,7 @@ resource federatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/f parent: managedIdentity[index] properties: { issuer: 'https://token.actions.githubusercontent.com' - subject: 'repo:bartvdbraak/keyweave:environment:${environment}' + subject: 'repo:bartvdbraak/keyweave:environment:test' audiences: [ 'api://AzureADTokenExchange' ] diff --git a/src/main.rs b/src/main.rs index 6aea81a..2b11a37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use std::io::Write; use std::sync::Arc; use tokio::sync::mpsc; use tokio::sync::Semaphore; +use azure_core::error::HttpError; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -59,6 +60,15 @@ async fn fetch_secrets_from_key_vault( Err(err) => { log!("\n"); error!("Failed to fetch secrets page: {}", err); + let specific_error = err.downcast_ref::(); + if let Some(specific_error) = specific_error { + // Check the contents of the specific error + if specific_error.error_message().unwrap().to_string().contains("does not have secrets list permission on key vault") { + info!("Make sure you have List permissions on the Key Vault."); + } else if specific_error.error_message().unwrap().to_string().contains("is not authorized and caller is not a trusted service") { + info!("Make sure you're on the Key Vaults Firewall allowlist."); + } + } return Err(err.into()); } }; @@ -69,6 +79,7 @@ async fn fetch_secrets_from_key_vault( Ok(secret_values) } + async fn fetch_secrets_from_page( client: &azure_security_keyvault::SecretClient, page: &KeyVaultGetSecretsResponse, @@ -127,6 +138,7 @@ async fn fetch_and_send_secret( } Err(err) => { error!("Error fetching secret: {}", err); + info!("Make sure you have Get permissions on the Key Vault."); (secret_id, String::new()) } } diff --git a/tests/e2e.rs b/tests/e2e.rs new file mode 100644 index 0000000..58b256b --- /dev/null +++ b/tests/e2e.rs @@ -0,0 +1,188 @@ +use assert_cmd::prelude::*; +use assert_fs::prelude::*; +use assert_fs::TempDir; +use std::process::Command; +use predicates::prelude::*; +use serial_test::serial; +use std::env; + +static BINARY: &str = "keyweave"; +static KEYVAULT: &str = "bvdbkeyweavetweukvt1"; +static FIREWALL_KEYVAULT: &str = "bvdbkeyweavetweukvt2"; +static NON_EXISTENT_KEYVAULT: &str = "bvdbkeyweavetweukvt3"; + +fn azure_cli_login(client_id: String, tenant_id: String, subscription_id: String) -> Result<(), std::io::Error> { + Command::new("az") + .arg("login") + .arg("--identity") + .arg("--username") + .arg(client_id) + .arg("--tenant") + .arg(tenant_id) + .output()?; + + Command::new("az") + .arg("account") + .arg("set") + .arg("--subscription") + .arg(subscription_id) + .output()?; + + Ok(()) +} + +/// Test with no access policies - expected to fail. +#[tokio::test] +#[serial] +async fn test_no_access_policies() { + azure_cli_login( + env::var("AZURE_CLIENT_ID_NO_ACCESS").expect("Failed to get AZURE_CLIENT_ID_NO_ACCESS"), + env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), + env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), + ).expect("Failed to log in to Azure CLI"); + + let temp_dir = TempDir::new().unwrap(); + let output_path = temp_dir.child(".env"); + + let mut cmd = Command::cargo_bin(BINARY).unwrap(); + cmd.arg("--vault-name").arg(KEYVAULT) + .arg("--output").arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains("Make sure you have List permissions on the Key Vault.")); + + temp_dir.close().unwrap(); +} + +/// Test with only Get access policy - expected to fail. +#[tokio::test] +#[serial] +async fn test_only_get_access_policy() { + azure_cli_login( + env::var("AZURE_CLIENT_ID_GET").expect("Failed to get AZURE_CLIENT_ID_GET"), + env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), + env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), + ).expect("Failed to log in to Azure CLI"); + + let temp_dir = TempDir::new().unwrap(); + let output_path = temp_dir.child(".env"); + + let mut cmd = Command::cargo_bin(BINARY).unwrap(); + cmd.arg("--vault-name").arg(KEYVAULT) + .arg("--output").arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains("Make sure you have List permissions on the Key Vault.")); + + temp_dir.close().unwrap(); +} + +/// Test with only List access policy - expected to succeed with get errors. +#[tokio::test] +#[serial] +async fn test_only_list_access_policy() { + azure_cli_login( + env::var("AZURE_CLIENT_ID_LIST").expect("Failed to get AZURE_CLIENT_ID_LIST"), + env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), + env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), + ).expect("Failed to log in to Azure CLI"); + + let temp_dir = TempDir::new().unwrap(); + let output_path = temp_dir.child(".env"); + + let mut cmd = Command::cargo_bin(BINARY).unwrap(); + cmd.arg("--vault-name").arg(KEYVAULT) + .arg("--output").arg(output_path.path()); + cmd.assert().success().stderr(predicate::str::contains("Make sure you have Get permissions on the Key Vault.")); + + temp_dir.close().unwrap(); +} + +/// Test with both Get and List access policies - expected to pass. +#[tokio::test] +#[serial] +async fn test_get_and_list_access_policies() { + azure_cli_login( + env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), + env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), + env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), + ).expect("Failed to log in to Azure CLI"); + + let temp_dir = TempDir::new().unwrap(); + let output_path = temp_dir.child(".env"); + + let mut cmd = Command::cargo_bin(BINARY).unwrap(); + cmd.arg("--vault-name").arg(KEYVAULT) + .arg("--output").arg(output_path.path()); + cmd.assert().success(); + + output_path.assert(predicate::path::is_file()); + output_path.assert(predicate::str::contains("testSecret=testSecretValue")); + output_path.assert(predicate::str::contains("filterTestSecret=filterTestSecretValue")); + + temp_dir.close().unwrap(); +} + +/// Test with both Get and List access policies and filter - expected to pass. +#[tokio::test] +#[serial] +async fn test_get_and_list_access_policies_filter() { + azure_cli_login( + env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), + env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), + env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), + ).expect("Failed to log in to Azure CLI"); + + let temp_dir = TempDir::new().unwrap(); + let output_path = temp_dir.child(".env"); + + let mut cmd = Command::cargo_bin(BINARY).unwrap(); + cmd.arg("--vault-name").arg(KEYVAULT) + .arg("--output").arg(output_path.path()) + .arg("--filter").arg("filter"); + cmd.assert().success(); + + output_path.assert(predicate::path::is_file()); + output_path.assert(predicate::str::contains("filterTestSecret=filterTestSecretValue")); + + temp_dir.close().unwrap(); +} + +/// Test with both Get and List access policies on a Key Vault with Firewall - expected to fail. +#[tokio::test] +#[serial] +async fn test_get_and_list_access_policies_firewall() { + azure_cli_login( + env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), + env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), + env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), + ).expect("Failed to log in to Azure CLI"); + + let temp_dir = TempDir::new().unwrap(); + let output_path = temp_dir.child(".env"); + + let mut cmd = Command::cargo_bin(BINARY).unwrap(); + cmd.arg("--vault-name").arg(FIREWALL_KEYVAULT) + .arg("--output").arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains("Make sure you're on the Key Vaults Firewall allowlist.")); + + temp_dir.close().unwrap(); +} + +/// Test with both Get and List access policies on a non-existent Key Vault - expected to fail. +#[tokio::test] +#[serial] +async fn test_get_and_list_access_policies_non_existent() { + azure_cli_login( + env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), + env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), + env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), + ).expect("Failed to log in to Azure CLI"); + + let temp_dir = TempDir::new().unwrap(); + let output_path = temp_dir.child(".env"); + + let mut cmd = Command::cargo_bin(BINARY).unwrap(); + cmd.arg("--vault-name").arg(NON_EXISTENT_KEYVAULT) + .arg("--output").arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains("Please check that the Key Vault exists or that you have no connectivity issues.")); + + temp_dir.close().unwrap(); +} + From 8432d03949b7410e88c4e5a3027def3ea7609977 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Wed, 22 Nov 2023 05:21:12 +0100 Subject: [PATCH 18/21] feat: add concurrency for bicep --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f2ce024..5247634 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,6 +15,8 @@ jobs: name: Deploy Azure resources environment: bicep runs-on: ubuntu-latest + concurrency: + group: bicep env: LOCATION: eastus DEPLOYMENT_NAME: keyweave-${{ github.run_id }} From 068e4063fc714df6f0a6f2ff78253c798a8dcb78 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Wed, 22 Nov 2023 05:24:02 +0100 Subject: [PATCH 19/21] fix: make sure environment variables are available --- .github/workflows/tests.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5247634..1fe19d7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,6 +40,13 @@ jobs: needs: bicep runs-on: ubuntu-latest environment: test + env: + AZURE_CLIENT_ID_NO_ACCESS: ${{ secrets.AZURE_CLIENT_ID_NO_ACCESS }} + AZURE_CLIENT_ID_GET: ${{ secrets.AZURE_CLIENT_ID_GET }} + AZURE_CLIENT_ID_LIST: ${{ secrets.AZURE_CLIENT_ID_LIST }} + AZURE_CLIENT_ID_GET_LIST: ${{ secrets.AZURE_CLIENT_ID_GET_LIST }} + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable From b7b2a3de6a56a97917d0e3b2fe2883eea1bf2b12 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Sat, 25 Nov 2023 18:17:17 +0100 Subject: [PATCH 20/21] chore: add debug output and logging --- tests/e2e.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/e2e.rs b/tests/e2e.rs index 58b256b..0d45949 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -12,22 +12,24 @@ static FIREWALL_KEYVAULT: &str = "bvdbkeyweavetweukvt2"; static NON_EXISTENT_KEYVAULT: &str = "bvdbkeyweavetweukvt3"; fn azure_cli_login(client_id: String, tenant_id: String, subscription_id: String) -> Result<(), std::io::Error> { - Command::new("az") + println!("Executing 'az login' with client ID: {}", client_id); + let login_output = Command::new("az") .arg("login") .arg("--identity") .arg("--username") - .arg(client_id) + .arg(&client_id) .arg("--tenant") - .arg(tenant_id) + .arg(&tenant_id) .output()?; - - Command::new("az") + println!("Login output: {:?}", login_output); + println!("Executing 'az account set' with subscription ID: {}", subscription_id); + let account_output = Command::new("az") .arg("account") .arg("set") .arg("--subscription") - .arg(subscription_id) + .arg(&subscription_id) .output()?; - + println!("Account output: {:?}", account_output); Ok(()) } From ce9aa2898f6f564585be8aba80ec173ca36e7b92 Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Sat, 25 Nov 2023 19:07:07 +0100 Subject: [PATCH 21/21] feat: test multiple jobs --- .github/workflows/checks.yml | 2 +- .github/workflows/tests.yml | 67 +++++++++++++--- bicep/modules/kv.bicep | 2 +- src/main.rs | 41 +++++----- tests/e2e.rs | 143 +++++++++++++---------------------- 5 files changed, 134 insertions(+), 121 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index cf465d3..c66d806 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -39,7 +39,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - name: Run tests + - name: Run unit tests run: cargo test --bins build: runs-on: ubuntu-latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1fe19d7..2eeba49 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,20 +35,67 @@ jobs: template: bicep/main.bicep deploymentName: ${{ env.DEPLOYMENT_NAME }} - test: - name: Tests + tests-no-access: + name: Tests with No Access needs: bicep runs-on: ubuntu-latest environment: test - env: - AZURE_CLIENT_ID_NO_ACCESS: ${{ secrets.AZURE_CLIENT_ID_NO_ACCESS }} - AZURE_CLIENT_ID_GET: ${{ secrets.AZURE_CLIENT_ID_GET }} - AZURE_CLIENT_ID_LIST: ${{ secrets.AZURE_CLIENT_ID_LIST }} - AZURE_CLIENT_ID_GET_LIST: ${{ secrets.AZURE_CLIENT_ID_GET_LIST }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable + - name: 'Az CLI login' + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID_NO_ACCESS }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Run all tests - run: cargo test --all + run: cargo test no_access + tests-get: + name: Tests with Get + needs: bicep + runs-on: ubuntu-latest + environment: test + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: 'Az CLI login' + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID_GET }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Run all tests + run: cargo test only_get + tests-list: + name: Tests with List + needs: bicep + runs-on: ubuntu-latest + environment: test + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: 'Az CLI login' + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID_LIST }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Run all tests + run: cargo test only_list + tests-get-list: + name: Tests with Get and List + needs: bicep + runs-on: ubuntu-latest + environment: test + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: 'Az CLI login' + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID_GET_LIST }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Run all tests + run: cargo test get_and_list_access diff --git a/bicep/modules/kv.bicep b/bicep/modules/kv.bicep index a0bd707..de4d025 100644 --- a/bicep/modules/kv.bicep +++ b/bicep/modules/kv.bicep @@ -8,7 +8,7 @@ var accessPolicies = [for identity in identities: { tenantId: tenant().tenantId objectId: identity.id permissions: { - secrets: contains(identity.name, 'get') && contains(identity.name, 'list') ? ['Get', 'List'] : contains(identity.name, 'get') ? ['Get'] : ['List'] + secrets: contains(identity.name, 'get') && contains(identity.name, 'list') ? ['Get', 'List'] : contains(identity.name, 'get') ? ['Get'] : contains(identity.name, 'list') ? ['List'] : [] } }] diff --git a/src/main.rs b/src/main.rs index 2b11a37..59fe640 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,16 @@ use anyhow::Result; +use azure_core::error::HttpError; use azure_identity::DefaultAzureCredential; use azure_security_keyvault::prelude::KeyVaultGetSecretsResponse; use azure_security_keyvault::KeyvaultClient; use clap::Parser; use futures::stream::StreamExt; -use paris::{info, log}; use paris::{error, Logger}; use std::fs::File; use std::io::Write; use std::sync::Arc; use tokio::sync::mpsc; use tokio::sync::Semaphore; -use azure_core::error::HttpError; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -32,21 +31,20 @@ struct Opts { async fn check_vault_dns(vault_name: &str) -> Result<()> { let vault_host = format!("{}.vault.azure.net", vault_name); - let lookup_result = { - tokio::net::lookup_host((vault_host.as_str(), 443)).await - }; + let lookup_result = { tokio::net::lookup_host((vault_host.as_str(), 443)).await }; match lookup_result { Ok(_) => Ok(()), Err(err) => { error!("DNS lookup failed for Key Vault: {}", vault_name); - info!("Please check that the Key Vault exists or that you have no connectivity issues."); + error!( + "Please check that the Key Vault exists or that you have no connectivity issues." + ); Err(err.into()) } } } - async fn fetch_secrets_from_key_vault( client: &KeyvaultClient, filter: Option<&str>, @@ -58,15 +56,24 @@ async fn fetch_secrets_from_key_vault( let page = match page { Ok(p) => p, Err(err) => { - log!("\n"); - error!("Failed to fetch secrets page: {}", err); + error!("\n"); + error!("Failed to fetch secrets."); let specific_error = err.downcast_ref::(); if let Some(specific_error) = specific_error { - // Check the contents of the specific error - if specific_error.error_message().unwrap().to_string().contains("does not have secrets list permission on key vault") { - info!("Make sure you have List permissions on the Key Vault."); - } else if specific_error.error_message().unwrap().to_string().contains("is not authorized and caller is not a trusted service") { - info!("Make sure you're on the Key Vaults Firewall allowlist."); + if specific_error + .error_message() + .unwrap() + .to_string() + .contains("does not have secrets list permission on key vault") + { + error!("Make sure you have List permissions on the Key Vault."); + } else if specific_error + .error_message() + .unwrap() + .to_string() + .contains("is not authorized and caller is not a trusted service") + { + error!("Make sure you're on the Key Vaults Firewall allowlist."); } } return Err(err.into()); @@ -79,7 +86,6 @@ async fn fetch_secrets_from_key_vault( Ok(secret_values) } - async fn fetch_secrets_from_page( client: &azure_security_keyvault::SecretClient, page: &KeyVaultGetSecretsResponse, @@ -136,9 +142,8 @@ async fn fetch_and_send_secret( let _ = tx.send((secret_id.clone(), bundle.value.clone())).await; (secret_id, bundle.value) } - Err(err) => { - error!("Error fetching secret: {}", err); - info!("Make sure you have Get permissions on the Key Vault."); + Err(_err) => { + error!("Error fetching secret. Make sure you have Get permissions on the Key Vault."); (secret_id, String::new()) } } diff --git a/tests/e2e.rs b/tests/e2e.rs index 0d45949..0b2332f 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -1,76 +1,47 @@ use assert_cmd::prelude::*; use assert_fs::prelude::*; use assert_fs::TempDir; -use std::process::Command; use predicates::prelude::*; use serial_test::serial; -use std::env; +use std::process::Command; static BINARY: &str = "keyweave"; static KEYVAULT: &str = "bvdbkeyweavetweukvt1"; static FIREWALL_KEYVAULT: &str = "bvdbkeyweavetweukvt2"; static NON_EXISTENT_KEYVAULT: &str = "bvdbkeyweavetweukvt3"; -fn azure_cli_login(client_id: String, tenant_id: String, subscription_id: String) -> Result<(), std::io::Error> { - println!("Executing 'az login' with client ID: {}", client_id); - let login_output = Command::new("az") - .arg("login") - .arg("--identity") - .arg("--username") - .arg(&client_id) - .arg("--tenant") - .arg(&tenant_id) - .output()?; - println!("Login output: {:?}", login_output); - println!("Executing 'az account set' with subscription ID: {}", subscription_id); - let account_output = Command::new("az") - .arg("account") - .arg("set") - .arg("--subscription") - .arg(&subscription_id) - .output()?; - println!("Account output: {:?}", account_output); - Ok(()) -} - -/// Test with no access policies - expected to fail. #[tokio::test] #[serial] async fn test_no_access_policies() { - azure_cli_login( - env::var("AZURE_CLIENT_ID_NO_ACCESS").expect("Failed to get AZURE_CLIENT_ID_NO_ACCESS"), - env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), - env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), - ).expect("Failed to log in to Azure CLI"); - let temp_dir = TempDir::new().unwrap(); let output_path = temp_dir.child(".env"); let mut cmd = Command::cargo_bin(BINARY).unwrap(); - cmd.arg("--vault-name").arg(KEYVAULT) - .arg("--output").arg(output_path.path()); - cmd.assert().failure().stderr(predicate::str::contains("Make sure you have List permissions on the Key Vault.")); + cmd.arg("--vault-name") + .arg(KEYVAULT) + .arg("--output") + .arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains( + "Make sure you have List permissions on the Key Vault.", + )); temp_dir.close().unwrap(); } -/// Test with only Get access policy - expected to fail. #[tokio::test] #[serial] async fn test_only_get_access_policy() { - azure_cli_login( - env::var("AZURE_CLIENT_ID_GET").expect("Failed to get AZURE_CLIENT_ID_GET"), - env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), - env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), - ).expect("Failed to log in to Azure CLI"); - let temp_dir = TempDir::new().unwrap(); let output_path = temp_dir.child(".env"); let mut cmd = Command::cargo_bin(BINARY).unwrap(); - cmd.arg("--vault-name").arg(KEYVAULT) - .arg("--output").arg(output_path.path()); - cmd.assert().failure().stderr(predicate::str::contains("Make sure you have List permissions on the Key Vault.")); + cmd.arg("--vault-name") + .arg(KEYVAULT) + .arg("--output") + .arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains( + "Make sure you have List permissions on the Key Vault.", + )); temp_dir.close().unwrap(); } @@ -79,19 +50,17 @@ async fn test_only_get_access_policy() { #[tokio::test] #[serial] async fn test_only_list_access_policy() { - azure_cli_login( - env::var("AZURE_CLIENT_ID_LIST").expect("Failed to get AZURE_CLIENT_ID_LIST"), - env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), - env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), - ).expect("Failed to log in to Azure CLI"); - let temp_dir = TempDir::new().unwrap(); let output_path = temp_dir.child(".env"); let mut cmd = Command::cargo_bin(BINARY).unwrap(); - cmd.arg("--vault-name").arg(KEYVAULT) - .arg("--output").arg(output_path.path()); - cmd.assert().success().stderr(predicate::str::contains("Make sure you have Get permissions on the Key Vault.")); + cmd.arg("--vault-name") + .arg(KEYVAULT) + .arg("--output") + .arg(output_path.path()); + cmd.assert().success().stderr(predicate::str::contains( + "Make sure you have Get permissions on the Key Vault.", + )); temp_dir.close().unwrap(); } @@ -100,23 +69,21 @@ async fn test_only_list_access_policy() { #[tokio::test] #[serial] async fn test_get_and_list_access_policies() { - azure_cli_login( - env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), - env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), - env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), - ).expect("Failed to log in to Azure CLI"); - let temp_dir = TempDir::new().unwrap(); let output_path = temp_dir.child(".env"); let mut cmd = Command::cargo_bin(BINARY).unwrap(); - cmd.arg("--vault-name").arg(KEYVAULT) - .arg("--output").arg(output_path.path()); + cmd.arg("--vault-name") + .arg(KEYVAULT) + .arg("--output") + .arg(output_path.path()); cmd.assert().success(); output_path.assert(predicate::path::is_file()); output_path.assert(predicate::str::contains("testSecret=testSecretValue")); - output_path.assert(predicate::str::contains("filterTestSecret=filterTestSecretValue")); + output_path.assert(predicate::str::contains( + "filterTestSecret=filterTestSecretValue", + )); temp_dir.close().unwrap(); } @@ -125,23 +92,22 @@ async fn test_get_and_list_access_policies() { #[tokio::test] #[serial] async fn test_get_and_list_access_policies_filter() { - azure_cli_login( - env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), - env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), - env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), - ).expect("Failed to log in to Azure CLI"); - let temp_dir = TempDir::new().unwrap(); let output_path = temp_dir.child(".env"); let mut cmd = Command::cargo_bin(BINARY).unwrap(); - cmd.arg("--vault-name").arg(KEYVAULT) - .arg("--output").arg(output_path.path()) - .arg("--filter").arg("filter"); + cmd.arg("--vault-name") + .arg(KEYVAULT) + .arg("--output") + .arg(output_path.path()) + .arg("--filter") + .arg("filter"); cmd.assert().success(); output_path.assert(predicate::path::is_file()); - output_path.assert(predicate::str::contains("filterTestSecret=filterTestSecretValue")); + output_path.assert(predicate::str::contains( + "filterTestSecret=filterTestSecretValue", + )); temp_dir.close().unwrap(); } @@ -150,19 +116,17 @@ async fn test_get_and_list_access_policies_filter() { #[tokio::test] #[serial] async fn test_get_and_list_access_policies_firewall() { - azure_cli_login( - env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), - env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), - env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), - ).expect("Failed to log in to Azure CLI"); - let temp_dir = TempDir::new().unwrap(); let output_path = temp_dir.child(".env"); let mut cmd = Command::cargo_bin(BINARY).unwrap(); - cmd.arg("--vault-name").arg(FIREWALL_KEYVAULT) - .arg("--output").arg(output_path.path()); - cmd.assert().failure().stderr(predicate::str::contains("Make sure you're on the Key Vaults Firewall allowlist.")); + cmd.arg("--vault-name") + .arg(FIREWALL_KEYVAULT) + .arg("--output") + .arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains( + "Make sure you're on the Key Vaults Firewall allowlist.", + )); temp_dir.close().unwrap(); } @@ -171,20 +135,17 @@ async fn test_get_and_list_access_policies_firewall() { #[tokio::test] #[serial] async fn test_get_and_list_access_policies_non_existent() { - azure_cli_login( - env::var("AZURE_CLIENT_ID_GET_LIST").expect("Failed to get AZURE_CLIENT_ID_GET_LIST"), - env::var("AZURE_TENANT_ID").expect("Failed to get AZURE_TENANT_ID"), - env::var("AZURE_SUBSCRIPTION_ID").expect("Failed to get AZURE_SUBSCRIPTION_ID"), - ).expect("Failed to log in to Azure CLI"); - let temp_dir = TempDir::new().unwrap(); let output_path = temp_dir.child(".env"); let mut cmd = Command::cargo_bin(BINARY).unwrap(); - cmd.arg("--vault-name").arg(NON_EXISTENT_KEYVAULT) - .arg("--output").arg(output_path.path()); - cmd.assert().failure().stderr(predicate::str::contains("Please check that the Key Vault exists or that you have no connectivity issues.")); + cmd.arg("--vault-name") + .arg(NON_EXISTENT_KEYVAULT) + .arg("--output") + .arg(output_path.path()); + cmd.assert().failure().stderr(predicate::str::contains( + "Please check that the Key Vault exists or that you have no connectivity issues.", + )); temp_dir.close().unwrap(); } -