2019-07-01 06:07:58 +08:00
|
|
|
// Copyright 2015 Matthew Holt and The Caddy Authors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2019-05-30 13:11:46 +08:00
|
|
|
package standardstek
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
2020-05-13 01:36:20 +08:00
|
|
|
"runtime/debug"
|
2019-05-30 13:11:46 +08:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2019-07-03 02:37:06 +08:00
|
|
|
"github.com/caddyserver/caddy/v2"
|
|
|
|
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
2019-05-30 13:11:46 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2019-08-22 00:46:35 +08:00
|
|
|
caddy.RegisterModule(standardSTEKProvider{})
|
2019-05-30 13:11:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type standardSTEKProvider struct {
|
|
|
|
stekConfig *caddytls.SessionTicketService
|
|
|
|
timer *time.Timer
|
|
|
|
}
|
|
|
|
|
2019-08-22 00:46:35 +08:00
|
|
|
// CaddyModule returns the Caddy module information.
|
|
|
|
func (standardSTEKProvider) CaddyModule() caddy.ModuleInfo {
|
|
|
|
return caddy.ModuleInfo{
|
2019-12-11 04:36:46 +08:00
|
|
|
ID: "tls.stek.standard",
|
|
|
|
New: func() caddy.Module { return new(standardSTEKProvider) },
|
2019-08-22 00:46:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 13:11:46 +08:00
|
|
|
// Initialize sets the configuration for s and returns the starting keys.
|
|
|
|
func (s *standardSTEKProvider) Initialize(config *caddytls.SessionTicketService) ([][32]byte, error) {
|
2019-06-04 05:35:14 +08:00
|
|
|
// keep a reference to the config; we'll need it when rotating keys
|
2019-05-30 13:11:46 +08:00
|
|
|
s.stekConfig = config
|
|
|
|
|
|
|
|
itvl := time.Duration(s.stekConfig.RotationInterval)
|
|
|
|
|
|
|
|
mutex.Lock()
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
|
|
|
// if this is our first rotation or we are overdue
|
|
|
|
// for one, perform a rotation immediately; otherwise,
|
|
|
|
// we assume that the keys are non-empty and fresh
|
|
|
|
since := time.Since(lastRotation)
|
|
|
|
if lastRotation.IsZero() || since > itvl {
|
|
|
|
var err error
|
|
|
|
keys, err = s.stekConfig.RotateSTEKs(keys)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
since = 0 // since this is overdue or is the first rotation, use full interval
|
|
|
|
lastRotation = time.Now()
|
|
|
|
}
|
|
|
|
|
|
|
|
// create timer for the remaining time on the interval;
|
|
|
|
// this timer is cleaned up only when Next() returns
|
|
|
|
s.timer = time.NewTimer(itvl - since)
|
|
|
|
|
|
|
|
return keys, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next returns a channel which transmits the latest session ticket keys.
|
|
|
|
func (s *standardSTEKProvider) Next(doneChan <-chan struct{}) <-chan [][32]byte {
|
|
|
|
keysChan := make(chan [][32]byte)
|
|
|
|
go s.rotate(doneChan, keysChan)
|
|
|
|
return keysChan
|
|
|
|
}
|
|
|
|
|
|
|
|
// rotate rotates keys on a regular basis, sending each updated set of
|
|
|
|
// keys down keysChan, until doneChan is closed.
|
|
|
|
func (s *standardSTEKProvider) rotate(doneChan <-chan struct{}, keysChan chan<- [][32]byte) {
|
2020-05-13 01:36:20 +08:00
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
log.Printf("[PANIC] standard STEK rotation: %v\n%s", err, debug.Stack())
|
|
|
|
}
|
|
|
|
}()
|
2019-05-30 13:11:46 +08:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case now := <-s.timer.C:
|
|
|
|
// copy the slice header to avoid races
|
|
|
|
mutex.RLock()
|
|
|
|
keysCopy := keys
|
|
|
|
mutex.RUnlock()
|
|
|
|
|
|
|
|
// generate a new key, rotating old ones
|
|
|
|
var err error
|
|
|
|
keysCopy, err = s.stekConfig.RotateSTEKs(keysCopy)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: improve this handling
|
|
|
|
log.Printf("[ERROR] Generating STEK: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace keys slice with updated value and
|
|
|
|
// record the timestamp of rotation
|
|
|
|
mutex.Lock()
|
|
|
|
keys = keysCopy
|
|
|
|
lastRotation = now
|
|
|
|
mutex.Unlock()
|
|
|
|
|
|
|
|
// send the updated keys to the service
|
|
|
|
keysChan <- keysCopy
|
|
|
|
|
|
|
|
// timer channel is already drained, so reset directly (see godoc)
|
|
|
|
s.timer.Reset(time.Duration(s.stekConfig.RotationInterval))
|
|
|
|
|
|
|
|
case <-doneChan:
|
|
|
|
// again, see godocs for why timer is stopped this way
|
|
|
|
if !s.timer.Stop() {
|
|
|
|
<-s.timer.C
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
lastRotation time.Time
|
|
|
|
keys [][32]byte
|
|
|
|
mutex sync.RWMutex // protects keys and lastRotation
|
|
|
|
)
|
|
|
|
|
|
|
|
// Interface guard
|
|
|
|
var _ caddytls.STEKProvider = (*standardSTEKProvider)(nil)
|