Skip to content

Commit 2d44a6a

Browse files
committed
Add Rule Type (HTTP/HTTPS/IP) field to proxy logs and report
Track which protocol/rule type each connection was processed with by adding a sess.rule_type variable to HAProxy and a Type column to the outbound traffic report.
1 parent 2d65c33 commit 2d44a6a

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed

docker/files/haproxy.cfg.template

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ frontend outbound_proxy
3232
tcp-request content set-var(sess.decision) str(BLOCKED)
3333
tcp-request content set-var(sess.reason) str(-)
3434
tcp-request content set-var-fmt(sess.target) %[dst]:%[dst_port]
35+
tcp-request content set-var(sess.rule_type) str(UNKNOWN)
3536

3637
# ACL: DNS-routed vs IP direct
3738
acl is_dns_routed dst 172.20.0.1
@@ -56,6 +57,7 @@ frontend outbound_proxy
5657
# ---------------------------------------------------------
5758
# 1. IP direct access (non DNS-routed)
5859
# ---------------------------------------------------------
60+
tcp-request content set-var(sess.rule_type) str(IP) if !is_dns_routed
5961
tcp-request content set-var(sess.decision) str(${HAPROXY_DECISION_LABEL}) if !is_dns_routed is_ip_match
6062
tcp-request content accept if !is_dns_routed is_ip_match
6163

@@ -66,6 +68,7 @@ frontend outbound_proxy
6668
# ---------------------------------------------------------
6769
# 2. TLS without SNI
6870
# ---------------------------------------------------------
71+
tcp-request content set-var(sess.rule_type) str(HTTPS) if is_tls
6972
tcp-request content set-var(sess.reason) str(missing-sni) if is_tls !has_sni
7073
tcp-request content reject if is_tls !has_sni
7174

@@ -94,7 +97,7 @@ frontend outbound_proxy
9497
default_backend http_filter_backend
9598

9699
# Set log format
97-
log-format "[%T] buildcage [%[var(sess.decision)]] \"%[var(sess.target)]\" %[var(sess.reason)]"
100+
log-format "[%T] buildcage [%[var(sess.decision)]] (%[var(sess.rule_type)]) \"%[var(sess.target)]\" %[var(sess.reason)]"
98101

99102
# --- IP direct passthrough backend ---
100103
backend ip_passthrough
@@ -110,6 +113,7 @@ backend tls_passthrough
110113
# deny responses leave sess.decision as BLOCKED (set in frontend).
111114
backend http_filter_backend
112115
mode http
116+
http-request set-var(sess.rule_type) str(HTTP)
113117

114118
# Host header check
115119
acl has_host hdr(host) -m found

report/main.mjs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ console.log();
2323

2424
// 3. Parse log lines
2525
const logPattern =
26-
/^\[.*?\]\s+buildcage\s+\[(AUDIT|ALLOWED|BLOCKED)\]\s+"([^"]+)"\s*(\S*)/;
26+
/^\[.*?\]\s+buildcage\s+\[(AUDIT|ALLOWED|BLOCKED)\]\s+\((\w+)\)\s+"([^"]+)"\s*(\S*)/;
2727

2828
const entries = [];
2929
for (const line of logs.split("\n")) {
3030
const m = line.match(logPattern);
3131
if (m) {
32-
const hostPort = m[2];
33-
const reason = m[3] || "-";
32+
const ruleType = m[2];
33+
const hostPort = m[3];
34+
const reason = m[4] || "-";
3435
const colonIdx = hostPort.lastIndexOf(":");
3536
let host, port;
3637
if (colonIdx > 0) {
@@ -42,6 +43,7 @@ for (const line of logs.split("\n")) {
4243
}
4344
entries.push({
4445
decision: m[1],
46+
ruleType,
4547
host,
4648
port,
4749
reason,
@@ -65,13 +67,13 @@ const isAudit = entries.some((e) => e.decision === "AUDIT");
6567
function aggregate(filtered) {
6668
const map = new Map();
6769
for (const e of filtered) {
68-
const key = `${e.host}\t${e.port}\t${e.reason}`;
70+
const key = `${e.host}\t${e.port}\t${e.ruleType}\t${e.reason}`;
6971
map.set(key, (map.get(key) || 0) + 1);
7072
}
7173
return [...map.entries()]
7274
.map(([key, count]) => {
73-
const [host, portStr, reason] = key.split("\t");
74-
return { host, port: Number(portStr), reason, count };
75+
const [host, portStr, ruleType, reason] = key.split("\t");
76+
return { host, port: Number(portStr), ruleType, reason, count };
7577
})
7678
.sort(
7779
(a, b) =>
@@ -83,15 +85,15 @@ function aggregate(filtered) {
8385

8486
function markdownTable(rows, { showReason = false } = {}) {
8587
if (showReason) {
86-
const lines = ["| Host | Port | Reason | Count |", "| --- | --- | --- | ---: |"];
88+
const lines = ["| Host | Port | Type | Reason | Count |", "| --- | --- | --- | --- | ---: |"];
8789
for (const r of rows) {
88-
lines.push(`| ${r.host} | ${r.port || ""} | ${r.reason} | ${r.count} |`);
90+
lines.push(`| ${r.host} | ${r.port || ""} | ${r.ruleType} | ${r.reason} | ${r.count} |`);
8991
}
9092
return lines.join("\n");
9193
}
92-
const lines = ["| Host | Port | Count |", "| --- | --- | ---: |"];
94+
const lines = ["| Host | Port | Type | Count |", "| --- | --- | --- | ---: |"];
9395
for (const r of rows) {
94-
lines.push(`| ${r.host} | ${r.port || ""} | ${r.count} |`);
96+
lines.push(`| ${r.host} | ${r.port || ""} | ${r.ruleType} | ${r.count} |`);
9597
}
9698
return lines.join("\n");
9799
}

0 commit comments

Comments
 (0)