Skip to content

Commit

Permalink
FEATURE: global setting for whitelisted IP (#5)
Browse files Browse the repository at this point in the history
Adds support for prometheus_trusted_ip_whitelist_regex which allows clients to define special IP 
addresses with access to prom routes
  • Loading branch information
pfaffman committed Apr 1, 2020
1 parent 5b7a8bb commit 5af25b8
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 5 deletions.
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# We want to use the KVM-based system, so require sudo
sudo: required
services:
- docker

before_install:
- git clone --depth=1 https://github.com/discourse/discourse-plugin-ci

install: true # Prevent travis doing bundle install

script:
- discourse-plugin-ci/script.sh
22 changes: 17 additions & 5 deletions lib/middleware/metrics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,22 @@ def call(env)

def is_private_ip?(env)
request = Rack::Request.new(env)
ip = IPAddr.new(request.ip) rescue nil
ip = IPAddr.new(request.ip) rescue false
!!(ip && ip.to_s =~ PRIVATE_IP)
end

def is_trusted_ip?(env)
return false if GlobalSetting.prometheus_trusted_ip_whitelist_regex.empty?
begin
trusted_ip_regex = Regexp.new GlobalSetting.prometheus_trusted_ip_whitelist_regex
request = Rack::Request.new(env)
ip = IPAddr.new(request.ip)
rescue
false
end
!!(trusted_ip_regex && ip && ip.to_s =~ trusted_ip_regex)
end

def is_admin?(env)
host = RailsMultisite::ConnectionManagement.host(env)
result = false
Expand All @@ -40,17 +52,17 @@ def is_admin?(env)

def intercept?(env)
if env["PATH_INFO"] == "/metrics"
return is_private_ip?(env) || is_admin?(env)
return is_private_ip?(env) || is_trusted_ip?(env) || is_admin?(env)
end
false
end

def metrics(env)
data = Net::HTTP.get(URI("http://localhost:#{GlobalSetting.prometheus_collector_port}/metrics"))
[200, {
"Content-Type" => "text/plain; charset=utf-8",
"Content-Length" => data.bytesize.to_s
}, [data]]
"Content-Type" => "text/plain; charset=utf-8",
"Content-Length" => data.bytesize.to_s
}, [data]]
end

end
Expand Down
1 change: 1 addition & 0 deletions plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module ::DiscoursePrometheus; end
require_relative("lib/middleware/metrics")

GlobalSetting.add_default :prometheus_collector_port, 9405
GlobalSetting.add_default :prometheus_trusted_ip_whitelist_regex, ''

Rails.configuration.middleware.unshift DiscoursePrometheus::Middleware::Metrics

Expand Down
41 changes: 41 additions & 0 deletions spec/middleware/metrics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,41 @@
::DiscoursePrometheus::Middleware::Metrics.new(app)
end

it "will 404 for unauthed if prometheus_trusted_ip_whitelist_regex is unset" do
status, = middleware.call("PATH_INFO" => '/metrics', "REMOTE_ADDR" => '200.0.1.1', "rack.input" => StringIO.new)
expect(status).to eq(404)
end

it "will 404 for unauthed" do
status, = middleware.call("PATH_INFO" => '/metrics', "REMOTE_ADDR" => '200.0.1.1', "rack.input" => StringIO.new)
expect(status).to eq(404)
end

it "will 404 for unauthed and invalid regex" do
GlobalSetting.stubs(:prometheus_trusted_ip_whitelist_regex).returns("unbalanced bracket[")
status, = middleware.call("PATH_INFO" => '/metrics', "REMOTE_ADDR" => '200.0.1.1', "rack.input" => StringIO.new)
expect(status).to eq(404)
end

it "will 404 for unauthed empty regex" do
status, = middleware.call("PATH_INFO" => '/metrics', "REMOTE_ADDR" => '200.0.1.1', "rack.input" => StringIO.new)
expect(status).to eq(404)
end

it "can proxy the dedicated port" do
stub_request(:get, "http://localhost:#{GlobalSetting.prometheus_collector_port}/metrics").
to_return(status: 200, body: "hello world", headers: {})

status, headers, body = middleware.call("PATH_INFO" => '/metrics', "REMOTE_ADDR" => '192.168.1.1')
body = body.join

expect(status).to eq(200)
expect(headers["Content-Type"]).to eq('text/plain; charset=utf-8')
expect(body).to include('hello world')
end

it "can proxy the dedicated port even with invalid regex" do
GlobalSetting.stubs(:prometheus_trusted_ip_whitelist_regex).returns("unbalanced bracket[")
stub_request(:get, "http://localhost:#{GlobalSetting.prometheus_collector_port}/metrics").
to_return(status: 200, body: "hello world", headers: {})

Expand All @@ -26,4 +54,17 @@
expect(headers["Content-Type"]).to eq('text/plain; charset=utf-8')
expect(body).to include('hello world')
end

it "can proxy the dedicated port on trusted IP" do
GlobalSetting.stubs(:prometheus_trusted_ip_whitelist_regex).returns("(200\.0)")
stub_request(:get, "http://localhost:#{GlobalSetting.prometheus_collector_port}/metrics").
to_return(status: 200, body: "hello world", headers: {})

status, headers, body = middleware.call("PATH_INFO" => '/metrics', "REMOTE_ADDR" => '200.0.0.1')
body = body.join

expect(status).to eq(200)
expect(headers["Content-Type"]).to eq('text/plain; charset=utf-8')
expect(body).to include('hello world')
end
end

0 comments on commit 5af25b8

Please sign in to comment.