Skip to content

Commit 753a2e8

Browse files
Merge pull request #3 from form3tech-oss/signing-impl
Implement message signing proxy
2 parents 70b2956 + 97451ab commit 753a2e8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3260
-2
lines changed

.github/dependabot.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: gomod
4+
directory: "/"
5+
schedule:
6+
interval: daily
7+
time: "08:00"
8+
timezone: Europe/London
9+
open-pull-requests-limit: 10
10+
pull-request-branch-name:
11+
separator: "-"

.github/workflows/ci.yaml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: CI
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request: {}
7+
8+
jobs:
9+
test:
10+
name: Linter and Tests
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout Code
14+
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
15+
- name: Setup Golang
16+
uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v3.2.0
17+
with:
18+
go-version-file: go.mod
19+
- name: Run Linter
20+
run: make lint
21+
- name: Run Tests
22+
run: make test
23+
- name: Calculate the next SemVer tag
24+
id: tag
25+
run: |
26+
TAG="$(make calculate-next-semver)"
27+
echo "::set-output name=tag::$TAG"
28+
tag:
29+
name: SemVer Tag
30+
needs: test
31+
if: github.ref == 'refs/heads/master'
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Checkout
35+
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
36+
with:
37+
fetch-depth: 0
38+
- name: Push SemVer Tag
39+
uses: mathieudutour/github-tag-action@d745f2e74aaf1ee82e747b181f7a0967978abee0 # v6.0
40+
with:
41+
custom_tag: ${{ needs.test.outputs.tag }}
42+
github_token: ${{ secrets.GITHUB_TOKEN }}
43+
44+
goreleaser:
45+
name: Create GH Release
46+
needs: tag
47+
if: github.ref == 'refs/heads/master'
48+
runs-on: ubuntu-latest
49+
steps:
50+
- name: Checkout
51+
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2
52+
with:
53+
fetch-depth: 0
54+
- name: Fetch all tags
55+
run: git fetch --force --tags
56+
- name: Setup Go
57+
uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v3.2.0
58+
with:
59+
go-version-file: go.mod
60+
- name: Run goreleaser release
61+
uses: goreleaser/goreleaser-action@b953231f81b8dfd023c58e0854a721e35037f28b # v2.9.1
62+
with:
63+
distribution: goreleaser
64+
version: v1.10.3
65+
args: release --rm-dist
66+
env:
67+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
# Test binary, built with `go test -c`
99
*.test
10+
tools/
1011

1112
# Output of the go coverage tool, specifically when used with LiteIDE
1213
*.out
1314

1415
# Dependency directories (remove the comment below to include it)
15-
vendor/
16+
vendor/
1617

17-
.idea/
18+
.idea/
19+
.vscode/

.goreleaser.yaml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
builds:
2+
-
3+
# GOOS list to build for.
4+
# For more info refer to: https://golang.org/doc/install/source#environment
5+
# Defaults are darwin and linux.
6+
goos:
7+
- darwin
8+
- linux
9+
- windows
10+
11+
# GOARCH to build for.
12+
# For more info refer to: https://golang.org/doc/install/source#environment
13+
# Defaults are 386, amd64 and arm64.
14+
goarch:
15+
- amd64
16+
- arm64
17+
18+
changelog:
19+
skip: false
20+
use: git
21+
sort: asc
22+
23+
groups:
24+
- title: Breaking Changes
25+
regexp: "^.*BREAKING CHANGE[(\\w)]*:+.*$"
26+
order: 0
27+
- title: Features
28+
regexp: "^.*feat[(\\w)]*:+.*$"
29+
order: 1
30+
- title: 'Bug fixes'
31+
regexp: "^.*fix[(\\w)]*:+.*$"
32+
order: 2
33+
- title: 'Enhancements'
34+
regexp: "^.*(chore|build|style|refactor|perf|test)[(\\w)]*:+.*$"
35+
order: 3
36+
- title: Others
37+
order: 999
38+
39+
filters:
40+
exclude:
41+
- '^docs'
42+
- '^ci'
43+
- typo
44+
45+
archives:
46+
# Additional files/template/globs you want to add to the archive.
47+
- files:
48+
- LICENSE
49+
- README.md
50+
- example/cert.crt
51+
- example/config_example.yaml
52+
- example/docker-compose.yaml
53+
- example/private.key
54+
- example/rsa_private_key.pem
55+
- example/rsa_public_key.pub

Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM golang:1.19.0-alpine3.16 AS build-env
2+
3+
WORKDIR /app
4+
5+
COPY ./go.mod ./
6+
COPY ./go.sum ./
7+
8+
RUN go mod download
9+
10+
COPY . ./
11+
12+
RUN go build -o /go/bin/proxy
13+
14+
FROM alpine:3.16
15+
16+
RUN apk add --no-cache ca-certificates && update-ca-certificates
17+
18+
COPY --from=build-env /go/bin/proxy /
19+
20+
ENV GIN_MODE=release
21+
22+
ENTRYPOINT ["./proxy"]

Makefile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
GOLANGCI_VERSION ?= 1.46.2
2+
GOIMPORTS_VERSION ?= v0.1.12
3+
4+
.PHONY: default
5+
default: lint test
6+
7+
.PHONY: test
8+
test:
9+
@echo "==> Executing tests..."
10+
@go test -test.v ./... -race -covermode=atomic -coverprofile=cover.out && \
11+
go tool cover -func=cover.out && rm cover.out
12+
13+
.PHONY: lint
14+
lint: tools/golangci-lint
15+
@echo "==> Running golangci-lint..."
16+
@tools/golangci-lint run
17+
18+
.PHONY: goimports
19+
goimports: tools/goimports
20+
@echo "==> Running goimports..."
21+
@tools/goimports -w $(GOFMT_FILES)
22+
23+
.PHONY: calculate-next-semver
24+
calculate-next-semver:
25+
@bash -e -o pipefail -c '(source ./scripts/calculate-next-version.sh && echo $${FULL_TAG}) | tail -n 1'
26+
27+
###########################
28+
# Tools targets
29+
###########################
30+
31+
.PHONY: tools/golangci-lint
32+
tools/golangci-lint:
33+
@echo "==> Installing golangci-lint..."
34+
@./scripts/install-golangci-lint.sh $(GOLANGCI_VERSION)
35+
36+
.PHONY: tools/goimports
37+
tools/goimports:
38+
@echo "==> Installing goimports..."
39+
@GOBIN=$$(pwd)/tools/ go install golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION)
40+

cmd/root.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/form3tech-oss/http-message-signing-proxy/config"
7+
"github.com/form3tech-oss/http-message-signing-proxy/logger"
8+
"github.com/form3tech-oss/http-message-signing-proxy/metric"
9+
"github.com/form3tech-oss/http-message-signing-proxy/proxy"
10+
"github.com/form3tech-oss/http-message-signing-proxy/signer"
11+
"github.com/sirupsen/logrus"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
func Execute() {
16+
rootCmd := NewRootCmd()
17+
err := rootCmd.Execute()
18+
if err != nil {
19+
logrus.WithError(err).Fatal("failed to start server")
20+
}
21+
}
22+
23+
func NewRootCmd() *cobra.Command {
24+
var (
25+
cfgFile string
26+
overrides []string
27+
)
28+
29+
rootCmd := &cobra.Command{
30+
RunE: func(cmd *cobra.Command, args []string) error {
31+
cfg, err := config.LoadConfig(cfgFile, overrides)
32+
if err != nil {
33+
return fmt.Errorf("failed to load config: %w", err)
34+
}
35+
36+
err = logger.Configure(cfg.Log)
37+
if err != nil {
38+
return fmt.Errorf("failed to configure logger: %w", err)
39+
}
40+
41+
reqSigner, err := signer.NewRequestSigner(cfg.Proxy.Signer)
42+
if err != nil {
43+
return fmt.Errorf("failed to initialise request signer: %w", err)
44+
}
45+
46+
signingProxy, err := proxy.NewReverseProxy(cfg.Proxy.UpstreamTarget)
47+
if err != nil {
48+
return fmt.Errorf("failed to create signing proxy: %w", err)
49+
}
50+
51+
metricPublisher := metric.NewMetricPublisher(cfg.Proxy.UpstreamTarget)
52+
handler := proxy.NewHandler(signingProxy, reqSigner, metricPublisher)
53+
server := proxy.NewServer(cfg.Server, handler, metricPublisher)
54+
server.Start()
55+
56+
return nil
57+
},
58+
}
59+
60+
f := rootCmd.Flags()
61+
f.StringVar(&cfgFile, "config", "", "path to config file")
62+
f.StringArrayVar(&overrides, "set", nil, "set value for certain config fields to override config file, can be set multiple times")
63+
64+
return rootCmd
65+
}

config/config.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package config
2+
3+
type Config struct {
4+
Proxy ProxyConfig `mapstructure:"proxy"`
5+
Server ServerConfig `mapstructure:"server"`
6+
Log LogConfig `mapstructure:"log"`
7+
}
8+
9+
type ServerConfig struct {
10+
Port int `mapstructure:"port"`
11+
SSL SSLConfig `mapstructure:"ssl"`
12+
}
13+
14+
type ProxyConfig struct {
15+
UpstreamTarget string `mapstructure:"upstreamTarget"`
16+
Signer SignerConfig `mapstructure:"signer"`
17+
}
18+
19+
type SSLConfig struct {
20+
Enable bool `mapstructure:"enable"`
21+
CertFilePath string `mapstructure:"certFilePath"`
22+
KeyFilePath string `mapstructure:"keyFilePath"`
23+
}
24+
25+
type SignerConfig struct {
26+
KeyId string `mapstructure:"keyId"`
27+
KeyFilePath string `mapstructure:"keyFilePath"`
28+
BodyDigestAlgo string `mapstructure:"bodyDigestAlgo"`
29+
SignatureHashAlgo string `mapstructure:"signatureHashAlgo"`
30+
Headers HeadersConfig `mapstructure:"headers"`
31+
}
32+
33+
type HeadersConfig struct {
34+
IncludeDigest bool `mapstructure:"includeDigest"`
35+
IncludeRequestTarget bool `mapstructure:"includeRequestTarget"`
36+
SignatureHeaders []string `mapstructure:"signatureHeaders"`
37+
}
38+
39+
type LogConfig struct {
40+
Level string `mapstructure:"level"`
41+
Format string `mapstructure:"format"`
42+
}

config/config_test.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
server:
2+
port: 8080
3+
ssl:
4+
enable: false
5+
certFilePath: "/etc/ssl/certs/cert.crt"
6+
keyFilePath: "/etc/ssl/private/private.key"
7+
8+
proxy:
9+
upstreamTarget: "https://api.form3.tech/v1"
10+
signer:
11+
keyId: "6f33b219-137c-467e-9a61-f61040a03363"
12+
keyFilePath: "/etc/form3/private/private.key"
13+
bodyDigestAlgo: "SHA-256"
14+
signatureHashAlgo: "SHA-256"
15+
headers:
16+
includeDigest: true
17+
includeRequestTarget: true
18+
signatureHeaders:
19+
- host
20+
- date
21+
- content-length
22+
23+
log:
24+
level: info
25+
format: json

config/error.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package config
2+
3+
// ValidationError describes an error validating the provided config.
4+
type ValidationError struct {
5+
message string
6+
}
7+
8+
func NewValidationError(message string) error {
9+
return &ValidationError{
10+
message: message,
11+
}
12+
}
13+
14+
func (e *ValidationError) Error() string {
15+
return e.message
16+
}

0 commit comments

Comments
 (0)