{"record":{"id":"5d2ea1704ba36f9b","repo":"nats-io/nats-server","slug":"w-invalid-signature","errorCode":null,"errorMessage":"%w: invalid signature","messagePattern":"%w: invalid signature","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/client_proxyproto.go","lineNumber":269,"sourceCode":"\tif err != nil {\n\t\treturn nil, firstBytes, err\n\t}\n\n\tswitch version {\n\tcase 1:\n\t\t// v1 parser expects \"PROXY \" prefix already consumed\n\t\treturn readProxyProtoV1Header(conn)\n\tcase 2:\n\t\t// Read rest of v2 signature (bytes 6-11, total 6 more bytes)\n\t\tremaining := make([]byte, 6)\n\t\tif _, err := io.ReadFull(conn, remaining); err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to read v2 signature: %w\", err)\n\t\t}\n\n\t\t// Verify full signature\n\t\tfullSig := string(firstBytes) + string(remaining)\n\t\tif fullSig != proxyProtoV2Sig {\n\t\t\treturn nil, nil, fmt.Errorf(\"%w: invalid signature\", errProxyProtoInvalid)\n\t\t}\n\n\t\t// Read rest of header: ver/cmd, fam/proto, addr-len (4 bytes)\n\t\theader := make([]byte, 4)\n\t\tif _, err := io.ReadFull(conn, header); err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to read v2 header: %w\", err)\n\t\t}\n\n\t\t// Continue with parsing\n\t\taddr, err := parseProxyProtoV2Header(conn, header)\n\t\treturn addr, nil, err\n\tdefault:\n\t\treturn nil, nil, fmt.Errorf(\"unsupported PROXY protocol version: %d\", version)\n\t}\n}\n\n// readProxyProtoV2Header is kept for backward compatibility and direct testing.\n// It reads and parses a PROXY protocol v2 header from the connection.","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/client_proxyproto.go#L251-L287","documentation":"This error is returned by readProxyProtoHeader when a connection declared as PROXY protocol v2 has bytes 0-11 that do not exactly match the 12-byte v2 binary signature \\r\\n\\r\\n\\x00\\r\\nQUIT\\n. It wraps errProxyProtoInvalid (\"invalid PROXY protocol header\"), so callers can match it with errors.Is(err, errProxyProtoInvalid). The version was detected as v2 from the first 6 bytes, but the following 6 bytes diverged, meaning the sender is not speaking valid PROXY protocol v2.","triggerScenarios":"detectProxyProtoVersion reads 6 bytes matching the first half of the v2 signature (proxyProtoV2Sig[:6]); the code then io.ReadFull's the remaining 6 signature bytes, concatenates them, and the full 12-byte string differs from proxyProtoV2Sig. This happens when a client sends data whose first 6 bytes coincidentally match \\x0D\\x0A\\x0D\\x0A\\x00\\x0D but is not a PROXY v2 header, or when a truncated/corrupted v2 header arrives.","commonSituations":"A load balancer (HAProxy, AWS NLB, Envoy) is configured for a different PROXY protocol variant than the client actually sends; a health checker or garbage/malicious client sends binary data that happens to start with the signature prefix; a proxy sends a truncated header; proxy protocol is enabled on the NATS server but the connecting peer actually sends plain client protocol (the first bytes are consumed and cannot be replayed for the v2 path).","solutions":["Fix the upstream proxy configuration so it actually emits PROXY protocol v2 (e.g. HAProxy 'send-proxy-v2' not 'send-proxy' for v1, or vice versa per listener expectations).","If clients do not send PROXY protocol at all, disable proxy protocol on that listener/port; the first 6 bytes of a non-PROXY client will otherwise match or corrupt detection.","Capture the raw first bytes of the offending connection and compare against the expected signature \\x0D\\x0A\\x0D\\x0A\\x00\\x0D\\x0A\\x51\\x55\\x49\\x54\\x0A to identify what the sender is actually emitting.","Verify no middleware/TLS terminator is mangling binary bytes; PROXY v2 is binary and must not pass through anything that transforms the byte stream."],"exampleFix":"// before: server expects v2 but LB sends v1 text lines\n// haproxy.cfg\n//   server nats1 10.0.0.1:4222 send-proxy\n// after: configure v2 to match the binary signature check\n//   server nats1 10.0.0.1:4222 send-proxy-v2","handlingStrategy":"validation","validationCode":"// Validate the first bytes of a connection before enabling PROXY protocol parsing:\n// the sender must begin with the 12-byte v2 signature.\nfunc looksLikeProxyV2(first []byte) bool {\n\tsig := []byte(\"\\x0D\\x0A\\x0D\\x0A\\x00\\x0D\\x0A\\x51\\x55\\x49\\x54\\x0A\")\n\treturn len(first) >= 12 && string(first[:12]) == string(sig)\n}","typeGuard":"func isProxyProtoInvalid(err error) bool {\n\treturn errors.Is(err, errProxyProtoInvalid)\n}","tryCatchPattern":"addr, extra, err := readProxyProtoHeader(conn)\nif err != nil {\n\tif errors.Is(err, errProxyProtoInvalid) {\n\t\t// treat as non-PROXY or misconfigured sender: log and close\n\t\tconn.Close()\n\t\treturn\n\t}\n\t// I/O or other failure\n\treturn\n}","preventionTips":["Pin the LB config to the exact PROXY variant: send-proxy-v2 for binary v2, send-proxy for v1 text.","Log/inspect the first bytes of rejected connections to identify misconfigured senders early.","Never enable PROXY protocol parsing on ports that also accept raw clients.","Ensure no middleware (TLS terminator, WAF) rewrites the binary header bytes."],"tags":["proxy-protocol","network","signature-validation"],"backgroundTag":"proxy-protocol-invalid-signature","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}