snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-15 20:40:15 +05:00
commit e241b33f26
1365 changed files with 339570 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
# Credential Reloading From Files
Credential reloading is a feature supported in the advancedtls library.
A very common way to achieve this is to reload from files.
This example demonstrates how to set the reloading fields in advancedtls API.
Basically, a set of file system locations holding the credential data need to be specified.
Once the credential data needs to be updated, users just change the credential data in the file system, and gRPC will pick up the changes automatically.
A couple of things to note:
1. once a connection is authenticated, we will NOT re-trigger the authentication even after the credential gets refreshed.
2. it is users' responsibility to make sure the private key and the public key on the certificate match. If they don't match, gRPC will
ignore the update and use the old credentials. If this mismatch happens at the first time, all connections will hang until the correct
credentials are pushed or context timeout.
## Try it
In directory `security/advancedtls/examples`:
```
go run server/main.go
```
```
go run client/main.go
```

View File

@@ -0,0 +1,111 @@
/*
*
* Copyright 2020 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.
*
*/
// The client demonstrates how to use the credential reloading feature in
// advancedtls to make a mTLS connection to the server.
package main
import (
"context"
"flag"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"google.golang.org/grpc/security/advancedtls"
"google.golang.org/grpc/security/advancedtls/testdata"
)
var address = "localhost:50051"
const (
// Default timeout for normal connections.
defaultTimeout = 2 * time.Second
// Intervals that set to monitor the credential updates.
credRefreshingInterval = 500 * time.Millisecond
)
func main() {
tmpKeyFile := flag.String("key", "", "temporary key file path")
tmpCertFile := flag.String("cert", "", "temporary cert file path")
flag.Parse()
if tmpKeyFile == nil || *tmpKeyFile == "" {
log.Fatalf("tmpKeyFile is nil or empty.")
}
if tmpCertFile == nil || *tmpCertFile == "" {
log.Fatalf("tmpCertFile is nil or empty.")
}
// Initialize credential struct using reloading API.
identityOptions := pemfile.Options{
CertFile: *tmpCertFile,
KeyFile: *tmpKeyFile,
RefreshDuration: credRefreshingInterval,
}
identityProvider, err := pemfile.NewProvider(identityOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", identityOptions, err)
}
rootOptions := pemfile.Options{
RootFile: testdata.Path("client_trust_cert_1.pem"),
RefreshDuration: credRefreshingInterval,
}
rootProvider, err := pemfile.NewProvider(rootOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", rootOptions, err)
}
options := &advancedtls.Options{
IdentityOptions: advancedtls.IdentityCertificateOptions{
IdentityProvider: identityProvider,
},
AdditionalPeerVerification: func(*advancedtls.HandshakeVerificationInfo) (*advancedtls.PostHandshakeVerificationResults, error) {
return &advancedtls.PostHandshakeVerificationResults{}, nil
},
RootOptions: advancedtls.RootCertificateOptions{
RootProvider: rootProvider,
},
VerificationType: advancedtls.CertVerification,
}
clientTLSCreds, err := advancedtls.NewClientCreds(options)
if err != nil {
log.Fatalf("advancedtls.NewClientCreds(%v) failed: %v", options, err)
}
// Make a connection using the credentials.
conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(clientTLSCreds))
if err != nil {
log.Fatalf("grpc.NewClient to %s failed: %v", address, err)
}
client := pb.NewGreeterClient(conn)
// Send the requests every 0.5s. The credential is expected to be changed in
// the bash script. We don't cancel the context nor call conn.Close() here,
// since the bash script is expected to close the client goroutine.
for {
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
_, err = client.SayHello(ctx, &pb.HelloRequest{Name: "gRPC"}, grpc.WaitForReady(true))
if err != nil {
log.Fatalf("client.SayHello failed: %v", err)
}
cancel()
time.Sleep(500 * time.Millisecond)
}
}

View File

@@ -0,0 +1,112 @@
/*
*
* Copyright 2020 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.
*
*/
// The server demonstrates how to use the credential reloading feature in
// advancedtls to serve mTLS connections from the client.
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/security/advancedtls"
"google.golang.org/grpc/security/advancedtls/testdata"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
var port = ":50051"
// Intervals that set to monitor the credential updates.
const credRefreshingInterval = 1 * time.Minute
type greeterServer struct {
pb.UnimplementedGreeterServer
}
// sayHello is a simple implementation of the pb.GreeterServer SayHello method.
func (greeterServer) SayHello(_ context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
flag.Parse()
fmt.Printf("server starting on port %s...\n", port)
identityOptions := pemfile.Options{
CertFile: testdata.Path("server_cert_1.pem"),
KeyFile: testdata.Path("server_key_1.pem"),
RefreshDuration: credRefreshingInterval,
}
identityProvider, err := pemfile.NewProvider(identityOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", identityOptions, err)
}
defer identityProvider.Close()
rootOptions := pemfile.Options{
RootFile: testdata.Path("server_trust_cert_1.pem"),
RefreshDuration: credRefreshingInterval,
}
rootProvider, err := pemfile.NewProvider(rootOptions)
if err != nil {
log.Fatalf("pemfile.NewProvider(%v) failed: %v", rootOptions, err)
}
defer rootProvider.Close()
// Start a server and create a client using advancedtls API with Provider.
options := &advancedtls.Options{
IdentityOptions: advancedtls.IdentityCertificateOptions{
IdentityProvider: identityProvider,
},
RootOptions: advancedtls.RootCertificateOptions{
RootProvider: rootProvider,
},
RequireClientCert: true,
AdditionalPeerVerification: func(params *advancedtls.HandshakeVerificationInfo) (*advancedtls.PostHandshakeVerificationResults, error) {
// This message is to show the certificate under the hood is actually reloaded.
fmt.Printf("Client common name: %s.\n", params.Leaf.Subject.CommonName)
return &advancedtls.PostHandshakeVerificationResults{}, nil
},
VerificationType: advancedtls.CertVerification,
}
serverTLSCreds, err := advancedtls.NewServerCreds(options)
if err != nil {
log.Fatalf("advancedtls.NewServerCreds(%v) failed: %v", options, err)
}
s := grpc.NewServer(grpc.Creds(serverTLSCreds), grpc.KeepaliveParams(keepalive.ServerParameters{
// Set the max connection time to be 0.5 s to force the client to
// re-establish the connection, and hence re-invoke the verification
// callback.
MaxConnectionAge: 500 * time.Millisecond,
}))
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
pb.RegisterGreeterServer(s, greeterServer{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

View File

@@ -0,0 +1,114 @@
#!/bin/bash
#
# Copyright 2020 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.
#
set +e
export TMPDIR=$(mktemp -d)
trap "rm -rf ${TMPDIR}" EXIT
clean () {
for i in {1..10}; do
jobs -p | xargs -n1 pkill -P
# A simple "wait" just hangs sometimes. Running `jobs` seems to help.
sleep 1
if jobs | read; then
return
fi
done
echo "$(tput setaf 1) clean failed to kill tests $(tput sgr 0)"
jobs
pstree
rm ${CLIENT_LOG}
rm ${SERVER_LOG}
rm ${KEY_FILE_PATH}
rm ${CERT_FILE_PATH}
exit 1
}
fail () {
echo "$(tput setaf 1) $1 $(tput sgr 0)"
clean
exit 1
}
pass () {
echo "$(tput setaf 2) $1 $(tput sgr 0)"
}
EXAMPLES=(
"credential_reloading_from_files"
)
declare -a EXPECTED_SERVER_OUTPUT=("Client common name: foo.bar.hoo.com" "Client common name: foo.bar.another.client.com")
cd ./security/advancedtls/examples
for example in ${EXAMPLES[@]}; do
echo "$(tput setaf 4) testing: ${example} $(tput sgr 0)"
KEY_FILE_PATH=$(mktemp)
cat ../testdata/client_key_1.pem > ${KEY_FILE_PATH}
CERT_FILE_PATH=$(mktemp)
cat ../testdata/client_cert_1.pem > ${CERT_FILE_PATH}
# Build server.
if ! go build -o /dev/null ./${example}/*server/*.go; then
fail "failed to build server"
else
pass "successfully built server"
fi
# Build client.
if ! go build -o /dev/null ./${example}/*client/*.go; then
fail "failed to build client"
else
pass "successfully built client"
fi
# Start server.
SERVER_LOG="$(mktemp)"
go run ./$example/*server/*.go &> $SERVER_LOG &
# Run client binary.
CLIENT_LOG="$(mktemp)"
go run ${example}/*client/*.go -key=${KEY_FILE_PATH} -cert=${CERT_FILE_PATH} &> $CLIENT_LOG &
# Wait for the client to send some requests using old credentials.
sleep 4s
# Switch to the new credentials.
cat ../testdata/another_client_key_1.pem > ${KEY_FILE_PATH}
cat ../testdata/another_client_cert_1.pem > ${CERT_FILE_PATH}
# Wait for the client to send some requests using new credentials.
sleep 4s
# Check server log for expected output.
for output in "${EXPECTED_SERVER_OUTPUT[@]}"; do
if ! grep -q "$output" $SERVER_LOG; then
fail "server log missing output: $output
got server log:
$(cat $SERVER_LOG)
"
else
pass "server log contains expected output: $output"
fi
done
clean
done

View File

@@ -0,0 +1,26 @@
module google.golang.org/grpc/security/advancedtls/examples
go 1.25.0
require (
google.golang.org/grpc v1.83.0
google.golang.org/grpc/examples v0.0.0-20260818050025-b6685c50ff98
google.golang.org/grpc/security/advancedtls v1.0.0
)
require (
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
google.golang.org/protobuf v1.36.12 // indirect
)
replace google.golang.org/grpc => ../../..
replace google.golang.org/grpc/examples => ../../../examples
replace google.golang.org/grpc/security/advancedtls => ../

View File

@@ -0,0 +1,30 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E=
github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=