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
COCKROACH_VANITY_NAME env var 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 COCKROACH_USERNAME env var
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 authored and fantapop committed Nov 15, 2024
1 parent ab4f78b commit f149712
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 27 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
8 changes: 6 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ provider "cockroach" {
# Instructions for getting an API Key
# https://www.cockroachlabs.com/docs/cockroachcloud/console-access-management.html#api-access
#
# The Terraform provider requires an environment variable COCKROACH_API_KEY
# The Terraform provider requires either the COCKROACH_API_KEY or COCKROACH_API_JWT environment variable for performing authentication.
# export COCKROACH_API_KEY="the API Key value here"
# export COCKROACH_API_JWT="the JWT value here"
}
```

Expand All @@ -30,4 +31,7 @@ provider "cockroach" {

### Optional

- `apikey` (String, Sensitive) apikey to access cockroach cloud
- `apijwt` (String, Sensitive) The JWT from a JWT Issuer configured for the CockroachDB Cloud Organization.
In this case, the vanity name of the organization is required and can be provided using the `COCKROACH_VANITY_NAME` environment variable. If the JWT is mapped to multiple identities, the identity to impersonate should be provided using the `COCKROACH_USERNAME` environment variable, and should contain either a user email address or a service account ID.
- `apikey` (String, Sensitive) The API key to access CockroachDB Cloud.
If this field is provided, it is used and `apijwt` is ignored.
3 changes: 2 additions & 1 deletion examples/provider/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ provider "cockroach" {
# Instructions for getting an API Key
# https://www.cockroachlabs.com/docs/cockroachcloud/console-access-management.html#api-access
#
# The Terraform provider requires an environment variable COCKROACH_API_KEY
# The Terraform provider requires either the COCKROACH_API_KEY or COCKROACH_API_JWT environment variable for performing authentication.
# export COCKROACH_API_KEY="the API Key value here"
# export COCKROACH_API_JWT="the JWT value here"
}
13 changes: 8 additions & 5 deletions internal/provider/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ import (
)

const (
CockroachAPIKey string = "COCKROACH_API_KEY"
APIServerURLKey string = "COCKROACH_SERVER"
UserAgent string = "terraform-provider-cockroach"
CockroachAPIKey string = "COCKROACH_API_KEY"
CockroachAPIJWT string = "COCKROACH_API_JWT"
APIServerURLKey string = "COCKROACH_SERVER"
UserAgent string = "terraform-provider-cockroach"
CockroachVanityName string = "COCKROACH_VANITY_NAME"
CockroachUsername string = "COCKROACH_USERNAME"
)

type Region struct {
Expand Down Expand Up @@ -63,8 +66,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
63 changes: 52 additions & 11 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type provider struct {
// providerData can be used to store data from the Terraform configuration.
type providerData struct {
ApiKey types.String `tfsdk:"apikey"`
ApiJWT types.String `tfsdk:"apijwt"`
}

func (p *provider) Configure(
Expand All @@ -74,20 +75,23 @@ func (p *provider) Configure(
apiKey = config.ApiKey.ValueString()
}

if apiKey == "" {
var apiJWT string
if !IsKnown(config.ApiJWT) {
apiJWT = os.Getenv(CockroachAPIJWT)
} else {
apiJWT = config.ApiJWT.ValueString()
}

if apiKey == "" && apiJWT == "" {
// Error vs warning - empty value must stop execution
resp.Diagnostics.AddError(
"Unable to find apikey",
"apikey cannot be an empty string",
"Unable to find authentication token",
"at least one of apikey or apijwt must be provided",
)
return
}

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

logLevel := os.Getenv("TF_LOG")
if logLevel == "DEBUG" || logLevel == "TRACE" {
Expand Down Expand Up @@ -167,9 +171,22 @@ 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" +
"If this field is provided, it is used and `apijwt` is ignored.",
Optional: true,
Sensitive: true,
},
"apijwt": schema.StringAttribute{
MarkdownDescription: "The JWT from a JWT Issuer configured for the " +
"CockroachDB Cloud Organization.\n" +
"In this case, the vanity name of the organization is required " +
"and can be provided using the `COCKROACH_VANITY_NAME` environment variable. " +
"If the JWT is mapped to multiple identities, the identity to " +
"impersonate should be provided using the `COCKROACH_USERNAME` environment " +
"variable, and should contain either a user email address or a " +
"service account ID.",
Optional: true,
Sensitive: true,
},
},
}
Expand All @@ -182,3 +199,27 @@ func New(version string) func() tf_provider.Provider {
}
}
}

func getClientConfiguration(apiKey, apiJWT string) *client.Configuration {
// If the API key is provided, use it, else use the JWT for auth.
apiToken := apiKey
if apiToken == "" {
apiToken = apiJWT
}

var cfgOpts []client.ConfigurationOption
if vanityName := os.Getenv(CockroachVanityName); vanityName != "" {
cfgOpts = append(cfgOpts, client.WithVanityName(vanityName))
}
if username := os.Getenv(CockroachUsername); username != "" {
cfgOpts = append(cfgOpts, client.WithUsername(username))
}

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

return cfg
}
19 changes: 11 additions & 8 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ 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
apiKey := os.Getenv(CockroachAPIKey)
apiJWT := os.Getenv(CockroachAPIJWT)

cfg := getClientConfiguration(apiKey, apiJWT)

cl = client.NewClient(cfg)
testAccProvider = New("test")()
}
Expand All @@ -51,8 +50,12 @@ var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServe
}

func testAccPreCheck(t *testing.T) {
if os.Getenv(CockroachAPIKey) == "" {
t.Fatalf("%s must be set for acceptance testing", CockroachAPIKey)
if os.Getenv(CockroachAPIKey) == "" && os.Getenv(CockroachAPIJWT) == "" {
t.Fatalf(
"%s or %s must be set for acceptance testing",
CockroachAPIKey,
CockroachAPIJWT,
)
}
}

Expand Down

0 comments on commit f149712

Please sign in to comment.