Files
logwisp/config/logwisp.toml
T

457 lines
24 KiB
TOML

###############################################################################
### LogWisp Configuration
### Default location: ~/.config/logwisp/logwisp.toml
### Precedence: CLI flags > Environment > File > Defaults
###
### Commented values are the built-in defaults unless marked "example".
### Uncommenting a default is a no-op.
###
### NOTE: http/tcp ingest sources and http_client/tcp_client sinks are not
### available in this version. TLS is available on every network source and
### sink (the [...tls] blocks below), the [...auth] blocks authenticate peers by
### certificate (mtls) or password (scram; credentials via `lw auth`), and the
### listeners' [...acl] blocks admit peers by address, or by the client an L4
### proxy's PROXY header names, and limit each client. Unknown keys anywhere in
### this file or in any plugin config are rejected at startup.
###
### Environment overrides use the LOGWISP_ prefix
### (LOGWISP_QUIET, LOGWISP_LOGGING_LEVEL, ...). Bare names are ignored.
### LOGWISP_CONFIG_FILE and LOGWISP_CONFIG_DIR select the file. Paths such as
### pipelines.0.name cannot be set from the CLI or environment at all.
###############################################################################
###############################################################################
### Global Settings
###############################################################################
quiet = false # No log of lw's own; pipeline output still flows
status_reporter = true # Periodic status logging (30s, DEBUG level); false without a file
auto_reload = false # Watch file; read at startup (signals always reread it)
color = "auto" # auto|always|never: level names on console sinks; auto on a terminal, NO_COLOR unset
###############################################################################
### Logging (LogWisp's internal operational logging)
###############################################################################
[logging]
output = "stderr" # file|stdout|stderr|split|all|none; stdout is the console sink's
level = "info" # debug|info|warn|error; warn without a file
# format = "txt" # raw|txt|json
# sanitization = "" # raw|json|txt|shell (empty = logger default)
# [logging.file] # Used when output is "file" or "all"
# directory = "./log"
# name = "logwisp"
# max_size_mb = 100
# max_total_size_mb = 1000
# retention_hours = 168.0 # 7 days
## Validated but NOT applied: the console destination comes from `output` above.
# [logging.console]
# target = "stdout" # stdout|stderr|split
###############################################################################
### Pipelines
### Each pipeline: plugin_sources -> flow (rate_limit|filters|format) -> plugin_sinks
### Names must be unique. 1+ source and 1+ sink required.
###############################################################################
[[pipelines]]
name = "default"
###============================================================================
### Flow (processing between sources and sinks)
###============================================================================
## policy="pass" short-circuits the size check too: enforcing a size cap needs
## rate > 0 AND policy = "drop".
# [pipelines.flow.rate_limit]
# rate = 0.0 # Entries/second (0 = limiter disabled)
# burst = 0.0 # Burst capacity (defaults to rate)
# policy = "pass" # pass|drop
# max_entry_size_bytes = 0 # 0 = unlimited
## Filters: sequential include/exclude chain, matched against "<source> <level> <message>"
# [[pipelines.flow.filters]]
# type = "include" # include|exclude
# logic = "or" # or|and
# patterns = [".*ERROR.*", ".*WARN.*"] # example; RE2 syntax
# [pipelines.flow.format]
# type = "raw" # raw|json|txt
# flags = 0 # Formatter flags (0 = defaults per type)
# timestamp_format = "" # Go time layout (formatter default if empty)
# sanitizer_policy = "" # raw|json|txt|shell (defaults per type)
## Flow-level heartbeat (fan-out to all sinks, traverses chain links)
# [pipelines.flow.heartbeat]
# enabled = false
# interval_ms = 1000 # Minimum 100
# include_timestamp = false
# include_stats = false
# format = "txt" # accepted, no effect: heartbeats use flow.format
###============================================================================
### TLS (shared shape; applies to every network source and sink)
###
### Listeners (tcp/http sinks, tcp_chain/http_chain sources):
### cert_file + key_file, self_signed, or issuer_cert_file + issuer_key_file
### (exactly one); client_auth + client_ca_file enable mTLS.
### Dialers (tcp_chain/http_chain sinks):
### ca_file + server_name, or pin_sha256, verify the server; cert_file +
### key_file present a client identity for mTLS.
### `lw tls ca` / `lw tls cert` make a CA and certificates.
###
### enabled = false Master switch
### cert_file = "" Local certificate
### key_file = "" Private key for cert_file (set together)
### client_auth = false Listener: require and verify a client certificate
### client_ca_file = "" Listener: CA bundle verifying client certs
### ca_file = "" Dialer: CA bundle verifying the server (empty = system)
### server_name = "" Dialer: SNI / name to verify (empty = configured host)
### self_signed = false Listener: make a self-signed certificate at startup;
### it logs the pin_sha256 dialers set (changes on restart)
### issuer_cert_file = "" Listener: make one at startup signed by this CA ...
### issuer_key_file = "" ... and its key (lw tls ca writes both)
### hosts = [] Listener: names/addresses of a made certificate, beyond
### host, os.Hostname(), localhost, 127.0.0.1, ::1
### insecure_skip_verify = false Dialer: disable verification (never in prod)
### pin_sha256 = "" Dialer: sha256//BASE64 of the server key, in place of
### ca_file; ';' between several
### min_version = "1.3" "1.2" or "1.3". No max_version, no cipher_suites.
###
### TLS alone is a CA membership check: ANY certificate the CA signed is
### accepted. Add an [...auth] block to decide WHICH of them may connect.
###============================================================================
###============================================================================
### AUTH (shared shape; sits beside [...tls] on every network source and sink)
###
### Both methods REQUIRE tls.enabled; dialers forbid tls.insecure_skip_verify.
### On the http sink the block gates stream_path AND status_path.
### Listeners (tcp/http sinks, tcp_chain/http_chain sources):
### mtls authorizes the client certificate; REQUIRES tls.client_auth.
### scram admits the users in credentials_file (`lw auth add-user`).
### Dialers (tcp_chain/http_chain sinks):
### mtls pins the server identity.
### scram logs in as username with the password in password_file.
### Chain sources additionally bind the node label to the identity or username.
### A block naming peers or credentials with type = "none" is refused.
###
### type = "none" none | mtls | scram
### identity = "cn" cn | san_dns | san_uri | san_email. Unset under scram;
### set on a scram listener (needs tls.client_auth), the
### certificate's field must equal the username.
### allow = [] mtls: exact identities. Empty allow AND allow_patterns
### admits any identity the CA vouches for (logged WARN).
### allow_patterns = [] mtls: RE2 patterns; anchor them yourself (^...$)
### node_binding = "" Chain sources only, default "force" with mtls or scram:
### none - trust_node governs, as before
### assert - declared label must equal the identity;
### per-entry node labels still follow trust_node
### force - label AND every entry take the identity
### Overrides trust_node. See doc/security.md.
### credentials_file = "" scram listeners: verifiers; read at (re)load, not watched
### token_lifetime_ms = 0 scram, http sink and http_chain source: 10 s to 24 h, 0 = 15 minutes
### username = "" scram dialers
### password_file = "" scram dialers: one trailing line break is trimmed
### trusted_proxies = [] scram http sink: TLS-terminating reverse proxies
### (addresses or CIDRs); browsers log in through them
###============================================================================
###============================================================================
### ACL (beside [...tls] and [...auth] on the listeners; dialers have none)
###
### Applied as a connection is accepted, before TLS: a refused peer is closed
### at once and counted in acl_denied (acl_limited past a limit), with a WARN
### at most once a minute.
### allow = [] addresses or CIDRs of the listener's family (either behind
### proxy_from or trusted_proxies); empty = all
### deny = [] refused, listed in allow or not
### proxy_protocol = "off" off | optional | required: PROXY v1/v2 headers,
### read before TLS; the rules then see the client.
### optional lets a headerless stream pass as the
### proxy: use it only while rolling headers out
### proxy_from = [] the proxies that may send one; a header from any
### other peer is refused
### max_connections_per_client = 0 per client (an IPv6 one by its /64);
### refused behind trusted_proxies
### requests_per_second_per_client = 0 HTTP listeners, /auth aside: past it,
### 429; behind trusted_proxies, per
### forwarded client
###============================================================================
###============================================================================
### Sources (1+ required)
###============================================================================
## Null source (testing)
# [[pipelines.plugin_sources]]
# id = "null_in"
# type = "null"
[[pipelines.plugin_sources]]
id = "default_source"
type = "file"
[pipelines.plugin_sources.config]
directory = "./" # Directory to monitor (required, not recursive)
pattern = "*.log" # Glob pattern (* and ? only)
## Tailing an already-open file polls at a fixed 100ms, regardless of this value.
check_interval_ms = 100 # Directory rescan interval (min 10)
## raw = true never parses a line; with format type "raw" the file is relayed byte for byte.
raw = false # Keep the whole line as the message
from = "end" # "end" or "start" of a newly discovered file
## Console source (stdin; one in the whole configuration, ends lw's pipeline at end of input)
# [[pipelines.plugin_sources]]
# id = "console_in"
# type = "console"
# [pipelines.plugin_sources.config]
# buffer_size = 1000
## Random source (testing; special=true exercises sanitizer policies)
# [[pipelines.plugin_sources]]
# id = "random_in"
# type = "random"
# [pipelines.plugin_sources.config]
# interval_ms = 500
# jitter_ms = 0 # Clamped to interval_ms
# format = "txt" # raw|txt|json
# length = 20
# special = false
## TCP chain source (stdlib listener; receives NDJSON from upstream tcp_chain sinks)
## Topology: jail [file source -> tcp_chain sink] -> host [tcp_chain source -> aggregate sink]
# [[pipelines.plugin_sources]]
# id = "chain_in"
# type = "tcp_chain"
# [pipelines.plugin_sources.config]
# host = "0.0.0.0" # IPv4 only; "::" or "::1": IPv6 only
# port = 9440 # Required
# buffer_size = 1000
# max_connections = 0 # 0 = unlimited
# read_timeout_ms = 0 # idle deadline, 0 = none
# hello_timeout_ms = 10000 # Protocol preamble deadline
# trust_node = true # false: label entries by remote address
# [pipelines.plugin_sources.config.tls]
# enabled = true # example: mTLS listener
# cert_file = "/etc/logwisp/tls/server.crt"
# key_file = "/etc/logwisp/tls/server.key"
# client_auth = true
# client_ca_file = "/etc/logwisp/tls/client-ca.crt"
# min_version = "1.3"
# [pipelines.plugin_sources.config.auth] # authenticate senders
# type = "none" # none | mtls | scram; mtls requires client_auth
# identity = "cn" # mtls default; under scram unset, set binds the cert to the user
# allow = [] # mtls: exact identities; empty = any the CA issued
# allow_patterns = [] # mtls: RE2, anchor them yourself
# node_binding = "force" # none | assert | force; overrides trust_node
# credentials_file = "" # scram: written by `lw auth add-user`
# [pipelines.plugin_sources.config.acl] # refuse peers by address, before TLS
# allow = [] # empty = every address deny does not list
# deny = []
# proxy_protocol = "off" # off | optional | required
# proxy_from = [] # proxies that may send a PROXY header
# max_connections_per_client = 0 # per client at once; 0 = unlimited
## HTTP chain source (stdlib listener; receives NDJSON batches from upstream http_chain sinks)
# [[pipelines.plugin_sources]]
# id = "hchain_in"
# type = "http_chain"
# [pipelines.plugin_sources.config]
# host = "0.0.0.0" # or "::" for IPv6 (IPv6 only)
# port = 9441 # Required
# ingest_path = "/ingest" # Must start with "/"
# buffer_size = 1000
# max_body_bytes = 8388608 # per-request cap (8 MiB)
# read_timeout_ms = 30000
# trust_node = true # false: label entries by remote address
# [pipelines.plugin_sources.config.tls]
# enabled = true # example: mTLS listener
# cert_file = "/etc/logwisp/tls/server.crt"
# key_file = "/etc/logwisp/tls/server.key"
# client_auth = true
# client_ca_file = "/etc/logwisp/tls/client-ca.crt"
# [pipelines.plugin_sources.config.auth] # authenticate senders
# type = "none" # none | mtls | scram; mtls requires client_auth
# identity = "cn" # mtls default; under scram unset, set binds the cert to the user
# allow = [] # mtls: exact identities; empty = any the CA issued
# allow_patterns = [] # mtls: RE2, anchor them yourself
# node_binding = "force" # none | assert | force; overrides trust_node
# credentials_file = "" # scram: written by `lw auth add-user`
# token_lifetime_ms = 0 # scram bearer tokens, 10 s to 24 h; 0 = 15 minutes
# [pipelines.plugin_sources.config.acl] # refuse peers by address, before TLS
# allow = [] # empty = every address deny does not list
# deny = []
# proxy_protocol = "off" # off | optional | required
# proxy_from = [] # proxies that may send a PROXY header
# max_connections_per_client = 0 # per client at once; 0 = unlimited
# requests_per_second_per_client = 0 # per client, a second's worth at once; 0 = unlimited
###============================================================================
### Sinks (1+ required, fan-out)
###============================================================================
## Null sink (testing)
# [[pipelines.plugin_sinks]]
# id = "null_out"
# type = "null"
[[pipelines.plugin_sinks]]
id = "default_sink"
type = "console"
[pipelines.plugin_sinks.config]
target = "stdout" # stdout|stderr ("split" NOT supported)
# buffer_size = 1000
# escape = "auto" # auto|always|never: control characters as <hex>, auto on a terminal
# color = "auto" # auto|always|never: level names in color (default: top-level color)
## File sink (rotating)
# [[pipelines.plugin_sinks]]
# id = "file_out"
# type = "file"
# [pipelines.plugin_sinks.config]
# directory = "./logs" # Required
# name = "output" # Required
# max_size_mb = 100
# max_total_size_mb = 1000
# min_disk_free_mb = 0 # 0 = no floor (only negatives become 100)
# retention_hours = 168.0
# buffer_size = 1000
# flush_interval_ms = 100
## HTTP sink (SSE streaming server + JSON status endpoint)
## Without an auth block both endpoints are UNAUTHENTICATED and the stream sends
## Access-Control-Allow-Origin: *. Bind to a trusted interface.
# [[pipelines.plugin_sinks]]
# id = "http_out"
# type = "http"
# [pipelines.plugin_sinks.config]
# host = "0.0.0.0" # or "::" for IPv6 (IPv6 only)
# port = 8081 # Required
# stream_path = "/stream" # Exact; starts with "/", clean, none of { } % ? #
# status_path = "/status" # Same rules; differs from stream_path
# buffer_size = 1000 # Sink input queue
# client_buffer_size = 256 # Per-client send queue
# write_timeout_ms = 0 # Per-event deadline, 0 = none
# max_connections = 0 # 0 = unlimited
# replay_lines = 0 # Last entries a new stream gets first, 0 = none
# login_page = false # GET /auth/login; scram behind auth.trusted_proxies
# viewer_page = false # GET /auth/view, needs login_page; scram + trusted_proxies only
# (without auth or under mtls the viewer is always on)
# [pipelines.plugin_sinks.config.tls]
# enabled = true # example
# cert_file = "/etc/logwisp/tls/server.crt"
# key_file = "/etc/logwisp/tls/server.key"
# client_auth = false
# client_ca_file = ""
# [pipelines.plugin_sinks.config.auth] # gates BOTH stream_path and status_path
# type = "none" # none | mtls | scram; browsers: mtls, or scram behind a proxy
# identity = "cn" # mtls default; under scram unset, set binds the cert to the user
# allow = [] # mtls: exact identities; empty = any the CA issued
# allow_patterns = [] # mtls: RE2, anchor them yourself
# credentials_file = "" # scram: log in with `lw auth token`
# token_lifetime_ms = 0 # scram bearer tokens, 10 s to 24 h; 0 = 15 minutes
# trusted_proxies = [] # scram: proxies ending the browsers' TLS; tls may then be off
# [pipelines.plugin_sinks.config.acl] # refuse peers by address, before TLS
# allow = [] # empty = every address deny does not list
# deny = []
# proxy_protocol = "off" # off | optional | required
# proxy_from = [] # proxies that may send a PROXY header
# max_connections_per_client = 0 # per client at once; 0 = unlimited
# requests_per_second_per_client = 0 # per client, a second's worth at once; 0 = unlimited
## TCP sink (streaming server)
# [[pipelines.plugin_sinks]]
# id = "tcp_out"
# type = "tcp"
# [pipelines.plugin_sinks.config]
# host = "0.0.0.0" # or "::" for IPv6 (IPv6 only)
# port = 9090 # Required
# buffer_size = 1000
# client_buffer_size = 256
# write_timeout_ms = 5000 # Missed deadline disconnects the client
# keep_alive = true
# keep_alive_period_ms = 30000
# max_connections = 0
# [pipelines.plugin_sinks.config.tls]
# enabled = true # example
# cert_file = "/etc/logwisp/tls/server.crt"
# key_file = "/etc/logwisp/tls/server.key"
# client_auth = true
# client_ca_file = "/etc/logwisp/tls/client-ca.crt"
# [pipelines.plugin_sinks.config.auth] # authenticate stream readers
# type = "none" # none | mtls | scram; mtls requires client_auth
# identity = "cn" # mtls default; under scram unset, set binds the cert to the user
# allow = [] # mtls: exact identities; empty = any the CA issued
# allow_patterns = [] # mtls: RE2, anchor them yourself
# credentials_file = "" # scram: readers use `lw auth stream`
# [pipelines.plugin_sinks.config.acl] # refuse peers by address, before TLS
# allow = [] # empty = every address deny does not list
# deny = []
# proxy_protocol = "off" # off | optional | required
# proxy_from = [] # proxies that may send a PROXY header
# max_connections_per_client = 0 # per client at once; 0 = unlimited
## TCP chain sink (stdlib client; forwards to downstream tcp_chain source)
## Do NOT point a chain sink at a chain source in the SAME pipeline: entries
## loop back in and amplify without bound.
# [[pipelines.plugin_sinks]]
# id = "chain_out"
# type = "tcp_chain"
# [pipelines.plugin_sinks.config]
# host = "10.0.0.1" # Required; IPv6 bare, e.g. "fd00::1"
# port = 9440 # Required
# node = "" # origin label, default hostname; preserved across hops
# buffer_size = 1000
# dial_timeout_ms = 5000
# write_timeout_ms = 5000
# backoff_min_ms = 500
# backoff_max_ms = 30000
# keep_alive = true
# keep_alive_period_ms = 30000
# [pipelines.plugin_sinks.config.tls]
# enabled = true # example: mTLS dialer
# ca_file = "/etc/logwisp/tls/ca.crt"
# server_name = ""
# insecure_skip_verify = false
# cert_file = "/etc/logwisp/tls/client.crt"
# key_file = "/etc/logwisp/tls/client.key"
# min_version = "1.3"
# [pipelines.plugin_sinks.config.auth] # pin the server (mtls) or log in (scram)
# type = "none" # none | mtls | scram; both require tls.enabled
# identity = "cn" # mtls: cn | san_dns | san_uri | san_email
# allow = [] # mtls: exact identities; empty = any the CA issued
# allow_patterns = [] # mtls: RE2, anchor them yourself
# username = "" # scram
# password_file = "" # scram
## HTTP chain sink (stdlib client; batched NDJSON POST to downstream http_chain source)
# [[pipelines.plugin_sinks]]
# id = "hchain_out"
# type = "http_chain"
# [pipelines.plugin_sinks.config]
# host = "10.0.0.1" # Required; IPv6 bare, e.g. "fd00::1"
# port = 9441 # Required
# ingest_path = "/ingest"
# node = "" # origin label, default hostname; preserved across hops
# buffer_size = 1000
# max_batch_count = 100
# max_batch_bytes = 1048576
# flush_interval_ms = 1000
# request_timeout_ms = 10000 # Covers dial + write + response
# backoff_min_ms = 500
# backoff_max_ms = 30000
# [pipelines.plugin_sinks.config.tls]
# enabled = true # example: mTLS dialer
# ca_file = "/etc/logwisp/tls/ca.crt"
# cert_file = "/etc/logwisp/tls/client.crt"
# key_file = "/etc/logwisp/tls/client.key"
# [pipelines.plugin_sinks.config.auth] # pin the server (mtls) or log in (scram)
# type = "none" # none | mtls | scram; both require tls.enabled
# identity = "cn" # mtls: cn | san_dns | san_uri | san_email
# allow = [] # mtls: exact identities; empty = any the CA issued
# allow_patterns = [] # mtls: RE2, anchor them yourself
# username = "" # scram
# password_file = "" # scram