From d725acb3803c4842fa2718b9d7dac70b770ade9e Mon Sep 17 00:00:00 2001 From: Kevin Schoonover Date: Mon, 6 Jun 2022 12:51:02 -0700 Subject: [PATCH] parse ACL token from authorization header (#12534) --- command/agent/http.go | 21 +++++++++++++ command/agent/http_test.go | 47 +++++++++++++++++++++++++----- website/content/api-docs/index.mdx | 14 +++++++-- 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/command/agent/http.go b/command/agent/http.go index c3284764bbf..38317d1ee7b 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -806,6 +806,27 @@ func (s *HTTPServer) parseToken(req *http.Request, token *string) { *token = other return } + + if other := req.Header.Get("Authorization"); other != "" { + // HTTP Authorization headers are in the format: [SPACE] + // Ref. https://tools.ietf.org/html/rfc7236#section-3 + parts := strings.Split(other, " ") + + // Authorization Header is invalid if containing 1 or 0 parts, e.g.: + // "" || "" || "" || "" + if len(parts) > 1 { + scheme := parts[0] + // Everything after "" is "", trimmed + value := strings.TrimSpace(strings.Join(parts[1:], " ")) + + // must be "Bearer" + if strings.ToLower(scheme) == "bearer" { + // Since Bearer tokens shouldn't contain spaces (rfc6750#section-2.1) + // "value" is tokenized, only the first item is used + *token = strings.TrimSpace(strings.Split(value, " ")[0]) + } + } + } } // parse is a convenience method for endpoints that need to parse multiple flags diff --git a/command/agent/http_test.go b/command/agent/http_test.go index ef1c62f118a..5c817c30883 100644 --- a/command/agent/http_test.go +++ b/command/agent/http_test.go @@ -535,16 +535,47 @@ func TestParseToken(t *testing.T) { s := makeHTTPServer(t, nil) defer s.Shutdown() - req, err := http.NewRequest("GET", "/v1/jobs", nil) - req.Header.Add("X-Nomad-Token", "foobar") - if err != nil { - t.Fatalf("err: %v", err) + cases := []struct { + Name string + HeaderKey string + HeaderValue string + ExpectedToken string + }{ + { + Name: "Parses token from X-Nomad-Token", + HeaderKey: "X-Nomad-Token", + HeaderValue: "foobar", + ExpectedToken: "foobar", + }, + { + Name: "Parses token from bearer authentication", + HeaderKey: "Authorization", + HeaderValue: "Bearer foobar", + ExpectedToken: "foobar", + }, + { + Name: "Fails to parse token from bad bearer authentication", + HeaderKey: "Authorization", + HeaderValue: "foobar", + ExpectedToken: "", + }, } - var token string - s.Server.parseToken(req, &token) - if token != "foobar" { - t.Fatalf("bad %s", token) + for i := range cases { + tc := cases[i] + t.Run(tc.Name, func(t *testing.T) { + req, err := http.NewRequest("GET", "/v1/jobs", nil) + req.Header.Add(tc.HeaderKey, tc.HeaderValue) + if err != nil { + t.Fatalf("err: %v", err) + } + + var token string + s.Server.parseToken(req, &token) + if token != tc.ExpectedToken { + t.Fatalf("bad %s", token) + } + }) } } diff --git a/website/content/api-docs/index.mdx b/website/content/api-docs/index.mdx index 997f3afafcf..7bd7ad0e235 100644 --- a/website/content/api-docs/index.mdx +++ b/website/content/api-docs/index.mdx @@ -66,15 +66,15 @@ administration. ## ACLs -Several endpoints in Nomad use or require ACL tokens to operate. The token are used to authenticate the request and determine if the request is allowed based on the associated authorizations. Tokens are specified per-request by using the `X-Nomad-Token` request header set to the `SecretID` of an ACL Token. +Several endpoints in Nomad use or require ACL tokens to operate. The token are used to authenticate the request and determine if the request is allowed based on the associated authorizations. Tokens are specified per-request by using the `X-Nomad-Token` request header or with the Bearer scheme in the authorization header set to the `SecretID` of an ACL Token. For more details about ACLs, please see the [ACL Guide](https://learn.hashicorp.com/collections/nomad/access-control). ## Authentication -When ACLs are enabled, a Nomad token should be provided to API requests using the `X-Nomad-Token` header. When using authentication, clients should communicate via TLS. +When ACLs are enabled, a Nomad token should be provided to API requests using the `X-Nomad-Token` header or with the Bearer scheme in the authorization header. When using authentication, clients should communicate via TLS. -Here is an example using curl: +Here is an example using curl with `X-Nomad-Token`: ```shell-session $ curl \ @@ -82,6 +82,14 @@ $ curl \ https://localhost:4646/v1/jobs ``` +Below is an example using `curl` with a [RFC6750](https://tools.ietf.org/html/rfc6750) Bearer token: + +```shell-session +$ curl \ + --header "Authorization: Bearer " \ + http://localhost:4646/v1/jobs +``` + ## Namespaces Nomad has support for namespaces, which allow jobs and their associated objects