diff --git a/database/certcache.go b/database/certcache.go new file mode 100644 index 0000000..41caba0 --- /dev/null +++ b/database/certcache.go @@ -0,0 +1,83 @@ +package database + +import ( + "context" + "log" + + r "github.com/GoRethink/gorethink" + "golang.org/x/crypto/acme/autocert" +) + +// CertCache extends DB to provide certificate management functions for +// https://godoc.org/golang.org/x/crypto/acme/autocert#Cache +type CertCache struct { + *DB +} + +// 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 + } + + 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, + } + + _, 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) + + return err +} diff --git a/main.go b/main.go index 04cd778..8eb6bf2 100644 --- a/main.go +++ b/main.go @@ -68,11 +68,9 @@ func main() { } func setupACME(s *server.Server) { - dc := autocert.DirCache("./var/certs") - m := autocert.Manager{ Prompt: autocert.AcceptTOS, - Cache: dc, + Cache: s.CertCache, HostPolicy: nil, Email: "xena@yolo-swag.com", } diff --git a/server/server.go b/server/server.go index 08a59c3..5f96c78 100644 --- a/server/server.go +++ b/server/server.go @@ -40,6 +40,8 @@ type Server struct { rpcAddr string ts *tunnel.Server + + CertCache *database.CertCache } // Config configures Server @@ -110,6 +112,10 @@ func New(cfg Config) (*Server, error) { rpcAddr: l.Addr().String(), ts: ts, + + CertCache: &database.CertCache{ + DB: db, + }, } rpcs.RegisterName("Urls", &RPCServer{Server: s})