Skip to content

Instantly share code, notes, and snippets.

@crhntr
Last active June 25, 2018 08:15
Show Gist options
  • Save crhntr/d2b95e96b0143df2032e6a398a0704f0 to your computer and use it in GitHub Desktop.
Save crhntr/d2b95e96b0143df2032e6a398a0704f0 to your computer and use it in GitHub Desktop.
autocert.Cert Implementation using mgo for MongoDB backend
package mgocert
import (
"context"
"golang.org/x/crypto/acme/autocert"
mgo "gopkg.in/mgo.v2"
)
type Cache struct {
DBSess *mgo.Session
DBName string
ColName string
// inMem sync.Map
}
type Cert struct {
Host string `json:"host" bson:"_id"`
Cert []byte `json:"cert" bson:"cert"`
}
// Get returns a certificate data for the specified key.
// If there's no such key, Get returns ErrCacheMiss.
func (cache *Cache) Get(ctx context.Context, key string) ([]byte, error) {
// if certVal, found := cache.inMem.Load(key); found {
// return certVal.(Cert).Cert, nil
// }
dbSess := cache.DBSess.Clone()
defer dbSess.Close()
var (
done = make(chan struct{})
err error
)
cert := Cert{}
go func() {
err = dbSess.DB(cache.DBName).C(cache.ColName).FindId(key).One(&cert)
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
}
// cache.inMem.Store(key, cert)
if err == mgo.ErrNotFound {
return nil, autocert.ErrCacheMiss
}
return cert.Cert, err
}
// 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 (cache *Cache) Put(ctx context.Context, key string, data []byte) error {
dbSess := cache.DBSess.Clone()
defer dbSess.Close()
var (
done = make(chan struct{})
err error
)
cert := Cert{
Host: key,
Cert: data,
}
go func() {
_, err = dbSess.DB(cache.DBName).C(cache.ColName).UpsertId(key, cert)
close(done)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-done:
}
// cache.inMem.Store(key, cert)
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 (cache *Cache) Delete(ctx context.Context, key string) error {
// cache.inMem.Delete(key)
dbSess := cache.DBSess.Clone()
defer dbSess.Close()
var (
done = make(chan struct{})
err error
)
go func() {
err = dbSess.DB(cache.DBName).C(cache.ColName).RemoveId(key)
close(done)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-done:
}
if err != mgo.ErrNotFound {
return err
}
return nil
}
func (cache *Cache) HostPolicy() autocert.HostPolicy {
return func(ctx context.Context, host string) error {
// if _, found := cache.inMem.Load(host); found {
// return nil
// }
dbSess := cache.DBSess.Clone()
defer dbSess.Close()
var (
done = make(chan struct{})
err error
)
cert := Cert{}
go func() {
err = dbSess.DB(cache.DBName).C(cache.ColName).FindId(host).One(&cert)
close(done)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-done:
}
if err != nil {
return autocert.ErrCacheMiss
}
return nil
}
}
@FlyingRatBull
Copy link

The Get() function is handling missing certificates wrong. It is just returning the Mongo error instead of the expected autocert.ErrCacheMiss which prevents autocert from fetching a new certificate.

Replace

return cert.Cert, err

with

if err == mgo.ErrNotFound {
    return nil, autocert.ErrCacheMiss
}

return cert.Cert, err

and it works correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment