Module: Surfguard
- Extended by:
- Surfguard
- Included in:
- Surfguard
- Defined in:
- lib/surfguard.rb,
lib/surfguard/version.rb
Overview
Resolve and classify addresses for callers that pin the selected address at connection time. Surfguard deliberately does not perform HTTP requests.
Defined Under Namespace
Classes: Unresolvable, Violation
Constant Summary collapse
- UNRESOLVABLE_MESSAGE =
"Host could not be resolved"- BLOCKED_MESSAGE =
"Refusing blocked address"- MALFORMED_MESSAGE =
"Refusing malformed address"- MAX_HOST_BYTES =
255- MAX_ADDRESSES =
256- POLICIES =
%i[default iana_special_use].freeze
- IANA_ALLOCATED_IPV6_UNICAST =
iana-generator:begin IANA_ALLOCATED_IPV6_UNICAST Generated from IANA IPv6 Global Unicast Status=ALLOCATED rows. Source provenance is checked in under script/iana.
%w[ 2001::/23 2001:200::/23 2001:400::/23 2001:600::/23 2001:800::/22 2001:c00::/23 2001:e00::/23 2001:1200::/23 2001:1400::/22 2001:1800::/23 2001:1a00::/23 2001:1c00::/22 2001:2000::/19 2001:4000::/23 2001:4200::/23 2001:4400::/23 2001:4600::/23 2001:4800::/23 2001:4a00::/23 2001:4c00::/23 2001:5000::/20 2001:8000::/19 2001:a000::/20 2001:b000::/20 2002::/16 2003::/18 2400::/12 2410::/12 2600::/12 2610::/23 2620::/23 2630::/12 2800::/12 2a00::/12 2a10::/12 2c00::/12 ].map { |cidr| IPAddr.new(cidr).freeze }.freeze
- DISALLOWED_IPV4 =
iana-generator:end IANA_ALLOCATED_IPV6_UNICAST
%w[ 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 168.63.129.16/32 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 192.88.99.0/24 192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 224.0.0.0/4 240.0.0.0/4 ].map { |cidr| IPAddr.new(cidr).freeze }.freeze
- DISALLOWED_IPV6 =
%w[ ::/128 100::/64 100:0:0:1::/64 2001::/32 2001:2::/48 2001:db8::/32 2002::/16 3fff::/20 5f00::/16 fec0::/10 ff00::/8 ].map { |cidr| IPAddr.new(cidr).freeze }.freeze
- IANA_SPECIAL_USE_IPV4 =
iana-generator:begin IANA_SPECIAL_USE_IPV4 Every prefix in the checked-in IANA IPv4 special-purpose snapshot.
%w[ 0.0.0.0/8 0.0.0.0/32 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.0.0/29 192.0.0.8/32 192.0.0.9/32 192.0.0.10/32 192.0.0.170/32 192.0.0.171/32 192.0.2.0/24 192.31.196.0/24 192.52.193.0/24 192.88.99.0/24 192.88.99.2/32 192.168.0.0/16 192.175.48.0/24 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 240.0.0.0/4 255.255.255.255/32 ].map { |cidr| IPAddr.new(cidr).freeze }.freeze
- IANA_SPECIAL_USE_IPV6 =
iana-generator:begin IANA_SPECIAL_USE_IPV6 Every prefix in the checked-in IANA IPv6 special-purpose snapshot.
%w[ ::1/128 ::/128 ::ffff:0:0/96 64:ff9b::/96 64:ff9b:1::/48 100::/64 100:0:0:1::/64 2001::/23 2001::/32 2001:1::1/128 2001:1::2/128 2001:1::3/128 2001:2::/48 2001:3::/32 2001:4:112::/48 2001:10::/28 2001:20::/28 2001:30::/28 2001:db8::/32 2002::/16 2620:4f:8000::/48 3fff::/20 5f00::/16 fc00::/7 fe80::/10 ].map { |cidr| IPAddr.new(cidr).freeze }.freeze
- IETF_PROTOCOL_ASSIGNMENTS =
iana-generator:end IANA_SPECIAL_USE_IPV6
IPAddr.new("2001::/23").freeze
- GLOBALLY_REACHABLE_IETF_ASSIGNMENTS =
%w[ 2001:3::/32 2001:4:112::/48 ].map { |cidr| IPAddr.new(cidr).freeze }.freeze
- NAT64_WELL_KNOWN =
IPAddr.new("64:ff9b::/96").freeze
- NAT64_LOCAL_USE =
IPAddr.new("64:ff9b:1::/48").freeze
- IPV4_TRANSLATABLE =
IPAddr.new("::ffff:0:0:0/96").freeze
- IPV4_COMPATIBLE =
IPAddr.new("::/96").freeze
- POLICY_RANGES =
{ default: { allocated_ipv6: IANA_ALLOCATED_IPV6_UNICAST, disallowed_ipv4: DISALLOWED_IPV4, disallowed_ipv6: DISALLOWED_IPV6 }.freeze, iana_special_use: { ipv4: IANA_SPECIAL_USE_IPV4, ipv6: IANA_SPECIAL_USE_IPV6 }.freeze }.freeze
- VERSION =
"0.2.0"
Instance Method Summary collapse
-
#blocked_address?(ip, policy: :default) ⇒ Boolean
Classify one endpoint.
- #enforce_public_ip(url, policy: :default) ⇒ Object
-
#resolvable_public_ip?(url, policy: :default) ⇒ Boolean
True only when the URL resolves and every answer is admitted.
-
#resolve_public_ip(url, policy: :default) ⇒ Object
Preserve resolver order here; unlike the plural API this method does not reorder address families.
-
#resolve_public_ips(host, policy: :default) ⇒ Object
Return every admitted address, with IPv4 before IPv6 and resolver order retained within each family.
Instance Method Details
#blocked_address?(ip, policy: :default) ⇒ Boolean
Classify one endpoint. Malformed inputs and networks fail closed.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/surfguard.rb', line 145 def blocked_address?(ip, policy: :default) validate_policy!(policy) ipaddr = normalize_ip(ip) return true if policy == :iana_special_use && iana_special_use?(ipaddr) return true if ipaddr.ipv4_mapped? || IPV4_COMPATIBLE.include?(ipaddr) return disallowed_ipv4?(ipaddr) if ipaddr.ipv4? return true if NAT64_LOCAL_USE.include?(ipaddr) if NAT64_WELL_KNOWN.include?(ipaddr) || IPV4_TRANSLATABLE.include?(ipaddr) return disallowed_ipv4?((ipaddr), policy: policy) end disallowed_ipv6?(ipaddr) rescue InvalidInput true end |
#enforce_public_ip(url, policy: :default) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/surfguard.rb', line 120 def enforce_public_ip(url, policy: :default) validate_policy!(policy) addresses = resolve(host_of(normalize_url(url))) if addresses.any? { |ip| blocked_address?(ip, policy: policy) } raise Violation, BLOCKED_MESSAGE, cause: nil end nil rescue InvalidInput raise Violation, MALFORMED_MESSAGE, cause: nil end |
#resolvable_public_ip?(url, policy: :default) ⇒ Boolean
True only when the URL resolves and every answer is admitted.
112 113 114 115 116 117 118 |
# File 'lib/surfguard.rb', line 112 def resolvable_public_ip?(url, policy: :default) validate_policy!(policy) addresses = resolve(host_of(normalize_url(url))) addresses.none? { |ip| blocked_address?(ip, policy: policy) } rescue InvalidInput, Unresolvable false end |
#resolve_public_ip(url, policy: :default) ⇒ Object
Preserve resolver order here; unlike the plural API this method does not reorder address families.
134 135 136 137 138 139 140 141 142 |
# File 'lib/surfguard.rb', line 134 def resolve_public_ip(url, policy: :default) validate_policy!(policy) addresses = resolve(host_of(normalize_url(url))) return nil if addresses.any? { |ip| blocked_address?(ip, policy: policy) } addresses.first.to_s.freeze rescue InvalidInput nil end |
#resolve_public_ips(host, policy: :default) ⇒ Object
Return every admitted address, with IPv4 before IPv6 and resolver order retained within each family. Malformed direct input returns [].
101 102 103 104 105 106 107 108 109 |
# File 'lib/surfguard.rb', line 101 def resolve_public_ips(host, policy: :default) validate_policy!(policy) addresses = resolve(normalize_host(host)) public_addresses = addresses.reject { |ip| blocked_address?(ip, policy: policy) } ipv4, ipv6 = public_addresses.partition(&:ipv4?) (ipv4 + ipv6).map { |ip| ip.to_s.freeze }.freeze rescue InvalidInput [].freeze end |