To get a certificate **simply annotate your pods** with a name. An X.509 (TLS/HTTPS) certificate is automatically created and mounted at `/var/run/autocert.step.sm/` along with a corresponding private key and root certificate (everything you need for [mTLS](#motivation)).
> *Note: this project is in **ALPHA**. DON'T use it for anything mission critical. EXPECT breaking changes in minor revisions with little or not warning. PLEASE provide feedback:*
Autocert uses [`step certificates`](https://github.com/smallstep/certificates) to generate keys and issue certificates from your own **internal certificate authority**. This process is secure and automatic, all you have to do is [install autocert](#install) and [annotate your pods](#annotate-pods). Features include:
* Builds on [`step certificates`](https://github.com/smallstep/certificates) so you can also issue certificates to servers, people, and code running in a different cluster and outside of kubernetes
## Motivation
TLS (e.g., HTTPS) is the most widely deployed cryptographic protocol in the world. Mutual TLS (mTLS) provides end-to-end security for service-to-service communication and can **replace complex VPNs** to secure communication into, out of, and between kubernetes clusters. But **to use mTLS to secure internal services you need certificates issued by your own certificate authority (CA)**.
Building and operating a CA, issuing certificates, and making sure they're renewed before they expire is tricky. Autocert does all of this for you.
You'll need `kubectl` and a kubernetes cluster running version `1.9` or later with [webhook admission controllers](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#admission-webhooks) enabled:
For `autocert` to inject a certificate pods must use the `autocert.step.sm/name` annotation to specify their name. The value of this annotation will appear as the name in the issued certificate (the X.509 common name and SAN).
Once the pod has started we can check that our certificate, private key, and root certificate have been properly mounted in our container at `/var/run/autocert.step.sm`.
```bash
$ export SLEEP_POD=$(kubectl get pods -l app=sleep \
-o jsonpath={$.items[0].metadata.name})
$ kubectl exec -it $SLEEP_POD -c sleep -- ls /var/run/autocert.step.sm
root.crt site.crt site.key
```
> 🤔 **Tip:** The `autocert-renewer` sidecar also installs the [`step` CLI tool](https://github.com/smallstep/cli), which we can use to inspect the issued certificate.
With `autocert` issuing and rotating certificates we can start using mTLS between services. The [`examples/hello-mtls`](examples/hello-mtls) directory demonstrates the right way to do mTLS in several languages (contributions welcome :). Let's deploy one.
### Mutual TLS
Build and deploy the `hello-mtls` server for golang:
> curl: (60) SSL certificate problem: unable to get local issuer certificate
> More details here: https://curl.haxx.se/docs/sslcerts.html
>
> curl failed to verify the legitimacy of the server and therefore could not
> establish a secure connection to it. To learn more about this situation and
> how to fix it, please visit the web page mentioned above.
> ```
>
> You'll get similar errors from other tools, libraries, and applications if they're not properly configured to use the `autocert` certificates and keys. Minimally, for (non-mutual) TLS:
>
> * Clients must be configured to trust the `autocert` root certificate (`/var/run/autocert.step.sm/root.crt`) to authenticate a server
> * Servers must be configured to use the key and certificate issued by `autocert` (`/var/run/autocert.step.sm/site.crt` and `/var/run/autocert.step.sm/site.key`) to authenticate *to* a client
>
> If you're doing mTLS the inverse is also true: the server must trust the root certificate to authenticate client, and the client must be configured to use the `autocert`-issued key and certificate. In other words, for mTLS both the client and server should be configured to use `autocert`'s `root.crt`, `site.crt`, and `site.key`. With `curl` this is done using the `--cacert`, `--cert`, and `--key` flags, respectively.
### Exposing services using mTLS
With properly configured mTLS, services can be safely exposed directly to the public internet: **only clients that have a certificate issued by the internal certificate authority will be allowed to connect**. To demonstrate let's expose our `hello-mtls` service.
If you need a refresher, here's a rough approximation of how an mTLS handshake works:
* It's the signing of random numbers that proves we're talking to the right remote. It's the digital equivalent of asking someone to send you a photo of them with today's newspaper.
* The client and server need to have prior knowledge of the root certificate(s) used for signing other certificates.
* The client and server need to be configured to use the correct certificate and private key (the certificate must have been issued by a CA with a trusted root certificate)
#### Exposing `hello-mtls`
Because `hello-mtls` does proper mTLS itself we can expose it simply using a [service with type LoadBalancer](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer).
To connect to `hello-mtls` from outside kubernetes we need a certificate issued by our internal CA. Since `autocert` is built on `step certificates` we can securely issue certificates to users, devices, and workloads running in other environments.
> 🤯 **Note:** To follow along you'll need `step` [installed locally](https://github.com/smallstep/cli#installing).
First, port-forward from localhost to the `step-ca` pod:
```
$ export CA_POD=$(kubectl -n step get pods -l app=ca \
-o jsonpath={$.items[0].metadata.name})
$ kubectl -n step port-forward $CA_POD 4443:4443
```
Now we can use `step` to securely grab the CA's root certificate and obtain a certificate. You'll be prompted to select a provisioner and enter the correct password to continue:
> 🤔 **Tip:** If you want someone (or something) to have a certificate with a particular name, but don't want to give them the ability to provision arbitrary certificates, you can generate a bootstrap token for them:
>
> ```bash
> $ step ca token snarf.local.dev \
> --ca-url https://127.0.0.1:4443 \
> --root root.crt
> eyJhbG...
> ```
>
> They can use the token to obtain a certificate (once):
>
> ```bash
> $ step ca certificate snarf.local.dev snarf.crt snarf.key --token "eyJhbG..."
> ```
>
> Actually, this is exactly what the `autocert` mutating webhook is doing for your pods! Read [how it works](#how-it-works) for more info.
#### Connecting to `hello-mtls`
We're ready to securely connect to `hello-mtls`.
```
$ export HELLO_MTLS_IP=$(kubectl get svc hello-mtls-lb -ojsonpath={$.status.loadBalancer.ingress...?})
> 🤯 **Note:** HTTPS clients check that the name in the server's cerificate match the `authority` portion of the URL (e.g., `https://smallstep.com/` must present a certificate with the name `smallstep.com`). (See [RFC2818](https://tools.ietf.org/html/rfc2818#section-3).)
>
> Our `hello-mtls` service's certificate binds the name `hello-mtls.default.svc.cluster.local` so we *must* connect to it using that name. If we use a different authority we'll get an error:
>
> ```
> $ curl --cacert root.crt \
> --cert snarf.crt \
> --key snarf.key \
> https://127.0.0.1
> curl: (51) SSL: no alternative certificate subject name matches target host name '127.0.0.1'
> ```
>
> In a real production environment you'd address this by either:
>
> * using a properly registered domain name and configuring DNS either globally (e.g., using [ExternalDNS](https://github.com/kubernetes-incubator/external-dns/)), or
> * using internal names and configuring DNS locally in each environment (e.g., using an [ExternalName service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname))
>
> In any case, `hello-mtls.default.svc.cluster.local` must resolve to the right IP.
>
> You could use `/etc/hosts`. Since we're testing with `curl` it's even easier to use the `--resolve` flag to override resolution for a single request.
## How it works
### Architecture
`Autocert` consists of a [webhook admission controllers](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#admission-webhooks) that injects one init container and one sidecar container to handle obtaining a certificate for the first time and renewing a certificate, respectively.
The `autocert` admission webhook will intercept this pod creation request and inject an [init container](bootstrapper/) and [sidecar](renewer/) to manage certificate issuance and renewal, respectively.
It integrates with [`step certificates`](https://github.com/smallstep/certificates) and uses the single-use token bootstrap protocol from that project to mutually authenticate a new pod with your certificate authority.
#### How is this different than [`cert-manager`](https://github.com/jetstack/cert-manager)
#### Doesn't kubernetes already ship with a certificate authority?
Yes, but it's designed for use by the kubernetes control plane rather than by your data plane services. You could use the kubernetes CA to issue certificates for data plane communication, but it's probably not a good idea.
#### Why not use kubernetes CSR resources for this?
Autocert certificates let you secure your data plane (service-to-service) communication using mutual TLS (mTLS). Services and proxies can limit access to clients that also have a certificate issued by your certificate authority (CA). Servers can identify which client is connecting improving visibility and enabling granular access control.
Once certificates are issued you can use mTLS to secure communication in to, out of, and between kubernetes clusters. Services can use mTLS to only allow connections from clients that have their own certificate issued from your CA.
It's like your own Let's Encrypt, but you control who gets a certificate.
#### How is this different than a service mesh?
Certificate management is a necessary building block for any service mesh that uses mutual TLS for authenticated encryption (e.g., istio, linkerd, consul connect). Typically, service mesh systems will provide their own certificate management solution. However, these systems
#### What about DaemonSets, ReplicaSets, StatefulSets, and all the other things that might need certificates?
...?
## Building
...
## Contributing
...
## License
Copyright 2019 Smallstep Labs
Licensed under [the Apache License, Version 2.0](https://github.com/smallstep/certificates/blob/master/LICENSE)