Skip to content

Commit

Permalink
[CC-30333] auth: use JWT for authentication
Browse files Browse the repository at this point in the history
This PR allows using the Terraform Provider via JWT authentication,
in addition to API Keys. The JWT auth mechanism requires a CC_VANITY_NAME
env capturing the vanity name of the org with the corresponding JWT Issuer.
In case the JWT is issued against multiple identities, it also requires a
CC_USERNAME env capturing the user / service account to impersonate.
Eventually, we will add a CI stage for running acceptance tests via
this auth mechanism.
  • Loading branch information
pritesh-lahoti committed Nov 15, 2024
1 parent ab4f78b commit 2d18ab4
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added support for authentication via JWT.

- Setting and fetching of `cidr_range` is now available for GCP Advanced tier
clusters.

Expand Down
4 changes: 3 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ provider "cockroach" {

### Optional

- `apikey` (String, Sensitive) apikey to access cockroach cloud
- `apikey` (String, Sensitive) The API key to access CockroachDB Cloud.
It is either the API Key from a Service Account or a JWT from a JWT Issuer configured for the CockroachDB Cloud Organization.
In the case of JWT, the vanity name of the organization is required and can be provided using the `CC_VANITY_NAME` environment variable. If the JWT is mapped to multiple identities, the identity to impersonate should be provided using the `CC_USERNAME` environment variable, and should contain either a user email address or a service account ID.
6 changes: 4 additions & 2 deletions internal/provider/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const (
CockroachAPIKey string = "COCKROACH_API_KEY"
APIServerURLKey string = "COCKROACH_SERVER"
UserAgent string = "terraform-provider-cockroach"
CCVanityName string = "CC_VANITY_NAME"
CCUsername string = "CC_USERNAME"
)

type Region struct {
Expand Down Expand Up @@ -63,8 +65,8 @@ type ClusterBackupConfig struct {
}

type UsageLimits struct {
RequestUnitLimit types.Int64 `tfsdk:"request_unit_limit"`
StorageMibLimit types.Int64 `tfsdk:"storage_mib_limit"`
RequestUnitLimit types.Int64 `tfsdk:"request_unit_limit"`
StorageMibLimit types.Int64 `tfsdk:"storage_mib_limit"`
ProvisionedVirtualCpus types.Int64 `tfsdk:"provisioned_virtual_cpus"`
}

Expand Down
47 changes: 35 additions & 12 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ func (p *provider) Configure(
return
}

var apiKey string
var apiKeyOrJWT string
if !IsKnown(config.ApiKey) {
apiKey = os.Getenv(CockroachAPIKey)
apiKeyOrJWT = os.Getenv(CockroachAPIKey)
} else {
apiKey = config.ApiKey.ValueString()
apiKeyOrJWT = config.ApiKey.ValueString()
}

if apiKey == "" {
if apiKeyOrJWT == "" {
// Error vs warning - empty value must stop execution
resp.Diagnostics.AddError(
"Unable to find apikey",
Expand All @@ -83,11 +83,7 @@ func (p *provider) Configure(
return
}

cfg := client.NewConfiguration(apiKey)
if server := os.Getenv(APIServerURLKey); server != "" {
cfg.ServerURL = server
}
cfg.UserAgent = UserAgent
cfg := getClientConfiguration(apiKeyOrJWT)

logLevel := os.Getenv("TF_LOG")
if logLevel == "DEBUG" || logLevel == "TRACE" {
Expand Down Expand Up @@ -167,9 +163,17 @@ func (p *provider) Schema(
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"apikey": schema.StringAttribute{
MarkdownDescription: "apikey to access cockroach cloud",
Optional: true,
Sensitive: true,
MarkdownDescription: "The API key to access CockroachDB Cloud.\n" +
"It is either the API Key from a Service Account or a JWT from a " +
"JWT Issuer configured for the CockroachDB Cloud Organization.\n" +
"In the case of JWT, the vanity name of the organization is required " +
"and can be provided using the `CC_VANITY_NAME` environment variable. " +
"If the JWT is mapped to multiple identities, the identity to " +
"impersonate should be provided using the `CC_USERNAME` environment " +
"variable, and should contain either a user email address or a " +
"service account ID.",
Optional: true,
Sensitive: true,
},
},
}
Expand All @@ -182,3 +186,22 @@ func New(version string) func() tf_provider.Provider {
}
}
}

func getClientConfiguration(apikeyOrJWT string) *client.Configuration {
var cfgOpts []client.ConfigurationOption
if vanityName := os.Getenv(CCVanityName); vanityName != "" {
cfgOpts = append(cfgOpts, client.WithVanityName(vanityName))
}
if username := os.Getenv(CCUsername); username != "" {
cfgOpts = append(cfgOpts, client.WithUsername(username))
}

cfg := client.NewConfiguration(apikeyOrJWT, cfgOpts...)
if server := os.Getenv(APIServerURLKey); server != "" {
cfg.ServerURL = server
}
cfg.UserAgent = UserAgent

return cfg

}
8 changes: 2 additions & 6 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,8 @@ var testAccProvider tf_provider.Provider
var cl *client.Client

func init() {
apikey := os.Getenv(CockroachAPIKey)
cfg := client.NewConfiguration(apikey)
if server := os.Getenv(APIServerURLKey); server != "" {
cfg.ServerURL = server
}
cfg.UserAgent = UserAgent
apiKeyOrJWT := os.Getenv(CockroachAPIKey)
cfg := getClientConfiguration(apiKeyOrJWT)
cl = client.NewClient(cfg)
testAccProvider = New("test")()
}
Expand Down

0 comments on commit 2d18ab4

Please sign in to comment.