feat: Google API Auth (#11084)
This commit is contained in:
parent
912e3362d2
commit
7d2016b84c
|
|
@ -2,10 +2,12 @@ package httpconfig
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/common/cookie"
|
||||
|
|
@ -30,12 +32,12 @@ type HTTPClientConfig struct {
|
|||
func (h *HTTPClientConfig) CreateClient(ctx context.Context, log telegraf.Logger) (*http.Client, error) {
|
||||
tlsCfg, err := h.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to set TLS config: %w", err)
|
||||
}
|
||||
|
||||
prox, err := h.HTTPProxy.Proxy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to set proxy: %w", err)
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ format by default.
|
|||
# token_url = "https://indentityprovider/oauth2/v1/token"
|
||||
# scopes = ["urn:opc:idm:__myscopes__"]
|
||||
|
||||
## Goole API Auth
|
||||
# google_application_credentials = "/etc/telegraf/example_secret.json"
|
||||
|
||||
## Optional TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
|
|
@ -104,6 +107,14 @@ format by default.
|
|||
# non_retryable_statuscodes = [409, 413]
|
||||
```
|
||||
|
||||
### Google API Auth
|
||||
|
||||
The `google_application_credentials` setting is used with Google Cloud APIs. It specifies the json key file. To learn about creating Google service accounts, consult Google's
|
||||
[oauth2 service account documentation][create_service_account]. An example use case is a metrics proxy deployed to
|
||||
Cloud Run. In this example, the service account must have the "run.routes.invoke" permission.
|
||||
|
||||
[create_service_account]: https://cloud.google.com/docs/authentication/production#create_service_account
|
||||
|
||||
### Optional Cookie Authentication Settings
|
||||
|
||||
The optional Cookie Authentication Settings will retrieve a cookie from the
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import (
|
|||
httpconfig "github.com/influxdata/telegraf/plugins/common/http"
|
||||
"github.com/influxdata/telegraf/plugins/outputs"
|
||||
"github.com/influxdata/telegraf/plugins/serializers"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/api/idtoken"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -50,6 +52,10 @@ type HTTP struct {
|
|||
|
||||
awsCfg *awsV2.Config
|
||||
internalaws.CredentialConfig
|
||||
|
||||
// Google API Auth
|
||||
CredentialsFile string `toml:"google_application_credentials"`
|
||||
oauth2Token *oauth2.Token
|
||||
}
|
||||
|
||||
func (h *HTTP) SetSerializer(serializer serializers.Serializer) {
|
||||
|
|
@ -168,6 +174,15 @@ func (h *HTTP) writeMetric(reqBody []byte) error {
|
|||
req.SetBasicAuth(h.Username, h.Password)
|
||||
}
|
||||
|
||||
// google api auth
|
||||
if h.CredentialsFile != "" {
|
||||
token, err := h.getAccessToken(context.Background(), h.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token.SetAuthHeader(req)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", internal.ProductToken())
|
||||
req.Header.Set("Content-Type", defaultContentType)
|
||||
if h.ContentEncoding == "gzip" {
|
||||
|
|
@ -220,3 +235,23 @@ func init() {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (h *HTTP) getAccessToken(ctx context.Context, audience string) (*oauth2.Token, error) {
|
||||
if h.oauth2Token.Valid() {
|
||||
return h.oauth2Token, nil
|
||||
}
|
||||
|
||||
ts, err := idtoken.NewTokenSource(ctx, audience, idtoken.WithCredentialsFile(h.CredentialsFile))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating oauth2 token source: %s", err)
|
||||
}
|
||||
|
||||
token, err := ts.Token()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching oauth2 token: %s", err)
|
||||
}
|
||||
|
||||
h.oauth2Token = token
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -526,6 +527,79 @@ func TestOAuthClientCredentialsGrant(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestOAuthAuthorizationCodeGrant(t *testing.T) {
|
||||
ts := httptest.NewServer(http.NotFoundHandler())
|
||||
defer ts.Close()
|
||||
|
||||
u, err := url.Parse(fmt.Sprintf("http://%s", ts.Listener.Addr().String()))
|
||||
require.NoError(t, err)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
tmpFile, err := os.CreateTemp(tmpDir, "test_key_file")
|
||||
require.NoError(t, err)
|
||||
|
||||
tmpTokenURI := u.String() + "/token"
|
||||
data := []byte(fmt.Sprintf("{\n \"type\": \"service_account\",\n \"project_id\": \"my-project\",\n \"private_key_id\": \"223423436436453645363456\",\n \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIICXAIBAAKBgQDX7Plvu0MJtA9TrusYtQnAogsdiYJZd9wfFIjH5FxE3SWJ4KAIE+yRWRqcqX8XnpieQLaNsfXhDPWLkWngTDydk4NO/jlAQk0e6+9+NeiZ2ViIHmtXERb9CyiiWUmo+YCd69lhzSEIMK9EPBSDHQTgQMtEfGak03G5rx3MCakE1QIDAQABAoGAOjRU4Lt3zKvO3d3u3ZAfet+zY1jn3DolCfO9EzUJcj6ymcIFIWhNgrikJcrCyZkkxrPnAbcQ8oNNxTuDcMTcKZbnyUnlQj5NtVuty5Q+zgf3/Q2pRhaE+TwrpOJ+ETtVp9R/PrPN2NC5wPo289fPNWFYkd4DPbdWZp5AJHz1XYECQQD3kKpinJxMYp9FQ1Qj1OkxGln0KPgdqRYjjW/rXI4/hUodfg+xXWHPFSGj3AgEjQIvuengbOAeH3qowF1uxVTlAkEA30hXM3EbboMCDQzNRNkkV9EiZ0MZXhj1aIGl+sQZOmOeFdcdjGkDdsA42nmaYqXCD9KAvc+S/tGJaa0Qg0VhMQJAb2+TAqh0Qn3yK39PFIH2JcAy1ZDLfq5p5L75rfwPm9AnuHbSIYhjSo+8gMG+ai3+2fTZrcfUajrJP8S3SfFRcQJBANQQPOHatxcKzlPeqMaPBXlyY553mAxK4CnVmPLGdL+EBYzwtlu5EVUj09uMSxkOHXYxk5yzHQVvtXbsrBZBOsECQBJLlkMjJmXrIIdLPmHQWL3bm9MMg1PqzupSEwz6cyrGuIIm/X91pDyxCHaKYWp38FXBkYAgohI8ow5/sgRvU5w=\\n-----END PRIVATE KEY-----\\n\",\n \"client_email\": \"test-service-account-email@example.iam.gserviceaccount.com\",\n \"client_id\": \"110300009813738675309\",\n \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n \"token_uri\": \"%s\",\n \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/test-service-account-email@example.iam.gserviceaccount.com\"\n}", tmpTokenURI))
|
||||
_, err = tmpFile.Write(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, tmpFile.Close())
|
||||
|
||||
const token = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg2NzUzMDliMjJiMDFiZTU2YzIxM2M5ODU0MGFiNTYzYmZmNWE1OGMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwOi8vMTI3LjAuMC4xOjU4MDI1LyIsImF6cCI6InRlc3Qtc2VydmljZS1hY2NvdW50LWVtYWlsQGV4YW1wbGUuY29tIiwiZW1haWwiOiJ0ZXN0LXNlcnZpY2UtYWNjb3VudC1lbWFpbEBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjk0NjY4NDgwMCwiaWF0Ijo5NDY2ODEyMDAsImlzcyI6Imh0dHBzOi8vYWNjb3VudHMudGVzdC5jb20iLCJzdWIiOiIxMTAzMDAwMDk4MTM3Mzg2NzUzMDkifQ.qi2LsXP2o6nl-rbYKUlHAgTBY0QoU7Nhty5NGR4GMdc8OoGEPW-vlD0WBSaKSr11vyFcIO4ftFDWXElo9Ut-AIQPKVxinsjHIU2-LoIATgI1kyifFLyU_pBecwcI4CIXEcDK5wEkfonWFSkyDZHBeZFKbJXlQXtxj0OHvQ-DEEepXLuKY6v3s4U6GyD9_ppYUy6gzDZPYUbfPfgxCj_Jbv6qkLU0DiZ7F5-do6X6n-qkpgCRLTGHcY__rn8oe8_pSimsyJEeY49ZQ5lj4mXkVCwgL9bvL1_eW1p6sgbHaBnPKVPbM7S1_cBmzgSonm__qWyZUxfDgNdigtNsvzBQTg"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *HTTP
|
||||
handler TestHandlerFunc
|
||||
tokenHandler TestHandlerFunc
|
||||
}{
|
||||
{
|
||||
name: "no credentials file",
|
||||
plugin: &HTTP{
|
||||
URL: u.String(),
|
||||
},
|
||||
handler: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
|
||||
require.Len(t, r.Header["Authorization"], 0)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
plugin: &HTTP{
|
||||
URL: u.String() + "/write",
|
||||
CredentialsFile: tmpFile.Name(),
|
||||
},
|
||||
tokenHandler: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
authHeader := fmt.Sprintf(`{"id_token":"%s"}`, token)
|
||||
_, err = w.Write([]byte(authHeader))
|
||||
require.NoError(t, err)
|
||||
},
|
||||
handler: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, []string{"Bearer " + token}, r.Header["Authorization"])
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/write":
|
||||
tt.handler(t, w, r)
|
||||
case "/token":
|
||||
tt.tokenHandler(t, w, r)
|
||||
}
|
||||
})
|
||||
|
||||
tt.plugin.SetSerializer(influx.NewSerializer())
|
||||
require.NoError(t, tt.plugin.Connect())
|
||||
require.NoError(t, tt.plugin.Write([]telegraf.Metric{getMetric()}))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultUserAgent(t *testing.T) {
|
||||
ts := httptest.NewServer(http.NotFoundHandler())
|
||||
defer ts.Close()
|
||||
|
|
|
|||
Loading…
Reference in New Issue