Module: Verikloak::BFF::ProxyTrust

Defined in:
lib/verikloak/bff/proxy_trust.rb

Overview

Determines whether the selected peer (via XFF/REMOTE_ADDR) is a trusted proxy.

Class Method Summary collapse

Class Method Details

.extract_peer_ip(env, strategy) ⇒ String?

Select the peer IP from X-Forwarded-For according to strategy or fall back to REMOTE_ADDR.

Parameters:

  • env (Hash)

    Rack environment

  • strategy (Symbol, String)

    :rightmost (default) or :leftmost

Returns:

  • (String, nil)

    Selected peer IP, or nil if not determinable



37
38
39
40
41
42
43
44
45
46
# File 'lib/verikloak/bff/proxy_trust.rb', line 37

def extract_peer_ip(env, strategy)
  mode = strategy.to_s.to_sym
  xff = env['HTTP_X_FORWARDED_FOR']
  if xff && !xff.strip.empty?
    parts = xff.split(',').map(&:strip)
    ip = mode == :leftmost ? parts.first : parts.last
    return ip
  end
  env['REMOTE_ADDR']
end

.ip_or_nil(str) ⇒ IPAddr?

Parse string to IPAddr or nil on failure. IPv4-mapped IPv6 addresses (e.g. ::ffff:127.0.0.1) are normalised to native IPv4 so that CIDR checks against plain IPv4 ranges succeed.

Parameters:

  • str (String)

Returns:

  • (IPAddr, nil)


64
65
66
67
68
69
70
# File 'lib/verikloak/bff/proxy_trust.rb', line 64

def ip_or_nil(str)
  addr = IPAddr.new(str)
  addr = addr.native if addr.respond_to?(:native)
  addr
rescue StandardError
  nil
end

.resolve_peer(env, preference, strategy) ⇒ String?

Resolve the peer value based on preference and strategy.

Only :xff_only selects the peer from X-Forwarded-For first. Every other value — the :remote_then_xff default, nil, or an unrecognized/typo'd preference — falls back to the safe REMOTE_ADDR-first path so a misconfiguration can never switch the trust decision onto the client-controlled X-Forwarded-For header.

Parameters:

  • env (Hash)
  • preference (Symbol)
  • strategy (Symbol)

Returns:

  • (String, nil)


118
119
120
121
122
123
124
125
126
# File 'lib/verikloak/bff/proxy_trust.rb', line 118

def resolve_peer(env, preference, strategy)
  return extract_peer_ip(env, strategy) if preference.to_s.to_sym == :xff_only

  remote = (env['REMOTE_ADDR'] || '').to_s.strip
  return remote unless remote.empty?

  # Fall back to X-Forwarded-For when REMOTE_ADDR is empty
  extract_peer_ip(env, strategy)
end

.rule_trusts?(rule, remote, remote_ip, env) ⇒ Boolean

Check whether a single rule trusts the selected remote. A rule that raises (e.g. an invalid CIDR string or a failing Proc) is treated as non-matching so that one bad rule cannot disable the rest of the allowlist.

Parameters:

  • rule (String, Regexp, Proc)
  • remote (String)
  • remote_ip (IPAddr, nil)
  • env (Hash)

Returns:

  • (Boolean)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/verikloak/bff/proxy_trust.rb', line 82

def rule_trusts?(rule, remote, remote_ip, env)
  case rule
  when String
    if rule.include?('/') # CIDR
      cidr = IPAddr.new(rule)
      remote_ip ? cidr.include?(remote_ip) : false
    else
      remote == rule
    end
  when Regexp
    remote =~ rule
  when Proc
    rule.call(remote, env)
  else
    false
  end
rescue StandardError => e
  # Isolate a single failing rule without disabling the rest of the
  # allowlist, but surface it under $DEBUG so a broken Proc/CIDR rule is
  # observable instead of silently denying trust.
  warn("[verikloak-bff] trusted_proxies rule raised and was skipped: #{e.class}: #{e.message}") if $DEBUG
  false
end

.selected_peer(env, preference, strategy) ⇒ String?

Return the selected peer IP according to preference and strategy.

Parameters:

  • env (Hash)
  • preference (Symbol)

    :remote_then_xff or :xff_only

  • strategy (Symbol)

    :rightmost or :leftmost

Returns:

  • (String, nil)


54
55
56
# File 'lib/verikloak/bff/proxy_trust.rb', line 54

def selected_peer(env, preference, strategy)
  resolve_peer(env, preference, strategy)
end

.trusted?(env, trusted, strategy = :rightmost, preference: :remote_then_xff) ⇒ Boolean

Determine if the immediate peer (based on REMOTE_ADDR / X-Forwarded-For) is trusted. strategy :rightmost (typical when proxy appends client IP to the right)

Examples:

CIDR + Regex allowlist

ProxyTrust.trusted?(env, ["10.0.0.0/8", /^192\.168\./], :rightmost)

Parameters:

  • env (Hash)

    Rack environment

  • trusted (Array<String, Regexp, Proc>, nil)

    Allowlist of proxy peers.

    • String: exact IP (e.g. "127.0.0.1") or CIDR (e.g. "10.0.0.0/8")
    • Regexp: matched against the selected peer IP
    • Proc: called as ->(ip, env) { ... } and returns truthy when trusted
  • strategy (Symbol, String) (defaults to: :rightmost)

    :rightmost (default) or :leftmost for XFF parsing

  • preference (Symbol) (defaults to: :remote_then_xff)

    :remote_then_xff (default) prefers REMOTE_ADDR, :xff_only selects the peer from X-Forwarded-For first

Returns:

  • (Boolean)

    true if the selected peer is trusted



27
28
29
30
# File 'lib/verikloak/bff/proxy_trust.rb', line 27

def trusted?(env, trusted, strategy = :rightmost, preference: :remote_then_xff)
  remote = resolve_peer(env, preference, strategy)
  trusted_remote?(remote, trusted, env)
end

.trusted_remote?(remote, trusted, env) ⇒ Boolean

Determine whether a remote peer appears in the trusted list.

Parameters:

  • remote (String, nil)
  • trusted (Array<String, Regexp, Proc>, nil)
  • env (Hash)

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
# File 'lib/verikloak/bff/proxy_trust.rb', line 134

def trusted_remote?(remote, trusted, env)
  return false if trusted.nil? || trusted.empty?
  return false unless remote

  remote_ip = ip_or_nil(remote)
  trusted.any? { |rule| rule_trusts?(rule, remote, remote_ip, env) }
rescue StandardError
  false
end