Skip to content

Commit

Permalink
Merge pull request #83 from rekby/devel
Browse files Browse the repository at this point in the history
Keepalive timeout setting
  • Loading branch information
rekby authored Jul 27, 2019
2 parents a06aa4f + 284ce3e commit 23dff2e
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 23 deletions.
2 changes: 1 addition & 1 deletion cmd/a_main-packr.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions cmd/static/default-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ MaxCount = 10
#It can be: IP (with default port 80), :Port (default - same IP as receive connection), IPv4:Port or [IPv6]:Port
DefaultTarget = ":80"

# After KeepAliveTimeoutSeconds of inactive incoming connection will close.
KeepAliveTimeoutSeconds = 900

# Array of '-' separated pairs or IP:Port. For example:
# [
# "1.2.3.4:443-2.2.2.2:1234",
Expand Down
4 changes: 2 additions & 2 deletions internal/acme_client_manager/client_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ type AcmeManager struct {
RenewAccountInterval time.Duration

ctx context.Context
cache cache.Cache
cache cache.Bytes

mu sync.Mutex
client *acme.Client
account *acme.Account
}

func New(ctx context.Context, cache cache.Cache) *AcmeManager {
func New(ctx context.Context, cache cache.Bytes) *AcmeManager {
return &AcmeManager{
ctx: ctx,
cache: cache,
Expand Down
2 changes: 1 addition & 1 deletion internal/cache/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

var ErrCacheMiss = errors.New("lets proxy: cache miss")

type Cache interface {
type Bytes interface {
// Get returns a certificate data for the specified key.
// If there's no such key, Get returns ErrCacheMiss.
Get(ctx context.Context, key string) ([]byte, error)
Expand Down
47 changes: 31 additions & 16 deletions internal/cert_manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

"github.com/rekby/lets-proxy2/internal/cache"
"go.uber.org/zap/zapcore"

"github.com/rekby/lets-proxy2/internal/log"

Expand All @@ -37,7 +38,7 @@ const (

const domainKeyRSALength = 2048

var errHaveNoCert = errors.New("have no certificate for domain")
var errHaveNoCert = errors.New("have no certificate for domain") // may return for any internal error

//nolint:varcheck,deadcode,unused
var errNotImplementedError = errors.New("not implemented yet")
Expand All @@ -55,7 +56,7 @@ const keyUnknown keyType = "unknown"
// Interface inspired to https://godoc.org/golang.org/x/crypto/acme/autocert#Manager but not compatible guarantee
type Manager struct {
CertificateIssueTimeout time.Duration
Cache cache.Cache
Cache cache.Bytes

// Client is used to perform low-level operations, such as account registration
// and requesting new certificates.
Expand All @@ -76,10 +77,10 @@ type Manager struct {
certStateMu sync.Mutex
certState cache.Value

httpTokens cache.Cache
httpTokens cache.Bytes
}

func New(client AcmeClient, c cache.Cache) *Manager {
func New(client AcmeClient, c cache.Bytes) *Manager {
res := Manager{}
res.Client = client
res.certForDomainAuthorize = cache.NewMemoryValueLRU("authcert")
Expand Down Expand Up @@ -158,17 +159,24 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (resultCert *tls.Ce
locked, err := isCertLocked(ctx, m.Cache, certName)
log.DebugDPanic(logger, err, "Check if certificate locked")

cert, err = getCertificate(ctx, m.Cache, certName, keyRSA)
if err == nil {
logger.Debug("Certificate loaded from cache")
cert, err = loadCertificateFromCache(ctx, m.Cache, certName, keyRSA)
logLevel := zapcore.DebugLevel
if err != nil && err != cache.ErrCacheMiss {
logLevel = zapcore.ErrorLevel
}
log.LevelParam(logger, logLevel, "Load certificate from cache", zap.Error(err))

if err == nil {
cert, err = validCertDer([]DomainName{needDomain}, cert.Certificate, cert.PrivateKey, locked, now)
logger.Debug("Check if certificate ok", zap.Error(err))
if err == nil {
certState.CertSet(ctx, locked, cert)
return cert, nil
}
}
if err != cache.ErrCacheMiss {
return nil, errHaveNoCert
}

if locked {
return nil, errHaveNoCert
Expand Down Expand Up @@ -598,7 +606,7 @@ func (m *Manager) deleteCertToken(ctx context.Context, key DomainName) {
}

// It isn't atomic syncronized - caller must not save two certificates with same name same time
func storeCertificate(ctx context.Context, cache cache.Cache, certName certNameType,
func storeCertificate(ctx context.Context, cache cache.Bytes, certName certNameType,
cert *tls.Certificate) error {
logger := zc.L(ctx)
if cache == nil {
Expand Down Expand Up @@ -662,7 +670,7 @@ func storeCertificate(ctx context.Context, cache cache.Cache, certName certNameT
return nil
}

func storeCertificateMeta(ctx context.Context, storage cache.Cache, key certNameType, certificate *tls.Certificate) error {
func storeCertificateMeta(ctx context.Context, storage cache.Bytes, key certNameType, certificate *tls.Certificate) error {
info := struct {
Domains []string
ExpireDate time.Time
Expand Down Expand Up @@ -693,7 +701,7 @@ func getKeyType(cert *tls.Certificate) keyType {
}
}

func getCertificate(ctx context.Context, cache cache.Cache, certName certNameType, keyType keyType) (cert *tls.Certificate, err error) {
func loadCertificateFromCache(ctx context.Context, c cache.Bytes, certName certNameType, keyType keyType) (cert *tls.Certificate, err error) {
logger := zc.L(ctx)
logger.Debug("Check certificate in cache")
defer func() {
Expand All @@ -702,38 +710,45 @@ func getCertificate(ctx context.Context, cache cache.Cache, certName certNameTyp

certKeyName := string(certName) + "." + string(keyType) + ".cer"

certBytes, err := cache.Get(ctx, certKeyName)
certBytes, err := c.Get(ctx, certKeyName)
log.DebugError(logger, err, "Get certificate from cache")
if err != nil {
return nil, err
}
keyBytes, err := getCertificateKeyBytes(ctx, cache, certName, keyType)
keyBytes, err := getCertificateKeyBytes(ctx, c, certName, keyType)
log.DebugError(logger, err, "Get certificate key from cache")
if err != nil {
return nil, err
}

cert2, err := tls.X509KeyPair(certBytes, keyBytes)
log.DebugError(logger, err, "Combine cert and key into pair")
if err != nil {
// logical error, may be system failure
return nil, err
}
if len(cert2.Certificate) > 0 {
cert2.Leaf, err = x509.ParseCertificate(cert2.Certificate[0])
if err != nil {
// logical error, may be system failure
return nil, err
}
}
locked, err := isCertLocked(ctx, cache, certName)
locked, err := isCertLocked(ctx, c, certName)
log.DebugError(logger, err, "Check if certificate locked")
if err != nil {
// logical error, may be system failure
return nil, err
}
return validCertTLS(&cert2, nil, locked, time.Now())
}

func getCertificateKeyBytes(ctx context.Context, cache cache.Cache, certName certNameType, keyType keyType) ([]byte, error) {
func getCertificateKeyBytes(ctx context.Context, cache cache.Bytes, certName certNameType, keyType keyType) ([]byte, error) {
keyKeyName := string(certName) + "." + string(keyType) + ".key"
return cache.Get(ctx, keyKeyName)
}

func getCertificateKey(ctx context.Context, cache cache.Cache, certName certNameType, keyType keyType) (crypto.Signer, error) {
func getCertificateKey(ctx context.Context, cache cache.Bytes, certName certNameType, keyType keyType) (crypto.Signer, error) {
certBytes, err := getCertificateKeyBytes(ctx, cache, certName, keyType)
if err != nil {
return nil, err
Expand Down Expand Up @@ -799,7 +814,7 @@ func isNeedRenew(cert *tls.Certificate, now time.Time) bool {
return cert.Leaf.NotAfter.Add(-time.Hour * 24 * 30).Before(now)
}

func isCertLocked(ctx context.Context, storage cache.Cache, certName certNameType) (bool, error) {
func isCertLocked(ctx context.Context, storage cache.Bytes, certName certNameType) (bool, error) {
lockName := certName.String() + ".lock"
_, err := storage.Get(ctx, lockName)
switch err {
Expand Down
7 changes: 7 additions & 0 deletions internal/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"

zc "github.com/rekby/zapcontext"
"go.uber.org/zap/zapcore"

"go.uber.org/zap"
)
Expand Down Expand Up @@ -169,3 +170,9 @@ func infoPanic(logger *zap.Logger, err error, mess string, fields ...zap.Field)
logger.Panic(mess, append(fields, zap.Error(err))...)
}
}

func LevelParam(logger *zap.Logger, level zapcore.Level, mess string, fields ...zap.Field) {
if ce := logger.Check(level, mess); ce != nil {
ce.Write(fields...)
}
}
9 changes: 6 additions & 3 deletions internal/proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net"
"strings"
"time"

"github.com/rekby/lets-proxy2/internal/log"

Expand All @@ -18,9 +19,10 @@ const defaultHTTPPort = 80

//nolint:lll
type Config struct {
DefaultTarget string
TargetMap []string
Headers []string
DefaultTarget string
TargetMap []string
Headers []string
KeepAliveTimeoutSeconds int
}

func (c *Config) Apply(ctx context.Context, p *HTTPProxy) error {
Expand All @@ -46,6 +48,7 @@ func (c *Config) Apply(ctx context.Context, p *HTTPProxy) error {

chainDirector := NewDirectorChain(chain...)
p.Director = chainDirector
p.IdleTimeout = time.Duration(c.KeepAliveTimeoutSeconds) * time.Second
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions internal/proxy/http-proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"net/http/httputil"
"net/url"
"time"

"github.com/rekby/lets-proxy2/internal/contexthelper"

Expand All @@ -31,6 +32,7 @@ type HTTPProxy struct {
ctx context.Context
listener net.Listener
httpReverseProxy httputil.ReverseProxy
IdleTimeout time.Duration
}

func NewHTTPProxy(ctx context.Context, listener net.Listener) *HTTPProxy {
Expand Down Expand Up @@ -64,6 +66,7 @@ func (p *HTTPProxy) Start() error {
})
httpServer := http.Server{}
httpServer.Handler = mux
httpServer.IdleTimeout = p.IdleTimeout

go func() {
<-p.ctx.Done()
Expand Down

0 comments on commit 23dff2e

Please sign in to comment.