{"id":13581917,"url":"https://github.com/johanbrandhorst/certify","last_synced_at":"2025-04-12T19:46:08.872Z","repository":{"id":38455134,"uuid":"126387751","full_name":"johanbrandhorst/certify","owner":"johanbrandhorst","description":"Automatic client and server certificate distribution and maintenance","archived":false,"fork":false,"pushed_at":"2023-04-04T21:22:11.000Z","size":7335,"stargazers_count":471,"open_issues_count":13,"forks_count":37,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-03T23:09:55.234Z","etag":null,"topics":["acmpca","aws","certificate","cfssl","go","golang","mtls","tls","vault"],"latest_commit_sha":null,"homepage":"https://jbrandhorst.com/post/certify/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johanbrandhorst.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-03-22T19:52:44.000Z","updated_at":"2025-03-27T23:30:04.000Z","dependencies_parsed_at":"2024-01-15T15:46:05.809Z","dependency_job_id":"c42fbbe1-d6a0-47d4-ae65-7ad1c24d3512","html_url":"https://github.com/johanbrandhorst/certify","commit_stats":null,"previous_names":["johanbrandhorst/certbot"],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johanbrandhorst%2Fcertify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johanbrandhorst%2Fcertify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johanbrandhorst%2Fcertify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johanbrandhorst%2Fcertify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johanbrandhorst","download_url":"https://codeload.github.com/johanbrandhorst/certify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248625491,"owners_count":21135513,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acmpca","aws","certificate","cfssl","go","golang","mtls","tls","vault"],"created_at":"2024-08-01T15:02:19.154Z","updated_at":"2025-04-12T19:46:08.839Z","avatar_url":"https://github.com/johanbrandhorst.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Certify\n\n[![CircleCI](https://img.shields.io/circleci/project/github/johanbrandhorst/certify/master.svg?style=flat-square)](https://circleci.com/gh/johanbrandhorst/certify)\n[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](https://godoc.org/github.com/johanbrandhorst/certify)\n[![Go Report Card](https://goreportcard.com/badge/github.com/johanbrandhorst/certify?style=flat-square)](https://goreportcard.com/report/github.com/johanbrandhorst/certify)\n[![Code Coverage](https://img.shields.io/codecov/c/github/johanbrandhorst/certify/master.svg?style=flat-square)](https://codecov.io/gh/johanbrandhorst/certify)\n[![Releases](https://img.shields.io/github/release/johanbrandhorst/certify.svg?style=flat-square)](https://github.com/johanbrandhorst/certify/releases)\n[![License](https://img.shields.io/github/license/johanbrandhorst/certify.svg?style=flat-square)](LICENSE)\n[![Join the chat at https://gitter.im/go-certify/community](https://img.shields.io/gitter/room/go-certify/community.svg?style=flat-square)](https://gitter.im/go-certify/community)\n\n![Certify](logo.png \"Certify\")\n\nCertify allows easy automatic certificate distribution and maintenance.\nCertificates are requested as TLS connections\nare made, courtesy of the `GetCertificate` and `GetClientCertificate`\n`tls.Config` hooks. Certificates are optionally cached. Simultaneous requests\nare deduplicated to minimize pressure on issuers.\n\n## Vault walkthrough\n\nMy presentation from [GolangPiter 2019](https://golangpiter.com/en/materials/2646)\ncontains a walkthrough of how to configure your Vault instance to securely issue\ncertificates for your Go clients and servers.\n\n[![Certify presentation](https://img.youtube.com/vi/boG7BSRaJ9E/0.jpg)](https://www.youtube.com/watch?v=boG7BSRaJ9E)\n\n## Users\n\n- [Georgetown University](https://www.georgetown.edu/)\n\nAre you using Certify and want to be visible here? Open an issue!\n\n## Issuers\n\nCertify exposes an `Issuer` interface which is used to allow switching\nbetween issuer backends.\n\nCurrently implemented issuers:\n\n- [Vault PKI Secrets Engine](https://vaultproject.io)\n- [Cloudflare CFSSL Certificate Authority](https://cfssl.org/)\n- [AWS Certificate Manager Private Certificate Authority](https://aws.amazon.com/certificate-manager/private-certificate-authority/)\n\n## Usage\n\nCreate an issuer:\n\n```go\nissuer := \u0026vault.Issuer{\n    URL: \u0026url.URL{\n        Scheme: \"https\",\n        Host: \"my-local-vault-instance.com\",\n    },\n    Token:     \"myVaultToken\",\n    Role:      \"myVaultRole\",\n}\n```\n\nCreate a Certify:\n\n```go\nc := \u0026certify.Certify{\n    // Used when request client-side certificates and\n    // added to SANs or IPSANs depending on format.\n    CommonName: \"MyServer.com\",\n    Issuer: issuer,\n    // It is recommended to use a cache.\n    Cache: certify.NewMemCache(),\n    // It is recommended to set RenewBefore.\n    // Refresh cached certificates when \u003c 24H left before expiry.\n    RenewBefore: 24*time.Hour,\n}\n```\n\nUse in your TLS Config:\n\n```go\ntlsConfig := \u0026tls.Config{\n    GetCertificate: c.GetCertificate,\n}\n```\n\nThat's it! Both server-side and client-side certificates\ncan be generated:\n\n```go\ntlsConfig := \u0026tls.Config{\n    GetClientCertificate: c.GetClientCertificate,\n}\n```\n\nFor an end-to-end example using gRPC with mutual TLS authentication,\nsee the [Vault tests](./issuers/vault/vault_test.go).\n\n### Vault PKI Key Types\n\nWhen 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.\n\nIf your Vault PKI role is created with a key type other than `ec` or `any`, API calls to Vault will fail with errors like\n\n```bash\nError making API request.\n\nURL: PUT https://localhost:8200/v1/pki/sign/example.com\nCode: 400. Errors:\n\n* role requires keys of type rsa\n```\n\nTo use Certify with `rsa` or `ed25519` keys, you'll need to pass a custom `KeyGenerator` to Certify which satisfies the `certify.KeyGenerator` [interface](https://github.com/johanbrandhorst/certify/blob/168d95c011b19e999a92014956c0d537ab6ff2fc/issuer.go#L17-L20). For example, for an `rsa` key:\n\n```go\n\ntype rsaKeyGenerator struct {\n    key crypto.PrivateKey\n    err error\n    o   sync.Once\n}\n\n// This satisfies the `certify.KeyGenerator` interface.\nfunc (s *rsaKeyGenerator) Generate() (crypto.PrivateKey, error) {\n    s.o.Do(func() {\n        // Use a different random data provider and key length if required.\n        s.key, s.err = rsa.GenerateKey(rand.Reader, 2048)\n    })\n    return s.key, s.err\n}\n\n// Configure Certify's CSR generator to use our custom KeyGenerator\ncfg := \u0026certify.CertConfig{\n    KeyGenerator: \u0026rsaKeyGenerator{},\n}\n\ncertify := \u0026certify.Certify{\n    CommonName:  \"service1.example.com\",\n    Cache:       certify.DirCache(\"certificates\"),\n    Issuer:      issuer,\n    RenewBefore: 10 * time.Minute,\n    // Pass our custom configuration to Certify\n    CertConfig:  cfg,\n}\n```\n\n## Docker image (sidecar model)\n\nIf you really want to use Certify but you are not able to use Go, there is\nnow a [Docker image](https://hub.docker.com/r/jfbrandhorst/certify) available!\n\nSimply configure this image as the access point for your Kubernetes pod and\nlet it proxy traffic to your server.\n\n## How does it work?\n\n![How it works](howitworks.svg \"How it works\")\n\nCertify hooks into the `GetCertificate` and `GetClientCertificate` methods of\nthe Go TLS stack `Config` struct. These get called when the server/client\nrespectively is required to present its certificate. If possible, this is\nfetched from the cache, based on the requested server name. If not, a new\ncertificate is issued with the requested server name present. For client\nrequests, the configured `CommonName` is used.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohanbrandhorst%2Fcertify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohanbrandhorst%2Fcertify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohanbrandhorst%2Fcertify/lists"}