Module: Otto::Privacy::Core

Included in:
Otto
Defined in:
lib/otto/privacy/core.rb

Overview

Core privacy configuration methods included in the Otto class. Provides the public API for configuring IP privacy features.

Instance Method Summary collapse

Instance Method Details

#configure_ip_privacy(octet_precision: nil, hash_rotation: nil, geo: nil, redis: nil, correlation_secret: nil, geo_header: nil, geo_db_path: nil, geo_db_reader: nil, profile: nil, asn: nil, asn_db_path: nil, asn_db_reader: nil, anonymizer: nil, anonymizer_db_path: nil, anonymizer_db_reader: nil) ⇒ Object

Configure IP privacy settings

Privacy is enabled by default. Use this method to customize privacy behavior without disabling it entirely.

rubocop:disable Metrics/ParameterLists – a keyword-only configuration method; the options are self-documenting at the call site and grouping them into a hash would only obscure the supported settings.

Examples:

Mask 2 octets instead of 1

otto.configure_ip_privacy(octet_precision: 2)

Disable geo-location

otto.configure_ip_privacy(geo: false)

Custom hash rotation

otto.configure_ip_privacy(hash_rotation: 24.hours)

Enable stable IP correlation (same visitor across days)

otto.configure_ip_privacy(correlation_secret: ENV['IP_CORRELATION_SECRET'])

Multi-server with Redis

redis = Redis.new(url: ENV['REDIS_URL'])
otto.configure_ip_privacy(redis: redis)

Declare the observability posture for a compliance deployment

otto.configure_ip_privacy(profile: :audit)

Parameters:

  • octet_precision (Integer) (defaults to: nil)

    Number of octets to mask (1 or 2, default: 1)

  • hash_rotation (Integer) (defaults to: nil)

    Seconds between key rotation (default: 86400)

  • geo (Boolean) (defaults to: nil)

    Enable geo-location resolution (default: true). When false, geo short-circuits entirely: no headers are read and no database is loaded or consulted.

  • geo_header (String) (defaults to: nil)

    Trusted, app-configured request header checked FIRST for the country code (e.g. ‘X-Client-Country’). Accepts the HTTP or ‘HTTP_*’ CGI form; both canonicalize to the env key. Pass ‘’ to clear.

  • geo_db_path (String) (defaults to: nil)

    Path to a MaxMind-format (.mmdb) country database for the local IP->country fallback (looked up on the MASKED IP). Requires the optional ‘maxmind-db’ gem. A bad path raises at boot, not per-request. Pass ‘’ to clear.

  • geo_db_reader (#get) (defaults to: nil)

    Bring-your-own MMDB reader (any object responding to #get); overrides geo_db_path. Omitted/nil leaves any existing reader unchanged; use geo: false to stop consulting a database.

  • redis (Redis) (defaults to: nil)

    Redis connection for multi-server atomic key generation

  • correlation_secret (String) (defaults to: nil)

    A secret string that turns on IP correlation: it lets you tell whether two requests, even months apart, came from the same visitor — without your app ever seeing the real IP. (Otto masks the IP before your app runs; with a secret set it also fingerprints the full IP into req.ip_correlation_hash, which can’t be reversed to an IP without the secret.) Omit it to leave any existing secret unchanged; pass an empty string to turn the feature back off.

  • profile (Symbol) (defaults to: nil)

    Named privacy profile (:anonymous, :masked, or :audit) applied FIRST as a preset over disabled/mask_private_ips, so any other option in the same call overrides it. This declares the deployment’s observability posture in one reviewable word; see Otto::Privacy::Config::PROFILES.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/otto/privacy/core.rb', line 103

def configure_ip_privacy(octet_precision: nil, hash_rotation: nil, geo: nil, redis: nil,
                         correlation_secret: nil, geo_header: nil, geo_db_path: nil,
                         geo_db_reader: nil, profile: nil, asn: nil, asn_db_path: nil,
                         asn_db_reader: nil, anonymizer: nil, anonymizer_db_path: nil,
                         anonymizer_db_reader: nil)
  # rubocop:enable Metrics/ParameterLists
  ensure_not_frozen!
  config = @security_config.ip_privacy_config

  # Geo headers are honored only for peers matching enumerated CIDR
  # matchers, never for count-trusted hops, so a geo_header configured
  # alongside depth mode could never be consulted. Fail loud here
  # (depth-then-geo order; the trusted_proxy_depth= setter catches
  # geo-then-depth). A blank geo_header canonicalizes to nil ("clear"),
  # which stays legal under depth.
  if Otto::Privacy::Config.canonicalize_geo_header(geo_header) &&
     @security_config.trusted_proxy_depth_mode?
    raise ArgumentError, Otto::Security::Config::GEO_HEADER_DEPTH_CONFLICT_MESSAGE
  end
  knobs = { profile: profile, octet_precision: octet_precision,
            hash_rotation: hash_rotation, geo: geo,
            correlation_secret: correlation_secret, redis: redis }

  # Dry-run the assignments on a throwaway copy and validate the combined
  # result there, so a rejected knob (octet_precision: 7 after a profile
  # preset, say) raises before the live config has been touched — the
  # call is all-or-nothing, never half-applied.
  apply_privacy_knobs(config.dup, knobs).validate!
  apply_privacy_knobs(config, knobs)

  apply_geo_config(config, geo: geo, geo_header: geo_header,
                           geo_db_path: geo_db_path, geo_db_reader: geo_db_reader)
  apply_enrichment_signal(config, :asn, asn, asn_db_path, asn_db_reader)
  apply_enrichment_signal(config, :anonymizer, anonymizer, anonymizer_db_path, anonymizer_db_reader)
end

#disable_ip_privacy!Object

Disable IP privacy to access original IP addresses

IMPORTANT: By default, Otto masks public IP addresses for privacy. Private/localhost IPs (127.0.0.0/8, 10.0.0.0/8, etc.) are never masked. Only disable this if you need access to original public IPs.

When disabled: - env[‘REMOTE_ADDR’] contains the real IP address - env[‘otto.original_ip’] also contains the real IP - No PrivateFingerprint is created

Examples:

otto.disable_ip_privacy!


23
24
25
26
# File 'lib/otto/privacy/core.rb', line 23

def disable_ip_privacy!
  ensure_not_frozen!
  @security_config.ip_privacy_config.disable!
end

#enable_full_ip_privacy!void

This method returns an undefined value.

Enable full IP privacy (mask ALL IPs including private/localhost)

By default, Otto exempts private and localhost IPs from masking for better development experience. Call this method to mask ALL IPs regardless of type.

Examples:

Enable full privacy (mask all IPs)

otto = Otto.new(routes_file)
otto.enable_full_ip_privacy!
# Now 127.0.0.1 → 127.0.0.0, 192.168.1.100 → 192.168.1.0

Raises:

  • (FrozenError)

    if called after configuration is frozen



41
42
43
44
# File 'lib/otto/privacy/core.rb', line 41

def enable_full_ip_privacy!
  ensure_not_frozen!
  @security_config.ip_privacy_config.mask_private_ips = true
end