Initial signer client implementation
This commit adds the project setup and implements a basic signer client that sends health check commands to the signer.
This commit is contained in:
commit
91d4f69a9b
10 changed files with 706 additions and 0 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.go text
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
*Pty
|
||||
/.idea/
|
||||
/config.yaml
|
||||
/dist/
|
||||
/signerclient
|
77
.golangci.yml
Normal file
77
.golangci.yml
Normal file
|
@ -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
|
20
Makefile
Normal file
20
Makefile
Normal file
|
@ -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
|
98
cmd/signerclient/main.go
Normal file
98
cmd/signerclient/main.go
Normal file
|
@ -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")
|
||||
}
|
||||
}
|
19
go.mod
Normal file
19
go.mod
Normal file
|
@ -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
|
||||
)
|
43
go.sum
Normal file
43
go.sum
Normal file
|
@ -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=
|
166
internal/client/client.go
Normal file
166
internal/client/client.go
Normal file
|
@ -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
|
||||
}
|
141
internal/config/config.go
Normal file
141
internal/config/config.go
Normal file
|
@ -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
|
||||
}
|
136
internal/handler/handler.go
Normal file
136
internal/handler/handler.go
Normal file
|
@ -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 a new issue