Certify allows easy automatic certificate distribution and maintenance.
Certificates are requested as TLS connections
are made, courtesy of the GetCertificate
and GetClientCertificate
tls.Config
hooks. Certificates are optionally cached. Simultaneous requests
are deduplicated to minimize pressure on issuers.
My presentation from GolangPiter 2019 contains a walkthrough of how to configure your Vault instance to securely issue certificates for your Go clients and servers.
Are you using Certify and want to be visible here? Open an issue!
Certify exposes an Issuer
interface which is used to allow switching
between issuer backends.
Currently implemented issuers:
- Vault PKI Secrets Engine
- Cloudflare CFSSL Certificate Authority
- AWS Certificate Manager Private Certificate Authority
Create an issuer:
issuer := &vault.Issuer{
URL: &url.URL{
Scheme: "https",
Host: "my-local-vault-instance.com",
},
Token: "myVaultToken",
Role: "myVaultRole",
}
Create a Certify:
c := &certify.Certify{
// Used when request client-side certificates and
// added to SANs or IPSANs depending on format.
CommonName: "MyServer.com",
Issuer: issuer,
// It is recommended to use a cache.
Cache: certify.NewMemCache(),
// It is recommended to set RenewBefore.
// Refresh cached certificates when < 24H left before expiry.
RenewBefore: 24*time.Hour,
}
Use in your TLS Config:
tlsConfig := &tls.Config{
GetCertificate: c.GetCertificate,
}
That's it! Both server-side and client-side certificates can be generated:
tlsConfig := &tls.Config{
GetClientCertificate: c.GetClientCertificate,
}
For an end-to-end example using gRPC with mutual TLS authentication, see the Vault tests.
When setting up a Vault PKI backend and creating a role for Certify to use when it requests certificates, you'll be asked to specify the key type for the role to use. By default, Certify uses ecdsa
keys with a 256-bit key length when it generates CSRs for Vault to sign.
If your Vault PKI role is created with a key type other than ec
or any
, API calls to Vault will fail with errors like
Error making API request.
URL: PUT https://localhost:8200/v1/pki/sign/example.com
Code: 400. Errors:
* role requires keys of type rsa
To use Certify with rsa
or ed25519
keys, you'll need to pass a custom KeyGenerator
to Certify which satisfies the certify.KeyGenerator
interface. For example, for an rsa
key:
type rsaKeyGenerator struct {
key crypto.PrivateKey
err error
o sync.Once
}
// This satisfies the `certify.KeyGenerator` interface.
func (s *rsaKeyGenerator) Generate() (crypto.PrivateKey, error) {
s.o.Do(func() {
// Use a different random data provider and key length if required.
s.key, s.err = rsa.GenerateKey(rand.Reader, 2048)
})
return s.key, s.err
}
// Configure Certify's CSR generator to use our custom KeyGenerator
cfg := &certify.CertConfig{
KeyGenerator: &rsaKeyGenerator{},
}
certify := &certify.Certify{
CommonName: "service1.example.com",
Cache: certify.DirCache("certificates"),
Issuer: issuer,
RenewBefore: 10 * time.Minute,
// Pass our custom configuration to Certify
CertConfig: cfg,
}
If you really want to use Certify but you are not able to use Go, there is now a Docker image available!
Simply configure this image as the access point for your Kubernetes pod and let it proxy traffic to your server.
Certify hooks into the GetCertificate
and GetClientCertificate
methods of
the Go TLS stack Config
struct. These get called when the server/client
respectively is required to present its certificate. If possible, this is
fetched from the cache, based on the requested server name. If not, a new
certificate is issued with the requested server name present. For client
requests, the configured CommonName
is used.