Skip to content

Filter internet-scanner noise from control-plane HTTP error log - #29

Draft
saltzm wants to merge 1 commit into
mainfrom
devin/1784840079-http-errorlog-scanner-filter
Draft

Filter internet-scanner noise from control-plane HTTP error log#29
saltzm wants to merge 1 commit into
mainfrom
devin/1784840079-http-errorlog-scanner-filter

Conversation

@saltzm

@saltzm saltzm commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

The control plane listens on public :443 (net.Listen("tcp", ":443") in lib/server.go), so its socket is constantly hit by internet port scanners and bots. Their failed TLS handshakes and malformed HTTP/2 prefaces are logged by Go's net/http server via http.Server.ErrorLog, which — because we never set it — falls back to log.Default() → stderr. In Datadog (source:vprox) these dominate the "error"-looking volume: ~1.88M http: TLS handshake error from <ip>: ... lines and ~800 http2: server: error reading preface from client <ip>: ... lines over 7 days. None are real server faults.

This wires http.Server.ErrorLog to a filtering io.Writer that drops those two line categories and forwards everything else to stderr (same destination/format as before, LstdFlags):

var scannerNoiseSubstrings = []string{
    "TLS handshake error",              // "http: TLS handshake error from 1.2.3.4:5678: ..."
    "error reading preface from client", // "http2: server: error reading preface from client ..."
}

type scannerNoiseFilter struct{}
func (scannerNoiseFilter) Write(p []byte) (int, error) {
    for _, s := range scannerNoiseSubstrings {
        if strings.Contains(string(p), s) { return len(p), nil } // drop
    }
    return os.Stderr.Write(p)
}

httpServer := &http.Server{
    ...
    ErrorLog: log.New(scannerNoiseFilter{}, "", log.LstdFlags),
}

Minimal and scoped: no change to vprox's own log.Printf call sites and no behavior change for any non-scanner log line. go build ./..., go vet ./..., go test ./..., and gofmt all pass.

Context / analysis

vprox currently has no log levels — everything is stdlib log.Printf at one undifferentiated severity, which is why Datadog classifies it all as status:info. The scanner lines are the highest-volume "errors" yet aren't emitted by vprox's own logger at all; they come from the stdlib http server, so this ErrorLog hook is the correct place to control them.

Alternatives (intentionally NOT in this PR)

  • log/slog migration: introduce a structured leveled logger (stdlib, no new dep vs zap/zerolog) and reclassify these lines as warn/debug instead of string-filtering, plus confirm the Datadog JSON pipeline maps levelstatus. Larger, mechanical, higher review surface — worth doing separately.
  • Datadog exclusion filter (no code): drop these substrings at ingestion. Instant/reversible and saves indexing cost, but loses per-line scan visibility; pair with an aggregate scan-count metric. Complementary to this change.

Draft for review of the approach/substring list before merge.

Link to Devin session: https://modal.devinenterprise.com/sessions/a2093159c7e541998fbd2fd1c877f3f8
Requested by: @saltzm

Co-Authored-By: matthew@modal.com <saltzm@gmail.com>
@saltzm saltzm self-assigned this Jul 23, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant