Skip to content

Commit

Permalink
add base support for BWS (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxlaverse authored Oct 9, 2024
1 parent bdf0188 commit 105439a
Show file tree
Hide file tree
Showing 46 changed files with 1,679 additions and 249 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
![Releases](https://img.shields.io/github/v/release/maxlaverse/terraform-provider-bitwarden?include_prereleases)
![Downloads](https://img.shields.io/badge/dynamic/json?color=7b42bc&label=Downloads&labelColor=black&logo=terraform&query=data.attributes.total&url=https%3A%2F%2Fregistry.terraform.io%2Fv2%2Fproviders%2F2657%2Fdownloads%2Fsummary&style=flat-square)

The Terraform Bitwarden provider is a plugin for Terraform that allows to manage different kind of Bitwarden resources.
The Terraform Bitwarden provider is a plugin for Terraform that allows to manage different kind of Bitwarden resources from their [Password Manager] and [Secrets Manager] products.
This project is not associated with the Bitwarden project nor 8bit Solutions LLC.

**[Explore the docs »][Terraform Registry docs]**
Expand All @@ -22,7 +22,7 @@ This project is not associated with the Bitwarden project nor 8bit Solutions LLC
## Supported Versions
The plugin has been tested and built with the following components:
- [Terraform] v1.6.1 / [OpenTofu] v1.8.0
- [Bitwarden CLI] v2023.2.0 (except with the experimental `embedded_client`)
- [Bitwarden CLI] v2023.2.0 (if not using the experimental `embedded_client`)
- [Go] 1.23.1 (for development)
- [Docker] 23.0.5 (for development)

Expand All @@ -37,7 +37,7 @@ terraform {
required_providers {
bitwarden = {
source = "maxlaverse/bitwarden"
version = ">= 0.9.0"
version = ">= 0.10.0"
}
}
}
Expand Down Expand Up @@ -114,3 +114,5 @@ Distributed under the Mozilla License. See [LICENSE](./LICENSE) for more informa
[Terraform Registry docs]: https://registry.terraform.io/providers/maxlaverse/bitwarden/latest/docs
[hashicorp/terraform-plugin-sdk#63]: https://github.com/hashicorp/terraform-plugin-sdk/issues/63
[Terraform's documentation on Data Storage]: https://bitwarden.com/help/data-storage/#on-your-local-machine
[Password Manager]: https://bitwarden.com/products/personal/
[Secrets Manager]: https://bitwarden.com/products/secrets-manager/
38 changes: 38 additions & 0 deletions docs/data-sources/project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "bitwarden_project Data Source - terraform-provider-bitwarden"
subcategory: ""
description: |-
Use this data source to get information on an existing project.
---

# bitwarden_project (Data Source)

Use this data source to get information on an existing project.

## Example Usage

```terraform
data "bitwarden_project" "example" {
id = "37a66d6a-96c1-4f04-9a3c-b1fc0135669e"
}
resource "bitwarden_secret" "example" {
project_id = data.bitwarden_project.example.id
key = "ACCESS_KEY"
value = "THIS-VALUE"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `id` (String) Identifier.
- `organization_id` (String) Identifier of the organization.

### Read-Only

- `name` (String) Name.
44 changes: 44 additions & 0 deletions docs/data-sources/secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "bitwarden_secret Data Source - terraform-provider-bitwarden"
subcategory: ""
description: |-
Use this data source to get information on an existing secret.
---

# bitwarden_secret (Data Source)

Use this data source to get information on an existing secret.

## Example Usage

```terraform
data "bitwarden_secret" "example" {
id = "37a66d6a-96c1-4f04-9a3c-b1fc0135669e"
}
resource "kubernetes_secret" "vpn_credentials" {
metadata {
name = "vpn-key"
}
data = {
"PASSWORD" = data.bitwarden_secret.value
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `id` (String) Identifier.
- `organization_id` (String) Identifier of the organization.

### Read-Only

- `key` (String) Name.
- `note` (String) Note.
- `project_id` (String) Identifier of the project.
- `value` (String) Value.
63 changes: 40 additions & 23 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
layout: ""
page_title: "Bitwarden Provider"
description: |-
Use the Bitwarden provider to read, create, or update logins, secure notes and folders in your Bitwarden Vaults.
Use the Bitwarden provider to manage your Logins, Secure Notes, and Secrets.
---

# Bitwarden Provider

Use the Bitwarden provider to interact with Bitwarden logins, secure notes, folders and org-collections.
You must configure the provider with proper credentials before you can use it, and have the [Bitwarden CLI] installed (unless you use the experimental `embedded_client` feature).
Use the Bitwarden provider to manage your [Password Manager] Logins and Secure Notes, and [Secrets Manager] Secrets.
You must configure the provider with proper credentials before you can use it.
If you're not trying out the experimental `embedded_client` feature, you also need a [Bitwarden CLI] installed locally.

## Example Usage

Expand All @@ -17,7 +18,7 @@ terraform {
required_providers {
bitwarden = {
source = "maxlaverse/bitwarden"
version = ">= 0.9.0"
version = ">= 0.10.0"
}
}
}
Expand Down Expand Up @@ -49,13 +50,17 @@ data "bitwarden_item_login" "example" {
```

## Authentication
The Bitwarden provider can use different combinations of credentials to authenticate:
* API key (requires `email`, `master_password`, `client_id` and `client_secret`)
* Email and Password (requires `email` and `master_password`) (prefer API keys instead)
* User-provided Session Key (requires `session_key`) (only works with a pre-downloaded Vault)
Depending on the type of credentials you use, you'll be able to connect either with a Password Manager or Secret Manager.
If you want your workspace to interact with both, have a look at [provider aliases].

### Generating a Client ID and Secret
The recommended way to interact with your Vault using the Bitwarden Provider Terraform plugin is to generate an API key.
### Password Manager
The Password Manager accepts different combinations of credentials to authenticate:
* _[Personal API Key]_ (requires `email`, `master_password`, `client_id` and `client_secret` to be set).
* _Email and Password_ (requires `email` and `master_password` to be set) (prefer _Personal API keys_ instead).
* User-provided _Session Key_ (requires `session_key` to be set), which only works with a pre-downloaded Vault (See _Generating a Session Key_).

#### Generating a Client ID and Secret
The recommended way to interact with your Password Manager Vault using the Bitwarden Provider Terraform plugin is to generate an API key.
This allows you to easily revoke access to your Vault without having to change your master password.

In order to generate a pair of Client ID and Secret, you need to:
Expand All @@ -65,7 +70,7 @@ In order to generate a pair of Client ID and Secret, you need to:
4. Click on _View API Key_ (or maybe another label if it's the first time)
5. Save the API credentials somewhere safe

### Generating a Session Key
#### Generating a Session Key

If you don't want to use an API key, you can use a Session Key instead.
When doing so, it's your responsibility to:
Expand All @@ -81,13 +86,28 @@ BITWARDENCLI_APPDATA_DIR=.bitwarden bw login
BITWARDENCLI_APPDATA_DIR=<vault_path> bw login
```

A Session Key is bound to a local copy of a Vault. It's therefore important that you set the right `BITWARDENCLI_APPDATA_DIR` to the path where your Vault is stored.

### Secrets Manager
The Secrets Manager only accepts [Access Tokens] (requires `access_token` to be set).

In order to generate an Access Token you need to:
1. Connect to your Vault on https://vault.bitwarden.com
2. Ensure the _Secrets Manager_ section is selected (bottom left)
3. Click on _Machine accounts_
4. Click on _New_
5. Click on your generated Machine Account
6. Select the _Access Tokens_ tab
7. Created a new Access Token and save it somewhere safe


## Configuration
Configuration for the Bitwarden Provider can be derived from two sources:
* Parameters in the provider configuration
* Environment variables

### Parameters
Credentials can be provided by adding a combination of `email`, `master_password`, `client_id`, `client_secret` or `session_key` to the bitwarden provider block.
Credentials can be provided by adding a combination of `email`, `master_password`, `client_id`, `client_secret`, `access_token` or `session_key` to the bitwarden provider block.
```terraform
provider "bitwarden" {
email = "terraform@example.com"
Expand All @@ -106,7 +126,7 @@ provider "bitwarden" {
```

### Environment variables
Credentials can be provided by using a combination of `BW_EMAIL`, `BW_PASSWORD`, `BW_CLIENTID`, `BW_CLIENTSECRET` or `BW_SESSION` environment variables.
Credentials can be provided by using a combination of `BW_EMAIL`, `BW_PASSWORD`, `BW_CLIENTID`, `BW_CLIENTSECRET`, `BWS_ACCESS_TOKEN` or `BW_SESSION` environment variables.

For example:
```bitwarden
Expand All @@ -123,14 +143,12 @@ export BW_CLIENTSECRET="my-client-secret"
<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `email` (String) Login Email of the Vault (env: `BW_EMAIL`).

### Optional

- `access_token` (String) Machine Account Access Token (env: `BWS_ACCESS_TOKEN`)).
- `client_id` (String) Client ID (env: `BW_CLIENTID`)
- `client_secret` (String) Client Secret (env: `BW_CLIENTSECRET`). Do not commit this information in Git unless you know what you're doing. Prefer using a Terraform `variable {}` in order to inject this value from the environment.
- `email` (String) Login Email of the Vault (env: `BW_EMAIL`).
- `experimental` (Block Set) Enable experimental features. (see [below for nested schema](#nestedblock--experimental))
- `extra_ca_certs` (String) Extends the well known 'root' CAs (like VeriSign) with the extra certificates in file (env: `NODE_EXTRA_CA_CERTS`).
- `master_password` (String) Master password of the Vault (env: `BW_PASSWORD`). Do not commit this information in Git unless you know what you're doing. Prefer using a Terraform `variable {}` in order to inject this value from the environment.
Expand All @@ -145,10 +163,9 @@ Optional:

- `embedded_client` (Boolean) Use the embedded client instead of an external binary.

[Bitwarden]: https://bitwarden.com/help/article/managing-items/
[Password Manager]: https://bitwarden.com/products/personal/
[Secrets Manager]: https://bitwarden.com/products/secrets-manager/
[Bitwarden CLI]: https://bitwarden.com/help/article/cli/#download-and-install

## Known issues

There is an open issue with the Bitwarden CLI that prevents it from creating attachments in recent version (see [bitwarden/clients#5618](https://github.com/bitwarden/clients/issues/5618)).
The only workaround is to use an older version of the Bitwarden CLI, e.g. 2023.02.
[Access Tokens]: https://bitwarden.com/help/access-tokens/
[Personal API Key]: https://bitwarden.com/help/personal-api-key/
[provider aliases]: https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations
45 changes: 45 additions & 0 deletions docs/resources/secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "bitwarden_secret Resource - terraform-provider-bitwarden"
subcategory: ""
description: |-
Manages a secret.
---

# bitwarden_secret (Resource)

Manages a secret.

## Example Usage

```terraform
resource "bitwarden_secret" "example" {
key = "DB_ACCESS_KEY_ID"
value = "2oOXi4GXtJ3JAkhgBmmBuA=="
project_id = "59f5d6eb-1f17-4ebb-a6c2-b1fc01355b15"
note = "This is the main account"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `key` (String) Name.
- `note` (String) Note.
- `project_id` (String) Identifier of the project.
- `value` (String) Value.

### Optional

- `id` (String) Identifier.
- `organization_id` (String) Identifier of the organization.

## Import

Import is supported using the following syntax:

```shell
$ terraform import bitwarden_secret.example <secret_id>
```
10 changes: 10 additions & 0 deletions examples/data-sources/bitwarden_project/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
data "bitwarden_project" "example" {
id = "37a66d6a-96c1-4f04-9a3c-b1fc0135669e"
}

resource "bitwarden_secret" "example" {
project_id = data.bitwarden_project.example.id

key = "ACCESS_KEY"
value = "THIS-VALUE"
}
13 changes: 13 additions & 0 deletions examples/data-sources/bitwarden_secret/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
data "bitwarden_secret" "example" {
id = "37a66d6a-96c1-4f04-9a3c-b1fc0135669e"
}

resource "kubernetes_secret" "vpn_credentials" {
metadata {
name = "vpn-key"
}

data = {
"PASSWORD" = data.bitwarden_secret.value
}
}
2 changes: 1 addition & 1 deletion examples/quick/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ terraform {
required_providers {
bitwarden = {
source = "maxlaverse/bitwarden"
version = ">= 0.9.0"
version = ">= 0.10.0"
}
}
}
Expand Down
1 change: 1 addition & 0 deletions examples/resources/bitwarden_secret/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ terraform import bitwarden_secret.example <secret_id>
6 changes: 6 additions & 0 deletions examples/resources/bitwarden_secret/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "bitwarden_secret" "example" {
key = "DB_ACCESS_KEY_ID"
value = "2oOXi4GXtJ3JAkhgBmmBuA=="
project_id = "59f5d6eb-1f17-4ebb-a6c2-b1fc01355b15"
note = "This is the main account"
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/maxlaverse/terraform-provider-bitwarden
go 1.23

require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/go-retryablehttp v0.7.7
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZt
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down
1 change: 0 additions & 1 deletion internal/bitwarden/bwcli/password_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ func (c *client) Logout(ctx context.Context) error {
}

func (c *client) DeleteObject(ctx context.Context, obj models.Object) error {
// TODO: Don't fail if object is already gone
args := []string{
"delete",
string(obj.Object),
Expand Down
9 changes: 9 additions & 0 deletions internal/bitwarden/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,12 @@ type PasswordManager interface {
LoginWithPassword(ctx context.Context, username, password string) error
Sync(context.Context) error
}

type SecretsManager interface {
CreateSecret(ctx context.Context, secret models.Secret) (*models.Secret, error)
DeleteSecret(ctx context.Context, secret models.Secret) error
EditSecret(ctx context.Context, secret models.Secret) (*models.Secret, error)
GetProject(ctx context.Context, project models.Project) (*models.Project, error)
GetSecret(ctx context.Context, secret models.Secret) (*models.Secret, error)
LoginWithAccessToken(ctx context.Context, accessKey string) error
}
Loading

0 comments on commit 105439a

Please sign in to comment.