2017-01-26 03:26:41 +00:00
|
|
|
package database
|
|
|
|
|
|
|
|
import (
|
2017-01-26 04:25:05 +00:00
|
|
|
"errors"
|
2017-01-26 03:26:41 +00:00
|
|
|
"log"
|
|
|
|
|
|
|
|
r "github.com/GoRethink/gorethink"
|
2017-01-26 04:22:27 +00:00
|
|
|
"github.com/brandur/simplebox"
|
2017-01-26 03:26:41 +00:00
|
|
|
"golang.org/x/crypto/acme/autocert"
|
2017-01-26 03:28:59 +00:00
|
|
|
"golang.org/x/net/context"
|
2017-01-26 03:26:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// CertCache extends DB to provide certificate management functions for
|
|
|
|
// https://godoc.org/golang.org/x/crypto/acme/autocert#Cache
|
|
|
|
type CertCache struct {
|
|
|
|
*DB
|
2017-01-26 04:22:27 +00:00
|
|
|
SimpleBox *simplebox.SimpleBox
|
2017-01-26 03:26:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CryptoLevel indicates what form of cryptography the certificate is stored
|
|
|
|
// with.
|
|
|
|
type CryptoLevel int
|
|
|
|
|
|
|
|
// Crypto levels / strategies defined
|
|
|
|
const (
|
|
|
|
// NOTE: this is defined for debugging / testing usage only
|
|
|
|
CryptoLevelNone CryptoLevel = iota
|
|
|
|
|
|
|
|
// Use the global set of secretbox keys
|
|
|
|
CryptoLevelSecretbox
|
|
|
|
)
|
|
|
|
|
|
|
|
// CachedCert is an individual cached certificate in the database.
|
|
|
|
type CachedCert struct {
|
|
|
|
Key string `gorethink:"id"`
|
|
|
|
CryptoLevel CryptoLevel `gorethink:"cryptoLevel"`
|
|
|
|
// PEM-encoded bytes with the above crypto level as a filter.
|
|
|
|
Body []byte `gorethink:"body"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns a certificate data for the specified key.
|
|
|
|
// If there's no such key, Get returns ErrCacheMiss.
|
|
|
|
func (c *CertCache) Get(ctx context.Context, key string) ([]byte, error) {
|
|
|
|
res, err := r.Table("certs").Get(key).Run(c.s)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cert := &CachedCert{}
|
|
|
|
err = res.One(cert)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("decoding cached cert failed: %v", err)
|
|
|
|
return nil, autocert.ErrCacheMiss
|
|
|
|
}
|
|
|
|
|
|
|
|
var body []byte
|
|
|
|
|
|
|
|
switch cert.CryptoLevel {
|
|
|
|
case CryptoLevelNone:
|
|
|
|
body = cert.Body
|
2017-01-26 04:25:05 +00:00
|
|
|
case CryptoLevelSecretbox:
|
|
|
|
if c.SimpleBox == nil {
|
|
|
|
return nil, errors.New("can't read this cert, no key in memory")
|
|
|
|
}
|
|
|
|
|
|
|
|
body, err = c.SimpleBox.Decrypt(cert.Body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, autocert.ErrCacheMiss
|
|
|
|
}
|
2017-01-26 03:26:41 +00:00
|
|
|
}
|
|
|
|
|
2017-01-26 03:33:12 +00:00
|
|
|
log.Printf("certcache: fetched: %s", key)
|
|
|
|
|
2017-01-26 03:26:41 +00:00
|
|
|
return body, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put stores the data in the cache under the specified key.
|
|
|
|
// Underlying implementations may use any data storage format,
|
|
|
|
// as long as the reverse operation, Get, results in the original data.
|
|
|
|
func (c *CertCache) Put(ctx context.Context, key string, data []byte) error {
|
|
|
|
cert := &CachedCert{
|
|
|
|
Key: key,
|
|
|
|
CryptoLevel: CryptoLevelNone,
|
|
|
|
Body: data,
|
|
|
|
}
|
|
|
|
|
2017-01-26 04:22:27 +00:00
|
|
|
if c.SimpleBox != nil {
|
|
|
|
cert.CryptoLevel = CryptoLevelSecretbox
|
|
|
|
cert.Body = c.SimpleBox.Encrypt(data)
|
|
|
|
}
|
|
|
|
|
2017-01-26 03:33:12 +00:00
|
|
|
log.Printf("certcache: added: %s", key)
|
|
|
|
|
2017-01-26 03:26:41 +00:00
|
|
|
_, err := r.Table("certs").Insert(cert).RunWrite(c.s)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removes a certificate data from the cache under the specified key.
|
|
|
|
// If there's no such key in the cache, Delete returns nil.
|
|
|
|
func (c *CertCache) Delete(ctx context.Context, key string) error {
|
|
|
|
_, err := r.Table("certs").Get(key).Delete().Run(c.s)
|
|
|
|
|
2017-01-26 03:33:12 +00:00
|
|
|
log.Printf("certcache: deleted: %s", key)
|
2017-01-26 03:26:41 +00:00
|
|
|
return err
|
|
|
|
}
|