Commit 598de9e6 authored by Matthew Holt's avatar Matthew Holt

vendor: Update certmagic

parent 393bc299
......@@ -16,12 +16,12 @@
// including TLS & HTTPS best practices such as robust OCSP stapling, caching,
// HTTP->HTTPS redirects, and more.
//
// Its high-level API serves your HTTP handlers over HTTPS by simply giving
// Its high-level API serves your HTTP handlers over HTTPS if you simply give
// the domain name(s) and the http.Handler; CertMagic will create and run
// the HTTPS server for you, fully managing certificates during the lifetime
// of the server. Similarly, it can be used to start TLS listeners or return
// a ready-to-use tls.Config -- whatever layer you need TLS for, CertMagic
// makes it easy.
// makes it easy. See the HTTPS, Listen, and TLS functions for that.
//
// If you need more control, create a Config using New() and then call
// Manage() on the config; but you'll have to be sure to solve the HTTP
......@@ -475,10 +475,14 @@ const (
// forward packets from the defaults to whatever these
// are set to; otherwise ACME challenges will fail.
var (
// HTTPPort is the port on which to serve HTTP.
// HTTPPort is the port on which to serve HTTP
// and, by extension, the HTTP challenge (unless
// AltHTTPPort is set).
HTTPPort = 80
// HTTPSPort is the port on which to serve HTTPS.
// HTTPSPort is the port on which to serve HTTPS
// and, by extension, the TLS-ALPN challenge
// (unless AltTLSALPNPort is set).
HTTPSPort = 443
)
......
......@@ -142,6 +142,12 @@ func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) {
// figure out which ports we'll be serving the challenges on
useHTTPPort := HTTPChallengePort
useTLSALPNPort := TLSALPNChallengePort
if HTTPPort > 0 && HTTPPort != HTTPChallengePort {
useHTTPPort = HTTPPort
}
if HTTPSPort > 0 && HTTPSPort != TLSALPNChallengePort {
useTLSALPNPort = HTTPSPort
}
if cfg.AltHTTPPort > 0 {
useHTTPPort = cfg.AltHTTPPort
}
......@@ -333,7 +339,7 @@ func (c *acmeClient) Renew(name string) error {
// Revoke revokes the certificate for name and deletes
// it from storage.
func (c *acmeClient) Revoke(name string) error {
if !c.config.certCache.storage.Exists(prefixSiteKey(c.config.CA, name)) {
if !c.config.certCache.storage.Exists(StorageKeys.SitePrivateKey(c.config.CA, name)) {
return fmt.Errorf("private key not found for %s", name)
}
......@@ -351,15 +357,15 @@ func (c *acmeClient) Revoke(name string) error {
c.config.OnEvent("acme_cert_revoked", name)
}
err = c.config.certCache.storage.Delete(prefixSiteCert(c.config.CA, name))
err = c.config.certCache.storage.Delete(StorageKeys.SiteCert(c.config.CA, name))
if err != nil {
return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err)
}
err = c.config.certCache.storage.Delete(prefixSiteKey(c.config.CA, name))
err = c.config.certCache.storage.Delete(StorageKeys.SitePrivateKey(c.config.CA, name))
if err != nil {
return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err)
}
err = c.config.certCache.storage.Delete(prefixSiteMeta(c.config.CA, name))
err = c.config.certCache.storage.Delete(StorageKeys.SiteMeta(c.config.CA, name))
if err != nil {
return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err)
}
......
......@@ -266,7 +266,7 @@ func (cfg *Config) ObtainCert(name string, interactive bool) error {
// we expect this to be a new site; if the
// cert already exists, then no-op
if cfg.certCache.storage.Exists(prefixSiteCert(cfg.CA, name)) {
if cfg.certCache.storage.Exists(StorageKeys.SiteCert(cfg.CA, name)) {
return nil
}
......
......@@ -104,15 +104,15 @@ func (cfg *Config) saveCertResource(cert *certificate.Resource) error {
all := []keyValue{
{
key: prefixSiteCert(cfg.CA, cert.Domain),
key: StorageKeys.SiteCert(cfg.CA, cert.Domain),
value: cert.Certificate,
},
{
key: prefixSiteKey(cfg.CA, cert.Domain),
key: StorageKeys.SitePrivateKey(cfg.CA, cert.Domain),
value: cert.PrivateKey,
},
{
key: prefixSiteMeta(cfg.CA, cert.Domain),
key: StorageKeys.SiteMeta(cfg.CA, cert.Domain),
value: metaBytes,
},
}
......@@ -122,15 +122,15 @@ func (cfg *Config) saveCertResource(cert *certificate.Resource) error {
func (cfg *Config) loadCertResource(domain string) (certificate.Resource, error) {
var certRes certificate.Resource
certBytes, err := cfg.certCache.storage.Load(prefixSiteCert(cfg.CA, domain))
certBytes, err := cfg.certCache.storage.Load(StorageKeys.SiteCert(cfg.CA, domain))
if err != nil {
return certRes, err
}
keyBytes, err := cfg.certCache.storage.Load(prefixSiteKey(cfg.CA, domain))
keyBytes, err := cfg.certCache.storage.Load(StorageKeys.SitePrivateKey(cfg.CA, domain))
if err != nil {
return certRes, err
}
metaBytes, err := cfg.certCache.storage.Load(prefixSiteMeta(cfg.CA, domain))
metaBytes, err := cfg.certCache.storage.Load(StorageKeys.SiteMeta(cfg.CA, domain))
if err != nil {
return certRes, err
}
......
......@@ -33,13 +33,13 @@ type FileStorage struct {
// Exists returns true if key exists in fs.
func (fs FileStorage) Exists(key string) bool {
_, err := os.Stat(fs.filename(key))
_, err := os.Stat(fs.Filename(key))
return !os.IsNotExist(err)
}
// Store saves value at key.
func (fs FileStorage) Store(key string, value []byte) error {
filename := fs.filename(key)
filename := fs.Filename(key)
err := os.MkdirAll(filepath.Dir(filename), 0700)
if err != nil {
return err
......@@ -49,7 +49,7 @@ func (fs FileStorage) Store(key string, value []byte) error {
// Load retrieves the value at key.
func (fs FileStorage) Load(key string) ([]byte, error) {
contents, err := ioutil.ReadFile(fs.filename(key))
contents, err := ioutil.ReadFile(fs.Filename(key))
if os.IsNotExist(err) {
return nil, ErrNotExist(err)
}
......@@ -59,7 +59,7 @@ func (fs FileStorage) Load(key string) ([]byte, error) {
// Delete deletes the value at key.
// TODO: Delete any empty folders caused by this operation
func (fs FileStorage) Delete(key string) error {
err := os.Remove(fs.filename(key))
err := os.Remove(fs.Filename(key))
if os.IsNotExist(err) {
return ErrNotExist(err)
}
......@@ -68,7 +68,7 @@ func (fs FileStorage) Delete(key string) error {
// List returns all keys that match prefix.
func (fs FileStorage) List(prefix string) ([]string, error) {
d, err := os.Open(fs.filename(prefix))
d, err := os.Open(fs.Filename(prefix))
if os.IsNotExist(err) {
return nil, ErrNotExist(err)
}
......@@ -81,7 +81,7 @@ func (fs FileStorage) List(prefix string) ([]string, error) {
// Stat returns information about key.
func (fs FileStorage) Stat(key string) (KeyInfo, error) {
fi, err := os.Stat(fs.filename(key))
fi, err := os.Stat(fs.Filename(key))
if os.IsNotExist(err) {
return KeyInfo{}, ErrNotExist(err)
}
......@@ -95,7 +95,9 @@ func (fs FileStorage) Stat(key string) (KeyInfo, error) {
}, nil
}
func (fs FileStorage) filename(key string) string {
// Filename returns the key as a path on the file
// system prefixed by fs.Path.
func (fs FileStorage) Filename(key string) string {
return filepath.Join(fs.Path, filepath.FromSlash(key))
}
......@@ -149,7 +151,7 @@ func (fs FileStorage) TryLock(key string) (Waiter, error) {
}
fw = &fileStorageWaiter{
filename: filepath.Join(lockDir, safeKey(key)+".lock"),
filename: filepath.Join(lockDir, StorageKeys.safe(key)+".lock"),
wg: new(sync.WaitGroup),
}
......
......@@ -52,7 +52,7 @@ func (certCache *Cache) stapleOCSP(cert *Certificate, pemBundle []byte) error {
// First try to load OCSP staple from storage and see if
// we can still use it.
ocspStapleKey := prefixOCSPStaple(cert, pemBundle)
ocspStapleKey := StorageKeys.OCSPStaple(cert, pemBundle)
cachedOCSP, err := certCache.storage.Load(ocspStapleKey)
if err == nil {
resp, err := ocsp.ParseResponse(cachedOCSP, nil)
......
......@@ -134,13 +134,13 @@ func (dhs distributedSolver) CleanUp(domain, token, keyAuth string) error {
// challengeTokensPrefix returns the key prefix for challenge info.
func (dhs distributedSolver) challengeTokensPrefix() string {
return filepath.Join(prefixCA(dhs.config.CA), "challenge_tokens")
return filepath.Join(StorageKeys.CAPrefix(dhs.config.CA), "challenge_tokens")
}
// challengeTokensKey returns the key to use to store and access
// challenge info for domain.
func (dhs distributedSolver) challengeTokensKey(domain string) string {
return filepath.Join(dhs.challengeTokensPrefix(), safeKey(domain)+".json")
return filepath.Join(dhs.challengeTokensPrefix(), StorageKeys.safe(domain)+".json")
}
type challengeInfo struct {
......
......@@ -117,91 +117,100 @@ type keyValue struct {
value []byte
}
const (
prefixACME = "acme"
prefixOCSP = "ocsp"
)
func prefixCA(ca string) string {
// KeyBuilder provides a namespace for methods that
// build keys and key prefixes, for addressing items
// in a Storage implementation.
type KeyBuilder struct{}
// CAPrefix returns the storage key prefix for
// the given certificate authority URL.
func (keys KeyBuilder) CAPrefix(ca string) string {
caURL, err := url.Parse(ca)
if err != nil {
caURL = &url.URL{Host: ca}
}
return path.Join(prefixACME, safeKey(caURL.Host))
return path.Join(prefixACME, keys.safe(caURL.Host))
}
func prefixSite(ca, domain string) string {
return path.Join(prefixCA(ca), "sites", safeKey(domain))
// SitePrefix returns a key prefix for items associated with
// the site using the given CA URL.
func (keys KeyBuilder) SitePrefix(ca, domain string) string {
return path.Join(keys.CAPrefix(ca), "sites", keys.safe(domain))
}
// prefixSiteCert returns the path to the certificate file for domain.
func prefixSiteCert(ca, domain string) string {
return path.Join(prefixSite(ca, domain), safeKey(domain)+".crt")
// SiteCert returns the path to the certificate file for domain.
func (keys KeyBuilder) SiteCert(ca, domain string) string {
return path.Join(keys.SitePrefix(ca, domain), keys.safe(domain)+".crt")
}
// prefixSiteKey returns the path to domain's private key file.
func prefixSiteKey(ca, domain string) string {
return path.Join(prefixSite(ca, domain), safeKey(domain)+".key")
// SitePrivateKey returns the path to domain's private key file.
func (keys KeyBuilder) SitePrivateKey(ca, domain string) string {
return path.Join(keys.SitePrefix(ca, domain), keys.safe(domain)+".key")
}
// prefixSiteMeta returns the path to the domain's asset metadata file.
func prefixSiteMeta(ca, domain string) string {
return path.Join(prefixSite(ca, domain), safeKey(domain)+".json")
// SiteMeta returns the path to the domain's asset metadata file.
func (keys KeyBuilder) SiteMeta(ca, domain string) string {
return path.Join(keys.SitePrefix(ca, domain), keys.safe(domain)+".json")
}
func prefixUsers(ca string) string {
return path.Join(prefixCA(ca), "users")
// UsersPrefix returns a key prefix for items related to
// users associated with the given CA URL.
func (keys KeyBuilder) UsersPrefix(ca string) string {
return path.Join(keys.CAPrefix(ca), "users")
}
// prefixUser gets the account folder for the user with email
func prefixUser(ca, email string) string {
// UserPrefix returns a key prefix for items related to
// the user with the given email for the given CA URL.
func (keys KeyBuilder) UserPrefix(ca, email string) string {
if email == "" {
email = emptyEmail
}
return path.Join(prefixUsers(ca), safeKey(email))
return path.Join(keys.UsersPrefix(ca), keys.safe(email))
}
// prefixUserReg gets the path to the registration file for the user with the
// given email address.
func prefixUserReg(ca, email string) string {
return safeUserKey(ca, email, "registration", ".json")
// UserReg gets the path to the registration file for the user
// with the given email address for the given CA URL.
func (keys KeyBuilder) UserReg(ca, email string) string {
return keys.safeUserKey(ca, email, "registration", ".json")
}
// prefixUserKey gets the path to the private key file for the user with the
// given email address.
func prefixUserKey(ca, email string) string {
return safeUserKey(ca, email, "private", ".key")
// UserPrivateKey gets the path to the private key file for the
// user with the given email address on the given CA URL.
func (keys KeyBuilder) UserPrivateKey(ca, email string) string {
return keys.safeUserKey(ca, email, "private", ".key")
}
func prefixOCSPStaple(cert *Certificate, pemBundle []byte) string {
// OCSPStaple returns a key for the OCSP staple associated
// with the given certificate. If you have the PEM bundle
// handy, pass that in to save an extra encoding step.
func (keys KeyBuilder) OCSPStaple(cert *Certificate, pemBundle []byte) string {
var ocspFileName string
if len(cert.Names) > 0 {
firstName := safeKey(cert.Names[0])
firstName := keys.safe(cert.Names[0])
ocspFileName = firstName + "-"
}
ocspFileName += fastHash(pemBundle)
return path.Join(prefixOCSP, ocspFileName)
}
// safeUserKey returns a key for the given email,
// with the default filename, and the filename
// ending in the given extension.
func safeUserKey(ca, email, defaultFilename, extension string) string {
// safeUserKey returns a key for the given email, with the default
// filename, and the filename ending in the given extension.
func (keys KeyBuilder) safeUserKey(ca, email, defaultFilename, extension string) string {
if email == "" {
email = emptyEmail
}
email = strings.ToLower(email)
filename := emailUsername(email)
filename := keys.emailUsername(email)
if filename == "" {
filename = defaultFilename
}
filename = safeKey(filename)
return path.Join(prefixUser(ca, email), filename+extension)
filename = keys.safe(filename)
return path.Join(keys.UserPrefix(ca, email), filename+extension)
}
// emailUsername returns the username portion of an email address (part before
// '@') or the original input if it can't find the "@" symbol.
func emailUsername(email string) string {
func (keys KeyBuilder) emailUsername(email string) string {
at := strings.Index(email, "@")
if at == -1 {
return email
......@@ -211,8 +220,9 @@ func emailUsername(email string) string {
return email[:at]
}
// safeKey standardizes and sanitizes str for use in a file path.
func safeKey(str string) string {
// safe standardizes and sanitizes str for use as
// a storage key. This method is idempotent.
func (keys KeyBuilder) safe(str string) string {
str = strings.ToLower(str)
str = strings.TrimSpace(str)
......@@ -229,6 +239,19 @@ func safeKey(str string) string {
return safeKeyRE.ReplaceAllLiteralString(str, "")
}
// StorageKeys provides methods for accessing
// keys and key prefixes for items in a Storage.
// Typically, you will not need to use this
// because accessing storage is abstracted away
// for most cases. Only use this if you need to
// directly access TLS assets in your application.
var StorageKeys KeyBuilder
const (
prefixACME = "acme"
prefixOCSP = "ocsp"
)
// safeKeyRE matches any undesirable characters in storage keys.
// Note that this allows dots, so you'll have to strip ".." manually.
var safeKeyRE = regexp.MustCompile(`[^\w@.-]`)
......
......@@ -155,7 +155,7 @@ func (cfg *Config) getEmail(userPresent bool) (string, error) {
func (cfg *Config) getUser(email string) (user, error) {
var user user
regBytes, err := cfg.certCache.storage.Load(prefixUserReg(cfg.CA, email))
regBytes, err := cfg.certCache.storage.Load(StorageKeys.UserReg(cfg.CA, email))
if err != nil {
if _, ok := err.(ErrNotExist); ok {
// create a new user
......@@ -163,7 +163,7 @@ func (cfg *Config) getUser(email string) (user, error) {
}
return user, err
}
keyBytes, err := cfg.certCache.storage.Load(prefixUserKey(cfg.CA, email))
keyBytes, err := cfg.certCache.storage.Load(StorageKeys.UserPrivateKey(cfg.CA, email))
if err != nil {
if _, ok := err.(ErrNotExist); ok {
// create a new user
......@@ -197,11 +197,11 @@ func (cfg *Config) saveUser(user user) error {
all := []keyValue{
{
key: prefixUserReg(cfg.CA, user.Email),
key: StorageKeys.UserReg(cfg.CA, user.Email),
value: regBytes,
},
{
key: prefixUserKey(cfg.CA, user.Email),
key: StorageKeys.UserPrivateKey(cfg.CA, user.Email),
value: keyBytes,
},
}
......@@ -240,13 +240,13 @@ func (cfg *Config) askUserAgreement(agreementURL string) bool {
// account, errors here are discarded to simplify code flow in
// the caller, and errors are not important here anyway.
func (cfg *Config) mostRecentUserEmail() string {
userList, err := cfg.certCache.storage.List(prefixUsers(cfg.CA))
userList, err := cfg.certCache.storage.List(StorageKeys.UsersPrefix(cfg.CA))
if err != nil || len(userList) == 0 {
return ""
}
sort.Slice(userList, func(i, j int) bool {
iInfo, _ := cfg.certCache.storage.Stat(prefixUser(cfg.CA, userList[i]))
jInfo, _ := cfg.certCache.storage.Stat(prefixUser(cfg.CA, userList[j]))
iInfo, _ := cfg.certCache.storage.Stat(StorageKeys.UserPrefix(cfg.CA, userList[i]))
jInfo, _ := cfg.certCache.storage.Stat(StorageKeys.UserPrefix(cfg.CA, userList[j]))
return jInfo.Modified.Before(iInfo.Modified)
})
user, err := cfg.getUser(userList[0])
......
......@@ -138,7 +138,7 @@
"importpath": "github.com/mholt/certmagic",
"repository": "https://github.com/mholt/certmagic",
"vcs": "git",
"revision": "8b6ddf223c912a863aaadd388bfdd29be295fb5d",
"revision": "5b3085c491553887f36460365533eb5955fdeef0",
"branch": "master",
"notests": true
},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment