Skip to content

Commit 66a3351

Browse files
craig[bot]golgeekmsbutlershubhamdhama
committed
141454: roachprod: refactor IAP authentication r=DarrylWong a=golgeek Previously, the authentication mechanism for testeng Identity-Aware Proxy protected endpoints was relying on a shared service account key accessed via an encrypted GCS bucket. This was causing the need for secret rotation and was limiting auditability as everyone was using the same JSON key that was cached in the `~/.roachprod` directory. This patch introduces a new mechanism based on short-lived OAuth tokens and service account impersonation through the default local credentials. The identity of the caller is determined with the following precedence: 1. `GOOGLE_EPHEMERAL_CREDENTIALS` environment variable 2. Application Default Credentials (ADC): a) `GOOGLE_APPLICATION_CREDENTIALS` environment variable b) Default service account (application_default_credentials.json) file c) App Engine standard environment d) GCE metadata server 4. `gcloud config config-helper` output The caller needs to have the `roles/iam.serviceAccountTokenCreator` role on the service account to be able to impersonate the service account and generate short lived OAuth AccessTokens. Both `promhelperclient` and `grafana annotations` switch to this new method, via the new `IAPTokenSourceIface` interface that handles the service account impersonation, the AccessToken caching and renewal and that provides a pre-authenticated `http.Client`. `@cockroachdb/dev-inf,` the added dependency (`github.com/binxio/gcloudconfig`) is a helper that runs `gcloud config config-helper` to get credentials from the account logged in in gcloud without the need for application default credentials file (when someone ran `gcloud auth login`, but not `gcloud auth application-default login`). Epic: none Release note: None 153630: backupdest: remove IncludeManifest arg in FindAllIncrementalPaths r=kev-cao a=msbutler Epic: none Release note: none 153672: server: always use dropDRPCHeaderListener with drpc server r=rafiss a=shubhamdhama Previously, the DRPC listener was conditionally created based on the DRPC setting. When DRPC was disabled, we used a noopListener that would block on Accept() until closed, effectively rejecting all DRPC connections. In #153503 we changed the DRPC server to always run regardless of the setting, but missed updating the corresponding listener logic. This commit completes that change by always using the dropDRPCHeaderListener to properly handle incoming DRPC connections. Fixes: #153612 Epic: none Release note: none Co-authored-by: Ludovic Leroux <[email protected]> Co-authored-by: Michael Butler <[email protected]> Co-authored-by: Shubham Dhama <[email protected]>
4 parents 9d9a48c + ba62711 + f274892 + 1bd2a09 commit 66a3351

File tree

19 files changed

+491
-518
lines changed

19 files changed

+491
-518
lines changed

DEPS.bzl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,16 @@ def go_deps():
11271127
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/bgentry/speakeasy/com_github_bgentry_speakeasy-v0.1.0.zip",
11281128
],
11291129
)
1130+
go_repository(
1131+
name = "com_github_binxio_gcloudconfig",
1132+
build_file_proto_mode = "disable_global",
1133+
importpath = "github.com/binxio/gcloudconfig",
1134+
sha256 = "82797ef5d9fa4cba09d64ca885a3b6b8867d046c8f144ed15dc102085b0c6ceb",
1135+
strip_prefix = "github.com/binxio/[email protected]",
1136+
urls = [
1137+
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/binxio/gcloudconfig/com_github_binxio_gcloudconfig-v0.1.5.zip",
1138+
],
1139+
)
11301140
go_repository(
11311141
name = "com_github_biogo_store",
11321142
build_file_proto_mode = "disable_global",

build/bazelutil/distdir_files.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ DISTDIR_FILES = {
288288
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/beorn7/perks/com_github_beorn7_perks-v1.0.1.zip": "25bd9e2d94aca770e6dbc1f53725f84f6af4432f631d35dd2c46f96ef0512f1a",
289289
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/bgentry/go-netrc/com_github_bgentry_go_netrc-v0.0.0-20140422174119-9fd32a8b3d3d.zip": "59fbb1e8e307ccd7052f77186990d744284b186e8b1c5ebdfb12405ae8d7f935",
290290
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/bgentry/speakeasy/com_github_bgentry_speakeasy-v0.1.0.zip": "d4bfd48b9bf68c87f92c94478ac910bcdab272e15eb909d58f1fb939233f75f0",
291+
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/binxio/gcloudconfig/com_github_binxio_gcloudconfig-v0.1.5.zip": "82797ef5d9fa4cba09d64ca885a3b6b8867d046c8f144ed15dc102085b0c6ceb",
291292
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/biogo/store/com_github_biogo_store-v0.0.0-20160505134755-913427a1d5e8.zip": "26551f8829c5ada84a68ef240732375be6747252aba423cf5c88bc03002c3600",
292293
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/bitly/go-hostpool/com_github_bitly_go_hostpool-v0.0.0-20171023180738-a3a6125de932.zip": "9a55584d7fa2c1639d0ea11cd5b437786c2eadc2401d825e699ad6445fc8e476",
293294
"https://storage.googleapis.com/cockroach-godeps/gomod/github.com/bitly/go-simplejson/com_github_bitly_go_simplejson-v0.5.0.zip": "53930281dc7fba8947c1b1f07c82952a38dcaefae23bd3c8e71d70a6daa6cb40",

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ require (
124124
github.com/aws/smithy-go v1.22.3
125125
github.com/axiomhq/hyperloglog v0.2.5
126126
github.com/bazelbuild/rules_go v0.26.0
127+
github.com/binxio/gcloudconfig v0.1.5
127128
github.com/biogo/store v0.0.0-20160505134755-913427a1d5e8
128129
github.com/blevesearch/snowballstem v0.9.0
129130
github.com/buchgr/bazel-remote v1.3.3

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
450450
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
451451
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
452452
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
453+
github.com/binxio/gcloudconfig v0.1.5 h1:nbvWtpqn7yJs4qPuXxTu9D3DYrSyc0FHkXraseMMCV4=
454+
github.com/binxio/gcloudconfig v0.1.5/go.mod h1:IpQXzgqmv2JS1i+hbhqhHqzeYWg5zWkdN4sZJznJDUM=
453455
github.com/biogo/store v0.0.0-20160505134755-913427a1d5e8 h1:tYoz1OeRpx3dJZlh9T4dQt4kAndcmpl+VNdzbSgFC/0=
454456
github.com/biogo/store v0.0.0-20160505134755-913427a1d5e8/go.mod h1:Iev9Q3MErcn+w3UOJD/DkEzllvugfdx7bGcMOFhvr/4=
455457
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=

pkg/backup/backupdest/backup_destination.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func ResolveDest(
228228
defer rootStore.Close()
229229
priors, err := FindAllIncrementalPaths(
230230
ctx, execCfg, incrementalStore, rootStore,
231-
chosenSuffix, OmitManifest, len(dest.IncrementalStorage) > 0,
231+
chosenSuffix, len(dest.IncrementalStorage) > 0,
232232
)
233233
if err != nil {
234234
return ResolvedDestination{}, errors.Wrap(err, "adjusting backup destination to append new layer to existing backup")

pkg/backup/backupdest/incrementals.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,14 @@ func FindAllIncrementalPaths(
8686
incStore cloud.ExternalStorage,
8787
rootStore cloud.ExternalStorage,
8888
subdir string,
89-
includeManifest bool,
9089
customIncLocation bool,
9190
) ([]string, error) {
9291
ctx, sp := tracing.ChildSpan(ctx, "backupdest.FindAllIncrementalPaths")
9392
defer sp.Finish()
9493

9594
// Backup indexes do not support custom incremental locations.
9695
if customIncLocation || !ReadBackupIndexEnabled.Get(&execCfg.Settings.SV) {
97-
return LegacyFindPriorBackups(ctx, incStore, includeManifest)
96+
return LegacyFindPriorBackups(ctx, incStore, OmitManifest)
9897
}
9998

10099
indexes, err := ListIndexes(ctx, rootStore, subdir)
@@ -105,25 +104,15 @@ func FindAllIncrementalPaths(
105104
// backup chain, we can assume the index is complete. So either an index has
106105
// been written for every backup in the chain, or there are no indexes at all.
107106
if len(indexes) == 0 {
108-
return LegacyFindPriorBackups(ctx, incStore, includeManifest)
107+
return LegacyFindPriorBackups(ctx, incStore, OmitManifest)
109108
}
110109

111-
paths, err := util.MapE(
110+
return util.MapE(
112111
indexes[1:], // We skip the full backup
113112
func(indexFilename string) (string, error) {
114113
return parseBackupFilePathFromIndexFileName(subdir, indexFilename)
115114
},
116115
)
117-
if err != nil {
118-
return nil, err
119-
}
120-
121-
if includeManifest {
122-
paths = util.Map(paths, func(p string) string {
123-
return path.Join(p, backupbase.DeprecatedBackupManifestName)
124-
})
125-
}
126-
return paths, nil
127116
}
128117

129118
// LegacyFindPriorBackups finds "appended" incremental backups via the legacy
@@ -194,7 +183,7 @@ func backupsFromLocation(
194183
defer incStore.Close()
195184

196185
return FindAllIncrementalPaths(
197-
ctx, execCfg, incStore, rootStore, subdir, false /* includeManifest */, customIncLocation,
186+
ctx, execCfg, incStore, rootStore, subdir, customIncLocation,
198187
)
199188
}
200189

pkg/backup/backupdest/incrementals_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ func TestFindAllIncrementalPaths(t *testing.T) {
243243

244244
incs, err := backupdest.FindAllIncrementalPaths(
245245
ctx, &execCfg, incStore, store,
246-
targetSubdir, false /* includeManifest */, false, /* customIncLocation */
246+
targetSubdir, false, /* customIncLocation */
247247
)
248248
require.NoError(t, err)
249249
require.Len(t, incs, len(targetChain)-1)
@@ -324,7 +324,7 @@ func TestFindAllIncrementalPathsFallbackLogic(t *testing.T) {
324324

325325
incs, err := backupdest.FindAllIncrementalPaths(
326326
ctx, &execCfg, stores.inc, stores.root,
327-
subdir, false /* includeManifest */, false, /* customIncLocation */
327+
subdir, false, /* customIncLocation */
328328
)
329329
require.NoError(t, err)
330330
require.Len(t, incs, numBackups-1)
@@ -348,7 +348,7 @@ func TestFindAllIncrementalPathsFallbackLogic(t *testing.T) {
348348

349349
incs, err := backupdest.FindAllIncrementalPaths(
350350
ctx, &execCfg, stores.customInc, stores.root,
351-
subdir, false /* includeManifest */, true, /* customIncLocation */
351+
subdir, true, /* customIncLocation */
352352
)
353353
require.NoError(t, err)
354354
require.Len(t, incs, numBackups-1)
@@ -390,14 +390,14 @@ func TestFindAllIncrementalPathsFallbackLogic(t *testing.T) {
390390
// List from both default and custom incremental locations.
391391
incs, err := backupdest.FindAllIncrementalPaths(
392392
ctx, &execCfg, stores.inc, stores.root,
393-
subdir, false /* includeManifest */, false, /* customIncLocation */
393+
subdir, false, /* customIncLocation */
394394
)
395395
require.NoError(t, err)
396396
require.Len(t, incs, numDefaultIncs)
397397

398398
incs, err = backupdest.FindAllIncrementalPaths(
399399
ctx, &execCfg, stores.customInc, stores.root,
400-
subdir, false /* includeManifest */, true, /* customIncLocation */
400+
subdir, true, /* customIncLocation */
401401
)
402402
require.NoError(t, err)
403403
require.Len(t, incs, numCustomIncs)

pkg/cmd/roachprod/cli/commands.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,9 +1723,9 @@ func (cr *commandRegistry) buildGrafanaAnnotationCmd() *cobra.Command {
17231723
Long: fmt.Sprintf(`Adds an annotation to the specified grafana instance
17241724
17251725
By default, we assume the grafana instance needs an authentication token to connect
1726-
to. A service account json and audience will be read in from the environment
1727-
variables %s and %s to attempt authentication through google IDP. Use the --insecure
1728-
option when a token is not necessary.
1726+
to. Unless the %s environment variable exists, the default Google Application Credentials
1727+
will be used to derive an Access Token to authenticate against Google Identity-Aware Proxy.
1728+
Use the --insecure option when a token is not necessary.
17291729
17301730
--tags specifies the tags the annotation should have.
17311731
@@ -1740,7 +1740,7 @@ creates an annotation over time range.
17401740
Example:
17411741
# Create an annotation over time range 1-100 on the centralized grafana instance, which needs authentication.
17421742
roachprod grafana-annotation grafana.testeng.crdb.io example-annotation-event --tags my-cluster --tags test-run-1 --dashboard-uid overview --time-range 1,100
1743-
`, roachprodutil.ServiceAccountJson, roachprodutil.ServiceAccountAudience),
1743+
`, roachprodutil.CredentialsEnvironmentVariable),
17441744
Args: cobra.ExactArgs(2),
17451745
Run: Wrap(func(cmd *cobra.Command, args []string) error {
17461746
req := grafana.AddAnnotationRequest{

pkg/cmd/roachprod/grafana/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ go_library(
2222
importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/grafana",
2323
visibility = ["//visibility:public"],
2424
deps = [
25+
"//pkg/roachprod/promhelperclient",
2526
"//pkg/roachprod/roachprodutil",
2627
"//pkg/util/httputil",
2728
"@com_github_cockroachdb_errors//:errors",
2829
"@com_github_go_openapi_strfmt//:strfmt",
2930
"@com_github_grafana_grafana_openapi_client_go//client",
3031
"@com_github_grafana_grafana_openapi_client_go//models",
31-
"@org_golang_google_api//idtoken",
3232
],
3333
)

pkg/cmd/roachprod/grafana/annotations.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,15 @@ package grafana
77

88
import (
99
"context"
10-
"fmt"
1110
"strings"
1211

12+
"github.com/cockroachdb/cockroach/pkg/roachprod/promhelperclient"
1313
"github.com/cockroachdb/cockroach/pkg/roachprod/roachprodutil"
1414
"github.com/cockroachdb/cockroach/pkg/util/httputil"
1515
"github.com/cockroachdb/errors"
1616
"github.com/go-openapi/strfmt"
1717
grafana "github.com/grafana/grafana-openapi-client-go/client"
1818
"github.com/grafana/grafana-openapi-client-go/models"
19-
"google.golang.org/api/idtoken"
2019
)
2120

2221
// newGrafanaClient is a helper function that creates an HTTP client to
@@ -26,30 +25,37 @@ import (
2625
func newGrafanaClient(
2726
ctx context.Context, host string, secure bool,
2827
) (*grafana.GrafanaHTTPAPI, error) {
29-
headers := map[string]string{}
3028
scheme := "http"
3129

30+
// Use the default HTTP client for unsecure Grafana calls.
31+
grafanaHttpClient := httputil.DefaultClient.Client
32+
3233
if secure {
3334
scheme = "https"
3435

35-
// Read in the service account key and audience, so we can retrieve the identity token.
36-
if _, err := roachprodutil.SetServiceAccountCredsEnv(ctx, false); err != nil {
37-
return nil, err
38-
}
39-
40-
token, err := roachprodutil.GetServiceAccountToken(ctx, idtoken.NewTokenSource)
36+
// Grafana annotations currently use the same service account
37+
// and OAuth client ID as the prometheus helper service.
38+
iapTokenSource, err := roachprodutil.NewIAPTokenSource(roachprodutil.IAPTokenSourceOptions{
39+
OAuthClientID: promhelperclient.OAuthClientID,
40+
ServiceAccountEmail: promhelperclient.ServiceAccountEmail,
41+
})
4142
if err != nil {
4243
return nil, err
4344
}
44-
headers["Authorization"] = fmt.Sprintf("Bearer %s", token)
45+
46+
// Override the default HTTP client with the one
47+
// that has the IAP token source.
48+
grafanaHttpClient = iapTokenSource.GetHTTPClient()
4549
}
4650

47-
headers[httputil.ContentTypeHeader] = httputil.JSONContentType
4851
cfg := &grafana.TransportConfig{
49-
Host: host,
50-
BasePath: "/api",
51-
Schemes: []string{scheme},
52-
HTTPHeaders: headers,
52+
Host: host,
53+
BasePath: "/api",
54+
Schemes: []string{scheme},
55+
HTTPHeaders: map[string]string{
56+
httputil.ContentTypeHeader: httputil.JSONContentType,
57+
},
58+
Client: grafanaHttpClient,
5359
}
5460

5561
return grafana.NewHTTPClientWithConfig(strfmt.Default, cfg), nil

0 commit comments

Comments
 (0)