add mTLS
This commit is contained in:
parent
84040af9a2
commit
0ef6d7f682
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIMaSy7CGp7A69DLcmREodmwU8Z06eJw5KxVMByIkVw+xoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEMlHIUMNtdUeoT2k+s0qGuEKcn5/MxTQsvTHn2UtV6llpKzEJwt8P
|
||||||
|
Xq9wJgKY92KJIVunncJfWH03Pt46w3rsXQ==
|
||||||
|
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBnDCCAUGgAwIBAgIINzq1sRDfD4UwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ
|
||||||
|
bG9jYWxob3N0MB4XDTIzMDUwMzA1MTc1NFoXDTMzMDQzMDA1MjI1NFowFDESMBAG
|
||||||
|
A1UEAxMJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMlHIUMNt
|
||||||
|
dUeoT2k+s0qGuEKcn5/MxTQsvTHn2UtV6llpKzEJwt8PXq9wJgKY92KJIVunncJf
|
||||||
|
WH03Pt46w3rsXaN9MHswDgYDVR0PAQH/BAQDAgGmMB0GA1UdJQQWMBQGCCsGAQUF
|
||||||
|
BwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQvGKzbHLBJ
|
||||||
|
gQRkitLTkbuzl8Yd7DAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwCgYIKoZI
|
||||||
|
zj0EAwIDSQAwRgIhAOVxPVGEjsRZ70QMfNsD2gfjrPguOVZZ6sgzKsVB38BuAiEA
|
||||||
|
0l2us3+vY1c5TARSVTk6OMmTCJrNYlBjYQP7BbWy6HQ=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"expiry": "87600h"
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"rootca": {
|
||||||
|
"usages": [
|
||||||
|
"signing",
|
||||||
|
"digital signature",
|
||||||
|
"key encipherment",
|
||||||
|
"cert sign",
|
||||||
|
"crl sign",
|
||||||
|
"server auth",
|
||||||
|
"client auth"
|
||||||
|
],
|
||||||
|
"ca_constraint": {
|
||||||
|
"is_ca": true
|
||||||
|
},
|
||||||
|
"expiry": "87600h"
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"usages": [
|
||||||
|
"signing",
|
||||||
|
"digital signing",
|
||||||
|
"key encipherment",
|
||||||
|
"server auth"
|
||||||
|
],
|
||||||
|
"expiry": "87600h"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"usages": [
|
||||||
|
"signing",
|
||||||
|
"digital signature",
|
||||||
|
"key encipherment",
|
||||||
|
"client auth"
|
||||||
|
],
|
||||||
|
"expiry": "87600h"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEILI/RrEZWQ1sOKQSpISjVjzjfnys4SbD53TKVOjCxgNZoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEZDh+LpU9TmChPAocLxlfgCUwa4TBZnsf/NcwOyo2HupLj4Pha63V
|
||||||
|
kvbg2DSLFF+Fe4HoSiTWpq4Kwd8dnBxrkA==
|
||||||
|
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBmTCCAUCgAwIBAgIUYF9Sr1vgTtG1L5BBlj6yGrPpI0kwCgYIKoZIzj0EAwIw
|
||||||
|
FDESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTIzMDUwMzA1MTgwMFoXDTMzMDQzMDA1
|
||||||
|
MTgwMFowFDESMBAGA1UEAxMJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
||||||
|
AQcDQgAEZDh+LpU9TmChPAocLxlfgCUwa4TBZnsf/NcwOyo2HupLj4Pha63Vkvbg
|
||||||
|
2DSLFF+Fe4HoSiTWpq4Kwd8dnBxrkKNwMG4wDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
JQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFAMk3Lo4DM5j
|
||||||
|
EubUazT8r2BuMjs6MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATAKBggqhkjO
|
||||||
|
PQQDAgNHADBEAiAGxGpa2fdAC/2uchVei6AZNJhPTTEhgTVQ9F51kqrjuAIgD6UA
|
||||||
|
2puaVsOnIzKIUxZFY7zhOgZYU7O7sdNCfZX8ic0=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"hosts": ["localhost", "127.0.0.1"],
|
||||||
|
"key": {
|
||||||
|
"algo": "ecdsa",
|
||||||
|
"size": 256
|
||||||
|
},
|
||||||
|
"CN": "localhost",
|
||||||
|
"names": []
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# https://github.com/cloudflare/cfssl
|
||||||
|
# https://rob-blackbourn.medium.com/how-to-use-cfssl-to-create-self-signed-certificates-d55f76ba5781
|
||||||
|
cfssl selfsign -config cfssl.json --profile rootca "Root CA" csr.json | cfssljson -bare ca
|
||||||
|
cfssl genkey csr.json | cfssljson -bare server
|
||||||
|
cfssl genkey csr.json | cfssljson -bare client
|
||||||
|
cfssl sign -ca ca.pem -ca-key ca-key.pem -config cfssl.json -profile server server.csr | cfssljson -bare server
|
||||||
|
cfssl sign -ca ca.pem -ca-key ca-key.pem -config cfssl.json -profile client client.csr | cfssljson -bare client
|
||||||
|
rm ./*.csr
|
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIBMUH4C8W0a0V4C7TQ0PowlIEPyz/j3AaffAOu6UmHNloAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAESCljhOQJWwvupZtt2HGPty6XJvuyqf7RpxheHTx5rWmEnLeI7DYK
|
||||||
|
DhKlIzDeH7Opf4rQrqOECxknfxf355/08A==
|
||||||
|
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBmjCCAUCgAwIBAgIUfqrcRuTGIZsXgJguUp2R/dw0dzgwCgYIKoZIzj0EAwIw
|
||||||
|
FDESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTIzMDUwMzA1MTgwMFoXDTMzMDQzMDA1
|
||||||
|
MTgwMFowFDESMBAGA1UEAxMJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
||||||
|
AQcDQgAESCljhOQJWwvupZtt2HGPty6XJvuyqf7RpxheHTx5rWmEnLeI7DYKDhKl
|
||||||
|
IzDeH7Opf4rQrqOECxknfxf355/08KNwMG4wDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
|
||||||
|
JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFJL7liSFyGC6
|
||||||
|
f85d6ic2cgsqBLJSMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATAKBggqhkjO
|
||||||
|
PQQDAgNIADBFAiEAq3vKTlnGRw3EysUyNzAaYATDkspyKFnoQ+ItjGq4ixACICpN
|
||||||
|
tsAyZ4rKQNy0ZGbbUIncM7MY7S+zDAMz8P1Y9YtT
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -2,6 +2,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -18,7 +20,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultAddress = "localhost:50001"
|
const defaultAddress = "localhost:50001"
|
||||||
|
@ -37,14 +39,7 @@ func main() {
|
||||||
_ = logger.Sync()
|
_ = logger.Sync()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
example := ""
|
example := parseExample()
|
||||||
flag.StringVar(
|
|
||||||
&example,
|
|
||||||
"example",
|
|
||||||
exampleUnary,
|
|
||||||
fmt.Sprintf("%s or %s (default: %s)", exampleUnary, exampleBidirectional, exampleUnary),
|
|
||||||
)
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
conn, err := newConn()
|
conn, err := newConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -70,18 +65,57 @@ func main() {
|
||||||
case exampleBidirectional:
|
case exampleBidirectional:
|
||||||
sayHelloBidirectional(ctx, client, logger)
|
sayHelloBidirectional(ctx, client, logger)
|
||||||
default:
|
default:
|
||||||
logger.Fatal("example doesn't exist", zap.String("example", example))
|
logger.Error("example doesn't exist", zap.String("example", example))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseExample() string {
|
||||||
|
example := ""
|
||||||
|
flag.StringVar(
|
||||||
|
&example,
|
||||||
|
"example",
|
||||||
|
exampleUnary,
|
||||||
|
fmt.Sprintf("%s or %s (default: %s)", exampleUnary, exampleBidirectional, exampleUnary),
|
||||||
|
)
|
||||||
|
flag.Parse()
|
||||||
|
return example
|
||||||
|
}
|
||||||
|
|
||||||
func newConn() (*grpc.ClientConn, error) {
|
func newConn() (*grpc.ClientConn, error) {
|
||||||
|
transport, err := newTransportCredentials()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return grpc.Dial(
|
return grpc.Dial(
|
||||||
"localhost:50001",
|
"localhost:50001",
|
||||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
grpc.WithTransportCredentials(transport),
|
||||||
grpc.WithBlock(),
|
grpc.WithBlock(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTransportCredentials() (credentials.TransportCredentials, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair("./certs/client.pem", "./certs/client-key.pem")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't load client cert: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile("./certs/ca.pem")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't load CA file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
capool := x509.NewCertPool()
|
||||||
|
if !capool.AppendCertsFromPEM(data) {
|
||||||
|
return nil, errors.New("couldn't add ca cart to cert pool")
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewTLS(&tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
RootCAs: capool,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
func sayHello(ctx context.Context, client proto.GreeterClient, logger *zap.Logger) {
|
func sayHello(ctx context.Context, client proto.GreeterClient, logger *zap.Logger) {
|
||||||
reply, err := client.SayHello(ctx, &proto.HelloRequest{Name: uuid.NewString()})
|
reply, err := client.SayHello(ctx, &proto.HelloRequest{Name: uuid.NewString()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,6 +125,7 @@ func sayHello(ctx context.Context, client proto.GreeterClient, logger *zap.Logge
|
||||||
}
|
}
|
||||||
|
|
||||||
func sayHelloBidirectional(ctx context.Context, client proto.GreeterClient, logger *zap.Logger) {
|
func sayHelloBidirectional(ctx context.Context, client proto.GreeterClient, logger *zap.Logger) {
|
||||||
|
// ctx is used in the 2nd goroutine to close the stream client
|
||||||
//nolint:contextcheck
|
//nolint:contextcheck
|
||||||
streamClient, err := client.SayHelloBidirectionalStream(context.Background())
|
streamClient, err := client.SayHelloBidirectionalStream(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -122,9 +157,7 @@ func sayHelloBidirectional(ctx context.Context, client proto.GreeterClient, logg
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second)
|
ticker := time.NewTicker(time.Second)
|
||||||
defer func() {
|
defer ticker.Stop()
|
||||||
ticker.Stop()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -2,6 +2,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -19,10 +21,11 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultAddress = "localhost:50001"
|
const defaultAddress = ":50001"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logger, err := internal.NewLogger()
|
logger, err := internal.NewLogger()
|
||||||
|
@ -54,10 +57,7 @@ func main() {
|
||||||
srv.GracefulStop()
|
srv.GracefulStop()
|
||||||
}(srv, logger)
|
}(srv, logger)
|
||||||
|
|
||||||
logger.Info(
|
logger.Info("listening and serving", zap.String("address", srv.lis.Addr().String()))
|
||||||
"listening and serving",
|
|
||||||
zap.String("address", srv.lis.Addr().String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err = srv.Serve(); err != nil {
|
if err = srv.Serve(); err != nil {
|
||||||
logger.Fatal("something went wrong while serving", zap.Error(err))
|
logger.Fatal("something went wrong while serving", zap.Error(err))
|
||||||
|
@ -72,6 +72,11 @@ type server struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServer(address string, logger *zap.Logger) (*server, error) {
|
func newServer(address string, logger *zap.Logger) (*server, error) {
|
||||||
|
transport, err := newTransportCredentials()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", address)
|
lis, err := net.Listen("tcp", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -80,6 +85,7 @@ func newServer(address string, logger *zap.Logger) (*server, error) {
|
||||||
grpc_zap.ReplaceGrpcLoggerV2(logger)
|
grpc_zap.ReplaceGrpcLoggerV2(logger)
|
||||||
|
|
||||||
srv := grpc.NewServer(
|
srv := grpc.NewServer(
|
||||||
|
grpc.Creds(transport),
|
||||||
grpc.ChainUnaryInterceptor(
|
grpc.ChainUnaryInterceptor(
|
||||||
grpc_recovery.UnaryServerInterceptor(grpc_recovery.WithRecoveryHandler(createPanicHandler(logger))),
|
grpc_recovery.UnaryServerInterceptor(grpc_recovery.WithRecoveryHandler(createPanicHandler(logger))),
|
||||||
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
|
||||||
|
@ -109,6 +115,29 @@ func (s *server) GracefulStop() {
|
||||||
s.grpcSrv.GracefulStop()
|
s.grpcSrv.GracefulStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTransportCredentials() (credentials.TransportCredentials, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair("./certs/server.pem", "./certs/server-key.pem")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't load server cert: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile("./certs/ca.pem")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't load CA file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
capool := x509.NewCertPool()
|
||||||
|
if !capool.AppendCertsFromPEM(data) {
|
||||||
|
return nil, errors.New("couldn't add ca cart to cert pool")
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials.NewTLS(&tls.Config{
|
||||||
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
ClientCAs: capool,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
func createPanicHandler(logger *zap.Logger) grpc_recovery.RecoveryHandlerFunc {
|
func createPanicHandler(logger *zap.Logger) grpc_recovery.RecoveryHandlerFunc {
|
||||||
return func(p interface{}) error {
|
return func(p interface{}) error {
|
||||||
logger.Panic(fmt.Sprintf("%v", p), zap.Stack("stack"))
|
logger.Panic(fmt.Sprintf("%v", p), zap.Stack("stack"))
|
||||||
|
@ -120,8 +149,6 @@ type greeterServer struct {
|
||||||
proto.UnimplementedGreeterServer
|
proto.UnimplementedGreeterServer
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ proto.GreeterServer = (*greeterServer)(nil)
|
|
||||||
|
|
||||||
func (g *greeterServer) SayHello(_ context.Context, r *proto.HelloRequest) (*proto.HelloReply, error) {
|
func (g *greeterServer) SayHello(_ context.Context, r *proto.HelloRequest) (*proto.HelloReply, error) {
|
||||||
return &proto.HelloReply{
|
return &proto.HelloReply{
|
||||||
Message: buildHelloMsg(r.GetName()),
|
Message: buildHelloMsg(r.GetName()),
|
||||||
|
|
Loading…
Reference in New Issue