From 8c53e3c165dc09b0d8ac9324d9f8fc5c88df416e Mon Sep 17 00:00:00 2001 From: Kevin Schoonover Date: Sun, 10 Apr 2022 12:34:58 -0700 Subject: [PATCH 1/3] parse ACL token from authorization header --- command/agent/http.go | 21 +++++++++++++++++ command/agent/http_test.go | 47 +++++++++++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/command/agent/http.go b/command/agent/http.go index c3284764bbf6..38317d1ee7b1 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 8346243173ad..107b5be84340 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) + } + }) } } From 434f895cba45bb2c8ee82d56514083e4956e5196 Mon Sep 17 00:00:00 2001 From: Kevin Schoonover Date: Sun, 10 Apr 2022 12:42:51 -0700 Subject: [PATCH 2/3] update documentation --- website/content/api-docs/index.mdx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/website/content/api-docs/index.mdx b/website/content/api-docs/index.mdx index 997f3afafcff..c02572dca6f4 100644 --- a/website/content/api-docs/index.mdx +++ b/website/content/api-docs/index.mdx @@ -72,9 +72,9 @@ For more details about ACLs, please see the [ACL Guide](https://learn.hashicorp. ## 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 From 1f6351bcf96963d6cab25d2ecf68c104db278736 Mon Sep 17 00:00:00 2001 From: Kevin Schoonover Date: Sun, 10 Apr 2022 12:50:49 -0700 Subject: [PATCH 3/3] missing docs reference --- website/content/api-docs/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/content/api-docs/index.mdx b/website/content/api-docs/index.mdx index c02572dca6f4..7bd7ad0e2358 100644 --- a/website/content/api-docs/index.mdx +++ b/website/content/api-docs/index.mdx @@ -66,7 +66,7 @@ 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).