Home

/

Keep PII Out of Your LLM

/

The Sidecar You Can Trust

The Sidecar You Can Trust

Chapter 14
Part IV
3
min read
The seven controls around the detection sidecar: TLS in transit, nothing persisted, logging off and asserted, egress denied, images pinned by digest, non-root least privilege, and a patch cadence

The uncomfortable property

The detection service sees 100% of your personal data. Not a sample, not the sensitive subset, all of it, because it has to read everything to decide what is sensitive.

Every other component in your estate sees a slice. The sidecar sees the whole thing, in plaintext, at the moment it is most concentrated. That inverts the usual risk calculation. A component with no business logic, no database, and no user interface has become one of the most security-relevant things you run.

Seven controls follow from that, in rough order of how much they matter.

In transit

Use TLS between the gateway and the sidecar, on a private network, inside the same cluster, without exception. The word doing the work in that sentence is the last one.

The objection is that internal traffic is already isolated, and the objection has been wrong for about a decade. Cluster networks are shared, service meshes route through infrastructure you do not own, cloud provider networking has had cross-tenant issues, and "internal" is a statement about intent rather than about who can observe a packet.

builder.Services.AddHttpClient<PresidioAnalyzer>(c =>
{
    c.BaseAddress = new Uri("https://presidio-analyzer:3000");
})
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
    SslOptions = new SslClientAuthenticationOptions
    {
        // Pin to your internal CA; do not fall back to the system trust store.
        RemoteCertificateValidationCallback = InternalCa.Validate
    }
});

Where your platform offers mutual TLS cheaply, take it, because it also authenticates the caller and control 6 needs that anyway. Where it does not, one-way TLS plus a shared secret is an acceptable stopping point.

At rest: persist nothing

The sidecar should write nothing to disk. No request bodies, no temporary files, no spooled uploads, no model cache containing user text.

Enforce it rather than requesting it:

# The only compose fragment in this book.
services:
  presidio-analyzer:
    image: presidio-analyzer@sha256:<digest>
    read_only: true
    tmpfs:
      - /tmp:size=64m,mode=1777
    user: "10001:10001"
    cap_drop: [ALL]
    security_opt:
      - no-new-privileges:true
    networks: [detection]
    environment:
      - LOG_LEVEL=WARNING

networks:
  detection:
    internal: true        # no route to the internet

A read-only root filesystem turns "we do not write files" from a claim into a property. If something tries, it fails, loudly, in testing, rather than quietly succeeding in production.

The one thing that legitimately persists is the token vault from Chapter 9, and it is the most sensitive store you own, because it holds the mapping from token back to person. Three requirements:

  • Encrypted at rest, with the key in a key management service rather than in configuration.
  • The service holding the ciphertext does not hold the key. Separation is what makes a stolen backup useless.
  • A TTL matched to the conversation. Not ninety days. Expire it and let the map go.

Logs: the detector's log is the worst log you have

Follow the logic. The detector reads all your personal data. Default log levels in most frameworks write request bodies at debug or trace. Someone raised the log level during an incident in March and it was never lowered.

You now have a single, centralised, indexed, long-retention store of every piece of personal data in your organisation, sitting in your observability platform, searchable by everyone with a dashboard login, replicated wherever your vendor replicates.

Turn request-body logging off explicitly, and then assert it:

[Fact]
public async Task Gateway_NeverLogsPromptContent()
{
    var sink = new CapturingLogSink();
    var gateway = BuildGateway(sink);

    await gateway.CompleteAsync(
        new GatewayRequest(Prompt: "Contact Sarah Whitfield at swhitfield@example.com"), default);

    Assert.DoesNotContain(sink.Entries, e =>
        e.Contains("Sarah Whitfield", StringComparison.OrdinalIgnoreCase)
        || e.Contains("swhitfield@example.com", StringComparison.OrdinalIgnoreCase));
}

That test is worth more than a policy document. It fails when someone adds a helpful debug line, and it fails in CI rather than in an audit.

Chapter 16 takes this further, because the problem is not confined to the sidecar.

the-leak-you-cant-see
blast-radius
what-counts-as-pii
the-five-doors
the-accuracy-reckoning
the-hybrid-that-does-not-work
deterministic-detection
npi-in-c
the-three-way-choice
calling-the-analyzer-from-c
measuring-your-own-demo-gap
choosing-the-operating-point
the-ladder-of-safeguards
pseudonymisation
the-round-trip
restoring-safely
when-masking-breaks-the-task
plausibility-hazard
the-architecture-that-holds
the-reference-architecture
dont-send-it-at-all
structure-beats-prose
the-gateway
failure-is-a-policy-decision
the-sidecar-you-can-trust
egress-deny-it-at-the-network
rag-and-agents
de-identify-before-you-embed
dual-model-separation
the-boring-controls
evidence-and-the-first-thirty-days
week-two-the-chokepoint-and-the-fast-layer
entity-catalogue-and-c-validators
mbi-positional-rules
tooling-at-a-glance
azure-ai-language-pii-in-detail
container-trust-checklist
sources
azure-ai-language
provider-retention

Download the full PDF for free?

Free download — no account required

Get the PDF
Get the PDF
Related Chapters
Free Download
Get the full PDF
All pages, including all code examples, diagrams, and the appendix reference card.
No spam. Unsubscribe at any time.
Your email won't be shared.
Oops! There's a problem with your request. We're working on fixing it. Please try again later.