diff --git a/cmd/logcli/main.go b/cmd/logcli/main.go index dd77d537f509..c435e26179e2 100644 --- a/cmd/logcli/main.go +++ b/cmd/logcli/main.go @@ -209,6 +209,8 @@ func newQueryClient(app *kingpin.Application) client.Client { app.Flag("cert", "Path to the client certificate. Can also be set using LOKI_CLIENT_CERT_PATH env var.").Default("").Envar("LOKI_CLIENT_CERT_PATH").StringVar(&client.TLSConfig.CertFile) app.Flag("key", "Path to the client certificate key. Can also be set using LOKI_CLIENT_KEY_PATH env var.").Default("").Envar("LOKI_CLIENT_KEY_PATH").StringVar(&client.TLSConfig.KeyFile) app.Flag("org-id", "adds X-Scope-OrgID to API requests for representing tenant ID. Useful for requesting tenant data when bypassing an auth gateway.").Default("").Envar("LOKI_ORG_ID").StringVar(&client.OrgID) + app.Flag("bearer-token", "adds the Authorization header to API requests for authentication purposes. Can also be set using LOKI_BEARER_TOKEN env var.").Default("").Envar("LOKI_BEARER_TOKEN").StringVar(&client.BearerToken) + app.Flag("bearer-token-file", "adds the Authorization header to API requests for authentication purposes. Can also be set using LOKI_BEARER_TOKEN_FILE env var.").Default("").Envar("LOKI_BEARER_TOKEN_FILE").StringVar(&client.BearerTokenFile) return client } diff --git a/docs/sources/getting-started/logcli.md b/docs/sources/getting-started/logcli.md index 6f11a1e1aca4..77c8baaaa53e 100644 --- a/docs/sources/getting-started/logcli.md +++ b/docs/sources/getting-started/logcli.md @@ -42,7 +42,7 @@ $ export LOKI_ADDR=http://localhost:3100 > Note: If you are running Loki behind a proxy server and you have > authentication configured, you will also have to pass in LOKI_USERNAME -> and LOKI_PASSWORD accordingly. +> and LOKI_PASSWORD, LOKI_BEARER_TOKEN or LOKI_BEARER_TOKEN_FILE accordingly. ```bash $ logcli labels job diff --git a/pkg/logcli/client/client.go b/pkg/logcli/client/client.go index 0e0db3cc413c..7f6b587da0a3 100644 --- a/pkg/logcli/client/client.go +++ b/pkg/logcli/client/client.go @@ -48,12 +48,14 @@ type Tripperware func(http.RoundTripper) http.RoundTripper // Client contains fields necessary to query a Loki instance type DefaultClient struct { - TLSConfig config.TLSConfig - Username string - Password string - Address string - OrgID string - Tripperware Tripperware + TLSConfig config.TLSConfig + Username string + Password string + Address string + OrgID string + Tripperware Tripperware + BearerToken string + BearerTokenFile string } // Query uses the /api/v1/query endpoint to execute an instant query @@ -181,6 +183,27 @@ func (c *DefaultClient) doRequest(path, query string, quiet bool, out interface{ req.Header.Set("X-Scope-OrgID", c.OrgID) } + if (c.Username != "" || c.Password != "") && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) { + return fmt.Errorf("at most one of HTTP basic auth (username/password), bearer-token & bearer-token-file is allowed to be configured") + } + + if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 { + return fmt.Errorf("at most one of the options bearer-token & bearer-token-file is allowed to be configured") + } + + if c.BearerToken != "" { + req.Header.Set("Authorization", "Bearer "+c.BearerToken) + } + + if c.BearerTokenFile != "" { + b, err := ioutil.ReadFile(c.BearerTokenFile) + if err != nil { + return fmt.Errorf("unable to read authorization credentials file %s: %s", c.BearerTokenFile, err) + } + bearerToken := strings.TrimSpace(string(b)) + req.Header.Set("Authorization", "Bearer "+bearerToken) + } + // Parse the URL to extract the host clientConfig := config.HTTPClientConfig{ TLSConfig: c.TLSConfig, @@ -237,6 +260,27 @@ func (c *DefaultClient) wsConnect(path, query string, quiet bool) (*websocket.Co h.Set("X-Scope-OrgID", c.OrgID) } + if (c.Username != "" || c.Password != "") && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) { + return nil, fmt.Errorf("at most one of HTTP basic auth (username/password), bearer-token & bearer-token-file is allowed to be configured") + } + + if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 { + return nil, fmt.Errorf("at most one of the options bearer-token & bearer-token-file is allowed to be configured") + } + + if c.BearerToken != "" { + h.Set("Authorization", "Bearer "+c.BearerToken) + } + + if c.BearerTokenFile != "" { + b, err := ioutil.ReadFile(c.BearerTokenFile) + if err != nil { + return nil, fmt.Errorf("unable to read authorization credentials file %s: %s", c.BearerTokenFile, err) + } + bearerToken := strings.TrimSpace(string(b)) + h.Set("Authorization", "Bearer "+bearerToken) + } + ws := websocket.Dialer{ TLSClientConfig: tlsConfig, }