Repository: grpc/grpc-go

Issue:
advancedtls: options.serverConfig checks minTLS > maxTLS before defaults are applied
### What version of gRPC are you using?

google.golang.org/grpc v1.76.0
google.golang.org/grpc/security/advancedtls v1.0.0

### What version of Go are you using (`go version`)?

go version go1.25.3 darwin/arm64

### What operating system (Linux, Windows, …) and version?

macOS 15, Ubuntu 24.04

### What did you do?

```go
tlsOpts := &advancedtls.Options{
	IdentityOptions: advancedtls.IdentityCertificateOptions{IdentityProvider: certProvider},
	// Note: Only MinTLSVersion is set, not MaxTLSVersion
	MinTLSVersion: tls.VersionTLS13,
}
creds, err := advancedtls.NewServerCreds(tlsOpts)
if err != nil {
	return nil, err
}
```

### What did you expect to see?

No error, and a valid credentials.TransportCredentials returned.

### What did you see instead?

`advancedtls.NewServerCreds` returns error `the minimum TLS version is larger than the maximum TLS version`.

The [documentation](https://pkg.go.dev/google.golang.org/grpc/security/advancedtls#Options) for `advancedtls.Options` the default for MinTLSVersion is 1.0 when acting as a server and MaxTLSVersion is 1.3.

In reality, these defaults are currently applied after checking whether min > max, so if you follow the documentation in believing the default for max is 1.3, you might think you can leave that unset while only setting the MinTLSVersion, but this causes an error and you must set both.

If the documentation is correct then [this check](https://github.com/grpc/grpc-go/blob/f199062ef31ddda54152e1ca5e3d15fb63903dc3/security/advancedtls/advancedtls.go#L328-L330) should come [after defaults are applied](https://github.com/grpc/grpc-go/blob/f199062ef31ddda54152e1ca5e3d15fb63903dc3/security/advancedtls/advancedtls.go#L338-L345).

Discussion:
@arjan-bal: Hi @gtcooke94, can you please take a look at this issue?@gtcooke94: Good point, would you like to submit the fix @jamesog - the fix you suggested I think is exactly correct@jamesog: Thanks @gtcooke94: #8684@gtcooke94: Thanks!

Solution patch:
diff --git a/security/advancedtls/advancedtls.go b/security/advancedtls/advancedtls.go
index c1f7a11f..3282dc7d 100644
--- a/security/advancedtls/advancedtls.go
+++ b/security/advancedtls/advancedtls.go
@@ -317,6 +317,14 @@ func (o *Options) serverConfig() (*tls.Config, error) {
 	if o.RequireClientCert && o.VerificationType == SkipVerification && o.AdditionalPeerVerification == nil {
 		return nil, fmt.Errorf("server needs to provide custom verification mechanism if choose to skip default verification, but require client certificate(s)")
 	}
+	// If the MinTLSVersion isn't set, default to 1.2
+	if o.MinTLSVersion == 0 {
+		o.MinTLSVersion = tls.VersionTLS12
+	}
+	// If the MaxTLSVersion isn't set, default to 1.3
+	if o.MaxTLSVersion == 0 {
+		o.MaxTLSVersion = tls.VersionTLS13
+	}
 	// Make sure users didn't specify more than one fields in
 	// RootCertificateOptions and IdentityCertificateOptions.
 	if num := o.RootOptions.nonNilFieldCount(); num > 1 {
@@ -338,14 +346,6 @@ func (o *Options) serverConfig() (*tls.Config, error) {
 		// buildVerifyFunc.
 		clientAuth = tls.RequireAnyClientCert
 	}
-	// If the MinTLSVersion isn't set, default to 1.2
-	if o.MinTLSVersion == 0 {
-		o.MinTLSVersion = tls.VersionTLS12
-	}
-	// If the MaxTLSVersion isn't set, default to 1.3
-	if o.MaxTLSVersion == 0 {
-		o.MaxTLSVersion = tls.VersionTLS13
-	}
 	config := &tls.Config{
 		ClientAuth:   clientAuth,
 		MinVersion:   o.MinTLSVersion,



Test patch:
diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go
index 4323a593..d747ac78 100644
--- a/security/advancedtls/advancedtls_test.go
+++ b/security/advancedtls/advancedtls_test.go
@@ -354,6 +354,17 @@ func (s) TestServerOptionsConfigSuccessCases(t *testing.T) {
 				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
 			},
 		},
+		{
+			desc:                   "MaxVersion default is applied when only MinVersion is set",
+			serverVerificationType: CertVerification,
+			IdentityOptions: IdentityCertificateOptions{
+				Certificates: []tls.Certificate{},
+			},
+			RootOptions: RootCertificateOptions{
+				RootProvider: fakeProvider{},
+			},
+			MinVersion: tls.VersionTLS12,
+		},
 	}
 	for _, test := range tests {
 		test := test