---
title: "Authenticate calls between your own · Agent Wranglers"
url: https://agentwranglers.se/playbook/infrastructure/networking-and-dns/service-to-service-auth
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Authenticate calls between your own services

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/infrastructure/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Networking and DNS](https://agentwranglers.se/playbook/infrastructure/networking-and-dns)
- Rule: Make every service prove who it is on every call to another service, even inside a private network. Refuse a call without a valid identity, and allow each caller only the services it needs.
- How: A private network proves nothing about the caller. An attacker who gets into one server can call every service that trusts the network. Give each service its own identity, such as a client certificate for mutual TLS or a short-lived signed token from the platform. A service mesh, such as Istio or Linkerd, can add mutual TLS without code changes. Keep a list of the callers each service accepts, in code. Tokens between services follow [Authenticate with tokens in headers](https://agentwranglers.se/playbook/api-design/api-security/token-auth).
- Check: From a server inside the network, a call to an internal service with no certificate or token fails the handshake or gets 401. A call with a valid identity that is missing from the caller list gets 403.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import https from 'node:https';
import { readFileSync } from 'node:fs';

const ALLOWED_CALLERS = new Set(['orders.internal.example.com']);

https.createServer({
  key: readFileSync('/etc/tls/billing.key'),
  cert: readFileSync('/etc/tls/billing.crt'),
  ca: readFileSync('/etc/tls/internal-ca.crt'),
  requestCert: true,
  rejectUnauthorized: true,
}, (req, res) => {
  const caller = req.socket.getPeerCertificate().subject?.CN;
  if (!ALLOWED_CALLERS.has(caller)) return res.writeHead(403).end();
  res.end('ok');
}).listen(8443);
```

### Python example

```python
import requests

response = requests.get(
    "https://billing.internal.example.com:8443/invoices/42",
    cert=("/etc/tls/orders.crt", "/etc/tls/orders.key"),
    verify="/etc/tls/internal-ca.crt",
    timeout=5,
)
```

### Shell example

```sh
# Run from an app server. A call without a client certificate must fail.
curl -sS --cacert /etc/tls/internal-ca.crt https://billing.internal.example.com:8443/invoices/42 \
  && echo "FAIL: call without identity accepted"
```

## Sources

- [NIST SP 800-207 Zero Trust Architecture](https://csrc.nist.gov/pubs/sp/800/207/final)
- [NIST SP 800-204A Building Secure Microservices-based Applications Using Service-Mesh Architecture](https://csrc.nist.gov/pubs/sp/800/204/a/final)

## Related rules

- [Authenticate with tokens in headers](https://agentwranglers.se/playbook/api-design/api-security/token-auth) in API design

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/infrastructure/networking-and-dns/service-to-service-auth">Agent Wranglers infrastructure playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Shorten the TTL before you move a record](https://agentwranglers.se/playbook/infrastructure/networking-and-dns/lower-ttl-before-changes)
- [Networking and DNS](https://agentwranglers.se/playbook/infrastructure/networking-and-dns)
