filter snmp communities from log
This commit is contained in:
parent
aed15dd6a1
commit
1f6151b5a8
6 changed files with 266 additions and 4 deletions
|
|
@ -54,7 +54,20 @@ config :mime, :types, %{
|
|||
}
|
||||
|
||||
# Filter sensitive parameters from logs
|
||||
config :phoenix, :filter_parameters, ["password", "snmp_community", "secret", "token", "api_key"]
|
||||
# These parameters will be replaced with "[FILTERED]" in Phoenix logs and error reports
|
||||
config :phoenix, :filter_parameters, [
|
||||
"password",
|
||||
"snmp_community",
|
||||
"community",
|
||||
"secret",
|
||||
"token",
|
||||
"api_key",
|
||||
# SNMPv3 credentials (for future use)
|
||||
"auth_password",
|
||||
"priv_password",
|
||||
"auth_pass",
|
||||
"priv_pass"
|
||||
]
|
||||
|
||||
# Use Jason for JSON parsing in Phoenix
|
||||
config :phoenix, :json_library, Jason
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
defmodule Towerops.HoneybadgerFilter do
|
||||
@moduledoc """
|
||||
Custom Honeybadger filter to exclude noisy errors during deployments.
|
||||
Custom Honeybadger filter to exclude noisy errors during deployments
|
||||
and filter sensitive SNMP credentials from error reports.
|
||||
|
||||
Filters out OTP supervisor/gen_server errors that occur during normal
|
||||
node shutdown/restart.
|
||||
|
|
@ -15,6 +16,20 @@ defmodule Towerops.HoneybadgerFilter do
|
|||
"Elixir.PollerSupervisor"
|
||||
]
|
||||
|
||||
# Sensitive keys that should be filtered from error reports
|
||||
@sensitive_keys [
|
||||
"password",
|
||||
"snmp_community",
|
||||
"community",
|
||||
"secret",
|
||||
"token",
|
||||
"api_key",
|
||||
"auth_password",
|
||||
"priv_password",
|
||||
"auth_pass",
|
||||
"priv_pass"
|
||||
]
|
||||
|
||||
@impl Honeybadger.Filter
|
||||
def filter_context(context) do
|
||||
cond do
|
||||
|
|
@ -27,10 +42,25 @@ defmodule Towerops.HoneybadgerFilter do
|
|||
nil
|
||||
|
||||
true ->
|
||||
super(context)
|
||||
context
|
||||
|> super()
|
||||
|> filter_sensitive_data()
|
||||
end
|
||||
end
|
||||
|
||||
@impl Honeybadger.Filter
|
||||
def filter_params(params) when is_map(params) do
|
||||
Map.new(params, fn {key, value} ->
|
||||
if should_filter?(key) do
|
||||
{key, "[FILTERED]"}
|
||||
else
|
||||
{key, filter_params(value)}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def filter_params(params), do: params
|
||||
|
||||
# Detect anonymous gen_server termination during shutdown
|
||||
defp otp_gen_server_shutdown?(context) do
|
||||
domain = get_in(context, [:domain])
|
||||
|
|
@ -38,4 +68,29 @@ defmodule Towerops.HoneybadgerFilter do
|
|||
|
||||
domain == ["otp"] and match?(["gen_server", "error_info", _], mfa)
|
||||
end
|
||||
|
||||
# Filter sensitive data from context
|
||||
defp filter_sensitive_data(nil), do: nil
|
||||
|
||||
defp filter_sensitive_data(context) when is_map(context) do
|
||||
context
|
||||
|> Map.update(:cgi_data, %{}, &filter_params/1)
|
||||
|> Map.update(:params, %{}, &filter_params/1)
|
||||
|> Map.update(:session, %{}, &filter_params/1)
|
||||
|> Map.update(:context, %{}, &filter_params/1)
|
||||
end
|
||||
|
||||
defp filter_sensitive_data(context), do: context
|
||||
|
||||
# Check if a key should be filtered
|
||||
defp should_filter?(key) when is_binary(key) do
|
||||
key_lower = String.downcase(key)
|
||||
Enum.any?(@sensitive_keys, &String.contains?(key_lower, &1))
|
||||
end
|
||||
|
||||
defp should_filter?(key) when is_atom(key) do
|
||||
should_filter?(Atom.to_string(key))
|
||||
end
|
||||
|
||||
defp should_filter?(_), do: false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -65,6 +65,17 @@
|
|||
required
|
||||
/>
|
||||
|
||||
<%= unless @invitation do %>
|
||||
<.input
|
||||
name="user[organization_name]"
|
||||
value=""
|
||||
type="text"
|
||||
label="Organization Name"
|
||||
placeholder="My Company"
|
||||
required
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
<.button phx-disable-with="Creating account..." class="w-full" variant="primary">
|
||||
{if @invitation, do: "Accept invitation and create account", else: "Create an account"}
|
||||
</.button>
|
||||
|
|
|
|||
159
test/towerops/honeybadger_filter_test.exs
Normal file
159
test/towerops/honeybadger_filter_test.exs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
defmodule Towerops.HoneybadgerFilterTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Towerops.HoneybadgerFilter
|
||||
|
||||
describe "filter_params/1" do
|
||||
test "filters SNMP community strings" do
|
||||
params = %{
|
||||
"name" => "Device 1",
|
||||
"snmp_community" => "public",
|
||||
"ip_address" => "192.168.1.1"
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered["name"] == "Device 1"
|
||||
assert filtered["snmp_community"] == "[FILTERED]"
|
||||
assert filtered["ip_address"] == "192.168.1.1"
|
||||
end
|
||||
|
||||
test "filters community strings with just 'community' key" do
|
||||
params = %{"community" => "secret123", "device_id" => "abc"}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered["community"] == "[FILTERED]"
|
||||
assert filtered["device_id"] == "abc"
|
||||
end
|
||||
|
||||
test "filters passwords" do
|
||||
params = %{"email" => "user@example.com", "password" => "secret"}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered["email"] == "user@example.com"
|
||||
assert filtered["password"] == "[FILTERED]"
|
||||
end
|
||||
|
||||
test "filters SNMPv3 credentials" do
|
||||
params = %{
|
||||
"snmp_version" => "3",
|
||||
"auth_password" => "authpass",
|
||||
"priv_password" => "privpass",
|
||||
"username" => "admin"
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered["snmp_version"] == "3"
|
||||
assert filtered["auth_password"] == "[FILTERED]"
|
||||
assert filtered["priv_password"] == "[FILTERED]"
|
||||
assert filtered["username"] == "admin"
|
||||
end
|
||||
|
||||
test "filters API keys and tokens" do
|
||||
params = %{
|
||||
"api_key" => "key123",
|
||||
"token" => "token456",
|
||||
"secret" => "secret789"
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered["api_key"] == "[FILTERED]"
|
||||
assert filtered["token"] == "[FILTERED]"
|
||||
assert filtered["secret"] == "[FILTERED]"
|
||||
end
|
||||
|
||||
test "filters nested params" do
|
||||
params = %{
|
||||
"device" => %{
|
||||
"name" => "Router",
|
||||
"snmp_community" => "private",
|
||||
"ip_address" => "10.0.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered["device"]["name"] == "Router"
|
||||
assert filtered["device"]["snmp_community"] == "[FILTERED]"
|
||||
assert filtered["device"]["ip_address"] == "10.0.0.1"
|
||||
end
|
||||
|
||||
test "filters atom keys" do
|
||||
params = %{
|
||||
name: "Device",
|
||||
snmp_community: "public",
|
||||
ip_address: "192.168.1.1"
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered[:name] == "Device"
|
||||
assert filtered[:snmp_community] == "[FILTERED]"
|
||||
assert filtered[:ip_address] == "192.168.1.1"
|
||||
end
|
||||
|
||||
test "handles case-insensitive filtering" do
|
||||
params = %{
|
||||
"SNMP_COMMUNITY" => "public",
|
||||
"SnmpCommunity" => "private",
|
||||
"API_KEY" => "secret"
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered["SNMP_COMMUNITY"] == "[FILTERED]"
|
||||
assert filtered["SnmpCommunity"] == "[FILTERED]"
|
||||
assert filtered["API_KEY"] == "[FILTERED]"
|
||||
end
|
||||
|
||||
test "does not filter non-sensitive data" do
|
||||
params = %{
|
||||
"name" => "Device",
|
||||
"ip_address" => "192.168.1.1",
|
||||
"description" => "Main router"
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_params(params)
|
||||
|
||||
assert filtered == params
|
||||
end
|
||||
end
|
||||
|
||||
describe "filter_context/1" do
|
||||
test "filters sensitive data from context params" do
|
||||
context = %{
|
||||
params: %{
|
||||
"device" => %{"snmp_community" => "public", "name" => "Router"}
|
||||
}
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_context(context)
|
||||
|
||||
assert filtered.params["device"]["name"] == "Router"
|
||||
assert filtered.params["device"]["snmp_community"] == "[FILTERED]"
|
||||
end
|
||||
|
||||
test "filters sensitive data from cgi_data" do
|
||||
context = %{
|
||||
cgi_data: %{"auth_token" => "Bearer token123", "user_agent" => "Mozilla/5.0"}
|
||||
}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_context(context)
|
||||
|
||||
assert filtered.cgi_data["auth_token"] == "[FILTERED]"
|
||||
assert filtered.cgi_data["user_agent"] == "Mozilla/5.0"
|
||||
end
|
||||
|
||||
test "filters ignored registered names" do
|
||||
context = %{registered_name: "memsup"}
|
||||
|
||||
filtered = HoneybadgerFilter.filter_context(context)
|
||||
|
||||
assert filtered == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -57,5 +57,26 @@ defmodule ToweropsWeb.UserRegistrationControllerTest do
|
|||
assert response =~ "Register"
|
||||
assert response =~ "can't be blank"
|
||||
end
|
||||
|
||||
test "creates organization with provided name", %{conn: conn} do
|
||||
email = unique_user_email()
|
||||
|
||||
conn =
|
||||
post(conn, ~p"/users/register", %{
|
||||
"user" => %{
|
||||
"email" => email,
|
||||
"password" => valid_user_password(),
|
||||
"organization_name" => "Acme Corp"
|
||||
}
|
||||
})
|
||||
|
||||
assert get_session(conn, :user_token)
|
||||
assert redirected_to(conn) == ~p"/devices"
|
||||
|
||||
# Verify organization was created with the provided name
|
||||
user = Towerops.Accounts.get_user_by_email(email)
|
||||
[org | _] = Towerops.Organizations.list_user_organizations(user.id)
|
||||
assert org.name == "Acme Corp"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ defmodule ToweropsWeb.UserRegistrationHTMLTest do
|
|||
render_to_string(ToweropsWeb.UserRegistrationHTML, "new", "html",
|
||||
flash: %{},
|
||||
current_scope: nil,
|
||||
form: form
|
||||
form: form,
|
||||
invitation: nil,
|
||||
invitation_token: nil
|
||||
)
|
||||
|
||||
assert html =~ "Register for an account"
|
||||
|
|
@ -24,6 +26,7 @@ defmodule ToweropsWeb.UserRegistrationHTMLTest do
|
|||
assert html =~ "Log in"
|
||||
assert html =~ "Create an account"
|
||||
assert html =~ "Password"
|
||||
assert html =~ "Organization Name"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue