Implement CA certificate information handling
Change the behaviour of the client to use the new CAInfoCommand support in cacert-gosigner. The client has a new mechanism to generate new commands as reaction to received responses. This functionality is used to retrieve CA certificate information when certificates previously unknown to the client appear and to trigger CRL retrieval for new certificates. New CA certificates announced by the signer are detected and information is retrieved. The retrieved CA certificate is stored alongside the CRL files in a configurable directory (defaults to "public" in the working directory of the signerclient process).
This commit is contained in:
parent
199f0ee0c0
commit
da24ae70b6
7 changed files with 339 additions and 121 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,8 +1,8 @@
|
|||
*Pty
|
||||
/.idea/
|
||||
/config.yaml
|
||||
/crls/
|
||||
/dist/
|
||||
/public/
|
||||
/signerclient
|
||||
go.work
|
||||
go.work.sum
|
||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module git.cacert.org/cacert-gosignerclient
|
|||
go 1.19
|
||||
|
||||
require (
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221202080952-37d3b1e02146
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221203104439-bc81ab84cb4a
|
||||
github.com/balacode/go-delta v0.1.0
|
||||
github.com/shamaton/msgpackgen v0.3.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,5 +1,11 @@
|
|||
git.cacert.org/cacert-gosigner v0.0.0-20221202080952-37d3b1e02146 h1:vbm3fIRxNKD4jahqVnIvvU7jc57JfHz5KijalJFlHJ4=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221202080952-37d3b1e02146/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221202122810-6f8ac9818cd1 h1:HRtgcV6tRM+jN8NxPx7DkuwdH2prZOTdydMBCFg/CWM=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221202122810-6f8ac9818cd1/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221202173159-afe7d23c9b6f h1:VcIwyogvdmYDpDwE7U0+S2P+xU5zwquppAVp2q4eI9k=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221202173159-afe7d23c9b6f/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221203104439-bc81ab84cb4a h1:yX3lhEoBQkUKu23xggAzAeYWuziCkRYktSjsAOfNGHY=
|
||||
git.cacert.org/cacert-gosigner v0.0.0-20221203104439-bc81ab84cb4a/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
|
||||
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/go.mod h1:pLeSAL3DhZ9L0JuiRkUtIX3mLOCtzBLnDhfmykbSmkE=
|
||||
|
|
|
@ -20,6 +20,7 @@ package client
|
|||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
@ -33,6 +34,7 @@ import (
|
|||
|
||||
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
||||
"git.cacert.org/cacert-gosignerclient/internal/command"
|
||||
"git.cacert.org/cacert-gosignerclient/internal/config"
|
||||
)
|
||||
|
||||
|
@ -49,30 +51,43 @@ type Profile struct {
|
|||
}
|
||||
|
||||
type CertInfo struct {
|
||||
Name string
|
||||
FetchCRL bool
|
||||
Name string
|
||||
FetchCert bool
|
||||
FetchCRL bool
|
||||
LastKnownCRL *big.Int
|
||||
Certificate *x509.Certificate
|
||||
Profiles map[string]*Profile
|
||||
}
|
||||
|
||||
type SignerInfo struct {
|
||||
SignerHealth bool
|
||||
SignerVersion string
|
||||
CACertificates []CertInfo
|
||||
UsableProfiles map[string][]Profile
|
||||
CACertificates []string
|
||||
}
|
||||
|
||||
func (i *SignerInfo) containsCA(caName string) bool {
|
||||
for _, name := range i.CACertificates {
|
||||
if name == caName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
port *serial.Port
|
||||
logger *logrus.Logger
|
||||
framer protocol.Framer
|
||||
in chan []byte
|
||||
out chan []byte
|
||||
commands chan *protocol.Command
|
||||
handler protocol.ClientHandler
|
||||
config *config.ClientConfig
|
||||
signerInfo *SignerInfo
|
||||
callback chan interface{}
|
||||
port *serial.Port
|
||||
logger *logrus.Logger
|
||||
framer protocol.Framer
|
||||
in chan []byte
|
||||
out chan []byte
|
||||
commands chan *protocol.Command
|
||||
handler protocol.ClientHandler
|
||||
config *config.ClientConfig
|
||||
signerInfo *SignerInfo
|
||||
knownCertificates map[string]*CertInfo
|
||||
callback chan interface{}
|
||||
sync.Mutex
|
||||
lastKnownCRLS map[string]*big.Int
|
||||
}
|
||||
|
||||
func (c *Client) Run(ctx context.Context) error {
|
||||
|
@ -158,63 +173,118 @@ func (c *Client) commandLoop(ctx context.Context) {
|
|||
fetchCRLTimer := time.NewTimer(c.config.FetchCRLStart)
|
||||
|
||||
for {
|
||||
newCommands := make([]*protocol.Command, 0)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case callbackData := <-c.callback:
|
||||
err := c.handleCallback(callbackData)
|
||||
addCommands, err := c.handleCallback(callbackData)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("callback handling failed")
|
||||
}
|
||||
|
||||
newCommands = append(newCommands, addCommands...)
|
||||
case <-fetchCRLTimer.C:
|
||||
for _, crlInfo := range c.buildCRLInfo() {
|
||||
var lastKnown []byte
|
||||
|
||||
if crlInfo.LastKnown != nil {
|
||||
lastKnown = crlInfo.LastKnown.Bytes()
|
||||
}
|
||||
|
||||
c.commands <- &protocol.Command{
|
||||
Announce: messages.BuildCommandAnnounce(messages.CmdFetchCRL),
|
||||
Command: &messages.FetchCRLCommand{
|
||||
IssuerID: crlInfo.Name,
|
||||
LastKnownID: lastKnown,
|
||||
},
|
||||
}
|
||||
for _, crlInfo := range c.requiredCRLs() {
|
||||
newCommands = append(newCommands, command.FetchCRL(crlInfo.Name, crlInfo.LastKnown))
|
||||
}
|
||||
|
||||
fetchCRLTimer.Reset(c.config.FetchCRLInterval)
|
||||
case <-healthTimer.C:
|
||||
c.commands <- &protocol.Command{
|
||||
Announce: messages.BuildCommandAnnounce(messages.CmdHealth),
|
||||
Command: &messages.HealthCommand{},
|
||||
}
|
||||
newCommands = append(newCommands, command.Health())
|
||||
|
||||
healthTimer.Reset(c.config.HealthInterval)
|
||||
}
|
||||
|
||||
for _, nextCommand := range newCommands {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case c.commands <- nextCommand:
|
||||
c.logger.WithField("command", nextCommand.Announce).Trace("sent command")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleCallback(data interface{}) error {
|
||||
func (c *Client) handleCallback(data interface{}) ([]*protocol.Command, error) {
|
||||
switch d := data.(type) {
|
||||
case SignerInfo:
|
||||
c.updateSignerInfo(d)
|
||||
return c.updateSignerInfo(d)
|
||||
case *messages.CAInfoResponse:
|
||||
return c.updateCAInformation(d)
|
||||
case *messages.FetchCRLResponse:
|
||||
c.updateCRL(d)
|
||||
return c.updateCRL(d)
|
||||
default:
|
||||
return fmt.Errorf("unknown callback data of type %T", data)
|
||||
return nil, fmt.Errorf("unknown callback data of type %T", data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) updateSignerInfo(signerInfo SignerInfo) {
|
||||
func (c *Client) updateSignerInfo(signerInfo SignerInfo) ([]*protocol.Command, error) {
|
||||
c.logger.Debug("update signer info")
|
||||
|
||||
c.Lock()
|
||||
c.signerInfo = &signerInfo
|
||||
c.Unlock()
|
||||
|
||||
c.learnNewCACertificates()
|
||||
|
||||
c.forgetRemovedCACertificates()
|
||||
|
||||
newCommands := make([]*protocol.Command, 0)
|
||||
|
||||
for _, caName := range c.requiredCertificateInfo() {
|
||||
newCommands = append(newCommands, command.CAInfo(caName))
|
||||
}
|
||||
|
||||
return newCommands, nil
|
||||
}
|
||||
|
||||
func (c *Client) updateCAInformation(d *messages.CAInfoResponse) ([]*protocol.Command, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.logger.Debug("update signer info")
|
||||
caInfo, ok := c.knownCertificates[d.Name]
|
||||
if !ok {
|
||||
c.logger.WithField("certificate", d.Name).Warn("unknown CA certificate")
|
||||
|
||||
c.signerInfo = &signerInfo
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(d.Certificate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse CA certificate for %s: %w", d.Name, err)
|
||||
}
|
||||
|
||||
if !cert.IsCA {
|
||||
return nil, fmt.Errorf("certificate for %s is not a CA certificate", d.Name)
|
||||
}
|
||||
|
||||
err = c.writeCertificate(caInfo.Name, d.Certificate)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).WithField("certificate", d.Name).Warn("could not write CA certificate files")
|
||||
}
|
||||
|
||||
caInfo.Certificate = cert
|
||||
caInfo.FetchCert = false
|
||||
|
||||
caInfo.Profiles = make(map[string]*Profile)
|
||||
|
||||
for _, p := range d.Profiles {
|
||||
caInfo.Profiles[p.Name] = &Profile{
|
||||
Name: p.Name,
|
||||
UseFor: p.UseFor.String(),
|
||||
}
|
||||
}
|
||||
|
||||
if len(cert.CRLDistributionPoints) == 0 {
|
||||
caInfo.FetchCRL = false
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []*protocol.Command{command.FetchCRL(caInfo.Name, c.lastKnownCRL(caInfo))}, nil
|
||||
}
|
||||
|
||||
type CRLInfo struct {
|
||||
|
@ -222,43 +292,63 @@ type CRLInfo struct {
|
|||
LastKnown *big.Int
|
||||
}
|
||||
|
||||
func (c *Client) buildCRLInfo() []CRLInfo {
|
||||
func (c *Client) requiredCRLs() []CRLInfo {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.signerInfo == nil {
|
||||
c.logger.Warn("no signer info available")
|
||||
if c.knownCertificates == nil {
|
||||
c.logger.Warn("no certificates known")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
infos := make([]CRLInfo, 0)
|
||||
|
||||
for _, caInfo := range c.signerInfo.CACertificates {
|
||||
for _, caInfo := range c.knownCertificates {
|
||||
if caInfo.FetchCRL {
|
||||
lastKnown := c.lastKnownCRL(caInfo.Name)
|
||||
|
||||
infos = append(infos, CRLInfo{Name: caInfo.Name, LastKnown: lastKnown})
|
||||
infos = append(infos, CRLInfo{Name: caInfo.Name, LastKnown: c.lastKnownCRL(caInfo)})
|
||||
}
|
||||
}
|
||||
|
||||
return infos
|
||||
}
|
||||
|
||||
func (c *Client) lastKnownCRL(caName string) *big.Int {
|
||||
func (c *Client) requiredCertificateInfo() []string {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.knownCertificates == nil {
|
||||
c.logger.Warn("no certificates known")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
infos := make([]string, 0)
|
||||
|
||||
for _, caInfo := range c.knownCertificates {
|
||||
if caInfo.FetchCert {
|
||||
infos = append(infos, caInfo.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return infos
|
||||
}
|
||||
|
||||
func (c *Client) lastKnownCRL(caInfo *CertInfo) *big.Int {
|
||||
caName := caInfo.Name
|
||||
|
||||
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 {
|
||||
lastKnown := caInfo.LastKnownCRL
|
||||
|
||||
if lastKnown == nil {
|
||||
derData, err := os.ReadFile(crlFileName)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).WithField("crl", crlFileName).Error("could not read CRL data")
|
||||
|
@ -274,27 +364,27 @@ func (c *Client) lastKnownCRL(caName string) *big.Int {
|
|||
}
|
||||
|
||||
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) {
|
||||
func (c *Client) updateCRL(d *messages.FetchCRLResponse) ([]*protocol.Command, error) {
|
||||
var (
|
||||
der []byte
|
||||
crlNumber *big.Int
|
||||
der []byte
|
||||
err error
|
||||
)
|
||||
|
||||
caInfo, ok := c.knownCertificates[d.IssuerID]
|
||||
if !ok {
|
||||
c.logger.WithField("certificate", d.IssuerID).Warn("unknown CA certificate")
|
||||
}
|
||||
|
||||
if d.UnChanged {
|
||||
c.logger.WithField("issuer", d.IssuerID).Debug("CRL did not change")
|
||||
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !d.IsDelta {
|
||||
|
@ -304,29 +394,25 @@ func (c *Client) updateCRL(d *messages.FetchCRLResponse) {
|
|||
if err != nil {
|
||||
c.logger.WithError(err).Error("CRL from signer could not be parsed")
|
||||
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
crlNumber = list.Number
|
||||
} else {
|
||||
crlFileName := c.buildCRLFileName(d.IssuerID)
|
||||
|
||||
der, err := c.patchCRL(crlFileName, d.CRLData)
|
||||
der, err = c.patchCRL(crlFileName, d.CRLData)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("CRL patching failed")
|
||||
|
||||
delete(c.lastKnownCRLS, d.IssuerID)
|
||||
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
list, err := x509.ParseRevocationList(der)
|
||||
if err != nil {
|
||||
c.logger.WithError(err).Error("could not parse patched CRL")
|
||||
|
||||
delete(c.lastKnownCRLS, d.IssuerID)
|
||||
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
crlNumber = list.Number
|
||||
|
@ -335,17 +421,57 @@ func (c *Client) updateCRL(d *messages.FetchCRLResponse) {
|
|||
if err := c.writeCRL(d.IssuerID, der); err != nil {
|
||||
c.logger.WithError(err).Error("could not store CRL")
|
||||
|
||||
delete(c.lastKnownCRLS, d.IssuerID)
|
||||
caInfo.LastKnownCRL = nil
|
||||
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c.lastKnownCRLS[d.IssuerID] = crlNumber
|
||||
caInfo.LastKnownCRL = crlNumber
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *Client) buildCRLFileName(caName string) string {
|
||||
return path.Join(c.config.PublicDataDirectory, fmt.Sprintf("%s.crl", caName))
|
||||
}
|
||||
|
||||
func (c *Client) buildCertificateFileName(caName string, certFormat string) string {
|
||||
return path.Join(c.config.PublicDataDirectory, fmt.Sprintf("%s.%s", caName, certFormat))
|
||||
}
|
||||
|
||||
func (c *Client) ensurePublicDataDirectory() error {
|
||||
if err := os.MkdirAll(c.config.PublicDataDirectory, worldReadableDirPerm); err != nil {
|
||||
return fmt.Errorf("could not create public CA data directory %s: %w", c.config.PublicDataDirectory, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) writeCertificate(caName string, derBytes []byte) error {
|
||||
if err := c.ensurePublicDataDirectory(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(
|
||||
c.buildCertificateFileName(caName, "crt"), derBytes, worldReadableFilePerm,
|
||||
); err != nil {
|
||||
c.logger.WithError(err).Error("could not write DER encoded certificate file")
|
||||
}
|
||||
|
||||
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
|
||||
if err := os.WriteFile(
|
||||
c.buildCertificateFileName(caName, "pem"), pemBytes, worldReadableFilePerm,
|
||||
); err != nil {
|
||||
c.logger.WithError(err).Error("could not write PEM encoded certificate file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 := c.ensurePublicDataDirectory(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(c.buildCRLFileName(caName), crlBytes, worldReadableFilePerm); err != nil {
|
||||
|
@ -374,6 +500,38 @@ func (c *Client) patchCRL(crlFileName string, diff []byte) ([]byte, error) {
|
|||
return der, nil
|
||||
}
|
||||
|
||||
func (c *Client) learnNewCACertificates() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
for _, caName := range c.signerInfo.CACertificates {
|
||||
if _, ok := c.knownCertificates[caName]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
c.knownCertificates[caName] = &CertInfo{
|
||||
Name: caName,
|
||||
FetchCert: true,
|
||||
FetchCRL: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) forgetRemovedCACertificates() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
for knownCA := range c.knownCertificates {
|
||||
if c.signerInfo.containsCA(knownCA) {
|
||||
continue
|
||||
}
|
||||
|
||||
c.logger.WithField("certificate", knownCA).Warn("signer did not send status for certificate")
|
||||
|
||||
delete(c.knownCertificates, knownCA)
|
||||
}
|
||||
}
|
||||
|
||||
func New(
|
||||
cfg *config.ClientConfig,
|
||||
logger *logrus.Logger,
|
||||
|
@ -387,15 +545,15 @@ func New(
|
|||
}
|
||||
|
||||
client := &Client{
|
||||
logger: logger,
|
||||
framer: cobsFramer,
|
||||
in: make(chan []byte),
|
||||
out: make(chan []byte),
|
||||
commands: commands,
|
||||
handler: handler,
|
||||
config: cfg,
|
||||
callback: callback,
|
||||
lastKnownCRLS: make(map[string]*big.Int),
|
||||
logger: logger,
|
||||
framer: cobsFramer,
|
||||
in: make(chan []byte),
|
||||
out: make(chan []byte),
|
||||
commands: commands,
|
||||
handler: handler,
|
||||
config: cfg,
|
||||
callback: callback,
|
||||
knownCertificates: make(map[string]*CertInfo),
|
||||
}
|
||||
|
||||
err = client.setupConnection(&serial.Config{
|
||||
|
|
55
internal/command/command.go
Normal file
55
internal/command/command.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 command
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
||||
)
|
||||
|
||||
func CAInfo(caName string) *protocol.Command {
|
||||
return &protocol.Command{
|
||||
Announce: messages.BuildCommandAnnounce(messages.CmdCAInfo),
|
||||
Command: &messages.CAInfoCommand{Name: caName},
|
||||
}
|
||||
}
|
||||
|
||||
func FetchCRL(caName string, crlNumber *big.Int) *protocol.Command {
|
||||
var lastKnown []byte
|
||||
|
||||
if crlNumber != nil {
|
||||
lastKnown = crlNumber.Bytes()
|
||||
}
|
||||
|
||||
return &protocol.Command{
|
||||
Announce: messages.BuildCommandAnnounce(messages.CmdFetchCRL),
|
||||
Command: &messages.FetchCRLCommand{
|
||||
IssuerID: caName,
|
||||
LastKnownID: lastKnown,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Health() *protocol.Command {
|
||||
return &protocol.Command{
|
||||
Announce: messages.BuildCommandAnnounce(messages.CmdHealth),
|
||||
Command: &messages.HealthCommand{},
|
||||
}
|
||||
}
|
|
@ -31,10 +31,10 @@ const (
|
|||
defaultHealthInterval = 30 * time.Second
|
||||
defaultHealthStart = 2 * time.Second
|
||||
defaultFetchCRLInterval = 5 * time.Minute
|
||||
defaultFetchCRLStart = 5 * time.Second
|
||||
defaultFetchCRLStart = 5 * time.Minute
|
||||
defaultResponseAnnounceTimeout = 30 * time.Second
|
||||
defaultResponseDataTimeout = 2 * time.Second
|
||||
defaultCRLDirectory = "crls"
|
||||
defaultFilesDirectory = "public"
|
||||
)
|
||||
|
||||
type SettingsError struct {
|
||||
|
@ -59,7 +59,7 @@ type ClientConfig struct {
|
|||
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"`
|
||||
PublicDataDirectory string `yaml:"public-data-directory"`
|
||||
}
|
||||
|
||||
func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
||||
|
@ -71,7 +71,7 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
|||
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"`
|
||||
PublicDataDirectory string `yaml:"public-data-directory"`
|
||||
}{}
|
||||
|
||||
err := n.Decode(&data)
|
||||
|
@ -129,11 +129,11 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
|||
|
||||
c.ResponseDataTimeout = data.ResponseDataTimeout
|
||||
|
||||
if data.CRLDirectory == "" {
|
||||
data.CRLDirectory = defaultCRLDirectory
|
||||
if data.PublicDataDirectory == "" {
|
||||
data.PublicDataDirectory = defaultFilesDirectory
|
||||
}
|
||||
|
||||
c.CRLDirectory = data.CRLDirectory
|
||||
c.PublicDataDirectory = data.PublicDataDirectory
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -112,6 +112,13 @@ func (s *SignerClientHandler) ResponseData(ctx context.Context, in chan []byte,
|
|||
return fmt.Errorf("could not unmarshal health response data: %w", err)
|
||||
}
|
||||
|
||||
response.Response = &resp
|
||||
case messages.RespCAInfo:
|
||||
var resp messages.CAInfoResponse
|
||||
if err := msgpack.Unmarshal(frame, &resp); err != nil {
|
||||
return fmt.Errorf("could not unmarshal CA info response data: %w", err)
|
||||
}
|
||||
|
||||
response.Response = &resp
|
||||
case messages.RespFetchCRL:
|
||||
var resp messages.FetchCRLResponse
|
||||
|
@ -146,6 +153,8 @@ func (s *SignerClientHandler) HandleResponse(ctx context.Context, response *prot
|
|||
s.logger.WithField("message", r.Message).Error("error from signer")
|
||||
case *messages.HealthResponse:
|
||||
s.handleHealthResponse(ctx, r)
|
||||
case *messages.CAInfoResponse:
|
||||
s.handleCAInfoResponse(ctx, r)
|
||||
case *messages.FetchCRLResponse:
|
||||
s.handleFetchCRLResponse(ctx, r)
|
||||
default:
|
||||
|
@ -173,38 +182,19 @@ func (s *SignerClientHandler) handleHealthResponse(ctx context.Context, r *messa
|
|||
|
||||
switch item.Source {
|
||||
case "HSM":
|
||||
signerInfo.CACertificates = make([]client.CertInfo, 0)
|
||||
signerInfo.UsableProfiles = make(map[string][]client.Profile)
|
||||
signerInfo.CACertificates = make([]string, 0)
|
||||
|
||||
for certName, value := range item.MoreInfo {
|
||||
certInfo, err := messages.ParseCertificateInfo(value)
|
||||
if err != nil {
|
||||
s.logger.WithError(err).Error("could not parse certificate information")
|
||||
for certName, status := range item.MoreInfo {
|
||||
if status == string(messages.CertStatusOk) {
|
||||
signerInfo.CACertificates = append(signerInfo.CACertificates, certName)
|
||||
|
||||
break
|
||||
continue
|
||||
}
|
||||
|
||||
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,
|
||||
client.CertInfo{Name: certName, FetchCRL: certInfo.Signing},
|
||||
)
|
||||
|
||||
if certInfo.Signing {
|
||||
for _, profile := range certInfo.Profiles {
|
||||
signerInfo.UsableProfiles[certName] = append(
|
||||
signerInfo.UsableProfiles[certName],
|
||||
client.Profile{Name: profile.Name, UseFor: string(profile.UseFor)},
|
||||
)
|
||||
}
|
||||
}
|
||||
"status": status,
|
||||
}).Warn("certificate has issues")
|
||||
}
|
||||
default:
|
||||
s.logger.WithField("source", item.Source).Warn("unhandled health source")
|
||||
|
@ -219,6 +209,15 @@ func (s *SignerClientHandler) handleHealthResponse(ctx context.Context, r *messa
|
|||
}
|
||||
}
|
||||
|
||||
func (s *SignerClientHandler) handleCAInfoResponse(ctx context.Context, r *messages.CAInfoResponse) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case s.clientCallback <- r:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignerClientHandler) handleFetchCRLResponse(ctx context.Context, r *messages.FetchCRLResponse) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
|
Loading…
Reference in a new issue