helmfile / vals

Helm-like configuration values loader with support for various sources
Apache License 2.0
553 stars 73 forks source link

Feature: support Bitwarden Secrets Manager #427

Open cortopy opened 5 months ago

cortopy commented 5 months ago

It seems vals support Bitwarden Vault as a backend. However, Bitwarden also has a product called "secrets manager", which has its own SDK and CLI

I Just tried to get a secret using current implementation and I get that the secret is not found. This is probably because unlike the vault, Secrets Manager is accessed with machine access tokens and scoped permissions per "project".

yxxhero commented 5 months ago

@cortopy PR is weclome.

carnei-ro commented 2 months ago

I can create the PR, my only question is, the SDK needs musl-gcc and CGO_ENABLED, is it a problem @yxxhero ?

image ref: https://github.com/bitwarden/sdk/blob/main/languages/go/INSTRUCTIONS.md#set-go-environment-info

carnei-ro commented 2 months ago

here a super simple example how to fetch a secret in the Bitwarden Secrets Manager - this can be easily adapted to vals.

package main

import (
    "fmt"
    "os"

    sdk "github.com/bitwarden/sdk-go"
    "github.com/gofrs/uuid"
)

func main() {
    apiURL := os.Getenv("API_URL")
    if apiURL == "" {
        apiURL = "https://api.bitwarden.com"
    }
    identityURL := os.Getenv("IDENTITY_URL")
    if identityURL == "" {
        identityURL = "https://identity.bitwarden.com"
    }

    bitwardenClient, _ := sdk.NewBitwardenClient(&apiURL, &identityURL)

    accessToken := os.Getenv("ACCESS_TOKEN") // An Access Token from a "Machine account". Ensure you granted "Can read" to a project.
    organizationIDStr := os.Getenv("ORGANIZATION_ID") // The Organization ID where the project is located. This is an UUID that you can find in the URL like: https://vault.bitwarden.com/#/sm/00000000-0000-0000-0000-000000000000, where 00000000-0000-0000-0000-000000000000 is the Organization ID.
    projectName := os.Getenv("PROJECT_NAME")
    secretName := os.Getenv("SECRET_NAME")

    if projectName == "" || organizationIDStr == "" || secretName == "" || accessToken == "" {
        panic("Environment variables required: ACCESS_TOKEN, ORGANIZATION_ID, PROJECT_NAME, SECRET_NAME")
    }

    err := bitwardenClient.AccessTokenLogin(accessToken, nil)
    if err != nil {
        panic(err)
    }

    organizationID, err := uuid.FromString(organizationIDStr)
    if err != nil {
        panic(err)
    }

    var projectID string

    projectList, err := bitwardenClient.Projects().List(organizationID.String())
    if err != nil {
        panic(err)
    }
    for _, project := range projectList.Data {
        if project.Name == projectName {
            projectID = project.ID
        }
    }

    secretsList, err := bitwardenClient.Secrets().List(organizationID.String())
    if err != nil {
        panic(err)
    }
    var secretValue string
    for _, secret := range secretsList.Data {
        if secret.Key == secretName {
            s, err := bitwardenClient.Secrets().Get(secret.ID)
            if err != nil {
                panic(err)
            }
            if *s.ProjectID == projectID {
                secretValue = s.Value
            }
        }
    }

    fmt.Println(secretValue)

    defer bitwardenClient.Close()
}
zhaque44 commented 2 months ago

please make a PR, we will test it and review it accordingly @carnei-ro

carnei-ro commented 2 months ago

@zhaque44 PR opened

carnei-ro commented 2 months ago

I guess we should re-open this issue.