Implement CRL and Health response handling
- add callback support to client and handler - implement support for updating the CA certificates and profiles from health data of the signer - implement CRL retrieval from the signer including delta CRL support - implement error response handling - add configurable start and interval timers for health and CRL data
This commit is contained in:
parent
0e32f7fd16
commit
da17fb69d7
7 changed files with 394 additions and 43 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
|||
*Pty
|
||||
/.idea/
|
||||
/config.yaml
|
||||
/crls/
|
||||
/dist/
|
||||
/signerclient
|
||||
go.work
|
|
@ -77,13 +77,14 @@ func main() {
|
|||
}
|
||||
|
||||
commands := make(chan *protocol.Command)
|
||||
callbacks := make(chan interface{}, client.CallBackBufferSize)
|
||||
|
||||
clientHandler, err := handler.New(clientConfig, logger, commands)
|
||||
clientHandler, err := handler.New(clientConfig, logger, commands, callbacks)
|
||||
if err != nil {
|
||||
logger.WithError(err).Fatal("could not setup client handler")
|
||||
}
|
||||
|
||||
signerClient, err := client.New(clientConfig, logger, clientHandler, commands)
|
||||
signerClient, err := client.New(clientConfig, logger, clientHandler, commands, callbacks)
|
||||
if err != nil {
|
||||
logger.WithError(err).Fatal("could not setup client")
|
||||
}
|
||||
|
|
6
go.mod
6
go.mod
|
@ -1,9 +1,10 @@
|
|||
module git.cacert.org/cacert-gosignerclient
|
||||
|
||||
go 1.17
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221129191234-c92b0455db62
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221130175146-fffc65a540d5
|
||||
github.com/balacode/go-delta v0.1.0
|
||||
github.com/shamaton/msgpackgen v0.3.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
|
||||
|
@ -11,6 +12,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/balacode/zr v1.0.0 // indirect
|
||||
github.com/dave/jennifer v1.4.1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/justincpresley/go-cobs v1.2.0 // indirect
|
||||
|
|
17
go.sum
17
go.sum
|
@ -1,6 +1,9 @@
|
|||
git.cacert.org/cacert-gosigner v0.0.0-20221129191234-c92b0455db62 h1:MKpVPTm+rOjVK1/XtEaeGvBp6hQj4FlWnuKl66xK5ls=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221129191234-c92b0455db62/go.mod h1:yFBRxW6BwOyiXGtHQH/Vpro4Dxd0oIl3tCIWg99nYGE=
|
||||
github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221130175146-fffc65a540d5 h1:ot7ENgYQj4xcqodTe1V2aIjvGLn+NVVhPu9+6XMuMTk=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221130175146-fffc65a540d5/go.mod h1:mb8oBdxQ26GI3xT4b8B7hXYWGED9vvjPGxehmbicyc4=
|
||||
github.com/balacode/go-delta v0.1.0 h1:pwz4CMn06P2bIaIfAx3GSabMPwJp/Ww4if+7SgPYa3I=
|
||||
github.com/balacode/go-delta v0.1.0/go.mod h1:wLNrwTI3lHbPBvnLzqbHmA7HVVlm1u22XLvhbeA6t3o=
|
||||
github.com/balacode/zr v1.0.0 h1:MCupkEoXvrnCljc4KddiDOhR04ZLUAACgtKuo3o+9vc=
|
||||
github.com/balacode/zr v1.0.0/go.mod h1:pLeSAL3DhZ9L0JuiRkUtIX3mLOCtzBLnDhfmykbSmkE=
|
||||
github.com/dave/jennifer v1.4.1 h1:XyqG6cn5RQsTj3qlWQTKlRGAyrTcsk1kUmWdZBzRjDw=
|
||||
github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -10,10 +13,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/justincpresley/go-cobs v1.2.0 h1:dyWszWzXObEv8sxVMJTAIo9XT7HEM10vkAOZq2eVEsQ=
|
||||
github.com/justincpresley/go-cobs v1.2.0/go.mod h1:L0d+EbGirv6IzsXNzwULduI2/z3ijkkAmsAuPMpLfqA=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/shamaton/msgpack/v2 v2.1.0 h1:9jJ2eGZw2Wa9KExPX3KaDDckVjgr4zhXGFCfWagUWqg=
|
||||
|
@ -24,18 +23,14 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
|
|||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -19,9 +19,15 @@ package client
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/balacode/go-delta"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tarm/serial"
|
||||
|
||||
|
@ -30,6 +36,25 @@ import (
|
|||
"git.cacert.org/cacert-gosignerclient/internal/config"
|
||||
)
|
||||
|
||||
const CallBackBufferSize = 50
|
||||
|
||||
const (
|
||||
worldReadableDirPerm = 0o755
|
||||
worldReadableFilePerm = 0o644
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
Name string
|
||||
UseFor string
|
||||
}
|
||||
|
||||
type SignerInfo struct {
|
||||
SignerHealth bool
|
||||
SignerVersion string
|
||||
CACertificates []string
|
||||
UsableProfiles map[string][]Profile
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
port *serial.Port
|
||||
logger *logrus.Logger
|
||||
|
@ -39,6 +64,10 @@ type Client struct {
|
|||
commands chan *protocol.Command
|
||||
handler protocol.ClientHandler
|
||||
config *config.ClientConfig
|
||||
signerInfo *SignerInfo
|
||||
callback chan interface{}
|
||||
sync.Mutex
|
||||
lastKnownCRLS map[string]*big.Int
|
||||
}
|
||||
|
||||
func (c *Client) Run(ctx context.Context) error {
|
||||
|
@ -97,6 +126,11 @@ func (c *Client) setupConnection(serialConfig *serial.Config) error {
|
|||
|
||||
c.port = s
|
||||
|
||||
err = c.port.Flush()
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Warn("could not flush buffers of port: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -116,11 +150,40 @@ func (c *Client) Close() error {
|
|||
|
||||
func (c *Client) commandLoop(ctx context.Context) {
|
||||
healthTimer := time.NewTimer(c.config.HealthStart)
|
||||
fetchCRLTimer := time.NewTimer(c.config.FetchCRLStart)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case callbackData := <-c.callback:
|
||||
err := c.handleCallback(callbackData)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("callback handling failed")
|
||||
}
|
||||
case <-fetchCRLTimer.C:
|
||||
for _, crlInfo := range c.buildCRLInfo() {
|
||||
announce, err := messages.BuildCommandAnnounce(messages.CmdFetchCRL)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("could not build fetch CRL command announce")
|
||||
}
|
||||
|
||||
var lastKnown []byte
|
||||
|
||||
if crlInfo.LastKnown != nil {
|
||||
lastKnown = crlInfo.LastKnown.Bytes()
|
||||
}
|
||||
|
||||
c.commands <- &protocol.Command{
|
||||
Announce: announce,
|
||||
Command: &messages.FetchCRLCommand{
|
||||
IssuerID: crlInfo.Name,
|
||||
LastKnownID: lastKnown,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fetchCRLTimer.Reset(c.config.FetchCRLInterval)
|
||||
case <-healthTimer.C:
|
||||
announce, err := messages.BuildCommandAnnounce(messages.CmdHealth)
|
||||
if err != nil {
|
||||
|
@ -137,11 +200,189 @@ func (c *Client) commandLoop(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleCallback(data interface{}) error {
|
||||
switch d := data.(type) {
|
||||
case SignerInfo:
|
||||
c.updateSignerInfo(d)
|
||||
case *messages.FetchCRLResponse:
|
||||
c.updateCRL(d)
|
||||
default:
|
||||
return fmt.Errorf("unknown callback data of type %T", data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) updateSignerInfo(signerInfo SignerInfo) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.logger.Debug("update signer info")
|
||||
|
||||
c.signerInfo = &signerInfo
|
||||
}
|
||||
|
||||
type CRLInfo struct {
|
||||
Name string
|
||||
LastKnown *big.Int
|
||||
}
|
||||
|
||||
func (c *Client) buildCRLInfo() []CRLInfo {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.signerInfo == nil {
|
||||
c.logger.Warn("no signer info available")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
infos := make([]CRLInfo, len(c.signerInfo.CACertificates))
|
||||
|
||||
for i, caName := range c.signerInfo.CACertificates {
|
||||
lastKnown := c.lastKnownCRL(caName)
|
||||
|
||||
infos[i] = CRLInfo{Name: caName, LastKnown: lastKnown}
|
||||
}
|
||||
|
||||
return infos
|
||||
}
|
||||
|
||||
func (c *Client) lastKnownCRL(caName string) *big.Int {
|
||||
crlFileName := c.buildCRLFileName(caName)
|
||||
|
||||
_, err := os.Stat(crlFileName)
|
||||
if err != nil {
|
||||
c.logger.WithField("crl", crlFileName).Debug("CRL file does not exist")
|
||||
|
||||
delete(c.lastKnownCRLS, caName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
lastKnown, ok := c.lastKnownCRLS[caName]
|
||||
if !ok {
|
||||
derData, err := os.ReadFile(crlFileName)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).WithField("crl", crlFileName).Error("could not read CRL data")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
crl, err := x509.ParseRevocationList(derData)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).WithField("crl", crlFileName).Error("could not parse CRL data")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
lastKnown = crl.Number
|
||||
|
||||
c.lastKnownCRLS[caName] = lastKnown
|
||||
}
|
||||
|
||||
return lastKnown
|
||||
}
|
||||
|
||||
func (c *Client) buildCRLFileName(caName string) string {
|
||||
return path.Join(c.config.CRLDirectory, fmt.Sprintf("%s.crl", caName))
|
||||
}
|
||||
|
||||
func (c *Client) updateCRL(d *messages.FetchCRLResponse) {
|
||||
var (
|
||||
der []byte
|
||||
crlNumber *big.Int
|
||||
)
|
||||
|
||||
if d.UnChanged {
|
||||
c.logger.WithField("issuer", d.IssuerID).Debug("CRL did not change")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !d.IsDelta {
|
||||
der = d.CRLData
|
||||
|
||||
list, err := x509.ParseRevocationList(der)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("CRL from signer could not be parsed")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
crlNumber = list.Number
|
||||
} else {
|
||||
crlFileName := c.buildCRLFileName(d.IssuerID)
|
||||
|
||||
der, err := c.patchCRL(crlFileName, d.CRLData)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("CRL patching failed")
|
||||
|
||||
delete(c.lastKnownCRLS, d.IssuerID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
list, err := x509.ParseRevocationList(der)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("could not parse patched CRL")
|
||||
|
||||
delete(c.lastKnownCRLS, d.IssuerID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
crlNumber = list.Number
|
||||
}
|
||||
|
||||
if err := c.writeCRL(d.IssuerID, der); err != nil {
|
||||
c.logger.WithError(err).Error("could not store CRL")
|
||||
|
||||
delete(c.lastKnownCRLS, d.IssuerID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.lastKnownCRLS[d.IssuerID] = crlNumber
|
||||
}
|
||||
|
||||
func (c *Client) writeCRL(caName string, crlBytes []byte) error {
|
||||
if err := os.MkdirAll(c.config.CRLDirectory, worldReadableDirPerm); err != nil {
|
||||
return fmt.Errorf("could not create CRL directory %s: %w", c.config.CRLDirectory, err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(c.buildCRLFileName(caName), crlBytes, worldReadableFilePerm); err != nil {
|
||||
c.logger.WithError(err).Error("could not write CRL file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) patchCRL(crlFileName string, diff []byte) ([]byte, error) {
|
||||
original, err := os.ReadFile(crlFileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read existing CRL %s: %w", crlFileName, err)
|
||||
}
|
||||
|
||||
patch, err := delta.Load(diff)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse CRL delta: %w", err)
|
||||
}
|
||||
|
||||
der, err := patch.Apply(original)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not apply CRL delta: %w", err)
|
||||
}
|
||||
|
||||
return der, nil
|
||||
}
|
||||
|
||||
func New(
|
||||
cfg *config.ClientConfig,
|
||||
logger *logrus.Logger,
|
||||
handler protocol.ClientHandler,
|
||||
commands chan *protocol.Command,
|
||||
callback chan interface{},
|
||||
) (*Client, error) {
|
||||
client := &Client{
|
||||
logger: logger,
|
||||
|
@ -151,6 +392,8 @@ func New(
|
|||
commands: commands,
|
||||
handler: handler,
|
||||
config: cfg,
|
||||
callback: callback,
|
||||
lastKnownCRLS: make(map[string]*big.Int),
|
||||
}
|
||||
|
||||
err := client.setupConnection(&serial.Config{
|
||||
|
|
|
@ -29,9 +29,12 @@ import (
|
|||
const (
|
||||
defaultSerialTimeout = 5 * time.Second
|
||||
defaultHealthInterval = 30 * time.Second
|
||||
defaultHealthStart = 5 * time.Second
|
||||
defaultHealthStart = 2 * time.Second
|
||||
defaultFetchCRLInterval = 5 * time.Minute
|
||||
defaultFetchCRLStart = 5 * time.Second
|
||||
defaultResponseAnnounceTimeout = 30 * time.Second
|
||||
defaultResponseDataTimeout = 2 * time.Second
|
||||
defaultCRLDirectory = "crls"
|
||||
)
|
||||
|
||||
type SettingsError struct {
|
||||
|
@ -52,8 +55,11 @@ type ClientConfig struct {
|
|||
Serial Serial `yaml:"serial"`
|
||||
HealthInterval time.Duration `yaml:"health-interval"`
|
||||
HealthStart time.Duration `yaml:"health-start"`
|
||||
FetchCRLStart time.Duration `yaml:"fetch-crl-start"`
|
||||
FetchCRLInterval time.Duration `yaml:"fetch-crl-interval"`
|
||||
ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"`
|
||||
ResponseDataTimeout time.Duration `yaml:"response-data-timeout"`
|
||||
CRLDirectory string `yaml:"crl-directory"`
|
||||
}
|
||||
|
||||
func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
||||
|
@ -61,8 +67,11 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
|||
Serial Serial `yaml:"serial"`
|
||||
HealthInterval time.Duration `yaml:"health-interval"`
|
||||
HealthStart time.Duration `yaml:"health-start"`
|
||||
FetchCRLStart time.Duration `yaml:"fetch-crl-start"`
|
||||
FetchCRLInterval time.Duration `yaml:"fetch-crl-interval"`
|
||||
ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"`
|
||||
ResponseDataTimeout time.Duration `yaml:"response-data-timeout"`
|
||||
CRLDirectory string `yaml:"crl-directory"`
|
||||
}{}
|
||||
|
||||
err := n.Decode(&data)
|
||||
|
@ -96,6 +105,18 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
|||
|
||||
c.HealthStart = data.HealthStart
|
||||
|
||||
if data.FetchCRLInterval == 0 {
|
||||
data.FetchCRLInterval = defaultFetchCRLInterval
|
||||
}
|
||||
|
||||
c.FetchCRLInterval = data.FetchCRLInterval
|
||||
|
||||
if data.FetchCRLStart == 0 {
|
||||
data.FetchCRLStart = defaultFetchCRLStart
|
||||
}
|
||||
|
||||
c.FetchCRLStart = data.FetchCRLStart
|
||||
|
||||
if data.ResponseAnnounceTimeout == 0 {
|
||||
data.ResponseAnnounceTimeout = defaultResponseAnnounceTimeout
|
||||
}
|
||||
|
@ -108,6 +129,12 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
|||
|
||||
c.ResponseDataTimeout = data.ResponseDataTimeout
|
||||
|
||||
if data.CRLDirectory == "" {
|
||||
data.CRLDirectory = defaultCRLDirectory
|
||||
}
|
||||
|
||||
c.CRLDirectory = data.CRLDirectory
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -119,7 +146,7 @@ func New(clientConfigFile string) (*ClientConfig, error) {
|
|||
|
||||
defer func() { _ = configFile.Close() }()
|
||||
|
||||
clientConfig, err := loadConfiguration(configFile)
|
||||
clientConfig, err := LoadConfiguration(configFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load client configuration from %s: %w", clientConfigFile, err)
|
||||
}
|
||||
|
@ -127,7 +154,7 @@ func New(clientConfigFile string) (*ClientConfig, error) {
|
|||
return clientConfig, nil
|
||||
}
|
||||
|
||||
func loadConfiguration(r io.Reader) (*ClientConfig, error) {
|
||||
func LoadConfiguration(r io.Reader) (*ClientConfig, error) {
|
||||
var config *ClientConfig
|
||||
|
||||
decoder := yaml.NewDecoder(r)
|
||||
|
|
|
@ -24,6 +24,8 @@ import (
|
|||
"github.com/shamaton/msgpackgen/msgpack"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"git.cacert.org/cacert-gosignerclient/internal/client"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
||||
|
@ -34,6 +36,7 @@ type SignerClientHandler struct {
|
|||
logger *logrus.Logger
|
||||
commands chan *protocol.Command
|
||||
config *config.ClientConfig
|
||||
clientCallback chan interface{}
|
||||
}
|
||||
|
||||
func (s *SignerClientHandler) Send(command *protocol.Command, out chan []byte) error {
|
||||
|
@ -47,7 +50,7 @@ func (s *SignerClientHandler) Send(command *protocol.Command, out chan []byte) e
|
|||
return fmt.Errorf("could not marshal command annoucement: %w", err)
|
||||
}
|
||||
|
||||
s.logger.WithField("announcement", command.Announce).Info("write command announcement")
|
||||
s.logger.WithField("announcement", command.Announce).Debug("write command announcement")
|
||||
|
||||
s.logger.Trace("writing command announcement")
|
||||
|
||||
|
@ -58,7 +61,7 @@ func (s *SignerClientHandler) Send(command *protocol.Command, out chan []byte) e
|
|||
return fmt.Errorf("could not marshal command data: %w", err)
|
||||
}
|
||||
|
||||
s.logger.WithField("command", command.Command).Info("write command data")
|
||||
s.logger.WithField("command", command.Command).Debug("write command data")
|
||||
|
||||
out <- frame
|
||||
|
||||
|
@ -103,6 +106,13 @@ func (s *SignerClientHandler) ResponseData(in chan []byte, response *protocol.Re
|
|||
return fmt.Errorf("could not unmarshal fetch CRL response data: %w", err)
|
||||
}
|
||||
|
||||
response.Response = &resp
|
||||
case messages.RespError:
|
||||
var resp messages.ErrorResponse
|
||||
if err := msgpack.Unmarshal(frame, &resp); err != nil {
|
||||
return fmt.Errorf("could not unmarshal error response data: %w", err)
|
||||
}
|
||||
|
||||
response.Response = &resp
|
||||
default:
|
||||
return fmt.Errorf("unhandled response code %s", response.Announce.Code)
|
||||
|
@ -118,19 +128,90 @@ func (s *SignerClientHandler) HandleResponse(response *protocol.Response) error
|
|||
s.logger.WithField("response", response.Announce).Info("handled response")
|
||||
s.logger.WithField("response", response).Debug("full response")
|
||||
|
||||
// TODO: add real implementations
|
||||
switch r := response.Response.(type) {
|
||||
case *messages.ErrorResponse:
|
||||
s.logger.WithField("message", r.Message).Error("error from signer")
|
||||
case *messages.HealthResponse:
|
||||
s.handleHealthResponse(r)
|
||||
case *messages.FetchCRLResponse:
|
||||
s.handleFetchCRLResponse(r)
|
||||
default:
|
||||
s.logger.WithField("response", response).Warnf("unhandled response of type %T", response.Response)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SignerClientHandler) handleHealthResponse(r *messages.HealthResponse) {
|
||||
signerInfo := client.SignerInfo{}
|
||||
|
||||
signerInfo.SignerHealth = r.Healthy
|
||||
signerInfo.SignerVersion = r.Version
|
||||
|
||||
if !r.Healthy {
|
||||
// it might be a good idea to notify monitoring if the signer is not OK
|
||||
s.logger.Error("signer is not healthy")
|
||||
}
|
||||
|
||||
for _, item := range r.Info {
|
||||
if !item.Healthy {
|
||||
s.logger.WithField("component", item.Source).Error("signer component is not healthy")
|
||||
}
|
||||
|
||||
switch item.Source {
|
||||
case "HSM":
|
||||
signerInfo.CACertificates = make([]string, 0)
|
||||
signerInfo.UsableProfiles = make(map[string][]client.Profile)
|
||||
|
||||
for certName, value := range item.MoreInfo {
|
||||
certInfo, err := item.ParseCertificateInfo(value)
|
||||
if err != nil {
|
||||
s.logger.WithError(err).Error("could not parse certificate information")
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
s.logger.WithFields(map[string]interface{}{
|
||||
"certificate": certName,
|
||||
"signing": certInfo.Signing,
|
||||
"profiles": certInfo.Profiles,
|
||||
"status": certInfo.Status,
|
||||
"valid-until": certInfo.ValidUntil,
|
||||
}).Trace("certificate info")
|
||||
|
||||
signerInfo.CACertificates = append(signerInfo.CACertificates, certName)
|
||||
|
||||
if certInfo.Signing {
|
||||
for _, profile := range certInfo.Profiles {
|
||||
signerInfo.UsableProfiles[certName] = append(
|
||||
signerInfo.UsableProfiles[certName],
|
||||
client.Profile{Name: profile.Name, UseFor: string(profile.UseFor)},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
s.logger.WithField("source", item.Source).Warn("unhandled health source")
|
||||
}
|
||||
}
|
||||
|
||||
s.clientCallback <- signerInfo
|
||||
}
|
||||
|
||||
func (s *SignerClientHandler) handleFetchCRLResponse(r *messages.FetchCRLResponse) {
|
||||
s.clientCallback <- r
|
||||
}
|
||||
|
||||
func New(
|
||||
config *config.ClientConfig,
|
||||
logger *logrus.Logger,
|
||||
commands chan *protocol.Command,
|
||||
clientCallback chan interface{},
|
||||
) (protocol.ClientHandler, error) {
|
||||
return &SignerClientHandler{
|
||||
logger: logger,
|
||||
config: config,
|
||||
commands: commands,
|
||||
clientCallback: clientCallback,
|
||||
}, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue