Skip to content

tschaefer/conntrackd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

conntrackd

Tag Go Version Go Report Card Coverage Contributors License

conntrackd is a small, efficient conntrack event fanout logger written in Go. It listens for Linux conntrack/netfilter connection tracking events and optional enriches them with GEO location information before emitting structured logs. It's intended for lightweight monitoring, auditing, and integration with log pipelines.

Features

  • Listen for conntrack events (new/updated/destroyed connections)
  • Enrich IP addresses with GEO location data
  • Fanout to multiple log sinks (stream, syslog, journald, Loki)

Getting Started

Prerequisites

  • Linux (netlink/conntrack support required)
  • Root privileges
  • (Optional) MaxMind GeoIP2/GeoLite2 City database

Installation and Usage

Download the latest release from the releases page.

Start the event listener and logger.

sudo conntrackd run --sink.journal.enable

For further configuration, see the command-line options below.

Filtering

conntrackd logs conntrack events to various sinks.

Protocol Support: Only TCP and UDP events are processed. All other protocols (ICMP, IGMP, etc.) are automatically ignored and never logged, regardless of filter rules.

You can use filters to control which TCP/UDP events are logged using a Domain-Specific Language (DSL). The --filter flag lets you specify filter rules:

sudo conntrackd run \
  --filter "drop destination address 8.8.8.8" \
  --filter "log protocol TCP and destination network PUBLIC" \
  --filter "drop any" \
  --sink.journal.enable

Filter Rules:

  • Rules are evaluated in order (first-match wins)
  • Events are logged by default when no rule matches
  • --filter flag can be repeated for multiple rules
  • Use drop any as a final rule to block all non-matching events from being logged

Important: Filters control which conntrack events are logged, not network traffic. Traffic always flows normally; filters only affect logging.

Common Filter Examples:

# Don't log events to a specific IP
--filter "drop destination address 8.8.8.8"

# Log only NEW TCP connections (deny everything else)
--filter "log type NEW and protocol TCP"
--filter "drop any"

# Don't log DNS to specific server
--filter "drop destination address 10.19.80.100 on port 53"

# Don't log any traffic to private networks
--filter "drop destination network PRIVATE"

# Log only traffic from public IPs using TCP
--filter "log source network PUBLIC and protocol TCP"
--filter "drop any"

See docs/filter.md for complete DSL documentation, including grammar, operators, and advanced examples.

Configuration

conntrackd can be configured via command-line flags, configuration files, environment variables, or a combination of these methods.

Configuration Files

By default, conntrackd searches for a configuration file named conntrackd.(yaml|yml|json|toml) in /etc/conntrackd directory.

You can also specify a custom config file using the --config flag:

sudo conntrackd run --config /path/to/config.yaml

Configuration files support YAML, JSON, and TOML formats. See contrib/config.yaml for a complete example configuration file.

Environment Variables

Configuration values can be set via environment variables with the CONNTRACKD_ prefix:

export CONNTRACKD_LOG_LEVEL=debug
export CONNTRACKD_SINK_STREAM_WRITER=discard
sudo -E conntrackd run

Use underscores (_) to represent nested keys: sink.stream.writer β†’ CONNTRACKD_SINK_STREAM_WRITER

Priority Order

Configuration values are applied in the following order (later overrides earlier):

  1. Default values
  2. Configuration file
  3. Environment variables
  4. Command-line flags

Note: Command-line flags always have the highest priority.

Configuration Flags

Flag Description Default
--config Path to configuration file
--filter Filter rule in DSL format (repeatable)
--geoip.database Path to GeoIP database
--log.level Log level (debug, info, warn, error) info
--sink.journal.enable Enable journald sink
--sink.syslog.enable Enable syslog sink
--sink.loki.enable Enable Loki sink
--sink.stream.enable Enable stream sink
--sink.syslog.address Syslog address udp://localhost:514
--sink.loki.address Loki address http://localhost:3100
--sink.loki.labels Loki labels (comma-separated key=value pairs)
--sink.stream.writer Stream writer (stdout, stderr, discard) stdout

Logging format

conntrackd emits structured logs for each conntrack event. A typical log entry includes:

  • type (connection event type)
  • flow (connection flow identifier)
  • src_addr, dst_addr (IP addresses)
  • src_port, dst_port (port numbers)
  • prot (transport protocol)

Additionally TCP field:

  • state (TCP connection state)

GEO location fields for source and destination if applicable with prefixes src_ and dst_:

  • city (city name)
  • country (country name)
  • lat (latitude)
  • lon (longitude)
Example log entry recorded by sink `syslog`
{
  "event": {
    "dst_port": 443,
    "dst_addr": "2600:1901:0:b3ea::",
    "flow": 221193769,
    "prot": "TCP",
    "src_port": 41348,
    "src_addr": "2003:cf:1716:7b64:da80:83ff:fecd:da51",
    "tcp_state": "LAST_ACK",
    "type": "UPDATE"
  },
  "level": "INFO",
  "logger.name": "samber/slog-syslog",
  "logger.version": "v2.5.2",
  "message": "UPDATE TCP connection from [2003:cf:1716:7b64:da80:83ff:fecd...",
  "timestamp": "2025-11-15T09:55:25.647544937Z"
}
Example log entry recorded by sink `journal`
{
	"__CURSOR" : "s=b3c7821dbfce47a59b06797aea9028ca;i=6772d3;b=100da27bd...",
	"_CAP_EFFECTIVE" : "1ffffffffff",
	"EVENT_SRC_PORT" : "39790",
	"_SOURCE_REALTIME_TIMESTAMP" : "1763200187611509",
	"_SYSTEMD_CGROUP" : "/user.slice/user-1000.slice/session-1.scope",
	"_SYSTEMD_OWNER_UID" : "1000",
	"_SYSTEMD_SESSION" : "1",
	"_EXE" : "/home/tschaefer/.env/bin/conntrackd",
	"_HOSTNAME" : "bullseye",
	"_GID" : "0",
	"PRIORITY" : "6",
	"_SYSTEMD_UNIT" : "session-1.scope",
	"EVENT_DST_PORT" : "443",
	"SLOG_LOGGER" : "tschaefer/slog-journal:v1.0.0",
	"_TRANSPORT" : "journal",
	"EVENT_SRC_ADDR" : "2003:cf:1716:7b64:da80:83ff:fecd:da51",
	"_COMM" : "conntrackd",
	"__MONOTONIC_TIMESTAMP" : "352829248481",
	"EVENT_TCP_STATE" : "LAST_ACK",
	"_MACHINE_ID" : "75b649379b874beea04d95463e59c3a1",
	"_SYSTEMD_SLICE" : "user-1000.slice",
	"_SYSTEMD_USER_SLICE" : "-.slice",
	"__SEQNUM_ID" : "b3c7821dbfce47a59b06797aea9028ca",
	"__REALTIME_TIMESTAMP" : "1763200187611631",
	"__SEQNUM" : "6779603",
	"_SYSTEMD_INVOCATION_ID" : "021760b3373342b98aaeabf9d12d8d74",
	"EVENT_FLOW" : "3478798157",
	"_PID" : "3794900",
	"_CMDLINE" : "conntrackd run --service.log.level debug --service.log....",
	"EVENT_PROT" : "TCP",
	"_AUDIT_SESSION" : "1",
	"_BOOT_ID" : "100da27bd8b94096b5c80cdac34d6063",
	"_RUNTIME_SCOPE" : "system",
	"_SELINUX_CONTEXT" : "unconfined\n",
	"EVENT_DST_ADDR" : "2600:1901:0:b3ea::",
	"_AUDIT_LOGINUID" : "1000",
	"_UID" : "0",
	"EVENT_TYPE" : "UPDATE",
	"MESSAGE" : "UPDATE TCP connection from [2003:cf:1716:7b64:da80:83ff:fe..."
}
Example log entry recorded by sink `loki`

Loki allows maximum 15 labels per log entry. Therefore, location fields are attached as structured metadata to each log line.

{
  "stream": {
    "detected_level": "INFO",
    "dst_addr": "2a01:4f8:160:5372::2",
    "dst_addr_extracted": "2a01:4f8:160:5372::2",
    "dst_city": "Falkenstein",
    "dst_country": "Germany",
    "dst_lat": "50.4777",
    "dst_lon": "12.3649",
    "dst_port": "443",
    "dst_port_extracted": "443",
    "flow": "4198226788",
    "flow_extracted": "4198226788",
    "host": "bullseye.u.coresec.zone",
    "level": "INFO",
    "prot": "TCP",
    "prot_extracted": "TCP",
    "service_name": "conntrackd",
    "src_addr": "2003:cf:1716:7b64:da80:83ff:fecd:da51",
    "src_addr_extracted": "2003:cf:1716:7b64:da80:83ff:fecd:da51",
    "src_city": "Garmisch-Partenkirchen",
    "src_country": "Germany",
    "src_lat": "47.4906",
    "src_lon": "11.1026",
    "src_port": "56110",
    "src_port_extracted": "56110",
    "tcp_state": "SYN_SENT",
    "tcp_state_extracted": "SYN_SENT",
    "type": "NEW",
    "type_extracted": "NEW"
  },
  "values": [
    [
      "1764163739570953291",
      "NEW TCP connection from [2003:cf:1716:7b64:da80:83ff:fecd:da51]:56110..."
    ]
  ]
}
Example log entry recorded by sink `stream`
{
  "time": "2025-11-25T12:35:11.082791653+01:00",
  "level": "INFO",
  "msg": "NEW TCP connection from [2003:cf:1716:7b64:da80:83ff:fecd:da51]:4...",
  "type": "NEW",
  "flow": 4000057915,
  "prot": "TCP",
  "src_addr": "2003:cf:1716:7b64:da80:83ff:fecd:da51",
  "dst_addr": "2a01:4f8:160:5372::2",
  "src_port": 41756,
  "dst_port": 443,
  "tcp_state": "SYN_SENT",
  "src_city": "Garmisch-Partenkirchen",
  "src_country": "Germany",
  "src_lat": 47.4906,
  "src_lon": 11.1026,
  "dst_city": "Falkenstein",
  "dst_country": "Germany",
  "dst_lat": 50.4777,
  "dst_lon": 12.3649
}

Security Notes

  • Observing conntrack/netlink events typically requires elevated privileges.
  • Keep GeoIP databases updated.
  • Be careful with log storage; connection events may contain sensitive network metadata.

Contributing

Contributions are welcome! Please fork the repository and submit a pull request. For major changes, open an issue first to discuss what you would like to change.

Ensure that your code adheres to the existing style and includes appropriate tests.

License

This project is licensed under the MIT License.