mirror of https://github.com/grpc/grpc-go.git
307 lines
9.4 KiB
Go
307 lines
9.4 KiB
Go
/*
|
|
*
|
|
* Copyright 2024 gRPC authors.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
// Binary client is an example client demonstrating use of advancedtls, to set
|
|
// up a secure gRPC client connection with various TLS authentication methods.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
pb "google.golang.org/grpc/examples/features/proto/echo"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/credentials/tls/certprovider"
|
|
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
|
|
"google.golang.org/grpc/security/advancedtls"
|
|
)
|
|
|
|
const credRefreshInterval = 1 * time.Minute
|
|
const serverAddr = "localhost"
|
|
const goodServerPort string = "50051"
|
|
const revokedServerPort string = "50053"
|
|
const insecurePort string = "50054"
|
|
const message string = "Hello"
|
|
|
|
// -- TLS --
|
|
|
|
func makeRootProvider(credsDirectory string) certprovider.Provider {
|
|
rootOptions := pemfile.Options{
|
|
RootFile: filepath.Join(credsDirectory, "ca_cert.pem"),
|
|
RefreshDuration: credRefreshInterval,
|
|
}
|
|
rootProvider, err := pemfile.NewProvider(rootOptions)
|
|
if err != nil {
|
|
fmt.Printf("Error %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
return rootProvider
|
|
}
|
|
|
|
func makeIdentityProvider(revoked bool, credsDirectory string) certprovider.Provider {
|
|
var certFile string
|
|
if revoked {
|
|
certFile = filepath.Join(credsDirectory, "client_cert_revoked.pem")
|
|
} else {
|
|
certFile = filepath.Join(credsDirectory, "client_cert.pem")
|
|
}
|
|
identityOptions := pemfile.Options{
|
|
CertFile: certFile,
|
|
KeyFile: filepath.Join(credsDirectory, "client_key.pem"),
|
|
RefreshDuration: credRefreshInterval,
|
|
}
|
|
identityProvider, err := pemfile.NewProvider(identityOptions)
|
|
if err != nil {
|
|
fmt.Printf("Error %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
return identityProvider
|
|
}
|
|
|
|
func runClientWithProviders(rootProvider certprovider.Provider, identityProvider certprovider.Provider, crlProvider advancedtls.CRLProvider, port string, shouldFail bool) {
|
|
options := &advancedtls.Options{
|
|
// Setup the certificates to be used
|
|
IdentityOptions: advancedtls.IdentityCertificateOptions{
|
|
IdentityProvider: identityProvider,
|
|
},
|
|
// Setup the roots to be used
|
|
RootOptions: advancedtls.RootCertificateOptions{
|
|
RootProvider: rootProvider,
|
|
},
|
|
// Tell the client to verify the server cert
|
|
VerificationType: advancedtls.CertVerification,
|
|
}
|
|
|
|
// Configure revocation and CRLs
|
|
options.RevocationOptions = &advancedtls.RevocationOptions{
|
|
CRLProvider: crlProvider,
|
|
}
|
|
|
|
clientTLSCreds, err := advancedtls.NewClientCreds(options)
|
|
|
|
if err != nil {
|
|
fmt.Printf("Error %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fullServerAddr := serverAddr + ":" + port
|
|
runWithCredentials(clientTLSCreds, fullServerAddr, !shouldFail)
|
|
}
|
|
|
|
func tlsWithCRLsToGoodServer(credsDirectory string) {
|
|
rootProvider := makeRootProvider(credsDirectory)
|
|
defer rootProvider.Close()
|
|
identityProvider := makeIdentityProvider(false, credsDirectory)
|
|
defer identityProvider.Close()
|
|
crlProvider := makeCRLProvider(credsDirectory)
|
|
defer crlProvider.Close()
|
|
|
|
runClientWithProviders(rootProvider, identityProvider, crlProvider, goodServerPort, false)
|
|
}
|
|
|
|
func tlsWithCRLsToRevokedServer(credsDirectory string) {
|
|
rootProvider := makeRootProvider(credsDirectory)
|
|
defer rootProvider.Close()
|
|
identityProvider := makeIdentityProvider(false, credsDirectory)
|
|
defer identityProvider.Close()
|
|
crlProvider := makeCRLProvider(credsDirectory)
|
|
defer crlProvider.Close()
|
|
|
|
runClientWithProviders(rootProvider, identityProvider, crlProvider, revokedServerPort, true)
|
|
}
|
|
|
|
func tlsWithCRLs(credsDirectory string) {
|
|
tlsWithCRLsToGoodServer(credsDirectory)
|
|
tlsWithCRLsToRevokedServer(credsDirectory)
|
|
}
|
|
|
|
func makeCRLProvider(crlDirectory string) *advancedtls.FileWatcherCRLProvider {
|
|
options := advancedtls.FileWatcherOptions{
|
|
CRLDirectory: crlDirectory,
|
|
}
|
|
provider, err := advancedtls.NewFileWatcherCRLProvider(options)
|
|
if err != nil {
|
|
fmt.Printf("Error making CRL Provider: %v\nExiting...", err)
|
|
os.Exit(1)
|
|
}
|
|
return provider
|
|
}
|
|
|
|
// --- Custom Verification ---
|
|
func customVerificaitonSucceed(info *advancedtls.HandshakeVerificationInfo) (*advancedtls.PostHandshakeVerificationResults, error) {
|
|
// Looks at info for what you care about as the custom verification implementer
|
|
if info.ServerName != "localhost:50051" {
|
|
return nil, fmt.Errorf("expected servername of localhost:50051, got %v", info.ServerName)
|
|
}
|
|
return &advancedtls.PostHandshakeVerificationResults{}, nil
|
|
}
|
|
|
|
func customVerificaitonFail(info *advancedtls.HandshakeVerificationInfo) (*advancedtls.PostHandshakeVerificationResults, error) {
|
|
// Looks at info for what you care about as the custom verification implementer
|
|
if info.ServerName != "ExampleDesignedToFail" {
|
|
return nil, fmt.Errorf("expected servername of ExampleDesignedToFail, got %v", info.ServerName)
|
|
}
|
|
return &advancedtls.PostHandshakeVerificationResults{}, nil
|
|
}
|
|
|
|
func customVerification(credsDirectory string) {
|
|
runClientWithCustomVerification(credsDirectory, goodServerPort)
|
|
|
|
}
|
|
|
|
func runClientWithCustomVerification(credsDirectory string, port string) {
|
|
rootProvider := makeRootProvider(credsDirectory)
|
|
defer rootProvider.Close()
|
|
identityProvider := makeIdentityProvider(false, credsDirectory)
|
|
defer identityProvider.Close()
|
|
fullServerAddr := serverAddr + ":" + port
|
|
{
|
|
// Run with the custom verification func that will succeed
|
|
options := &advancedtls.Options{
|
|
// Setup the certificates to be used
|
|
IdentityOptions: advancedtls.IdentityCertificateOptions{
|
|
IdentityProvider: identityProvider,
|
|
},
|
|
// Setup the roots to be used
|
|
RootOptions: advancedtls.RootCertificateOptions{
|
|
RootProvider: rootProvider,
|
|
},
|
|
// Tell the client to verify the server cert
|
|
VerificationType: advancedtls.CertVerification,
|
|
AdditionalPeerVerification: customVerificaitonSucceed,
|
|
}
|
|
|
|
clientTLSCreds, err := advancedtls.NewClientCreds(options)
|
|
|
|
if err != nil {
|
|
fmt.Printf("Error %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
runWithCredentials(clientTLSCreds, fullServerAddr, true)
|
|
}
|
|
{
|
|
// Run with the custom verification func that will fail
|
|
options := &advancedtls.Options{
|
|
// Setup the certificates to be used
|
|
IdentityOptions: advancedtls.IdentityCertificateOptions{
|
|
IdentityProvider: identityProvider,
|
|
},
|
|
// Setup the roots to be used
|
|
RootOptions: advancedtls.RootCertificateOptions{
|
|
RootProvider: rootProvider,
|
|
},
|
|
// Tell the client to verify the server cert
|
|
VerificationType: advancedtls.CertVerification,
|
|
AdditionalPeerVerification: customVerificaitonFail,
|
|
}
|
|
|
|
clientTLSCreds, err := advancedtls.NewClientCreds(options)
|
|
|
|
if err != nil {
|
|
fmt.Printf("Error %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
runWithCredentials(clientTLSCreds, fullServerAddr, false)
|
|
}
|
|
}
|
|
|
|
// -- credentials.NewTLS example --
|
|
func credentialsNewTLSExample(credsDirectory string) {
|
|
cert, err := tls.LoadX509KeyPair(filepath.Join(credsDirectory, "client_cert.pem"), filepath.Join(credsDirectory, "client_key.pem"))
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
rootPem, err := os.ReadFile(filepath.Join(credsDirectory, "ca_cert.pem"))
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
root := x509.NewCertPool()
|
|
if !root.AppendCertsFromPEM(rootPem) {
|
|
os.Exit(1)
|
|
}
|
|
|
|
config := &tls.Config{
|
|
Certificates: []tls.Certificate{cert},
|
|
RootCAs: root,
|
|
}
|
|
|
|
// Directly create credentials from a tls.Config.
|
|
creds := credentials.NewTLS(config)
|
|
port := goodServerPort
|
|
fullServerAddr := serverAddr + ":" + port
|
|
runWithCredentials(creds, fullServerAddr, true)
|
|
|
|
}
|
|
|
|
// -- Insecure --
|
|
func insecureCredentialsExample() {
|
|
creds := insecure.NewCredentials()
|
|
port := insecurePort
|
|
fullServerAddr := serverAddr + ":" + port
|
|
runWithCredentials(creds, fullServerAddr, true)
|
|
}
|
|
|
|
// -- Main and Runner --
|
|
|
|
// All of these examples differ in how they configure the
|
|
// credentials.TransportCredentials object. Once we have that, actually making
|
|
// the calls with gRPC is the same.
|
|
func runWithCredentials(creds credentials.TransportCredentials, fullServerAddr string, shouldSucceed bool) {
|
|
conn, err := grpc.NewClient(fullServerAddr, grpc.WithTransportCredentials(creds))
|
|
if err != nil {
|
|
fmt.Printf("Error during grpc.NewClient %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer conn.Close()
|
|
client := pb.NewEchoClient(conn)
|
|
req := &pb.EchoRequest{
|
|
Message: message,
|
|
}
|
|
context, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
resp, err := client.UnaryEcho(context, req)
|
|
defer cancel()
|
|
|
|
if shouldSucceed && err != nil {
|
|
fmt.Printf("Error during client.UnaryEcho %v\n", err)
|
|
} else if !shouldSucceed && err == nil {
|
|
fmt.Printf("Should have failed but didn't, got response: %v\n", resp)
|
|
}
|
|
|
|
}
|
|
func main() {
|
|
credsDirectory := flag.String("credentials_directory", "", "Path to the creds directory of this example repo")
|
|
flag.Parse()
|
|
|
|
if *credsDirectory == "" {
|
|
fmt.Println("Must set credentials_directory argument to this repo's creds directory")
|
|
os.Exit(1)
|
|
}
|
|
tlsWithCRLs(*credsDirectory)
|
|
customVerification(*credsDirectory)
|
|
credentialsNewTLSExample(*credsDirectory)
|
|
insecureCredentialsExample()
|
|
}
|