What's new
  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

[SCRIPT] Asuswrt-Merlin Log Sanitizer — safely redact syslogs before sharing

BGood

Regular Contributor
First, let me say I'm not a coder. I had need to share my router log file with someone to review, but I wanted to strip out "sensitive" information first. So I spent time with ChatGPT having it create a tool to do the job. It took a good 2 hours of back-and-forth and in the end it needed to be split into to files to work reliably due to some apparent BusyBox constraints. I thought it might be useful to at least some of you, but don't rely on the output without careful review at least the first time. You'll notice I had things like Tivo information in my log. You very well may have other types of sensitive information in your logs depending on your setup, so you've been warned. Trust but verify.

There's no warranty, and I really can't help you edit the code other than to say if you copy it and paste it into ChatGPT it should then be able to provide edits to handle other situations.

Good luck.

🧰 Asuswrt-Merlin Log Sanitizer
Safely share your router logs without leaking personal data
Version: 1.9.11 (2025-11-12)
Tested on: GT-AX11000 (Merlin 3004.388.10_2), RT-AX88U Pro, RT-AC68U <<==I only tested on GT-AX11000. ChatGPT might have hallucinated the other two models' testing

---

Purpose
When posting to SNBForums or Reddit for troubleshooting, your `/tmp/syslog.log` may contain sensitive information (IP addresses, MACs, DDNS names, etc.).
This sanitizer automatically redacts private data while keeping technical context intact — so others can help you debug safely.

---

What It Does

CategoryAction
IP addresses→ replaced with IP_1, IP_2, etc.
MAC addresses→ replaced with MAC_#
Hostnames / DDNS / domains→ replaced with HOST_#
Emails→ replaced with EMAIL_#
Wi-Fi SSIDs→ numbered (SSID_1, SSID_2, ...)
DHCP hostnames→ numbered (DHCP_HOST_1, DHCP_HOST_2, ...)
VPN names→ numbered (VPN_1, VPN_2, ...)
Usernames (SSH/OpenVPN)→ [REDACTED_USER]
Certificates, keys, thumbprints→ [REDACTED_*]
Authorization headers & tokens→ [REDACTED]
SSH fingerprints→ [REDACTED_FINGERPRINT]
Interface namesKept visible (e.g., eth0, wl1.2, tun11)

Everything diagnostic (RSSI, firmware version, MTU, cipher, etc.) stays visible.

---

Files

FilePurpose
/jffs/scripts/sanitize_logs.shShell wrapper — orchestrates backup, run, and verification
/jffs/scripts/sanitize_logs.awkAWK engine — performs the actual redactions

---

Installation

1. Enable JFFS scripts
Administration → System → Enable JFFS custom scripts = Yes
(reboot once if newly enabled)

2. Copy both files to your router:
Code:
/jffs/scripts/sanitize_logs.sh
/jffs/scripts/sanitize_logs.awk

3. Make them executable:
Code:
chmod +x /jffs/scripts/sanitize_logs.sh
chmod 644 /jffs/scripts/sanitize_logs.awk

4. Run it:
Code:
/jffs/scripts/sanitize_logs.sh

---

Output

All results go to `/tmp/`:

FilePurpose
syslog_original_<timestamp>.logOriginal backup (unmodified)
syslog_sanitized_<timestamp>.logClean version — safe to share
syslog_map_<timestamp>.txtPrivate mapping (for your reference only)
syslog_counts_<timestamp>.txtRedaction counts per category

Example run:
Code:
sanitize_logs.sh v1.9.11 (2025-11-12)
Original log backed up to /tmp/syslog_original_20251112-1110.log
Sanitizing /tmp/syslog.log ...
✅ Verification passed: no unredacted sensitive strings detected.

Summary of redactions:
EMAIL: 2
MAC: 46
IP: 38
HOST: 4
SSID: 3
DHCP_HOST: 2

---

Verification

Every run automatically checks for:
• Unredacted credentials or domains
• Missed ACME or DDNS tokens
• Residual fingerprints

Output shows:
Code:
✅ Verification passed: no unredacted sensitive strings detected.

If anything’s missed, it lists the lines for you to review.

---

Benefits

✔ Keeps router data private before public posting
✔ Works entirely on-device — no external upload
✔ Maintains technical info for troubleshooting
✔ Compatible with BusyBox 1.25+ (Merlin default)
✔ Tested on multiple models and firmware versions <<==ChatGPT said this but I'm not sure how that's true. I have one model and one Merlin FW, 3004.388.10_2.

---

Optional Shortcut

You can add an alias for convenience:
Code:
echo "alias sanitize='/jffs/scripts/sanitize_logs.sh'" >> /jffs/configs/profile.add
Then just type:
Code:
sanitize

---


---

Download
Download both and remove the .txt extension, save them to your router, and make executable.
No external dependencies required — everything runs locally.

---
 

Attachments

Last edited:
I think you posted the .sh file twice and not the .awk file.
 
Now just the .sh

Interested in the workings, because i think this could be done in syslog-ng as well.
 
Last edited:
It seems the contents of the awk file are causing some trouble with posting. Zips not allowed. When I paste the text it won't save. Saved as PDF but you should be able to copy/paste, I think. Remember you only want LF for the router, not CR/LF that's the Windows default.

If anybody has a better way to include the awk text, let me know.
 

Attachments

Last edited:
It seems the contents of the awk file are causing some trouble with posting. Zips not allowed. When I paste the text it won't save. Saved as PDF but you should be able to copy/paste, I think. Remember you only want LF for the router, not CR/LF that's the Windows default.

If anybody has a better way to include the awk text, let me know.
If you don't have or want to have a GitHub account, another alternative would be to put your files in PasteBin [https://pastebin.com/] (or similar web services, e.g. https://pastes.io/) to make them easier to download and avoid "copy & paste" mistakes.

Just my 2 cents.
 
I wanted to strip out "sensitive" information first. So I spent time with ChatGPT having it create a tool to do the job.
I use Merlin snd SSH etc so can install and use this reasonably easily myself, but I wonder if it would be worth your while porting this to a windows batch file for those users who want to to save and share logs, but may not have Merlin installed or struggle with SSH to their routers.

They could just “wash” it through a Windows batch file instead?

I think this would be a great parallel script :-).
 
"Sanitizing" local IPs may make troubleshooting harder.
 
"Sanitizing" local IPs may make troubleshooting harder.
Yes, agree, for local (private) IPs, these definitely do not need to be hidden.

IP addresses→ replaced with IP_1, IP_2, etc.

And as @ColinTaylor often says:
No, it's pointless masking MAC addresses. There is no security reason. It can actually make debugging problems more difficult.
Ref https://www.snbforums.com/threads/android-tablet-loses-connection-to-wi-fi.81550/post-798550

So maybe consider a switch (toggle) to disable masking of specific parameters?
 
Last edited:
There are tons of examples of rewriting log messages within syslog-ng, including replacing IP addresses with REDACTED or even localnet.<lastoctet>. So all of this could be done with a filter within scribe as well.
 
There are tons of examples of rewriting log messages within syslog-ng, including replacing IP addresses with REDACTED or even localnet.<lastoctet>. So all of this could be done with a filter within scribe as well.
Will that redact such that the replacements get their own IDs? Mine will do MAC_1, MAC_2, IP_1, IP_2, etc.

As for not redacting the IP addresses, you can just remove that section from the awk file. I'll try to go back and have a switch added for IPs.

I will see if this script can be converted to run on Windows.

Finally, I will look into PasteBin.
 
I had it in mind using the pattern databases, so any IP address (pattern @IPv4@ would become REDACTED), but I think you can do partial replacements, so that 192.168.1.1 becomes xx.xx.xx.1, and 192.168.1.2 becomes xx.xx.xx.2, etc. Or IP.1, IP.2, etc. Or localnet.1, localnet.2, which might be of more use in troubleshooting. So the same would work with MACs. You could then keep an anonymized set of log files in a separate directory, presumably with a very short logrotate scheme.
 
Lol, poor guy, probably thought he was pretty much ‘scripted and done’, but nooooo, post it here to help folks and all of a sudden ‘oiks’ like me want an all singing, all dancing version, with switches and windows and goodness knows what other permutations … bwahahahahhaha … that’ll learn you … :D
 
Here's a revised AWK file: https://pastebin.com/qkjc5473

I haven't attempted to use it, but there are now switches for several things. I won't have time to work on this until December, so have at it.

sanitize_logs.sh changes for v2.1


Added:


  • argument parsing for --mask-ip=
  • argument parsing for --keep-external-ip
  • environment passing to AWK
  • usage/help text
  • new log header
  • verification scanner updates

Modified:


  • output formatting
  • error handling
  • backup naming consistency

Unchanged:


  • file-location logic
  • redaction-map and count handling
  • syslog logging

Here's the main file:
#!/bin/sh
# sanitize_logs.sh – Asuswrt-Merlin log sanitizer controller
# Version: v2.1.0 (2025-11-12)
#
# Default behavior:
# * All IPs (LAN + WAN) sanitized → IP_#
#
# Switches:
# --mask-ip=A.B.C.D Mask LAN IP octets (1=keep, 0=mask)
# Automatically enables LAN masking.
#
# --keep-external-ip Preserve WAN/public IPs instead of sanitizing.
#
# -h, --help Display usage.
#
# Notes:
# * LAN masking applies only to RFC1918 IPs (10/8, 172.16–31/12, 192.168/16)
# * Public IPs are sanitized unless --keep-external-ip is used
#
###############################################################################

VERSION="v2.1.0 (2025-11-12)"

INPUT="/tmp/syslog.log"
KEEP_EXTERNAL_IP=0
MASK_IP="" # A.B.C.D mask for LAN IPs

###############################################################################
# Parse arguments
###############################################################################

while [ "$#" -gt 0 ]; do
case "$1" in
--mask-ip=*)
MASK_IP=$(echo "$1" | sed 's/--mask-ip=//')
;;
--keep-external-ip)
KEEP_EXTERNAL_IP=1
;;
-h|--help)
echo "sanitize_logs.sh $VERSION"
echo ""
echo "Usage: sanitize_logs.sh [options] [input_log]"
echo ""
echo "Options:"
echo " --mask-ip=A.B.C.D Mask LAN octets (1=keep, 0=mask)"
echo " Example: 1.0.0.1 keeps first+last octet"
echo ""
echo " --keep-external-ip Keep public IPs unmodified"
echo " (LAN IPs still masked if mask-ip used)"
echo ""
echo " -h, --help Show help"
echo ""
echo "If no input_log is given, defaults to /tmp/syslog.log"
exit 0
;;
-*)
echo "Warning: unknown option: $1" >&2
;;
*)
# Non-option argument treated as input log path
INPUT="$1"
;;
esac
shift
done

###############################################################################
# Prepare file paths
###############################################################################

STAMP=$(date +%Y%m%d-%H%M)
BACKUP="/tmp/syslog_original_${STAMP}.log"
OUTPUT="/tmp/syslog_sanitized_${STAMP}.log"
MAPFILE="/tmp/syslog_map_${STAMP}.txt"
COUNTFILE="/tmp/syslog_counts_${STAMP}.txt"

###############################################################################
# Display settings
###############################################################################

echo "sanitize_logs.sh $VERSION"
echo "Input log: $INPUT"
echo "Mask IP: ${MASK_IP:-none}"
echo "Keep external IPs: $KEEP_EXTERNAL_IP"
echo ""

###############################################################################
# Backup input log
###############################################################################

if [ -f "$INPUT" ]; then
cp "$INPUT" "$BACKUP"
echo "Original log backed up to: $BACKUP"
else
echo "ERROR: input log not found: $INPUT" >&2
fi

echo "Sanitizing $INPUT ..."

# Empty map and count files
: >"$MAPFILE"
: >"$COUNTFILE"

###############################################################################
# Run AWK engine
###############################################################################

MASK_IP="$MASK_IP" \
KEEP_EXTERNAL_IP="$KEEP_EXTERNAL_IP" \
awk -v MAP="$MAPFILE" -v COUNTFILE="$COUNTFILE" \
-f /jffs/scripts/sanitize_logs.awk \
"$INPUT" > "$OUTPUT"

logger "sanitize_logs $VERSION: sanitized $INPUT -> $OUTPUT"

echo "Done. Sanitized log: $OUTPUT"
echo "Mapping (keep private): $MAPFILE"
echo "Redaction counts: $COUNTFILE"
echo "Original backup: $BACKUP"
echo ""

###############################################################################
# Verification Pass (spot-check)
###############################################################################

echo "Running verification scan for unredacted entries..."

BAD=$(grep -E \
"Authorization:|acme_challenge=|ACCOUNT_THUMBPRINT|Adding txt value|dhcp-option DOMAIN|RADIUS:|dropbear|sshd|VERIFY OK|SHA1|SHA256|MD5" \
"$OUTPUT" 2>/dev/null \
| grep -v "\[REDACTED" )

if [ -n "$BAD" ]; then
echo "⚠️ Potential unredacted sensitive lines:"
echo "$BAD"
else
echo "✅ Verification passed: no unredacted sensitive data detected."
fi

echo ""
echo "Summary of redactions:"
cat "$COUNTFILE" 2>/dev/null
 

Latest threads

Support SNBForums w/ Amazon

If you'd like to support SNBForums, just use this link and buy anything on Amazon. Thanks!

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!

Staff online

Back
Top