diff --git a/Contributing.md b/Contributing.md index f17eb245..4e7920a0 100644 --- a/Contributing.md +++ b/Contributing.md @@ -25,6 +25,14 @@ Please follow [best practices](https://github.com/trein/dev-best-practices/wiki/ When your code is ready to be submitted, you can [submit a pull request](https://help.github.com/articles/creating-a-pull-request/) to begin the code review process. +#### Add license + +Please use license headers in all code files. You can apply the `./license_header` template by utilizing [addlicense](https://github.com/google/addlicense). + +```sh +addlicense -f ./license_header ./ +``` + #### Generate Go Code If your work requires re-generating the code (e.g. mock), please run `./gen.sh` at the project root. diff --git a/utils/cert_reload.go b/certreload/cert_reload.go similarity index 99% rename from utils/cert_reload.go rename to certreload/cert_reload.go index 941b706d..6fd8d8a9 100644 --- a/utils/cert_reload.go +++ b/certreload/cert_reload.go @@ -1,7 +1,7 @@ // Copyright 2024 Yahoo Inc. // Licensed under the terms of the Apache License 2.0. Please see LICENSE file in project root for terms. -package utils +package certreload import ( "bytes" diff --git a/utils/cert_reload_test.go b/certreload/cert_reload_test.go similarity index 99% rename from utils/cert_reload_test.go rename to certreload/cert_reload_test.go index be441b68..d8f71a69 100644 --- a/utils/cert_reload_test.go +++ b/certreload/cert_reload_test.go @@ -1,7 +1,7 @@ // Copyright 2024 Yahoo Inc. // Licensed under the terms of the Apache License 2.0. Please see LICENSE file in project root for terms. -package utils +package certreload import ( "crypto/tls" diff --git a/utils/generate_test.go b/certreload/generate_test.go similarity index 96% rename from utils/generate_test.go rename to certreload/generate_test.go index fc557a74..70c1ed32 100644 --- a/utils/generate_test.go +++ b/certreload/generate_test.go @@ -1,7 +1,7 @@ // Copyright 2022 Yahoo Inc. // Licensed under the terms of the Apache License 2.0. Please see LICENSE file in project root for terms. -package utils +package certreload //go:generate certstrap init --passphrase "" --common-name "ca" --years 80 //go:generate certstrap request-cert --passphrase "" --common-name client diff --git a/utils/testdata/ca.crt b/certreload/testdata/ca.crt similarity index 100% rename from utils/testdata/ca.crt rename to certreload/testdata/ca.crt diff --git a/utils/testdata/client.crt b/certreload/testdata/client.crt similarity index 100% rename from utils/testdata/client.crt rename to certreload/testdata/client.crt diff --git a/utils/testdata/client.key b/certreload/testdata/client.key similarity index 100% rename from utils/testdata/client.key rename to certreload/testdata/client.key diff --git a/config/config.go b/config/config.go index 32b23507..f6fa24aa 100644 --- a/config/config.go +++ b/config/config.go @@ -148,6 +148,20 @@ type Config struct { // PKCS11RequestTimeout indicates the max time an HSM can take to process a signing request for a // certificate in seconds. PKCS11RequestTimeout uint `json:"requestTimeout"` + + // OTel defines the configuration for oTel instrumentation. + OTel struct { + // Enabled indicates whether to enable OTel metrics. + Enabled bool + // OTELCollectorEndpoint specifies the endpoint of OTel collector. + OTELCollectorEndpoint string + // ClientCertPath specifies path to client cert used to obtain mTLS with OTel collector. + ClientCertPath string + // ClientKeyPath specifies path to client key used to obtain mTLS with OTel collector. + ClientKeyPath string + // CACertPath specifies path to root CA cert, used to verify OTel collector cert. + CACertPath string + } } // Parse loads configuration values from input file and returns config object and CA cert. diff --git a/config/config_test.go b/config/config_test.go index d3f27e92..a7e473c4 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -48,6 +48,19 @@ func TestParse(t *testing.T) { ReadTimeout: 10, WriteTimeout: 10, PKCS11RequestTimeout: 15, + OTel: struct { + Enabled bool + OTELCollectorEndpoint string + ClientCertPath string + ClientKeyPath string + CACertPath string + }{ + Enabled: true, + OTELCollectorEndpoint: "http://localhost:4317", + ClientCertPath: "/path/to/client/cert", + ClientKeyPath: "/path/to/client/key", + CACertPath: "/path/to/ca/cert", + }, } testcases := map[string]struct { filePath string diff --git a/config/testdata/testconf-good.json b/config/testdata/testconf-good.json index 7ed87bfa..ba750db7 100644 --- a/config/testdata/testconf-good.json +++ b/config/testdata/testconf-good.json @@ -22,5 +22,12 @@ "IdleTimeout": 30, "ReadTimeout": 10, "WriteTimeout": 10, - "RequestTimeout": 15 + "RequestTimeout": 15, + "OTel": { + "Enabled": true, + "OTELCollectorEndpoint": "http://localhost:4317", + "ClientCertPath": "/path/to/client/cert", + "ClientKeyPath": "/path/to/client/key", + "CACertPath": "/path/to/ca/cert" + } } diff --git a/go.mod b/go.mod index f5e3396d..9b3b8e39 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,22 @@ module github.com/theparanoids/crypki -go 1.21 +go 1.22 -toolchain go1.22.4 +toolchain go1.23.0 require ( github.com/golang/mock v1.6.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 github.com/miekg/pkcs11 v1.1.1 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 + go.opentelemetry.io/otel v1.31.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 + go.opentelemetry.io/otel/metric v1.31.0 + go.opentelemetry.io/otel/sdk v1.31.0 + go.opentelemetry.io/otel/sdk/metric v1.31.0 + go.opentelemetry.io/otel/trace v1.31.0 golang.org/x/crypto v0.29.0 google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 google.golang.org/grpc v1.67.1 @@ -17,13 +25,16 @@ require ( ) require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/stretchr/testify v1.7.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.9.0 // indirect golang.org/x/sys v0.27.0 // indirect golang.org/x/text v0.20.0 // indirect diff --git a/go.sum b/go.sum index b3c58f70..7c75a227 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -13,6 +15,11 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -27,6 +34,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= @@ -38,8 +47,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -47,19 +57,36 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -88,8 +115,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/license_comment b/license_header similarity index 100% rename from license_comment rename to license_header diff --git a/otel/otel.go b/otel/otel.go new file mode 100644 index 00000000..2a15fa60 --- /dev/null +++ b/otel/otel.go @@ -0,0 +1,40 @@ +// Copyright 2024 Yahoo Inc. +// Licensed under the terms of the Apache License 2.0. Please see LICENSE file in project root for terms. + +package otellib + +import ( + "context" + "crypto/tls" + "log" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" +) + +// InitOTelSDK initializes the OTel meter provider +func InitOTelSDK(ctx context.Context, collectorEndpoint string, tlsConfig *tls.Config, res *resource.Resource) (shutdown func(context.Context) error) { + // Set up an OTel exporter for metrics. + var opts []otlpmetrichttp.Option + opts = append(opts, otlpmetrichttp.WithEndpoint(collectorEndpoint)) + opts = append(opts, otlpmetrichttp.WithTLSClientConfig(tlsConfig)) + + metricExporter, err := otlpmetrichttp.New(ctx, opts...) + if err != nil { + log.Fatalf("failed to create metric exporter: %v", err) + } + + // Set up a metric provider. + meterProvider := sdkmetric.NewMeterProvider( + sdkmetric.WithReader( + sdkmetric.NewPeriodicReader(metricExporter), + ), + sdkmetric.WithResource(res), + ) + + otel.SetMeterProvider(meterProvider) + + return meterProvider.Shutdown +} diff --git a/server/server.go b/server/server.go index bf4c6a04..d26174c0 100644 --- a/server/server.go +++ b/server/server.go @@ -22,6 +22,10 @@ import ( recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/theparanoids/crypki/certreload" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -31,6 +35,7 @@ import ( "github.com/theparanoids/crypki/config" "github.com/theparanoids/crypki/healthcheck" "github.com/theparanoids/crypki/oor" + otellib "github.com/theparanoids/crypki/otel" "github.com/theparanoids/crypki/pkcs11" "github.com/theparanoids/crypki/proto" "github.com/theparanoids/crypki/server/interceptor" @@ -129,6 +134,29 @@ func Main() { log.SetOutput(file) go logRotate(file) + if cfg.OTel.Enabled { + otelResource, err := resource.Merge( + resource.Default(), + resource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceNameKey.String("crypki")), + ) + if err != nil { + log.Fatalf("Error merging resources: %v", err) + } + otelTLSConf, err := tlsClientConfiguration(cfg.OTel.CACertPath, cfg.OTel.ClientCertPath, + cfg.OTel.ClientKeyPath) + if err != nil { + log.Fatalf("Error loading otel TLS config: %v", err) + } + shutdownProvider := otellib.InitOTelSDK(context.Background(), + cfg.OTel.OTELCollectorEndpoint, otelTLSConf, otelResource) + + defer func() { + if err := shutdownProvider(context.Background()); err != nil { + log.Fatalf("Error shutting down OTLP provider: %v", err) + } + }() + } + type priorityDispatchInfo struct { endpoint string priSchedFeature bool @@ -177,7 +205,7 @@ func Main() { // Following TLS config will be used to initialize grpc server and // grpc gateway server. - tlsConfig, err := tlsConfiguration( + tlsConfig, err := tlsServerConfiguration( cfg.TLSCACertPath, cfg.TLSServerCertPath, cfg.TLSServerKeyPath, @@ -221,6 +249,7 @@ func Main() { // Setup gRPC server and http server grpcServer = grpc.NewServer([]grpc.ServerOption{ grpc.Creds(credentials.NewTLS(tlsConfig)), + grpc.StatsHandler(otelgrpc.NewServerHandler()), grpc.ChainUnaryInterceptor(interceptors...), }...) @@ -243,7 +272,7 @@ func Main() { hs.InRotation = oorh.InRotation // healthcheck http listener tls config hh := &hcHandler{hcService: hs} - hctc, err := tlsConfiguration( + hctc, err := tlsServerConfiguration( cfg.TLSCACertPath, cfg.TLSServerCertPath, cfg.TLSServerKeyPath, @@ -296,8 +325,8 @@ func (h *hcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { log.Print(err) } -// tlsConfiguration returns tls configuration. -func tlsConfiguration(caCertPath string, certPath, keyPath string, clientAuthMode tls.ClientAuthType) (*tls.Config, error) { +// tlsServerConfiguration returns tls configuration. +func tlsServerConfiguration(caCertPath string, certPath, keyPath string, clientAuthMode tls.ClientAuthType) (*tls.Config, error) { cfg := &tls.Config{ MinVersion: tls.VersionTLS12, NextProtos: []string{"h2", "http/1.1"}, // prefer HTTP/2 explicitly @@ -331,19 +360,68 @@ func tlsConfiguration(caCertPath string, certPath, keyPath string, clientAuthMod } // Use only modern ciphers. - cfg.CipherSuites = []uint16{ + cfg.CipherSuites = standardCipherSuites() + + return cfg, nil +} + +func tlsClientConfiguration(caCertPath, certPath, keyPath string) (*tls.Config, error) { + reloader, err := certreload.NewCertReloader( + certreload.CertReloadConfig{ + CertKeyGetter: func() ([]byte, []byte, error) { + certPEMBlock, err := os.ReadFile(certPath) + if err != nil { + return nil, nil, err + } + keyPEMBlock, err := os.ReadFile(keyPath) + if err != nil { + return nil, nil, err + } + return certPEMBlock, keyPEMBlock, nil + }, + PollInterval: 6 * time.Hour, + }) + if err != nil { + return nil, fmt.Errorf("unable to get client cert reloader: %s", err) + } + + caCertPool := x509.NewCertPool() + caCert, err := os.ReadFile(caCertPath) + if err != nil { + return nil, fmt.Errorf(`failed to read OTel CA certificate %q, err:%v`, caCertPath, err) + } + if ok := caCertPool.AppendCertsFromPEM(caCert); !ok { + return nil, fmt.Errorf(`failed to parse certificate %q`, caCertPath) + } + + cfg := &tls.Config{ + MinVersion: tls.VersionTLS12, + CipherSuites: standardCipherSuites(), + SessionTicketsDisabled: true, // Don't allow session resumption + GetClientCertificate: reloader.GetClientCertificate, + RootCAs: caCertPool, + InsecureSkipVerify: false, + } + + return cfg, nil +} + +func standardCipherSuites() []uint16 { + return []uint16{ + // TLS 1.3 cipher suites. tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_CHACHA20_POLY1305_SHA256, + + // TLS 1.2 cipher suites. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + // Go stdlib currently does not support AES CCM cipher suite - https://github.com/golang/go/issues/27484 + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, } - - return cfg, nil } // logRotate handles log rotation without process restart.