Skip to main content

Splunk S2S

Synopsis

Creates a Splunk-to-Splunk (S2S) listener that receives events from Splunk universal and heavy forwarders over Splunk's native S2S wire protocol, so an existing forwarder fleet can be repointed here without touching outputs.conf.

Schema

- id: <numeric>
name: <string>
description: <string>
type: splunks2s
tags: <string[]>
pipelines: <pipeline[]>
status: <boolean>
properties:
address: <string>
port: <numeric>
timeout: <numeric>
max_message_size: <numeric>
max_connections: <numeric>
protocol: <string>
line_breaking: <string>
ack: <boolean>
keep_internal: <boolean>
keep_control_fields: <boolean>
max_lifetime: <numeric>
timezone: <string>
tls:
status: <boolean>
cert_name: <string>
key_name: <string>
passphrase: <string>
client_ca_name: <string>
client_auth_required: <boolean>
min_tls_version: <string>
max_tls_version: <string>
access_list:
- action: <string>
ip_blocks: <string>
access_list_default: <string>
tenants:
- tenant_id: <string>
ip_blocks: <string>

Configuration

The following fields are used to define the device:

Device

FieldRequiredDefaultDescription
idY-Unique numeric identifier
nameY-Device name
descriptionN-Optional description
typeY-Must be splunks2s
tagsN-Optional tags
pipelinesN-Optional pre-processor pipelines
statusNtrueEnable/disable the device

Connection

FieldRequiredDefaultDescription
addressN"0.0.0.0"Network address to bind
portN9997TCP port to listen on. 9997 is Splunk's own receiving port, so an existing forwarder fleet needs no change to its outputs.conf
timeoutN600Idle read timeout in seconds. S2S has no heartbeat in either direction, so an idle connection is expected. An explicit 0 resolves to this default rather than disabling the timeout
max_message_sizeN67108864Maximum size in bytes of one decoded message or event payload (default 64 MB). An explicit 0 resolves to this default
max_connectionsN10000Maximum concurrently admitted forwarder connections. There is no unlimited setting: 0 or an unparseable value resolves to this default rather than removing the cap

Protocol

FieldRequiredDefaultDescription
protocolNautoFraming granted in the handshake reply. auto grants v4 (compact) framing, which every Splunk forwarder from 6.6 onward then uses regardless of what is granted. v3 withholds v4; only senders that honor the reply stay on legacy cooked framing — current Splunk forwarders ignore it and use v4 anyway, so this value is for third-party senders and diagnostics
line_breakingNautoHow a forwarder's payload is split into events. auto breaks on newlines unless the sender already parsed the payload (a heavy forwarder or INDEXED_EXTRACTIONS); always breaks every payload on newlines; never passes each payload through whole
ackNtrueHonor a forwarder's request for indexer acknowledgement (useACK in its outputs.conf). An acknowledgement means the message id's events were accepted or deterministically refused, not that they were durably persisted
keep_internalNfalsePass through events a forwarder ships about itself, destined for Splunk's _internal index and the other underscore-prefixed indexes it reserves for itself
keep_control_fieldsNfalsePass through the bookkeeping a relaying Splunk instance attaches to forwarded events: _savedHost, _savedPort, _savedSourcetype, crcSalt
max_lifetimeN0Close a connection after this many seconds even while it is active, at a frame boundary. 0 disables the ceiling
timezoneN-Per-device timestamp offset adjustment

TLS

FieldRequiredDefaultDescription
tls.statusNfalseEnable TLS. Splunk forwarders speak S2S over TLS when their outputs.conf sets clientCert
tls.cert_nameY*-TLS certificate
tls.key_nameY*-TLS private key
tls.passphraseN-Passphrase for an encrypted private key
tls.client_ca_nameN-CA bundle used to verify forwarder certificates (mTLS)
tls.client_auth_requiredN**falseRequire connecting forwarders to present a valid certificate. When false, a certificate is verified only if one is presented
tls.min_tls_versionNtls1.2Minimum accepted TLS version (tls1.0, tls1.1, tls1.2, tls1.3)
tls.max_tls_versionN-Maximum accepted TLS version. When unset, the highest mutually supported version is negotiated

* = Required when tls.status is true.
** = Requires tls.client_ca_name. If tls.client_auth_required is true and tls.client_ca_name is empty, the configuration is rejected and the device fails to start.

note

TLS material fields (cert_name, key_name, ca_name, client_ca_name) accept any of the following:

  • File name — resolved relative to the service root directory. Nested paths such as certs/prod/server.pem are supported.
  • Absolute path — honored only if it resolves inside the service root. Any path that escapes the root is refused.
  • Inline PEM content — used verbatim when the value contains -----BEGIN.
  • Environment variable${ENV_VAR}.
  • Vault reference$secret{id=...} or $secret{store=...,ref=...}.

Access Control

FieldRequiredDefaultDescription
access_listN-Ordered source-IP firewall rules. The first rule whose ip_blocks contains the connecting forwarder's address decides; an address matching no rule falls to access_list_default
access_list[].actionY*-accept (aliases allow, permit) or drop (aliases deny, reject, block)
access_list[].ip_blocksY*-CIDR, single IP, dotted-netmask, or lo-hi range. A comma-separated string or an array
access_list_defaultNautoVerdict for a source IP matching no rule: accept/allow/permit or drop/deny/reject/block. Left unset, the verdict is derived from the rules: drop if any accept rule is configured (allowlist intent), otherwise accept

* = Required for each object in access_list; the list itself is optional. With no access_list configured, every source IP is accepted.

Multi-Tenancy

FieldRequiredDefaultDescription
tenantsN-Maps a connecting forwarder's source IP to a tenant id. The first matching rule wins
tenants[].tenant_idY*-Tenant identifier attached to records from a matching source IP
tenants[].ip_blocksY*-CIDR, single IP, dotted-netmask, or lo-hi range. A comma-separated string or an array

* = Required for each object in tenants; the list itself is optional.

A matching rule's tenant id is written to _vmetric.event.tenant_id, for use downstream in Routes. Unlike the token-based tenancy on the other analytics devices, S2S carries no per-request credential to bind a tenant to — tenancy here is resolved from the forwarder's source IP alone.

Ingest Buffer

FieldRequiredDefaultDescription
max_buffer_sizeN128MBIngest buffer admission cap. Also sets this listener's eager heap reservation
note

max_buffer_size is the ingest-buffer admission cap, and is distinct from any buffer_size field above, which sizes the network read buffer. Left unset it is 128MB, which commits 384 MiB of heap per listener worker before any data arrives — and with reuse: true a device runs one worker per listener. Size strings are binary, and 32MB and 32MiB are exact synonyms.

See Listener Memory Sizing for the arithmetic, the sizing table, and what happens when the cap is exhausted.

Details

The device runs a single accept loop; there is no workers property. Every accepted connection is still served concurrently on its own goroutine, up to max_connections.

The device has no application-layer authentication. Splunk's own outputs.conf sends no credential over S2S, so access control is entirely a matter of network placement, tls.client_auth_required (mutual TLS), and the access_list source-IP firewall.

Protocol negotiation. A sender opens the connection with a capability advertisement, and the device answers once with an accept reply carrying either v4 or v3 framing depending on protocol. Real Splunk forwarders from version 6.6 onward use v4 (the compact framing) regardless of what the reply grants, so protocol: v3 only affects senders that actually honor the withheld grant. Compression needs no setting: a sender that compresses its stream (compressed = true in outputs.conf) does so from its very first message, before any negotiation could refuse it, so the device always detects and accepts it.

Line breaking. A Universal Forwarder does not run Splunk's line-breaking phase, so one payload from it routinely holds many log lines; line_breaking: auto (the default) splits such payloads on newlines. A heavy forwarder, or a forwarder reading INDEXED_EXTRACTIONS-parsed input, ships one event per payload already, and auto passes those through whole. always and never override the per-payload detection uniformly for every sender on the device.

Acknowledgement. ack: true (the default) makes the device honor a forwarder's useACK request: it echoes the message id back once the events in that message have been handed to the collector. This is "accepted or refused", not "delivered" — a record larger than the ingest buffer can ever admit is dropped and its message id is still acknowledged, because the refusal is deterministic and withholding the ack would only make the forwarder replay a block that can never be accepted. In the default configuration nothing downstream is durable either, so the ack is not what makes data lossy; ack: false refuses acknowledgement outright, which stalls a forwarder that requires it.

Internal telemetry and control fields. keep_internal and keep_control_fields both default to false. A forwarder ships its own ForwarderInfo heartbeat into an underscore-prefixed index (_internal, _audit, _introspection, and others Splunk reserves for itself) alongside the operator's data; keep_internal: false drops those events rather than forwarding Splunk's telemetry as if it were content. keep_control_fields gates a relaying Splunk instance's own bookkeeping fields (_savedHost, _savedPort, _savedSourcetype, crcSalt). _savedHost in particular is shaped like event attribution but is not: it records the address the relay received the event from, so on a two-hop path it names the middle hop rather than the host the event originated on.

Connection ceilings. max_connections has no unlimited setting — 0 or an unparseable value resolves to the 10000 default. Each open connection holds buffers that grow toward max_message_size for its lifetime, so an uncapped port would let a single unauthenticated peer turn a few hundred idle connections into tens of gigabytes of memory without ever sending data. max_lifetime closes a still-active connection at a frame boundary after the configured number of seconds, which is what lets a receiver placed behind a name or load balancer eventually pick up forwarders that connected once and never disconnected; 0 disables it.

Emitted event. Each decoded event is forwarded as a JSON envelope: _raw (the event text), host, source, sourcetype, and index when the sender supplied them, time (the sender's own event time, as a Unix epoch, with a decimal fraction when a subsecond value is present) when available, followed by the event's indexed fields and any other fields the sender attached — excluding S2S's own protocol bookkeeping keys, and excluding the control fields above unless keep_control_fields is true. Arrival time is not part of the envelope; the record's own ingestion timestamp is set separately from the sender's time.

timezone sets a per-device timestamp offset fallback, exposed downstream as _vmetric.device.tz_offset; it does not affect a decoded event's own time, since every event carries its own epoch already.

Examples

Basic

Creating a minimal S2S listener on Splunk's default receiving port...

- id: 1
name: basic_splunk_s2s
type: splunks2s
properties:
port: 9997

Mutual TLS

Requiring forwarder certificates on top of TLS encryption...

- id: 2
name: mtls_splunk_s2s
type: splunks2s
properties:
port: 9997
tls:
status: true
cert_name: "s2s.crt"
key_name: "s2s.key"
client_ca_name: "forwarders-ca.pem"
client_auth_required: true

Source-IP Access Control

Accepting forwarders only from two known subnets and dropping everything else...

- id: 3
name: filtered_splunk_s2s
type: splunks2s
properties:
port: 9997
access_list:
- action: accept
ip_blocks: "10.0.0.0/8,192.168.0.0/16"
note

An accept rule with no matching drop rule still results in a drop default for every other source, because the presence of an accept rule signals allowlist intent.

Multi-Tenancy

Attributing forwarders from two customer networks to separate tenants...

- id: 4
name: tenanted_splunk_s2s
type: splunks2s
properties:
port: 9997
tenants:
- tenant_id: "acme"
ip_blocks: "10.10.0.0/16"
- tenant_id: "globex"
ip_blocks: "10.20.0.0/16"

Connection Tuning

Raising the connection and message-size ceilings for a larger forwarder fleet, and capping how long any one connection is held...

- id: 5
name: highvol_splunk_s2s
type: splunks2s
properties:
port: 9997
max_connections: 25000
max_message_size: 134217728
max_lifetime: 3600