Skip to content

Commit 0d8fee1

Browse files
committed
docs(security): enhance explanation of peer certificate authentication
Signed-off-by: Ronald Ngounou <ronald.ngounou@yahoo.com>
1 parent 51b6961 commit 0d8fee1

1 file changed

Lines changed: 54 additions & 2 deletions

File tree

content/en/docs/v3.7/op-guide/security.md

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,57 @@ Since [v3.2.2](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.2
306306

307307
when peer B's remote IP address is `10.138.0.2` and `invalid.domain` is a invalid host. When peer B tries to join the cluster, peer A successfully authenticates B, since Subject Alternative Name (SAN) field has a valid matching IP address. See [issue#8206](https://github.com/etcd-io/etcd/issues/8206) for more detail.
308308

309-
Since [v3.2.5](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.2.md#v325-2017-08-04), [server supports reverse-lookup on wildcard DNS `SAN`](https://github.com/etcd-io/etcd/pull/8281). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of names mapping to that address (e.g. `nslookup IPADDR`). Then accepts the connection if those names have a matching name with peer cert's DNS names (either by exact or wildcard match). If none is matched, server forward-lookups each DNS entry in peer cert (e.g. look up `example.default.svc` when the entry is `*.example.default.svc`), and accepts connection only when the host's resolved addresses have the matching IP address with the peer's remote IP address. For example, peer B's CSR (with `cfssl`) is:
309+
Since [v3.2.5](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.2.md#v325-2017-08-04), [server supports reverse-lookup on wildcard DNS `SAN`](https://github.com/etcd-io/etcd/pull/8281). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of host names. And either exact or wildcard match the host names with peer B's cert DNS names in Subject Alternative Name (SAN) field. If none of reverse/forward lookups worked, it returns an error `"tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]`. See [issue#8268](https://github.com/etcd-io/etcd/issues/8268) for more detail.
310+
311+
[v3.3.0](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.3.md) adds [`etcd --peer-cert-allowed-cn`](https://github.com/etcd-io/etcd/pull/8616) flag to support [CN(Common Name)-based auth for inter-peer connections](https://github.com/etcd-io/etcd/issues/8262). Kubernetes TLS bootstrapping involves generating dynamic certificates for etcd members and other system components (e.g. API server, kubelet, etc.). Maintaining different CAs for each component provides tighter access control to etcd cluster but often tedious. When `--peer-cert-allowed-cn` flag is specified, node can only join with matching common name even with shared CAs. For example, each member in 3-node cluster is set up with CSRs (with `cfssl`) as below:
312+
313+
```json
314+
{
315+
"CN": "etcd.local",
316+
"hosts": [
317+
"m1.etcd.local",
318+
"127.0.0.1",
319+
"localhost"
320+
],
321+
```
322+
323+
```json
324+
{
325+
"CN": "etcd.local",
326+
"hosts": [
327+
"m2.etcd.local",
328+
"127.0.0.1",
329+
"localhost"
330+
],
331+
```
332+
333+
```json
334+
{
335+
"CN": "etcd.local",
336+
"hosts": [
337+
"m3.etcd.local",
338+
"127.0.0.1",
339+
"localhost"
340+
],
341+
```
342+
343+
Then only peers with matching common names will be authenticated if `--peer-cert-allowed-cn etcd.local` is given. And nodes with different CNs in CSRs or different `--peer-cert-allowed-cn` will be rejected:
344+
345+
```bash
346+
$ etcd --peer-cert-allowed-cn m1.etcd.local
347+
348+
I | embed: rejected connection from "127.0.0.1:48044" (error "CommonName authentication failed", ServerName "m1.etcd.local")
349+
I | embed: rejected connection from "127.0.0.1:55702" (error "remote error: tls: bad certificate", ServerName "m3.etcd.local")
350+
```
351+
352+
Additional clarification: how matching works
353+
-----------------------------------------
354+
- --peer-cert-allowed-cn performs an exact string comparison against the certificate's Subject Common Name (CN). The certificate's CN must exactly equal the value passed to `--peer-cert-allowed-cn`. There is no implicit "is a subdomain of" or suffix matching for CN (for example, a CN of `m1.etcd.local` does NOT match `etcd.local`).
355+
- Hostname-based flags such as `--peer-cert-allowed-hostname` and `--client-cert-allowed-hostname` use the certificate's Subject Alternative Name (SAN) entries (DNSNames and IPAddresses) for verification and follow standard TLS name-matching rules:
356+
- DNS SAN entries support exact and wildcard matches per usual TLS rules (e.g., `*.example.com` can match `a.example.com`).
357+
- IP SAN entries are matched by exact IP address equality.
358+
- SAN is preferred for name verification; implementations use SAN entries for hostname checks rather than the CN. If an environment or client library falls back to CN when no SAN is present, that is a compatibility behavior of the client library, not the SAN-first rule.
359+
- In short: use `--peer-cert-allowed-cn` only when you want an exact CN equality check; use the hostname flags when you want SAN-based hostname/IP verification (and to take advantage of wildcard DNS matching where appropriate).
310360

311361
```json
312362
{
@@ -317,7 +367,9 @@ Since [v3.2.5](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.2
317367
],
318368
```
319369

320-
when peer B's remote IP address is `10.138.0.2`. When peer B tries to join the cluster, peer A reverse-lookup the IP `10.138.0.2` to get the list of host names. And either exact or wildcard match the host names with peer B's cert DNS names in Subject Alternative Name (SAN) field. If none of reverse/forward lookups worked, it returns an error `"tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]`. See [issue#8268](https://github.com/etcd-io/etcd/issues/8268) for more detail.
370+
when peer B's remote IP address is `10.138.0.2` and `invalid.domain` is a invalid host. When peer B tries to join the cluster, peer A successfully authenticates B, since Subject Alternative Name (SAN) field has a valid matching IP address. See [issue#8206](https://github.com/etcd-io/etcd/issues/8206) for more detail.
371+
372+
Since [v3.2.5](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.2.md#v325-2017-08-04), [server supports reverse-lookup on wildcard DNS `SAN`](https://github.com/etcd-io/etcd/pull/8281). For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of host names. And either exact or wildcard match the host names with peer B's cert DNS names in Subject Alternative Name (SAN) field. If none of reverse/forward lookups worked, it returns an error `"tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]`. See [issue#8268](https://github.com/etcd-io/etcd/issues/8268) for more detail.
321373

322374
[v3.3.0](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.3.md) adds [`etcd --peer-cert-allowed-cn`](https://github.com/etcd-io/etcd/pull/8616) flag to support [CN(Common Name)-based auth for inter-peer connections](https://github.com/etcd-io/etcd/issues/8262). Kubernetes TLS bootstrapping involves generating dynamic certificates for etcd members and other system components (e.g. API server, kubelet, etc.). Maintaining different CAs for each component provides tighter access control to etcd cluster but often tedious. When `--peer-cert-allowed-cn` flag is specified, node can only join with matching common name even with shared CAs. For example, each member in 3-node cluster is set up with CSRs (with `cfssl`) as below:
323375

0 commit comments

Comments
 (0)