-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2823 metrics authorization return 401 (#2836)
* Expose subset of Promex config * Working - pre refactor * refactor * Remove obsolete PromEx auth * Additional tests for Config.API * Config clean up * Extra tests for MetricsAuth * Update CHANGELOG
- Loading branch information
1 parent
cca74c3
commit 3e63380
Showing
11 changed files
with
311 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
defmodule LightningWeb.Plugs.MetricsAuth do | ||
@moduledoc """ | ||
Implements Bearer token authorization for /metrics endpoint that is managed by | ||
PromEx. | ||
""" | ||
import Plug.Conn | ||
|
||
def init(opts), do: opts | ||
|
||
def call(conn, _opts) do | ||
if metrics_path?(conn) && authorization_required?() do | ||
if valid_token?(auth_header(conn)) && valid_scheme?(conn) do | ||
conn | ||
else | ||
halt_as_unauthorized(conn) | ||
end | ||
else | ||
conn | ||
end | ||
end | ||
|
||
defp metrics_path?(conn) do | ||
conn.path_info == ["metrics"] | ||
end | ||
|
||
defp authorization_required? do | ||
Lightning.Config.promex_metrics_endpoint_authorization_required?() | ||
end | ||
|
||
defp auth_header(conn) do | ||
Plug.Conn.get_req_header(conn, "authorization") | ||
end | ||
|
||
defp valid_token?(["Bearer " <> provided_token]) do | ||
expected_token = Lightning.Config.promex_metrics_endpoint_token() | ||
Plug.Crypto.secure_compare(provided_token, expected_token) | ||
end | ||
|
||
defp valid_token?(_auth_header) do | ||
false | ||
end | ||
|
||
defp valid_scheme?(conn) do | ||
provided_scheme = Atom.to_string(conn.scheme) | ||
expected_scheme = Lightning.Config.promex_metrics_endpoint_scheme() | ||
provided_scheme == expected_scheme | ||
end | ||
|
||
defp halt_as_unauthorized(conn) do | ||
conn | ||
|> put_resp_header("www-authenticate", "Bearer") | ||
|> send_resp(401, "Unauthorized") | ||
|> halt() | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
defmodule LightningWeb.Plugs.PromexWrapper do | ||
@moduledoc """ | ||
Ensures that the MetricsAuth plug always comes before the PromEx plug. | ||
""" | ||
use Plug.Builder | ||
|
||
plug LightningWeb.Plugs.MetricsAuth | ||
plug PromEx.Plug, prom_ex_module: Lightning.PromEx | ||
end |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
defmodule Lightning.Configtest do | ||
use ExUnit.Case, async: true | ||
|
||
alias Lightning.Config.API | ||
|
||
describe "API" do | ||
test "returns the appropriate PromEx endpoint auth setting" do | ||
expected = | ||
extract_from_config( | ||
Lightning.PromEx, | ||
:metrics_endpoint_authorization_required | ||
) | ||
|
||
actual = API.promex_metrics_endpoint_authorization_required?() | ||
|
||
assert expected == actual | ||
end | ||
|
||
test "returns the appropriate Promex endpoint token" do | ||
expected = | ||
extract_from_config(Lightning.PromEx, :metrics_endpoint_token) | ||
|
||
actual = API.promex_metrics_endpoint_token() | ||
|
||
assert expected == actual | ||
end | ||
|
||
test "returns the appropriate PromEx endpoint scheme" do | ||
expected = | ||
extract_from_config(Lightning.PromEx, :metrics_endpoint_scheme) | ||
|
||
actual = API.promex_metrics_endpoint_scheme() | ||
|
||
assert expected == actual | ||
end | ||
end | ||
|
||
defp extract_from_config(config, key) do | ||
Application.get_env(:lightning, config) |> Keyword.get(key) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
defmodule LightningWeb.Plugs.MetricsAuthTest do | ||
use LightningWeb.ConnCase, async: true | ||
|
||
import Plug.Test | ||
|
||
alias LightningWeb.Plugs.MetricsAuth | ||
|
||
setup do | ||
token = "test_token" | ||
|
||
Mox.stub(Lightning.MockConfig, :promex_metrics_endpoint_token, fn -> | ||
token | ||
end) | ||
|
||
Mox.stub(Lightning.MockConfig, :promex_metrics_endpoint_scheme, fn -> | ||
"http" | ||
end) | ||
|
||
%{token: token} | ||
end | ||
|
||
describe "init" do | ||
test "returns the provided options as they are" do | ||
assert MetricsAuth.init(a: 1, b: 2) == [a: 1, b: 2] | ||
end | ||
end | ||
|
||
describe "metrics request and authorization required" do | ||
setup do | ||
Mox.stub( | ||
Lightning.MockConfig, | ||
:promex_metrics_endpoint_authorization_required?, | ||
fn -> true end | ||
) | ||
|
||
conn = conn(:get, "/metrics") | ||
|
||
%{conn: conn} | ||
end | ||
|
||
test "passes if the token and scheme match", %{conn: conn, token: token} do | ||
conn = | ||
conn | ||
|> put_req_header("authorization", "Bearer #{token}") | ||
|> MetricsAuth.call([]) | ||
|
||
assert_passed_request?(conn) | ||
end | ||
|
||
test "is unauthorized if no authorization header", %{conn: conn} do | ||
conn = | ||
conn | ||
|> MetricsAuth.call([]) | ||
|
||
assert_unauthorized_request?(conn) | ||
end | ||
|
||
test "is unauthorized if no bearer token", %{conn: conn, token: token} do | ||
conn = | ||
conn | ||
|> put_req_header("authorization", "Basic #{token}") | ||
|> MetricsAuth.call([]) | ||
|
||
assert_unauthorized_request?(conn) | ||
end | ||
|
||
test "is unauthorized if bearer token does not match", %{ | ||
conn: conn, | ||
token: token | ||
} do | ||
conn = | ||
conn | ||
|> put_req_header("authorization", "Bearer not-#{token}") | ||
|> MetricsAuth.call([]) | ||
|
||
assert_unauthorized_request?(conn) | ||
end | ||
|
||
test "is unauthorised if the scheme does not match", | ||
%{conn: conn, token: token} do | ||
Mox.stub(Lightning.MockConfig, :promex_metrics_endpoint_scheme, fn -> | ||
"https" | ||
end) | ||
|
||
conn = | ||
conn | ||
|> put_req_header("authorization", "Bearer #{token}") | ||
|> MetricsAuth.call([]) | ||
|
||
assert_unauthorized_request?(conn) | ||
end | ||
end | ||
|
||
describe "not metrics and authorization required" do | ||
setup do | ||
Mox.stub( | ||
Lightning.MockConfig, | ||
:promex_metrics_endpoint_authorization_required?, | ||
fn -> true end | ||
) | ||
|
||
conn = conn(:get, "/not-metrics") | ||
|
||
%{conn: conn} | ||
end | ||
|
||
test "passes the request regardless of what is provided", %{conn: conn} do | ||
conn = conn |> MetricsAuth.call([]) | ||
|
||
assert_passed_request?(conn) | ||
end | ||
end | ||
|
||
describe "metrics request but authorization not required" do | ||
setup do | ||
Mox.stub( | ||
Lightning.MockConfig, | ||
:promex_metrics_endpoint_authorization_required?, | ||
fn -> false end | ||
) | ||
|
||
conn = conn(:get, "/metrics") | ||
|
||
%{conn: conn} | ||
end | ||
|
||
test "passes the request regardless of what is provided", %{conn: conn} do | ||
conn = conn |> MetricsAuth.call([]) | ||
|
||
assert_passed_request?(conn) | ||
end | ||
end | ||
|
||
def assert_passed_request?(conn) do | ||
assert conn.status == nil | ||
assert get_resp_header(conn, "www-authenticate") == [] | ||
assert conn.resp_body == nil | ||
refute conn.halted | ||
end | ||
|
||
def assert_unauthorized_request?(conn) do | ||
assert conn.status == 401 | ||
assert get_resp_header(conn, "www-authenticate") == ["Bearer"] | ||
assert conn.resp_body == "Unauthorized" | ||
assert conn.halted | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
defmodule LightningWeb.PromEx.MetricsEndpointTest do | ||
use LightningWeb.ConnCase, async: true | ||
|
||
describe "unauthorized request to GET /metrics" do | ||
setup do | ||
Mox.stub( | ||
Lightning.MockConfig, | ||
:promex_metrics_endpoint_authorization_required?, | ||
fn -> true end | ||
) | ||
|
||
:ok | ||
end | ||
|
||
test "returns 401", %{conn: conn} do | ||
conn = get(conn, "/metrics") | ||
|
||
assert conn.status == 401 | ||
end | ||
end | ||
end |
Oops, something went wrong.