feat: reproduce NetFlow collectors + force user-data replacement (INFRA-12)
Some checks are pending
Deploy / deploy (push) Waiting to run

- Build nfdump 1.6.23 from source in user-data (rrdtool-devel for librrd;
  not packaged on AL2023) and run nfcapd collectors as systemd units:
  nfcapd.service (Ronkonkoma udp/2055), nfcapd-locust.service (Locust udp/2056),
  + netflow-retention.timer (30d sweep). 1 GiB swapfile for build headroom.
- userDataCausesReplacement: true — a user-data change must actually re-run,
  so force instance replacement (stateless box, EIP re-associates).

Validated: build + all services active, listeners on 514/2055/2056.
This commit is contained in:
Adam Moussa 2026-06-09 14:11:58 -04:00
parent a8276b44fd
commit 97bc751782

View file

@ -84,6 +84,15 @@ export class SyslogServerStack extends cdk.Stack {
userData.addCommands(
"set -euxo pipefail",
"",
"# ── 1 GiB swap (build headroom + stability on the 512 MiB t4g.nano) ──",
"if [ ! -f /swapfile ]; then",
" fallocate -l 1G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=1024",
" chmod 600 /swapfile",
" mkswap /swapfile",
" echo '/swapfile none swap sw 0 0' >> /etc/fstab",
"fi",
"swapon -a || true",
"",
"# ── rsyslog: listen on UDP/TCP 514 ──",
"dnf install -y rsyslog",
"cat > /etc/rsyslog.d/10-listen.conf <<'EOF'",
@ -131,6 +140,74 @@ export class SyslogServerStack extends cdk.Stack {
" -a fetch-config -m ec2 \\",
" -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s",
"systemctl enable amazon-cloudwatch-agent",
"",
"# ── NetFlow/IPFIX collectors (nfcapd) ──",
"# nfdump is not packaged for AL2023; build 1.6.23 from source (needs",
"# rrdtool-devel for librrd). Reconstructed under IaC for INFRA-12 — the",
"# original instance ran these as hand-installed systemd units. Captures",
"# are local-only (no consumer/shipping today); 30-day retention enforced.",
"dnf install -y gcc gcc-c++ make automake autoconf libtool flex bison libpcap-devel zlib-devel bzip2-devel rrdtool-devel tar",
"NFVER=1.6.23",
"curl -sfL https://github.com/phaag/nfdump/archive/refs/tags/v${NFVER}.tar.gz | tar xz -C /tmp",
"( cd /tmp/nfdump-${NFVER} && ./autogen.sh && ./configure && make -j1 && make install )",
"ldconfig",
"",
"mkdir -p /var/log/netflow/ronkonkoma /var/log/netflow/locust",
"chown -R ec2-user:ec2-user /var/log/netflow",
"",
"# Ronkonkoma gateway -> UDP 2055",
"cat > /etc/systemd/system/nfcapd.service <<'EOF'",
"[Unit]",
"Description=nfcapd NetFlow collector (Ronkonkoma, udp/2055)",
"After=network.target",
"[Service]",
"Type=simple",
"User=ec2-user",
"ExecStart=/usr/local/bin/nfcapd -p 2055 -l /var/log/netflow/ronkonkoma",
"Restart=always",
"[Install]",
"WantedBy=multi-user.target",
"EOF",
"",
"# Locust Ave gateway -> UDP 2056",
"cat > /etc/systemd/system/nfcapd-locust.service <<'EOF'",
"[Unit]",
"Description=nfcapd NetFlow collector (Locust Ave, udp/2056)",
"After=network.target",
"[Service]",
"Type=simple",
"User=ec2-user",
"ExecStart=/usr/local/bin/nfcapd -p 2056 -l /var/log/netflow/locust",
"Restart=always",
"[Install]",
"WantedBy=multi-user.target",
"EOF",
"",
"# 30-day retention sweep (daily 03:30 UTC)",
"cat > /usr/local/sbin/netflow-retention.sh <<'EOF'",
"#!/bin/bash",
"find /var/log/netflow -type f -name 'nfcapd.*' -mtime +30 -delete",
"EOF",
"chmod +x /usr/local/sbin/netflow-retention.sh",
"cat > /etc/systemd/system/netflow-retention.service <<'EOF'",
"[Unit]",
"Description=Delete NetFlow captures older than 30 days",
"[Service]",
"Type=oneshot",
"ExecStart=/usr/local/sbin/netflow-retention.sh",
"EOF",
"cat > /etc/systemd/system/netflow-retention.timer <<'EOF'",
"[Unit]",
"Description=Daily NetFlow retention sweep",
"[Timer]",
"OnCalendar=*-*-* 03:30:00 UTC",
"Persistent=true",
"[Install]",
"WantedBy=timers.target",
"EOF",
"",
"systemctl daemon-reload",
"systemctl enable --now nfcapd.service nfcapd-locust.service netflow-retention.timer",
);
const instance = new ec2.Instance(this, "Instance", {
@ -148,6 +225,9 @@ export class SyslogServerStack extends cdk.Stack {
securityGroup: sg,
role,
userData,
// A user-data change must actually re-run, so force instance replacement
// (the box is stateless — logs live in CloudWatch, the EIP re-associates).
userDataCausesReplacement: true,
blockDevices: [
{
deviceName: "/dev/xvda",