Product documentation

ALLOD | SWG

A self-hosted secure web gateway - inline firewall, DLP, CASB and ZTNA - running entirely in infrastructure you control.

Overview

ALLOD SWG inspects every outbound request from enrolled endpoints before it leaves your network. Traffic is routed through a proxy node that evaluates firewall, data-loss prevention, and cloud-app control rules in real time. No traffic ever transits Allod Solutions infrastructure.

Key properties:

  • All inspection runs on hardware you control - your cloud or bare metal.
  • Traffic is not retained unless a rule explicitly triggers logging.
  • The event log is encrypted per-device using HKDF-derived keys.
  • Allod Solutions has no access to your traffic, policy, or event log - by architecture, not by promise.

Architecture

ComponentRole
ControllerHosts the admin UI (:8080) and controller API (:8081). Stores firewall and DLP config. In single-node mode, runs the proxy as well.
Proxy nodeStateless CONNECT proxy (:8443). Polls the controller for config every 30 s and applies updates atomically - no connection drops during policy changes.
AgentLightweight daemon on each endpoint (macOS, Windows, Linux). Configures the OS proxy to route all traffic through the proxy.
ConnectorOptional daemon on private networks. Dials out to the controller so agents can reach internal resources without inbound firewall rules.

Data flow

  1. The agent configures the OS proxy to route all traffic through the proxy.
  2. The proxy decrypts TLS (when inspection is enabled), evaluates firewall, DLP, and CASB rules, then forwards or blocks.
  3. The proxy re-encrypts with a leaf certificate signed by the SWG CA (installed on each device by the agent or via MDM).
  4. Rule-triggered events are written encrypted to the event log. Non-matching traffic is not retained.
QUIC / HTTP-3 Full inspection (quic_mode: inspect) does not achieve real QUIC inspection on any platform today - Chrome and Firefox both reject private CAs for QUIC's own certificate validation regardless of OS, and the agent's network-level QUIC redirect (to a local relay that terminates HTTP/3 with a per-SNI certificate) only exists on Linux to begin with. Because of this, inspect is hidden from the admin UI and is always treated as unsupported: it's unconditionally substituted with the configured fallback (downgrade by default) for every agent, regardless of platform. quic_mode: downgrade itself - reject outbound UDP/443 so the browser retries over TCP, where ordinary TLS inspection applies - now has parity across all three platforms: nftables/iptables on Linux, the Windows Firewall on Windows, and pf on macOS.

Quick start

Single node (SQLite)

Download the allodswg binary and create a minimal config:

# server.yaml
server:
  controller_api: ":8081"
  admin_ui: ":8080"

proxy:
  listen: ":8443"

ca:
  organization: "My Organisation"
  country: "SE"

data_dir: "./data"
firewall_config: "./firewall.yaml"
dlp_config: "./dlp.yaml"
allodswg -config server.yaml

The admin UI is at http://localhost:8080. Create an API key under Admin → API Keys, distribute the agent to endpoints with agent.yaml pointing at the controller URL.

CA distribution A self-signed CA is generated in data/ca.pem on first start. Push it to endpoints as a trusted root (via MDM or group policy) before enabling TLS inspection.

Scale out (multi-node)

Migrate the event store to PostgreSQL and deploy additional stateless proxy nodes:

# server.yaml addition
event_store:
  driver: "postgres"
  dsn: "postgres://swg:pass@db:5432/swg?sslmode=require"
# proxy.yaml (additional node)
controller_url: "https://controller.example.com:8081"
api_key: "sk_myrs_..."
listen: ":8443"
ca_path: "/etc/allodswg/ca"
config_poll_interval: "30s"

server.yaml reference

Passed to the controller via allodswg -config /path/to/server.yaml.

server:
  controller_api: ":8081"   # Used by proxy nodes and DAM
  admin_ui: ":8080"         # Web UI and REST API
  metrics: ":9090"          # Prometheus metrics (optional)

proxy:
  listen: ":8443"
  upstream_dns: ""         # Override DNS resolver; empty = system
  idle_timeout: "5m"
  require_agent: false     # Reject non-agent connections
  quic_mode: "bypass"       # bypass | downgrade ("inspect" is accepted but always treated as unsupported - see the QUIC / HTTP-3 callout below)

ca:
  path: "./data/ca.pem"
  regenerate: false
  algorithm: "ecdsa_p256"   # ecdsa_p256 | rsa_2048 | rsa_4096
  ca_validity_days: 365
  leaf_validity_days: 7
  organization: "My Organisation"
  country: "SE"

logging:
  level: "info"             # debug | info | warn | error
  format: "text"            # text | json
  access_log: true

data_dir: "./data"
firewall_config: "./firewall.yaml"
dlp_config: "./dlp.yaml"

event_store:
  driver: "sqlite"          # sqlite | postgres
  dsn: ""

dlp:
  s3:
    enabled: false
    endpoint: ""
    bucket: "allod-dlp"
    access_key: "${DLP_S3_ACCESS_KEY}"
    secret_key: "${DLP_S3_SECRET_KEY}"
    region: "us-east-1"
    retention_days: 90

threat_feed:
  enabled: false
  update_interval: "1h"

# Optional local LLM
# ai:
#   enabled: false
#   model: "llama3"
#   ollama_url: "http://localhost:11434"

Environment variables are expanded: ${VAR} in any string value is replaced at startup.

firewall.yaml reference

Path set by firewall_config: in server.yaml.

default_policy: "allow"          # allow | block
default_tls_inspection: false

# ZTNA: CIDRs pushed to agents for transparent interception
access_routes: []
#  - "10.0.0.0/8"

rules:
  - action: block
    type: category
    value: malware

  - action: allow
    type: domain
    value: "*.internal.example.com"
    process: curl

  - action: block
    type: region
    value: "RU,BY"

  - action: allow
    type: category
    value: cloud_storage
    tls_inspection: enforce
FieldValuesNotes
actionallow / block / proxyproxy routes through a connector for private resources.
typedomain / category / regionWhat the rule matches on.
valuestringDomain pattern, category name, or comma-separated ISO codes.
processstringOptional - match only traffic from this process name.
tls_inspectionoff / enforceOverride the default TLS inspection setting for this rule.

dlp.yaml reference

Path set by dlp_config: in server.yaml.

mode: "standard"       # off | headers | standard | full | selective
action: "log"          # log | alert | block
upload_threshold: 0   # Flag uploads over this size in bytes; 0 = disabled
min_size: 0

# post_detection_window: "15m"
# selective_categories:
#   - cloud_storage

keyword_lists:
  - name: "PII"
    keywords: ["personal number", "ssn", "passport"]

rules:
  - action: block
    direction: upload
    keyword_list: PII

  - action: alert
    direction: upload
    category: cloud_storage
    type: archive

  - action: block
    direction: upload
    regex: '\b4[0-9]{12}(?:[0-9]{3})?\b'

agent.yaml reference

Placed alongside the agent binary. Default paths: /etc/allodswg/agent.yaml (Linux/macOS), C:\ProgramData\allodswg\agent.yaml (Windows).

controller:
  url: "https://proxy.company.com:8081"
  api_key: "sk_myrs_..."    # RoleAgent key from Admin → API Keys

agent:
  heartbeat_interval: "30s"
  startup_timeout: "30s"
  retry_interval: "10s"
  max_retries: 5
  auto_start: true
  tunnel_ports: [9003]   # local ports reachable via the agent's reverse tunnel - see ZTNA & Connectors

Distribute alongside the agent binary via MDM. The agent auto-installs the CA certificate on supported platforms.

proxy.yaml reference

Passed to stateless proxy nodes via allodswg-proxy -config /path/to/proxy.yaml. Not needed in single-node deployments.

controller_url: "https://controller.example.com:8081"
api_key: "sk_myrs_..."          # RoleProxy key
listen: ":8443"
ca_path: "/etc/allodswg/ca"
data_dir: "/var/lib/allodswg-proxy"
upstream_dns: ""
idle_timeout: "5m"
access_log: true
config_poll_interval: "30s"

Firewall

Rules are evaluated top-to-bottom. The first match wins; unmatched traffic follows default_policy. Domain categories are managed in Admin → Categories and visible in the CASB view.

Process-aware filtering: When the agent reports the originating process, rules can target specific processes - e.g. allow curl to reach internal services while blocking a browser on the same domain.

Source IP scoping: any firewall or DLP rule can also be scoped to a source IP or CIDR range, alongside the existing device, user, and group scoping - restrict a rule to traffic from a specific subnet (an office network, a jump host) without needing a separate device or ZTNA-based condition to express it.

Fingerprint matching: a ja4 rule matches the TLS client fingerprint and requires the JA4 license feature. Without that license, a mercury rule matches the same class of traffic by application name instead - a lower-fidelity but unlicensed fallback for the same use case. ja3 is a separate, independently combinable fingerprint condition derived from the same ClientHello - both a JA3 and a JA4 condition can scope the same rule at once, since some clients are only well-fingerprinted by one or the other.

DGA & typosquat detection: both run on-device against every resolved domain, with no feed to poll and nothing to keep in sync - they work offline and catch domains no threat feed has seen yet. DGA detection flags algorithmically generated hostnames, the kind malware families like Qakbot use for command-and-control. Typosquat detection matches candidate domains against your approved app catalog, catching lookalikes (e.g. rn swapped for m, 0 for o) that a static block-list would miss because they were never registered before.

Email authenticity checks: inbound mail (IMAP and POP3) is checked against your identity provider's user directory: a From: display name that matches a real employee while the address doesn't belong to them, an address on one of your own domains that matches no provisioned user or a disabled account, and a sending domain your own organization's SPF record doesn't authorize - all classic business-email-compromise signals. A self-learned check catches the same kind of impersonation for outside senders too - a display name (e.g. a courier or vendor brand) seen repeatedly from one domain over time, then suddenly appearing from a different one, without needing a maintained brand catalog. Alongside identity, messages are checked for SPF/DKIM/DMARC alignment, a sending domain registered only days ago, senders or links already flagged by threat intelligence, and suspicious attachments or links - password-protected archives, a file whose real type doesn't match its extension, a macro-enabled document, a script file (even hidden inside a zip), links to a bare IP address or a shortened link, and a link serving a full HTML page directly from public cloud storage (Google Cloud Storage, S3, Azure Blob, and similar) - a known technique for evading domain-reputation checks, since these hosts are otherwise legitimate. A message whose plain-text part is trivial boilerplate while the real content only exists in its HTML part - a way of evading plain-text keyword inspection - is flagged the same way. "Your own domains" aren't a separate setting to maintain - they're derived automatically from whatever domains are already present in your synced SCIM directory. Unlike the phishing-link checks above, these only ever raise an alert, never block delivery: several of these signals have a higher false-positive ceiling than link matching - shared mailboxes, service accounts, distribution lists, and external partners who happen to share a name with an employee can all trigger it, and mail shouldn't be silently dropped on that basis.

IMAP flagging instead of a desktop notification: for IMAP specifically, a message caught by any of the checks above - or the malware-hash match covered under DLP - is flagged in place with the standard $Phishing IMAP keyword (some mail clients, e.g. Thunderbird, already render it specially), so the signal sits right on the email instead of a desktop popup you'd have to correlate back yourself.

Rule order More specific rules must appear before broader ones. A block-all rule at the end will not prevent earlier allow rules from matching.

Data Loss Prevention

DLP inspects outbound HTTP/HTTPS request bodies for sensitive content entirely in-process on the proxy node - body data is never sent to Allod Solutions.

ModeWhat is inspected
offDisabled.
headersRequest/response headers only. Lowest overhead.
standardHeaders plus content-type and upload metadata.
fullFull body, clipboard, and file-path metadata.
selectiveFull inspection for selective_categories, headers-only elsewhere.

Post-detection escalation: A DLP hit can temporarily escalate the device to full inspection for a configurable window (e.g. post_detection_window: "15m"), providing increased visibility after a potential leak.

S3 body samples: When dlp_s3 is configured, rule-matching request bodies are stored encrypted in your S3-compatible bucket. You control retention and access - Allod Solutions has none.

Type verification: file-type rules check magic bytes, not just the extension or declared content-type - a renamed invoice.exe saved as invoice.pdf is still detected as an executable. Archive uploads (.zip, .7z) are inspected recursively, so keyword and regex rules also match against each entry's decoded content, not just the archive's own filename.

Chat & email: the same rule set covers file uploads inside Slack, Discord, and Microsoft Teams - fetching surrounding message context with the session's own bearer token - and SMTP attachments extracted from intercepted mail, not only browser uploads. Links inside the same intercepted mail are separately checked for phishing, and the sender identity for impersonation of one of your own people or domains - see Firewall → Email phishing-link detection and Firewall → Email authenticity checks.

Personal-account leak prevention: a common blind spot when a work and personal email account are both configured in the same mail client (Outlook, Apple Mail, Thunderbird) is dragging a file - or forwarding a document - from the work inbox into the personal one. A DLP rule can require the outbound MAIL FROM address to belong to one of your own SCIM domains before an attachment is allowed through. Unlike a plain external-recipient check, this catches the leak even when the recipient is an internal colleague, since it looks at who the mail is from, not just where it's going. A blocked message gets a rejection naming the attachment and the account it was sent from, so the sender understands exactly what to fix instead of seeing a generic error.

Cross-Protocol Attachment Lineage: a file's SHA-256 is tagged the moment it's seen as an inbound email attachment (SMTP, IMAP, or POP3), and that tag follows the file forward - a DLP rule can block or alert on an outbound send of that same file, or restrict it to specific sender domains, entirely independent of what the outbound content-inspection rules see. This closes the gap where a file arrives externally, gets saved to disk, and leaves again through a channel that never re-triggers a content match.

Provenance: uploads are correlated with clipboard history, so a DLP event can show which application and window the pasted content came from. A file's SHA-256 is also tracked across events, so a blocked upload that matches a file downloaded days earlier is linked back to that download automatically.

VDI/RDP boundary enforcement: a published RDP/Citrix/VDI desktop is meant to be a trust boundary, but the client software's own clipboard and drive redirection quietly cross it - syncing remote clipboard content and dragged-out files to local disk without ever generating a network request the proxy could inspect. Both are covered: a copy out of a remote session is checked before it can be pasted anywhere locally, using only a hash of the text, never the content itself, and a block clears the local clipboard. A file dragged out of a session is inspected exactly like an upload the moment it lands, and a block deletes it. Ordinary RDP/Citrix use isn't flagged by default - this is policy scoped to whichever clients and users you choose, the same rule engine as everywhere else.

Local transform tracking: zipping, encrypting, or otherwise transforming a sensitive file on disk before uploading it doesn't bypass DLP either - the agent tracks a file's sensitivity tag through common transform tools (archivers, GPG, cp, rsync), so an upload of the derived file still carries the original DLP verdict, even if it's zipped or encrypted first.

Live lineage recheck: long-lived connections - a sync client like OneDrive or Dropbox can hold one connection open for hours across many transfers - can outlive the point-in-time check made when the connection opened. For these, the proxy can also ask the agent live whether a previously flagged file is currently open in the uploading process. This is also the only DLP signal available for a certificate-pinned application (see TLS & CA), where content inspection is bypassed entirely to keep the app working - it can't see that traffic either, but can still confirm a flagged file was open in that process and raise an alert. This only catches files already flagged by an earlier check, not entirely new sensitive content no other check has seen.

Removable media: writes to USB and other removable drives are evaluated against the same DLP rule engine as network uploads - scope a rule to removable_write under Direction to write policy specific to physical media. A block action deletes the file from the drive; it's still uploaded to the controller as evidence first, from the original local file if one exists, or from the removable copy itself just before it's removed.

Print spooler DLP: printing never touches the network stack, so it's invisible to every channel above. The agent holds each new print job the moment it appears - before it's read at all - so a block verdict here is real prevention: the job is cancelled before it reaches paper, not just logged after the fact. The page content is evaluated against the same DLP rules as uploads, so no separate print-specific policy authoring is needed. Anything not blocked is released with, at most, a brief pause.

Print job provenance: printed jobs are also linked back to file provenance - if a print job's title matches a file already flagged sensitive elsewhere (for example, downloaded from a tracked SharePoint site), that verdict carries over. It can only make the outcome stricter, never weaker, than what the printed content matches on its own.

Beyond HTTP - SSH and Git: scp, sftp, rsync, ftp, and plain git over SSH don't speak HTTP, so the proxy has nothing to inspect on those channels. The agent instead watches these processes' file reads directly and evaluates the same rules that govern browser uploads against what's being read - one rule set covers both paths, with a matching event recorded either way. This path is alert-only: the read has typically already completed by the time it's observed, and there's no HTTP response to hold back. git push is the exception - the agent checks the destination remote against your CASB tenant restrictions and scans the files about to be pushed against DLP rules before the push proceeds, killing the git process outright when either check comes back block-severity.

Origin-aware rules: rules can also match on where a file originally came from (origin_domain, origin_local), not just where it's currently headed. This check runs after the real-time allow/block decision, so it never blocks retroactively - a match can only raise a logged event to an alert, never the reverse.

SharePoint & OneDrive site awareness: downloads from SharePoint or OneDrive are attributed to their source site automatically, whether the file came through a browser download or was synced silently in the background by the OneDrive client - both feed the same rule field, so one rule covers either case.

Google Shared Drive awareness: the same idea, for Google Drive - a file synced in the background by Google Drive for Desktop is attributed back to the Shared Drive it came from, so a DLP rule can give a specific Shared Drive a standing sensitivity tag the same way it can for a SharePoint site.

SMB/CIFS network-share provenance: a file copied from a mapped network drive never touches the proxy, so it's invisible to the mechanisms above - but the agent still records which network share it came from, on Linux, macOS, and Windows alike. A file copied off a finance file share still carries that origin if it's later uploaded to personal cloud storage, even when the upload itself looks completely ordinary.

Sensitive-context protection: screenshot content and screen-sharing are inspected only while a device is accessing a destination explicitly marked sensitive - never as general surveillance. A destination becomes sensitive via any of three sources: a firewall rule flagged sensitive: true, an active step-up SSO/JIT grant, or - when Allod DAM integration is configured - an app DAM's RoPA data classifies Confidential or Restricted. While that context is active, screenshots are OCR'd locally, and only the extracted text - never the image itself - is scanned through the normal DLP pipeline. Screen-sharing is detected without any OS-level screen-capture hooks.

Screenshot context: alongside the OCR'd text, a screenshot event carries the foreground window's process and title (or the active browser tab's URL) at the moment of capture, plus the domain the device was browsing. This is forensic-timeline context, not an additional inspection surface: it's still gated on the same sensitive-context requirement as the rest of this section, and best-effort (empty on a headless session).

Email security

ALLOD SWG intercepts SMTP (outbound), IMAP, and POP3 (inbound) mail with the same TLS MITM it uses for HTTP/HTTPS, so email runs through the same policy engine as everything else - not a bolt-on integration or a separate product. The individual checks live under Firewall and DLP since they reuse those engines directly; this section is a map of everything email-related in one place.

CheckWhat it does
Phishing-link detectionTyposquat/lookalike and cloaked-link checks on every link in a message body. Blocks on POP3 retrieval, alerts on outbound SMTP, flags on IMAP.
Email authenticity checksBusiness-email-compromise signals against your IdP directory, self-learned brand-impersonation detection, SPF/DKIM/DMARC alignment, sending-domain age, threat-intel matches, and suspicious attachments/links/content. Alert-only.
IMAP flaggingA hit on any of the above (or a DLP malware-hash match) sets the standard $Phishing IMAP keyword in place, instead of a desktop notification.
Attachment DLPSMTP attachments run through the same DLP rule engine as browser uploads - keyword/regex, file-type verification, malware-hash matching, recursive archive inspection.
Personal-account leak preventionA DLP rule can require the outbound MAIL FROM address to belong to one of your own SCIM domains before an attachment is allowed through - catches a work file crossing into a personal account configured in the same mail client.
Cross-Protocol Attachment LineageA file tagged as an inbound email attachment carries that tag forward - a DLP rule can block or restrict its later outbound send, independent of what content inspection alone would catch.

CASB

Every connection is classified against the built-in app catalog. Unseen apps are automatically reported to ALLOD DAM for triage when SWG integration is configured.

  • Tenant restrictions: Allow corporate tenants of a SaaS app while blocking personal accounts using domain-level rules.
  • Upload controls: Combine DLP category rules to block uploads to unsanctioned cloud storage while allowing approved services.
  • Shadow IT visibility: The admin dashboard shows all observed apps across the fleet, ranked by usage.
  • ZTNA resource visibility: private resources reached over a ZTNA connector tunnel appear in the same dashboard's Corporate IT tab, not Shadow IT - they're admin-sanctioned by virtue of the connector registration itself - with a "Local" badge and a fixed "Internal" category, whether or not Allod DAM integration is configured. One unified view of application usage, public SaaS and private internal tools together, rather than a second dashboard for internal resources. In a multi-proxy deployment, a resource reached via cross-proxy connector fallback is still counted but without its resource name attributed.
  • Shared account detection: when three or more distinct people are observed using the same non-SSO login, the account is flagged as shared - a common indicator of an unmanaged team password. This isn't limited to catalogued apps: login-form submissions are inspected on any site, so the same detection covers shadow IT with no catalog entry at all. Sightings that match a device's own IdP-verified identity are excluded, so a person's own legitimate SSO-linked account never counts against it.
  • Internal-tool discovery: shadow-IT visibility above only covers apps in the public SaaS catalog. Hosts under your admin-configured internal DNS zone with no catalog match - a self-hosted git server, an internal wiki - are separately surfaced for triage as Allod DAM asset candidates. Matching is DNS-zone-based rather than IP/CIDR-based: private ranges like 10.0.0.0/8 are exactly what home routers commonly default to, and a CIDR match would risk sweeping employees' home devices into the queue.

Device compliance

Compliance checks are not a fixed checklist - you supply the script. Upload an arbitrary shell script per operating system under Admin → Compliance. The controller signs each script with an Ed25519 key before distributing it to agents; the signed payload is scoped to the target OS, so a script written for Linux cannot be replayed against a Windows or macOS agent.

Agents run the signed script locally and report pass/fail back to the controller. Setting require_compliance: true gates network access at the point a device connects - a non-compliant device is blocked before it can reach the proxy at all. Compliance state is also available as a scoping condition on firewall and DLP rules, so a rule can apply only to devices currently failing their check.

Why scripts, not presets A fixed checklist (disk encryption on/off, OS version ≥ X) can't express organisation-specific requirements. A script can check anything the OS exposes - a registry key, a running process, a config file's contents - without waiting on a product release to add the check you need.

ZTNA & Connectors

Private resource access does not require a VPN. Add CIDRs to access_routes in firewall.yaml - the controller pushes these to enrolled agents, which route matching traffic through the proxy and then through a connector tunnel.

Connectors run inside your private network and dial out to the controller. No inbound firewall rules are needed on the private network side. Deploy multiple connectors per network for redundancy.

Reverse tunnels: the same connector protocol also runs in the other direction - an internal server can reach a port on an enrolled agent's machine (e.g. attaching a debugger to a developer's laptop) without either side opening an inbound rule. The agent must explicitly expose the port first via tunnel_ports in agent.yaml; a dial to any other port is refused. In a single-node deployment, where the proxy and connector Hub run in the same process, this reaches the agent's virtual IP directly - no separate connector daemon needs to be deployed just to bridge the Hub back to itself.

Split-tunnel by default Only traffic matching the CIDRs configured in access_routes takes the extra connector hop to reach a private network. General internet traffic still goes through the proxy for inspection like everything else - it just connects straight to its destination from there, with no connector involved.

Step-up SSO & JIT access

A firewall rule - domain or ZTNA access rule alike - can require a fresh interactive login or a time-boxed grant before traffic is allowed. This is enforced at the same proxy CONNECT chokepoint shared by SaaS domains and ZTNA resources: when no active grant exists, the proxy serves a challenge page instead of forwarding the connection.

authBehaviour
ssoRequires a fresh interactive IdP login (step-up re-authentication) before the connection proceeds.
jitRequires a time-boxed access grant. jit_approval: self auto-grants on request; jit_approval: approval holds the request for an approver.

Grant lifetime defaults to 8 hours and is configurable per rule via auth_ttl (a Go duration, e.g. "4h"). Approval-required requests notify the approvers listed in jit_approvers through the alerting webhook dispatcher's jit_access_requested event and, when configured, through interactive Slack and Microsoft Teams approval buttons or a PagerDuty page - see Integrations. Admins can also approve or deny from Admin → Access Requests.

Self-service pre-provisioning: a public self-service page lets a user request a JIT grant before opening a non-HTTP client - a database tool, an SSH client - that can't render the proxy's browser-based challenge page itself. Reach it at the short, memorable https://access address - not a real DNS name, a proxy-side construct every enrolled device already resolves and trusts, since it already routes through and trusts this proxy. https://login works the same way, pointing at the login form - most useful for LDAP-only customers (see Identity → LDAP as a login backend) who have no cloud IdP bookmark to start from otherwise.

Best-effort approval integrations Slack, Teams, and PagerDuty notifications are each independently optional. A down chat integration never blocks issuing or denying a grant - the admin UI path always works.

Identity (SCIM / LDAP / OIDC)

SWG syncs users and groups from your identity directory to enable user-aware policy, identity-verified device tokens, and per-user event attribution. Two sync modes are available.

Push (IdP → SWG)

Your IdP pushes changes to SWG's SCIM 2.0 endpoint as they happen. Enable under Admin → Identity → SCIM push and paste the endpoint URL into your IdP's SCIM connector. Supported: Okta, Microsoft Entra ID, Authentik, and any RFC-7644-compliant IdP.

Pull (SWG polls IdP)

Use pull mode when the IdP cannot reach your SWG instance (e.g. behind NAT or a firewall). Configure under Admin → Identity → Pull from IdP.

ProviderRequired credentials
scim - Generic SCIM 2.0SCIM v2 base URL, Bearer token
authentik - Authentik REST APIAuthentik base URL, API token
entra - Microsoft Entra IDTenant ID, Client ID, Client secret
Required Graph permissions: User.Read.All, Group.Read.All, GroupMember.Read.All
ldap - LDAP / Active DirectorySee below

LDAP / Active Directory

Required fields: LDAP URL (ldap://host:389 or ldaps://host:636), Bind DN, Bind password, User base DN, Group base DN.

Attribute defaults (override under Advanced / Attribute mapping):

FieldDefaultActive Directory override
User filter(objectClass=inetOrgPerson)(objectClass=person)
Group filter(objectClass=groupOfNames)(objectClass=group)
Username attruidsAMAccountName
Email attrmailmail
Display name attrcncn
Member attrmember (DN-style)member
posixGroup schemas Set Member attr to memberUID. SWG resolves both DN-style (Active Directory, groupOfNames) and UID-style (posixGroup) membership references automatically.
Active Directory disabled accounts The userAccountControl ACCOUNTDISABLE bit is read during sync. Disabled AD accounts are imported as inactive and cannot be linked to devices or used in policy.

LDAP as a login backend

The same LDAP connection configured above can also serve as a direct login backend, not just directory sync - useful for customers with no cloud OIDC/SAML identity provider to point at. Enable Backend: ldap under Admin → Settings → Login to switch admin/approver console login, step-up SSO/JIT grant issuance, device-identity binding, and allodswg-ctl CLI login over to a real LDAP bind-as-user against the directory. Everything downstream of login - session issuance, grant and JIT-request creation, device verification - is unchanged; only how a credential is checked is swapped in. Password-based login at /admin/login remains available regardless of backend.

OIDC (admin SSO)

Enable SSO for admin UI login under Admin → Settings → OIDC. The configuration is stored in data/oidc.yaml and takes effect immediately without a restart. Password-based login remains available as a fallback at /admin/login.

# data/oidc.yaml
enabled: true
issuer_url: "https://idp.example.com/application/o/allodswg"
client_id: "allodswg"
client_secret: "your-client-secret"
redirect_url: "https://swg.example.com/auth/callback"
admin_group: "swg-admins"   # optional - restrict login to members of this group

Event log & retention

Events are written only when a rule triggers. Regular allowed traffic is not stored. Each event is encrypted with an HKDF-derived key tied to the originating device - a breach of one device's events does not expose others.

  • Article 15 export: Admin → Users → Export data - produces a JSON file for subject access requests.
  • Article 17 erasure: Admin → Users → Erase - deletes all events for a user without vendor involvement.
  • Retention window: Configured in Admin → Settings. Events older than the window are deleted automatically.

Integrations

All integrations are configured under Admin → Settings → Integrations and take effect immediately without a restart. Credentials are stored in server.yaml under the integrations key.

CrowdStrike LogScale

Streams proxy events to a LogScale (formerly Humio) repository in real time. Each event includes process name, destination, user, verdict, and DLP action.

integrations:
  logscale:
    enabled: true
    url: "https://cloud.humio.com"
    token: "your-ingest-token"

Zeek

Publishes HTTP events to a Zeek Broker WebSocket endpoint as Allod::http_request records. Useful for feeding SWG visibility into an existing Zeek/SIEM pipeline.

integrations:
  zeek:
    enabled: true
    broker_addr: "zeek.internal:9997"
    broker_tls: false
    broker_topic: "allod/http"

CrowdStrike Falcon EDR

Pushes blocked indicators (domains and IPs) to Falcon as custom IOCs with action=prevent. Triggered by any heuristic block: JA4DB, SSLBL, DGA, typosquatting, or threat feed.

integrations:
  crowdstrike_falcon:
    enabled: true
    base_url: "https://api.crowdstrike.com"
    client_id: "abc123..."
    client_secret: "your-secret"

MISP

Polls a MISP instance for threat indicators and blocks matching traffic at the proxy. Supported attribute types: domain, hostname, ip-dst, ip-src, url, ja3-fingerprint-md5, jarm-fingerprint, ja4-fingerprint, user-agent, uri, filename, filename|sha256, sha256, port.

integrations:
  misp:
    url: "https://misp.internal"
    key: "your-api-key"
    interval: "30m"
    to_ids_only: true
Indicator typeBlock behaviour
domain / hostname / urlBlocked at category resolution, before firewall rules
ip-dst / ip-srcBlocked at IP resolution, same priority as URLhaus / Feodo
ja3-fingerprint-md5Blocked at CONNECT, alongside SSLBL
ja4-fingerprintBlocked at CONNECT, alongside JA4DB (no license required)
jarm-fingerprintBlocked on cached JARM result - first connection to a new IP is not checked
portBlocked at CONNECT time before any HTTP is processed
user-agentBlocked per HTTP request (H1 and H2); returns 403
uriBlocked per HTTP request path+query (H1 and H2); returns 403
filename / filename|sha256 / filename|md5 / filename|sha1 / filename|sha512Blocked on upload (after body peek) and download (before body is streamed); returns 403
sha256Blocked on upload after body peek (first 32 KB); not checked on downloads

Indicators are cached to disk (data_dir/mispfeed_cache.json) and loaded at startup so blocking is active before the first poll completes. Attributes with tags containing "phishing" are classified as phishing; all others as malware. Set to_ids_only: true (the default) to import only attributes flagged as IDS indicators.

TLS verification MISP connections use the system certificate store. For self-signed certificates, add your CA to the system trust store rather than disabling verification.

Reporting back: the integration isn't read-only. From any event's detail view in the admin UI, an admin can push that event to the configured MISP instance as a new event - so an indicator your own fleet discovered enriches the shared instance instead of staying siloed in SWG's log.

Allod DAM

Connects SWG to an Allod DAM instance for CASB app classification. Authentication uses the shared license key - no separate API key is required.

integrations:
  allod_dam:
    url: "https://dam.example.com"

Alerting

Security events - DLP hits, firewall blocks, threat-feed matches, compliance failures, JIT access requests, and more - can be pushed to one or more outgoing webhooks in real time, independently of the event log sinks below. Configure webhooks under Admin → Settings → Integrations → Alerting; each has its own URL, format, enabled flag, and event-type filter.

FormatPayload
genericRaw JSON event object - the default, for custom receivers.
slackSlack incoming-webhook Block Kit message.
teamsMicrosoft Teams Adaptive Card via a Workflows-based incoming webhook. Read-only - no action buttons, unlike the interactive JIT approval cards. Teams' legacy Office 365 Connector (MessageCard) webhooks were retired by Microsoft in May 2026, so a Workflows webhook is required.

Each webhook enables only the event types it cares about (dlp_alert, firewall_block, jit_access_requested, compliance_fail, and more) rather than receiving the full firehose. When include_admin_links is enabled, events carry a deep link straight to the relevant record in the admin UI.

Event log sinks

Security events (firewall blocks, DLP hits, threat detections) can be exported to external systems via three independent sinks configured under logging.event_log:

SinkProtocolUse case
FileJSON lines, rotatedLocal archiving, log shippers
BeatsElastic Beats TCPLogstash / Elasticsearch
SyslogLocal /dev/logSystem journal, rsyslog, Splunk UF

TLS & CA

ALLOD SWG generates a self-signed CA on first start. This CA signs short-lived leaf certificates (default 7 days) for each inspected domain. The CA certificate must be trusted on all endpoints.

AlgorithmNotes
ecdsa_p256Default. Fast signing, small certs, broadly supported.
rsa_2048Wider compatibility with older TLS stacks.
rsa_4096Higher security margin at the cost of signing speed.
CA rotation Setting regenerate: true invalidates all existing leaf certificates immediately. Push the new CA to endpoints before re-enabling TLS inspection.

Certificate-pinning bypass: an application that pins its own certificate rejects ALLOD SWG's leaf certificate outright. ALLOD SWG detects that rejection and automatically stops attempting inspection for that host for 24 hours, so the app keeps working instead of breaking - a narrow, per-host exception rather than a blanket policy change. DLP's live lineage recheck still runs against those connections as a compensating, alert-only signal.

API reference

Every admin action is available via REST. See the interactive reference at /api-docs → SWG.

Authenticate with an API key in the Authorization header:

Authorization: Bearer sk_myrs_...
RoleAccess
RoleSuperAdminFull read/write. The only admin-facing role today - there is no separate lower-privilege admin tier.
RoleProxyInternal proxy-to-controller endpoints (/internal/*) - for proxy nodes only.
RoleEnrollmentEnrollment, heartbeat, config fetch, and the agent/connector WebSocket and tunnel endpoints - for endpoint agents and connectors.

Allod DAM does not use a separate API-key role - it authenticates with the shared license key (see Integrations → Allod DAM).


Product documentation

ALLOD | DAM

Automated vendor risk assessment and shadow IT governance - from discovery to compliant Art. 30 register, without leaving your infrastructure.

Overview

ALLOD DAM (Data Asset Management) closes two gaps most organisations have: they don't know which SaaS tools their people are using, and the ones they do know about haven't been properly assessed.

When SWG observes a new application on the fleet, DAM automatically queues it for triage. Automated probing, a GLEIF entity lookup, and a local LLM do the groundwork - so your team reviews conclusions, not raw documents. All processing stays inside your infrastructure.

Each system carries its own record of the humans and paperwork behind it: technical, billing, legal, and security contacts (encrypted at rest with a pre-shared key), alongside attached documents such as the DPIA, an exit plan, or a risk analysis. It's the same register used for reviews and the Art. 30 export, not a separate spreadsheet kept in sync by hand.

Architecture

ComponentRole
DAM serverAdmin UI, REST API, and background worker coordination. Stores all vendor data in a local SQLite or PostgreSQL database.
Probe checkerRuns on a configurable schedule. Checks TLS configuration, hosting geography, SPF, MX, privacy policy, and DPA for each vendor.
LLM enricherCalls a local Ollama instance to extract retention periods, subprocessor lists, and breach notification commitments from fetched privacy policy / DPA text. No data leaves your infrastructure.
GLEIF clientMatches vendors to the Global LEI Index via the Allod license server. Tracks ultimate parent and jurisdiction changes.
Sanctions monitorFetches the EU consolidated sanctions list daily and checks every vendor and its ultimate parent. Raises a notification on any match.
SchedulerGenerates review tasks based on configured cycles - annual, contract renewal, DPIA, etc. Moves systems to pending offboarding when no traffic is seen for zero_traffic_days.
SWG syncPolls the SWG controller for shadow IT observations and imports new apps into the DAM triage queue.

Quick start

# dam.yaml
server:
  listen: ":9090"
  admin_password: "changeme"

store:
  dsn: "./data/dam.db"    # SQLite; use a postgres:// DSN for PostgreSQL

license:
  key: "lic_..."          # Enables probing, GLEIF lookups, and sanctions

data_dir: "./data"

swg:
  url: "https://swg.example.com:8081"
  poll_interval: "15m"
alloddam -config dam.yaml

The admin UI is at http://localhost:9090. With a license key and SWG URL configured, DAM will start importing shadow IT observations and queuing vendors for assessment automatically.

License key required Network probing, GLEIF entity verification, and sanctions monitoring require a valid license key. The SWG integration and manual vendor management work without one.

dam.yaml reference

server:
  listen: ":9090"
  admin_password: "changeme"
  contact_psk: "${CONTACT_PSK}"   # Hex-encoded key for PII encryption (32+ bytes)

store:
  dsn: "./data/dam.db"

license:
  key: "lic_..."
  scan_interval: "24h"

probe:
  interval: "24h"
  proxy_url: ""             # Optional outbound proxy for probe requests

enricher:
  ollama_url: "http://localhost:11434"
  model: "qwen2.5:7b"       # Any Ollama-served model
  interval: "2h"

scheduler:
  interval: "1h"            # How often to check for due reviews

swg:
  url: "https://swg.example.com:8081"
  poll_interval: "15m"
  min_device_pct: 0        # Only triage apps seen by at least N% of fleet; 0 = off

usage:
  zero_traffic_days: 60    # Move to pending_offboarding after N days without traffic

data_dir: "./data"

SWG integration

When swg.url is configured, DAM polls the SWG controller every poll_interval for observed SaaS applications across the fleet. New apps not already in DAM's vendor inventory are automatically added to the triage queue.

Auth between DAM and SWG uses a per-customer peer token issued by the license server. DAM caches this token locally and refreshes it on each license check-in.

Usage-aware review scheduling

DAM tracks the last time each vendor was observed sending traffic. Systems that go quiet for zero_traffic_days (default 60) are automatically moved to pending offboarding status, flagging them for review before the contract or DPA is renewed.

Similar systems

Both triage queues - new shadow IT waiting for a decision, and systems flagged for offboarding - show a sidebar of similar existing systems. For a new discovery, that's usually the fastest way to spot "this is just another team's Trello" instead of registering a duplicate. For an offboarding candidate, it's the same list framed the other way: consolidate onto an active alternative, or confirm there isn't one and offboard outright.

The min_device_pct filter lets you ignore shadow IT that has only appeared on a small fraction of the fleet - useful in large organisations where one-off experiments shouldn't create triage work automatically.

Vendor assessment

The probe checker runs automatically for all vendors that have a catalog entry. Each probe run collects:

ProbeWhat it checks
TLSCertificate chain, protocol version, cipher suite, expiry.
Hosting geographyIP geolocation, ASN, and cloud provider for each resolved host.
SPF / MXEmail infrastructure - indicates where mail data may be processed.
Privacy policyFetches and stores the vendor's privacy policy page for LLM extraction and change tracking.
DPAFetches and stores the Data Processing Agreement if publicly available.
ToSFetches and stores the Terms of Service page for change tracking.

Probe results are stored in the vendor record and surfaced in the admin UI review workflow. The LLM enricher processes privacy policy and DPA text to extract structured data.

OSCAL scanning

Licensed instances additionally probe a vendor's domain for a publicly served OSCAL System Security Plan or component definition, checked at the well-known locations vendors commonly publish them (/.well-known/oscal.json and similar). When found, it's parsed and attached to the vendor record as machine-readable evidence of the vendor's own security control implementation - no manual document collection required.

Policy drift detection

DAM re-fetches privacy policies, DPAs, and Terms of Service on the same schedule as the probe checker and compares each new version against the stored baseline using a content hash.

What triggers a drift notification

  • Privacy policy content changes (new subprocessors, revised retention periods, altered transfer mechanisms)
  • DPA content changes (updated legal basis, changed breach notification commitments)
  • ToS content changes (new data use clauses, jurisdiction changes)

When a change is detected, a notification is raised in the admin UI linking directly to the affected vendor. The previous and current versions are both stored so your DPO can review what changed before deciding whether a re-assessment is needed.

Drift vs. probe failure

A fetch error (HTTP 4xx/5xx, DNS failure) is recorded as a probe failure, not a drift event. Drift is only raised when content is successfully fetched and differs from the stored baseline.

LLM re-enrichment When a privacy policy or DPA drift event is raised, the LLM enricher is queued to re-process the document automatically so structured fields stay current.

GLEIF & Ownership monitoring

Every vendor is matched against the Global LEI (Legal Entity Identifier) Index via the Allod license server. A successful match yields:

  • Verified legal entity name and registration jurisdiction
  • Full ownership chain to the ultimate parent entity
  • Country of the ultimate parent - relevant for data transfer assessments

Ownership change alerts

DAM polls for ownership changes every 5 minutes. When an acquisition moves a vendor's ultimate parent to a new jurisdiction, a notification is raised immediately - before your next scheduled review cycle. This gives you time to reassess data transfer impact before contracts renew.

Sanctions monitoring

DAM fetches the EU consolidated sanctions list from the Allod license server daily and checks every vendor name and its ultimate parent against the list.

A match triggers an immediate notification in the admin UI. The same vendor is not re-notified on subsequent checks unless it appears under a new sanctions entry.

Coverage Monitoring is based on name matching against the EU consolidated list. It complements, but does not replace, formal sanctions screening by a qualified compliance team.

GDPR workflows & Art. 30 register

DAM's review system is built around the GDPR Article 30 Records of Processing Activities register - not bolted on after the fact.

Review cycles

Each vendor can be assigned a review cycle:

  • Annual: Standard review triggered once per year.
  • Contract renewal: Review triggered when the contract renewal date approaches.
  • DPIA: Data Protection Impact Assessment - for high-risk processing activities.

The scheduler checks for due reviews every hour and creates review tasks automatically. Tasks include structured fields: processing purpose, legal basis, data categories, retention period, transfer mechanism, and owner assignment.

Art. 30 register export

The complete Art. 30 register is exportable from Admin → Register as a structured JSON or CSV file, ready for submission to your DPO or supervisory authority.

Vendor lifecycle management

DAM tracks vendors across their full lifecycle - not just during the initial review. Each stage can trigger structured tasks:

  • Onboarding: New vendor discovered via SWG or added manually - review task created, DPA and privacy policy fetched, owner assigned.
  • Active monitoring: Continuous probe runs, ownership polling, sanctions checks, and policy drift detection run in the background.
  • Renewal: Contract renewal date triggers a re-assessment task with pre-filled fields from the previous review.
  • Re-assessment: Any drift event (policy change, ownership change, sanctions match) can trigger an out-of-cycle review task.
  • Offboarding: Marking a vendor as inactive closes open review tasks and archives the Art. 30 record with a decommission timestamp.

Owner management

Each system has an assigned owner. When an owner's account is disabled or removed from the identity provider (via SCIM), DAM raises a notification for every system they own so accountability gaps are caught immediately.

LLM enrichment

The enricher calls a locally hosted Ollama model to extract structured data from privacy policy and DPA text fetched by the probe checker. Extracted fields include:

  • Data retention periods
  • Subprocessor list and their jurisdictions
  • Breach notification commitment (timeframe)
  • Data transfer mechanisms (SCCs, adequacy decision, etc.)

No text is sent to Allod Solutions or any external AI provider. The Ollama instance runs on hardware you control. The default model is qwen2.5:7b; any Ollama-compatible model can be used.

The enricher is triggered automatically after each probe run and also runs on a configurable interval (enricher.interval, default 2 h) to process any newly fetched text.

Identity (SCIM / LDAP)

DAM syncs users and groups from your identity directory to enable owner assignment, accountability gap detection, and group-based access control for the admin UI.

Push (IdP → DAM)

Configure under Admin → Identity → SCIM push. Supported: Okta, Microsoft Entra ID, Authentik, and any RFC-7644-compliant SCIM 2.0 IdP.

Pull (DAM polls IdP)

Configure under Admin → Identity → Pull from IdP. DAM supports the same four providers as SWG: scim, authentik, entra, and ldap. Credentials, LDAP field defaults, and Active Directory attribute overrides are identical - see the SWG identity section for the full reference.

Owner lifecycle

Each system in DAM has an assigned owner. When a sync run detects that an owner's account has been deprovisioned or deactivated in the directory, DAM raises a notification for every system they own - so accountability gaps are caught immediately, without waiting for a manual review cycle.

API reference

The DAM REST API exposes all admin actions - vendor management, review workflows, SCIM config, and probe triggers. See the interactive reference at /api-docs → DAM.

Authenticate with the admin password via HTTP Basic, or configure API keys in Admin → API Keys.

Also see: ALLOD SWG API reference →