Initial signer client implementation
This commit adds the project setup and implements a basic signer client that sends health check commands to the signer.main
commit
91d4f69a9b
@ -0,0 +1 @@
|
|||||||
|
*.go text
|
@ -0,0 +1,5 @@
|
|||||||
|
*Pty
|
||||||
|
/.idea/
|
||||||
|
/config.yaml
|
||||||
|
/dist/
|
||||||
|
/signerclient
|
@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
run:
|
||||||
|
skip-files:
|
||||||
|
- internal/config/amd64.go
|
||||||
|
- internal/config/arm64.go
|
||||||
|
- internal/config/armhf.go
|
||||||
|
- pkg/messages/resolver.msgpackgen.go
|
||||||
|
|
||||||
|
output:
|
||||||
|
sort-results: true
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
cyclop:
|
||||||
|
max-complexity: 15
|
||||||
|
goheader:
|
||||||
|
values:
|
||||||
|
const:
|
||||||
|
ORGANIZATION: CAcert Inc.
|
||||||
|
template: |-
|
||||||
|
Copyright {{ YEAR-RANGE }} {{ ORGANIZATION }}
|
||||||
|
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.
|
||||||
|
gomnd:
|
||||||
|
ignored-functions:
|
||||||
|
- 'strconv.*'
|
||||||
|
ignored-numbers: ["2", "16", "128", "0o600", "0o700"]
|
||||||
|
goimports:
|
||||||
|
local-prefixes: code.cacert.org,git.cacert.org
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
ignore-words:
|
||||||
|
- CAcert
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: false
|
||||||
|
enable:
|
||||||
|
- bodyclose
|
||||||
|
- containedctx
|
||||||
|
- contextcheck
|
||||||
|
- cyclop
|
||||||
|
- decorder
|
||||||
|
- errorlint
|
||||||
|
- exportloopref
|
||||||
|
- forbidigo
|
||||||
|
- forcetypeassert
|
||||||
|
- gocognit
|
||||||
|
- goconst
|
||||||
|
- gocritic
|
||||||
|
- gofmt
|
||||||
|
- goheader
|
||||||
|
- goimports
|
||||||
|
- gomnd
|
||||||
|
- gosec
|
||||||
|
- lll
|
||||||
|
- makezero
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- nestif
|
||||||
|
- nlreturn
|
||||||
|
- nolintlint
|
||||||
|
- predeclared
|
||||||
|
- revive
|
||||||
|
- rowserrcheck
|
||||||
|
- sqlclosecheck
|
||||||
|
- wrapcheck
|
||||||
|
- wsl
|
@ -0,0 +1,20 @@
|
|||||||
|
GOFILES := $(shell find -type f -name '*.go')
|
||||||
|
BUILD_TIME := $(shell date --rfc-3339=seconds)
|
||||||
|
COMMIT := $(shell git show-ref --head --abbrev=8 HEAD|cut -d ' ' -f 1)
|
||||||
|
VERSION := $(shell git describe --always --dirty)
|
||||||
|
|
||||||
|
all: lint test signerclient
|
||||||
|
|
||||||
|
lint:
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -race ./...
|
||||||
|
|
||||||
|
signerclient: $(GOFILES)
|
||||||
|
go build -race -ldflags="-X 'main.date=$(BUILD_TIME)' -X 'main.commit=$(COMMIT)' -X 'main.version=$(VERSION)'" ./cmd/signerclient
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f signerclient
|
||||||
|
|
||||||
|
.PHONY: test lint all clean
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
||||||
|
"git.cacert.org/cacert-gosignerclient/internal/client"
|
||||||
|
"git.cacert.org/cacert-gosignerclient/internal/config"
|
||||||
|
"git.cacert.org/cacert-gosignerclient/internal/handler"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
commit = "dev"
|
||||||
|
date = "unknown"
|
||||||
|
version = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultConfigFile = "config.yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
showVersion, verbose bool
|
||||||
|
configFile, logLevel string
|
||||||
|
logger *logrus.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logrus.New()
|
||||||
|
logger.SetOutput(os.Stdout)
|
||||||
|
logger.SetLevel(logrus.InfoLevel)
|
||||||
|
|
||||||
|
logger.Infof("cacert-gosignerclient %s (%s) - build %s", version, commit, date)
|
||||||
|
|
||||||
|
flag.StringVar(&configFile, "config", defaultConfigFile, "signer client configuration file")
|
||||||
|
flag.BoolVar(&showVersion, "version", false, "show version")
|
||||||
|
flag.BoolVar(&verbose, "verbose", false, "verbose output")
|
||||||
|
flag.StringVar(&logLevel, "loglevel", "INFO", "log level")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if showVersion {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedLevel, err := logrus.ParseLevel(logLevel)
|
||||||
|
if err != nil {
|
||||||
|
logger.WithError(err).Fatal("could not parse log level")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.SetLevel(parsedLevel)
|
||||||
|
|
||||||
|
clientConfig, err := config.New(configFile)
|
||||||
|
if err != nil {
|
||||||
|
logger.WithError(err).Fatal("could not configure client")
|
||||||
|
}
|
||||||
|
|
||||||
|
commands := make(chan *protocol.Command)
|
||||||
|
|
||||||
|
clientHandler, err := handler.New(clientConfig, logger, commands)
|
||||||
|
if err != nil {
|
||||||
|
logger.WithError(err).Fatal("could not setup client handler")
|
||||||
|
}
|
||||||
|
|
||||||
|
signerClient, err := client.New(clientConfig, logger, clientHandler, commands)
|
||||||
|
if err != nil {
|
||||||
|
logger.WithError(err).Fatal("could not setup client")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = signerClient.Close() }()
|
||||||
|
|
||||||
|
logger.Info("setup complete, starting client operation")
|
||||||
|
|
||||||
|
if err = signerClient.Run(context.Background()); err != nil {
|
||||||
|
logger.WithError(err).Fatal("error in client")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
module git.cacert.org/cacert-gosignerclient
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.cacert.org/cacert-gosigner v0.0.0-20221129130510-af40662c7d61
|
||||||
|
github.com/sirupsen/logrus v1.9.0
|
||||||
|
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
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
|
||||||
|
github.com/shamaton/msgpack/v2 v2.1.0 // indirect
|
||||||
|
github.com/shamaton/msgpackgen v0.3.0 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||||
|
)
|
@ -0,0 +1,43 @@
|
|||||||
|
git.cacert.org/cacert-gosigner v0.0.0-20221129130510-af40662c7d61 h1:kUML0ocTKcCQeazZc40Df3GYIr00gxJrYal0arxG+tQ=
|
||||||
|
git.cacert.org/cacert-gosigner v0.0.0-20221129130510-af40662c7d61/go.mod h1:yFBRxW6BwOyiXGtHQH/Vpro4Dxd0oIl3tCIWg99nYGE=
|
||||||
|
github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
|
||||||
|
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=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
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=
|
||||||
|
github.com/shamaton/msgpack/v2 v2.1.0/go.mod h1:aTUEmh31ziGX1Ml7wMPLVY0f4vT3CRsCvZRoSCs+VGg=
|
||||||
|
github.com/shamaton/msgpackgen v0.3.0 h1:q6o7prOEJFdF9BAPgkOtfzJbs55pQi7g44RUnEVUxtM=
|
||||||
|
github.com/shamaton/msgpackgen v0.3.0/go.mod h1:fd99fDDuxuTiWzkHC59uEGzrt/WDu+ltGZTbEWwVXIc=
|
||||||
|
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=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/tarm/serial"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||||
|
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
||||||
|
"git.cacert.org/cacert-gosignerclient/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Run(ctx context.Context) error {
|
||||||
|
protocolErrors := make(chan error)
|
||||||
|
framerErrors := make(chan error)
|
||||||
|
|
||||||
|
go func(f protocol.Framer) {
|
||||||
|
framerErrors <- f.ReadFrames(c.port, c.in)
|
||||||
|
}(c.framer)
|
||||||
|
|
||||||
|
go func(f protocol.Framer) {
|
||||||
|
framerErrors <- f.WriteFrames(c.port, c.out)
|
||||||
|
}(c.framer)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
clientProtocol := protocol.NewClient(c.handler, c.commands, c.in, c.out, c.logger)
|
||||||
|
|
||||||
|
protocolErrors <- clientProtocol.Handle()
|
||||||
|
}()
|
||||||
|
|
||||||
|
ctx, cancelCommandLoop := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
go c.commandLoop(ctx)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
cancelCommandLoop()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
case err := <-framerErrors:
|
||||||
|
cancelCommandLoop()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error from framer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
case err := <-protocolErrors:
|
||||||
|
cancelCommandLoop()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error from protocol: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) setupConnection(serialConfig *serial.Config) error {
|
||||||
|
s, err := serial.OpenPort(serialConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not open serial port: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.port = s
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
close(c.in)
|
||||||
|
close(c.out)
|
||||||
|
|
||||||
|
if c.port != nil {
|
||||||
|
err := c.port.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not close serial port: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) commandLoop(ctx context.Context) {
|
||||||
|
healthTimer := time.NewTimer(c.config.HealthStart)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-healthTimer.C:
|
||||||
|
announce, err := messages.BuildCommandAnnounce(messages.CmdHealth)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.WithError(err).Error("could not build health command announce")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.commands <- &protocol.Command{
|
||||||
|
Announce: announce,
|
||||||
|
Command: &messages.HealthCommand{},
|
||||||
|
}
|
||||||
|
|
||||||
|
healthTimer.Reset(c.config.HealthInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(
|
||||||
|
cfg *config.ClientConfig,
|
||||||
|
logger *logrus.Logger,
|
||||||
|
handler protocol.ClientHandler,
|
||||||
|
commands chan *protocol.Command,
|
||||||
|
) (*Client, error) {
|
||||||
|
client := &Client{
|
||||||
|
logger: logger,
|
||||||
|
framer: protocol.NewCOBSFramer(logger),
|
||||||
|
in: make(chan []byte),
|
||||||
|
out: make(chan []byte),
|
||||||
|
commands: commands,
|
||||||
|
handler: handler,
|
||||||
|
config: cfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.setupConnection(&serial.Config{
|
||||||
|
Name: cfg.Serial.Device,
|
||||||
|
Baud: cfg.Serial.Baud,
|
||||||
|
ReadTimeout: cfg.Serial.Timeout,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultSerialTimeout = 5 * time.Second
|
||||||
|
defaultHealthInterval = 30 * time.Second
|
||||||
|
defaultHealthStart = 5 * time.Second
|
||||||
|
defaultResponseAnnounceTimeout = 30 * time.Second
|
||||||
|
defaultResponseDataTimeout = 2 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type SettingsError struct {
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se SettingsError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid Settings %s", se.msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Serial struct {
|
||||||
|
Device string `yaml:"device"`
|
||||||
|
Baud int `yaml:"baud"`
|
||||||
|
Timeout time.Duration `yaml:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
Serial Serial `yaml:"serial"`
|
||||||
|
HealthInterval time.Duration `yaml:"health-interval"`
|
||||||
|
HealthStart time.Duration `yaml:"health-start"`
|
||||||
|
ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"`
|
||||||
|
ResponseDataTimeout time.Duration `yaml:"response-data-timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
||||||
|
data := struct {
|
||||||
|
Serial Serial `yaml:"serial"`
|
||||||
|
HealthInterval time.Duration `yaml:"health-interval"`
|
||||||
|
HealthStart time.Duration `yaml:"health-start"`
|
||||||
|
ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"`
|
||||||
|
ResponseDataTimeout time.Duration `yaml:"response-data-timeout"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := n.Decode(&data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not decode YAML: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Serial.Device == "" {
|
||||||
|
return SettingsError{"you must specify a serial 'device'"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Serial.Baud == 0 {
|
||||||
|
data.Serial.Baud = 115200
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Serial.Timeout == 0 {
|
||||||
|
data.Serial.Timeout = defaultSerialTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Serial = data.Serial
|
||||||
|
|
||||||
|
if data.HealthInterval == 0 {
|
||||||
|
data.HealthInterval = defaultHealthInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
c.HealthInterval = data.HealthInterval
|
||||||
|
|
||||||
|
if data.HealthStart == 0 {
|
||||||
|
data.HealthStart = defaultHealthStart
|
||||||
|
}
|
||||||
|
|
||||||
|
c.HealthStart = data.HealthStart
|
||||||
|
|
||||||
|
if data.ResponseAnnounceTimeout == 0 {
|
||||||
|
data.ResponseAnnounceTimeout = defaultResponseAnnounceTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseAnnounceTimeout = data.ResponseAnnounceTimeout
|
||||||
|
|
||||||
|
if data.ResponseDataTimeout == 0 {
|
||||||
|
data.ResponseDataTimeout = defaultResponseDataTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseDataTimeout = data.ResponseDataTimeout
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(clientConfigFile string) (*ClientConfig, error) {
|
||||||
|
configFile, err := os.Open(clientConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not open signer client configuration file %s: %w", clientConfigFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = configFile.Close() }()
|
||||||
|
|
||||||
|
clientConfig, err := loadConfiguration(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not load client configuration from %s: %w", clientConfigFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfiguration(r io.Reader) (*ClientConfig, error) {
|
||||||
|
var config *ClientConfig
|
||||||
|
|
||||||
|
decoder := yaml.NewDecoder(r)
|
||||||
|
err := decoder.Decode(&config)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse YAML configuration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
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 handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/shamaton/msgpackgen/msgpack"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
||||||
|
"git.cacert.org/cacert-gosignerclient/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignerClientHandler struct {
|
||||||
|
logger *logrus.Logger
|
||||||
|
commands chan *protocol.Command
|
||||||
|
config *config.ClientConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerClientHandler) Send(command *protocol.Command, out chan []byte) error {
|
||||||
|
var (
|
||||||
|
frame []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
frame, err = msgpack.Marshal(command.Announce)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not marshal command annoucement: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.WithField("announcement", command.Announce).Info("write command announcement")
|
||||||
|
|
||||||
|
s.logger.Trace("writing command announcement")
|
||||||
|
|
||||||
|
out <- frame
|
||||||
|
|
||||||
|
frame, err = msgpack.Marshal(command.Command)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not marshal command data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.WithField("command", command.Command).Info("write command data")
|
||||||
|
|
||||||
|
out <- frame
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerClientHandler) ResponseAnnounce(in chan []byte) (*protocol.Response, error) {
|
||||||
|
response := &protocol.Response{}
|
||||||
|
|
||||||
|
var announce messages.ResponseAnnounce
|
||||||
|
|
||||||
|
select {
|
||||||
|
case frame := <-in:
|
||||||
|
if err := msgpack.Unmarshal(frame, &announce); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not unmarshal response announcement: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Announce = &announce
|
||||||
|
|
||||||
|
s.logger.WithField("announcement", response.Announce).Debug("received response announcement")
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
case <-time.After(s.config.ResponseAnnounceTimeout):
|
||||||
|
return nil, protocol.ErrResponseAnnounceTimeoutExpired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerClientHandler) ResponseData(in chan []byte, response *protocol.Response) error {
|
||||||
|
select {
|
||||||
|
case frame := <-in:
|
||||||
|
switch response.Announce.Code {
|
||||||
|
case messages.RespHealth:
|
||||||
|
var resp messages.HealthResponse
|
||||||
|
if err := msgpack.Unmarshal(frame, &resp); err != nil {
|
||||||
|
return fmt.Errorf("could not unmarshal health response data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Response = &resp
|
||||||
|
case messages.RespFetchCRL:
|
||||||
|
var resp messages.FetchCRLResponse
|
||||||
|
if err := msgpack.Unmarshal(frame, &resp); err != nil {
|
||||||
|
return fmt.Errorf("could not unmarshal fetch CRL response data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Response = &resp
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unhandled response code %s", response.Announce.Code)
|
||||||
|
}
|
||||||
|
case <-time.After(s.config.ResponseDataTimeout):
|
||||||
|
return protocol.ErrResponseDataTimeoutExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(
|
||||||
|
config *config.ClientConfig,
|
||||||
|
logger *logrus.Logger,
|
||||||
|
commands chan *protocol.Command,
|
||||||
|
) (protocol.ClientHandler, error) {
|
||||||
|
return &SignerClientHandler{
|
||||||
|
logger: logger,
|
||||||
|
config: config,
|
||||||
|
commands: commands,
|
||||||
|
}, nil
|
||||||
|
}
|
Loading…
Reference in New Issue