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) ⇒ 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
# 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)
  # rubocop:enable Metrics/ParameterLists
  ensure_not_frozen!
  config = @security_config.ip_privacy_config
  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)
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