Skip to content

Commit 1923fa1

Browse files
authored
add HTTP and GRPC health check endpoints (#1258)
* add HTTP and GRPC health check endpoints Signed-off-by: Bob Callaway <[email protected]> * update prometheus check, add readiness probe, add nosec Signed-off-by: Bob Callaway <[email protected]> * bump k8s to v1.24 Signed-off-by: Bob Callaway <[email protected]> * export struct to quiet lint, update issuers with cluster.local, expose grpc Signed-off-by: Bob Callaway <[email protected]> * bump setup-kind, remove grpc readiness Signed-off-by: Bob Callaway <[email protected]> * re-enable readiness probe for grpc Signed-off-by: Bob Callaway <[email protected]> --------- Signed-off-by: Bob Callaway <[email protected]>
1 parent 489d73a commit 1923fa1

File tree

7 files changed

+72
-33
lines changed

7 files changed

+72
-33
lines changed

.github/workflows/verify-k8s.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ jobs:
7373
- uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6
7474

7575
- name: Setup Cluster
76-
uses: chainguard-dev/actions/setup-kind@7d1eb557f464d97e5fe5177807a9226141eb9308 # main
76+
uses: chainguard-dev/actions/setup-kind@f5a6616ce43b6ffabeddb87480a13721fffb3588 # main
7777
with:
78-
k8s-version: v1.22.x
78+
k8s-version: 1.24.x
7979
registry-authority: ${{ env.REGISTRY_NAME }}:${{ env.REGISTRY_PORT }}
8080

8181
- name: Generate temporary CA files
@@ -121,6 +121,7 @@ jobs:
121121
server.yaml: |-
122122
host: 0.0.0.0
123123
port: 5555
124+
grpc-port: 5554
124125
ca: fileca
125126
fileca-cert: /etc/fulcio-secret/cert.pem
126127
fileca-key: /etc/fulcio-secret/key.pem

cmd/app/grpc.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"github.com/spf13/viper"
4343
"google.golang.org/grpc"
4444
"google.golang.org/grpc/credentials"
45+
health "google.golang.org/grpc/health/grpc_health_v1"
4546
)
4647

4748
const (
@@ -180,6 +181,8 @@ func createGRPCServer(cfg *config.FulcioConfig, ctClient *ctclient.LogClient, ba
180181
myServer := grpc.NewServer(serverOpts...)
181182

182183
grpcCAServer := server.NewGRPCCAServer(ctClient, baseca, ip)
184+
185+
health.RegisterHealthServer(myServer, grpcCAServer)
183186
// Register your gRPC service implementations.
184187
gw.RegisterCAServer(myServer, grpcCAServer)
185188

@@ -239,6 +242,10 @@ func (g *grpcServer) startUnixListener() {
239242
}()
240243
}
241244

245+
func (g *grpcServer) ExposesGRPCTLS() bool {
246+
return viper.IsSet("grpc-tls-certificate") && viper.IsSet("grpc-tls-key")
247+
}
248+
242249
func createLegacyGRPCServer(cfg *config.FulcioConfig, v2Server gw.CAServer) (*grpcServer, error) {
243250
logger, opts := log.SetupGRPCLogging()
244251

cmd/app/http.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package app
1717

1818
import (
1919
"context"
20+
"crypto/tls"
2021
"errors"
2122
"fmt"
2223
"net/http"
@@ -32,7 +33,9 @@ import (
3233
"github.com/sigstore/fulcio/pkg/log"
3334
"github.com/sigstore/fulcio/pkg/server"
3435
"google.golang.org/grpc"
36+
"google.golang.org/grpc/credentials"
3537
"google.golang.org/grpc/credentials/insecure"
38+
health "google.golang.org/grpc/health/grpc_health_v1"
3639
"google.golang.org/grpc/metadata"
3740
"google.golang.org/protobuf/proto"
3841
)
@@ -48,10 +51,22 @@ func extractOIDCTokenFromAuthHeader(_ context.Context, req *http.Request) metada
4851
}
4952

5053
func createHTTPServer(ctx context.Context, serverEndpoint string, grpcServer, legacyGRPCServer *grpcServer) httpServer {
54+
opts := []grpc.DialOption{}
55+
if grpcServer.ExposesGRPCTLS() {
56+
/* #nosec G402 */ // InsecureSkipVerify is only used for the HTTP server to call the TLS-enabled grpc endpoint.
57+
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})))
58+
} else {
59+
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
60+
}
61+
cc, err := grpc.Dial(grpcServer.grpcServerEndpoint, opts...)
62+
if err != nil {
63+
log.Logger.Fatal(err)
64+
}
65+
5166
mux := runtime.NewServeMux(runtime.WithMetadata(extractOIDCTokenFromAuthHeader),
52-
runtime.WithForwardResponseOption(setResponseCodeModifier))
67+
runtime.WithForwardResponseOption(setResponseCodeModifier),
68+
runtime.WithHealthzEndpoint(health.NewHealthClient(cc)))
5369

54-
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
5570
if err := gw.RegisterCAHandlerFromEndpoint(ctx, mux, grpcServer.grpcServerEndpoint, opts); err != nil {
5671
log.Logger.Fatal(err)
5772
}

config/deployment.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ spec:
6565
readOnly: true
6666
- name: oidc-info
6767
mountPath: /var/run/fulcio
68+
livenessProbe:
69+
httpGet:
70+
path: /healthz
71+
port: 5555
72+
initialDelaySeconds: 5
73+
readinessProbe:
74+
grpc:
75+
port: 5554
76+
initialDelaySeconds: 5
6877
resources:
6978
requests:
7079
memory: "1G"

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ services:
3939
- ~/.config/gcloud:/root/.config/gcloud/:z # for GCP authentication
4040
- ./config/config.jsn:/etc/fulcio-config/config.json:z
4141
healthcheck:
42-
test: ["CMD", "curl", "-f", "http://localhost:5555/ping"]
42+
test: ["CMD", "curl", "-f", "http://localhost:5555/healthz"]
4343
interval: 10s
4444
timeout: 3s
4545
retries: 3
@@ -62,7 +62,7 @@ services:
6262
volumes:
6363
- ./config/dex:/etc/config/:ro
6464
healthcheck:
65-
test: ["CMD", "curl", "-f", "http://localhost:8888/auth/healthz"]
65+
test: ["CMD", "wget", "-O", "/dev/null", "http://localhost:8888/auth/healthz"]
6666
interval: 10s
6767
timeout: 3s
6868
retries: 3

pkg/server/grpc_server.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,21 @@ import (
3232
"github.com/sigstore/fulcio/pkg/log"
3333
"github.com/sigstore/sigstore/pkg/cryptoutils"
3434
"google.golang.org/grpc/codes"
35+
health "google.golang.org/grpc/health/grpc_health_v1"
3536
"google.golang.org/grpc/metadata"
37+
"google.golang.org/grpc/status"
3638
)
3739

38-
type grpcCAServer struct {
40+
type GRPCCAServer struct {
3941
fulciogrpc.UnimplementedCAServer
42+
health.HealthServer
4043
ct *ctclient.LogClient
4144
ca certauth.CertificateAuthority
4245
identity.IssuerPool
4346
}
4447

45-
func NewGRPCCAServer(ct *ctclient.LogClient, ca certauth.CertificateAuthority, ip identity.IssuerPool) fulciogrpc.CAServer {
46-
return &grpcCAServer{
48+
func NewGRPCCAServer(ct *ctclient.LogClient, ca certauth.CertificateAuthority, ip identity.IssuerPool) *GRPCCAServer {
49+
return &GRPCCAServer{
4750
ct: ct,
4851
ca: ca,
4952
IssuerPool: ip,
@@ -54,7 +57,7 @@ const (
5457
MetadataOIDCTokenKey = "oidcidentitytoken"
5558
)
5659

57-
func (g *grpcCAServer) CreateSigningCertificate(ctx context.Context, request *fulciogrpc.CreateSigningCertificateRequest) (*fulciogrpc.SigningCertificate, error) {
60+
func (g *GRPCCAServer) CreateSigningCertificate(ctx context.Context, request *fulciogrpc.CreateSigningCertificateRequest) (*fulciogrpc.SigningCertificate, error) {
5861
logger := log.ContextLogger(ctx)
5962

6063
// OIDC token either is passed in gRPC field or was extracted from HTTP headers
@@ -228,7 +231,7 @@ func (g *grpcCAServer) CreateSigningCertificate(ctx context.Context, request *fu
228231
return result, nil
229232
}
230233

231-
func (g *grpcCAServer) GetTrustBundle(ctx context.Context, _ *fulciogrpc.GetTrustBundleRequest) (*fulciogrpc.TrustBundle, error) {
234+
func (g *GRPCCAServer) GetTrustBundle(ctx context.Context, _ *fulciogrpc.GetTrustBundleRequest) (*fulciogrpc.TrustBundle, error) {
232235
trustBundle, err := g.ca.TrustBundle(ctx)
233236
if err != nil {
234237
return nil, handleFulcioGRPCError(ctx, codes.Internal, err, retrieveTrustBundleCAError)
@@ -252,7 +255,7 @@ func (g *grpcCAServer) GetTrustBundle(ctx context.Context, _ *fulciogrpc.GetTrus
252255
return resp, nil
253256
}
254257

255-
func (g *grpcCAServer) GetConfiguration(ctx context.Context, _ *fulciogrpc.GetConfigurationRequest) (*fulciogrpc.Configuration, error) {
258+
func (g *GRPCCAServer) GetConfiguration(ctx context.Context, _ *fulciogrpc.GetConfigurationRequest) (*fulciogrpc.Configuration, error) {
256259
cfg := config.FromContext(ctx)
257260
if cfg == nil {
258261
err := errors.New("configuration not loaded")
@@ -263,3 +266,11 @@ func (g *grpcCAServer) GetConfiguration(ctx context.Context, _ *fulciogrpc.GetCo
263266
Issuers: cfg.ToIssuers(),
264267
}, nil
265268
}
269+
270+
func (g *GRPCCAServer) Check(_ context.Context, _ *health.HealthCheckRequest) (*health.HealthCheckResponse, error) {
271+
return &health.HealthCheckResponse{Status: health.HealthCheckResponse_SERVING}, nil
272+
}
273+
274+
func (g *GRPCCAServer) Watch(_ *health.HealthCheckRequest, _ health.Health_WatchServer) error {
275+
return status.Error(codes.Unimplemented, "unimplemented")
276+
}

test/prometheus/main.go

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -75,31 +75,27 @@ func checkLatency(latency *dto.MetricFamily) error {
7575
if *latency.Type != *dto.MetricType_HISTOGRAM.Enum() {
7676
return fmt.Errorf("Wrong type, wanted %+v, got: %+v", dto.MetricType_HISTOGRAM.Enum(), latency.Type)
7777
}
78-
if len(latency.Metric) != 1 {
79-
return fmt.Errorf("Got multiple entries, or none for metric, wanted one, got: %+v", latency.Metric)
80-
}
81-
// Make sure there's a 'post' and it's a 201.
82-
var code string
83-
var method string
84-
for _, value := range latency.Metric[0].Label {
85-
if *value.Name == "code" {
86-
code = *value.Value
78+
79+
for _, metric := range latency.Metric {
80+
var code string
81+
var method string
82+
for _, value := range metric.Label {
83+
if *value.Name == "code" {
84+
code = *value.Value
85+
}
86+
if *value.Name == "method" {
87+
method = *value.Value
88+
}
8789
}
88-
if *value.Name == "method" {
89-
method = *value.Value
90+
if code == "201" && method == "post" {
91+
if *metric.Histogram.SampleCount != 1 {
92+
return fmt.Errorf("Unexpected samplecount, wanted 1, got %d", *metric.Histogram.SampleCount)
93+
}
94+
return nil
9095
}
9196
}
92-
if code != "201" {
93-
return fmt.Errorf("unexpected code, wanted 201, got %s", code)
94-
}
95-
if method != "post" {
96-
return fmt.Errorf("unexpected method, wanted post, got %s", method)
97-
}
9897

99-
if *latency.Metric[0].Histogram.SampleCount != 1 {
100-
return fmt.Errorf("Unexpected samplecount, wanted 1, got %d", *latency.Metric[0].Histogram.SampleCount)
101-
}
102-
return nil
98+
return fmt.Errorf("Got multiple entries, or none for metric, wanted one, got: %+v", latency.Metric)
10399
}
104100

105101
func checkCertCount(certCount *dto.MetricFamily) error {

0 commit comments

Comments
 (0)