mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			138 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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.
 | 
						|
 | 
						|
package standardstek
 | 
						|
 | 
						|
import (
 | 
						|
	"log"
 | 
						|
	"runtime/debug"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/caddyserver/caddy/v2"
 | 
						|
	"github.com/caddyserver/caddy/v2/modules/caddytls"
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	caddy.RegisterModule(standardSTEKProvider{})
 | 
						|
}
 | 
						|
 | 
						|
type standardSTEKProvider struct {
 | 
						|
	stekConfig *caddytls.SessionTicketService
 | 
						|
	timer      *time.Timer
 | 
						|
}
 | 
						|
 | 
						|
// CaddyModule returns the Caddy module information.
 | 
						|
func (standardSTEKProvider) CaddyModule() caddy.ModuleInfo {
 | 
						|
	return caddy.ModuleInfo{
 | 
						|
		ID:  "tls.stek.standard",
 | 
						|
		New: func() caddy.Module { return new(standardSTEKProvider) },
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Initialize sets the configuration for s and returns the starting keys.
 | 
						|
func (s *standardSTEKProvider) Initialize(config *caddytls.SessionTicketService) ([][32]byte, error) {
 | 
						|
	// keep a reference to the config; we'll need it when rotating keys
 | 
						|
	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) {
 | 
						|
	defer func() {
 | 
						|
		if err := recover(); err != nil {
 | 
						|
			log.Printf("[PANIC] standard STEK rotation: %v\n%s", err, debug.Stack())
 | 
						|
		}
 | 
						|
	}()
 | 
						|
	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)
 |