> ## Documentation Index
> Fetch the complete documentation index at: https://docs.safesquid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Access restrictions

The **Access** section defines the primary security perimeter: who may connect, whether they authenticate, which proxy features they may use, and which User-Groups and bypass flags attach to the connection.

<Note>
  Configure authentication mechanisms in [Authentication](/configuration/start_here/authentication). LDAP directory setup is in [Integrate LDAP](/configuration/application_setup/integrate_ldap). Content allow/deny is in [Access Profiles](/configuration/restriction_policies/access_profiles).
</Note>

## Core mechanics

<Warning>
  **Access restrictions is not cumulative — it is first-match-wins, per list.** Access Profiles evaluates every enabled entry top to bottom, and a later entry can add to or override an earlier one. Access restrictions does not: within a list, the first enabled entry whose fields all match wins the pass, and every entry after it in that same list is skipped for that pass. A broad entry with no restriction (for example, a catch-all Allow meant for "everyone who authenticates") sitting above a narrower entry makes the narrow entry unreachable — it never fires, never logs as matched, and nothing in the Web UI flags the conflict. The only way to catch it is to read the list top to bottom, in order, before relying on it.
</Warning>

### Default policy and list order

With **Default Access Policy = Deny** (recommended):

1. Walk the **Allow** list top-down; first enabled row whose gates pass wins for that pass.
2. If an Allow row matched, walk the **Deny** list; a Deny match revokes access.
3. If no Allow row matched, access is denied.

With Default Allow, Deny is walked first, then Allow. Within one list, **first match wins** — lower rows are not evaluated for that pass.

```mermaid theme={null}
flowchart TB
conn[New connection] --> policy{Default Access Policy?}
policy -->|Allow| inverted["List order inverts: walk Deny list first, then Allow - same first-match-wins mechanics per list"]
policy -->|Deny - recommended| walkAllow[Walk Allow list top-down]
walkAllow --> gate["Row gates: Profiles, Interface, IP, LDAP Profiles - any failure skips to next row"]
gate --> firstMatch{First enabled row where gates pass?}
firstMatch -->|Match found - lower rows not evaluated| allowMatched[Allow row matched]
firstMatch -->|No match| denied[Access denied]
allowMatched --> walkDeny[Walk Deny list]
walkDeny --> denyMatch{Deny row matches?}
denyMatch -->|Yes| revoke[Access revoked]
denyMatch -->|No| grant["Apply access rights, groups, bypass, concurrency"]
```

### Inner gate order

For each enabled row: **Profiles** → **Interface** → **IP** → **LDAP Profiles**. Any failure skips to the next row.

### Client IP and X-Forwarded-For

When `X-Forwarded-For` is present, it replaces the TCP client IP for `ip` matching.

<Warning>
  **Spoofing risk:** a client sending `X-Forwarded-For: 127.0.0.1` can match localhost IP rows and receive CONFIG (Web UI) access. Expose the proxy only on trusted networks.
</Warning>

### LDAP Profiles need identity first

An entry that sets only **LDAP Profiles** cannot match on the first, identity-less request — SafeSquid has no directory group list to test yet — and by itself will not trigger a login prompt. LDAP Profiles only has anything to test once identity is already known, from a prior Kerberos/SSO login, a completed System authentication challenge, or an IP-to-user map.

### Two-pass authentication

* **First pass (no credentials)** — If gates pass and `pamauth=TRUE` or User name is set, the row matches and issues a 407/401 challenge. If `pamauth=FALSE` and User name is blank, the row matches immediately — unauthenticated access.
* **Second pass (credentials present)** — SafeSquid populates the connection's LDAP distinguished name with group strings (commas stored as spaces). LDAP Profiles rows can match; PAM validation runs during the access check.

- **If the first list produces no match at all** — not even a credential challenge — the second list is skipped entirely and Default Access Policy decides the outcome directly.

### User-Groups assembly on match

On match, SafeSquid rebuilds the connection's User-Groups in this order: the entry's own **Add to User-Groups** values, then every LDAP group the signed-in user belongs to, then the user's own directory identity. **This assembled set — not just the entry's Add to User-Groups field — is what every later section's User-Groups matching actually tests** (Access Profiles, DLP, and similar).

### Kerberos / SSO (`ntlm_authentication`)

UI label **Kerberos / SSO**; internal field `ntlm_authentication`. When TRUE, SafeSquid offers **Negotiate** (GSSAPI/Kerberos). When FALSE, Negotiate is not advertised. Requires `HTTP.keytab` for SSO to succeed. See [Authentication](/configuration/start_here/authentication).

### DLP bypass

Requires both the **Allow bypassing** access right (`BYPASS`) and the **DLP** bypass checkbox. Either alone leaves DLP active.

### If nothing matches either list

When neither list produces a match, the connection is denied outright: it receives a block page and the connection is closed. If a match still needed credentials that never arrived, the client instead receives an authentication challenge and can retry.

## Recommended entry ordering

1. IP/interface no-auth rows (specific addresses first)
2. Profile-bypass rows (e.g. BYPASS AUTHENTICATION) — before auth rows
3. LDAP Profiles — before PAM regex (group beats username pattern)
4. PAM regex and entry-credential rows
5. Profile-gated showcase rows (each has a `profiles` constraint)
6. PAM catch-all — last Allow row
7. Deny exceptions — paired with an Allow range to demonstrate revoke

Open **Configure → Application Setup → Access restrictions → Allow list**. Row fields are Enabled,
Comment, Trace Entry, Profiles or IP Address, **PAM authentication** (labeled differently than
System authentication below — same underlying gate), Access, and Add to User-Groups.

<Frame caption="Access restrictions — Allow list rows">
  <img src="https://mintcdn.com/safe-squid-labs-12a0916f/T2tf5IJBpEmDK3ub/images/configuration/access_restrictions-allow_list.webp?fit=max&auto=format&n=T2tf5IJBpEmDK3ub&q=85&s=8abd53de55a815af77945b215e3332b5" alt="SafeSquid console showing Access restrictions Allow list rows with the bypass-authentication and tunnelled-UI-access entries, the Edit Policies icon on the first row circled" width="1440" height="700" data-path="images/configuration/access_restrictions-allow_list.webp" />
</Frame>

## Global fields

* **Default Access Policy** — Deny recommended. Controls default when no row matches and which list is walked first.
* **Kerberos / SSO** — Enables Negotiate when keytab is present.

## Rule fields

All configured criteria on a row must match. Blank means any.

* **IP Address** — Single IP, comma list, or hyphen range. CIDR not supported. Uses XFF when present.
* **Interface** — Listen socket IP:port of the accept.
* **System authentication** — Cache → PAM → LDAP bind; User name is a regex filter; Password ignored.
* **User name / Password** — With System auth off: exact case-sensitive match. Non-empty User name still triggers challenge on first pass.
* **LDAP Profiles** — Exact match against the connection's LDAP distinguished name; write DNs with spaces not commas.
* **Access** — CONFIG, PROXY, HTTP, TRANSPARENT, CONNECT, BYPASS — independent rights.
* **MAX Concurrent Connections** — Keyed `username@client-ip`; 0 = no cap.

- **Profiles (`add_profiles`)** — limits the entry to connections that already carry a listed Access Profile. Blank = ignored.
- **Negation** — `LDAP Profiles` and `Profiles` both accept `!value` to negate a match.
- **Add to User-Groups (`add_user_groups`)** — group names added to the connection's User-Groups on match. LDAP groups and the user's directory identity are added automatically alongside these, regardless of this field — see "User-Groups assembly on match" above.
- **Bypass (`bypass`)** — modules skipped on match: Header filtering, Redirect, Cookie filtering, Rewrite, External, Forward, Text analyzer/Keyword filtering, DNSBL, Limits, Antivirus, ICAP, DLP.
- **Interface username / Interface password (`ifusername` / `ifpassword`)** — optional second Web UI login, required together; only relevant when this entry also grants the **Web interface** Access right.
- **Comment (`comment`)** — written to native security logs when this entry matches.
- **Trace Entry (`profile_tracing`)** — logs each User-Group this entry adds; turn off after testing.
- **MAX Concurrent Connections — the uncapped case** — `0` removes the connection ceiling, but connections are still counted against the entry, not skipped from counting.

## Examples

<Tip>
  ### The bug this file exists to prevent

  **Config:** Allow list, in this order — (1) a general Allow entry with no IP, no Interface, no LDAP Profiles, no User name, matching any authenticated user; (2) an Accountants entry with LDAP Profiles set to the Accountants directory group, granting `Add to User-Groups: ACCOUNTANTS`.

  **Result:** every accountant matches entry (1) first, because it is unconditional and sits above entry (2). Entry (2) is unreachable — never logged as matched, never adds the `ACCOUNTANTS` User-Group — and nothing anywhere flags this. Reorder so the Accountants entry sits above the general entry.
</Tip>

<Tip>
  ### Localhost Web UI and proxy

  **Config:** IP `127.0.0.1`, Access CONFIG,PROXY,HTTP,TRANSPARENT,CONNECT, pamauth off, no username.

  **Result:** Immediate match; User-Group LOCALHOST. Beware XFF spoofing on untrusted networks.
</Tip>

<Tip>
  ### Hyphen IP range without auth

  **Config:** IP `203.0.113.0-203.0.113.255` (TEST-NET-3), pamauth off.

  **Result:** Any address in range matches first pass; no challenge.
</Tip>

<Tip>
  ### LDAP group after login

  **Config:** ldap\_profiles with Sales group DN, pamauth off, above PAM catch-all.

  **Result:** First pass: ldap gate fails (empty dn), challenge from PAM row. Second pass: group match grants LDAP\_SALES; password not rechecked unless pamauth=TRUE.
</Tip>

<Tip>
  ### DLP bypass

  **Config:** Access BYPASS + bypass DLP checkbox + profiles constraint.

  **Result:** DLP skipped. Missing either condition — DLP still runs.
</Tip>

<Tip>
  ### Deny exception

  **Config:** Allow range 203.0.113.0–255 + Deny 203.0.113.50; policy Deny.

  **Result:** Allow matches, then Deny revokes. Deny row rights/groups not applied.
</Tip>

## How to verify

1. Test from a known client IP without spoofed XFF (unless testing that path).
2. **Reports → Detailed logs** — username, IP, access filter.
3. Enable **Trace Entry** on one row; check native logs.
4. SECURITY log family for authentication outcomes.

5) If Default Access Policy is Deny and an Allow-listed client is unexpectedly blocked, check the Deny list for a second-pass override before suspecting the Allow entry.
