Skip to content

Commit 2b6c8d5

Browse files
committed
ING-1363: Ensure client cert auth fails when disabled
1 parent 5650e1f commit 2b6c8d5

6 files changed

Lines changed: 144 additions & 3 deletions

File tree

gateway/auth/cbauthauthenticator.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
var (
1515
ErrInvalidCredentials = errors.New("invalid credentials")
1616
ErrInvalidCertificate = errors.New("invalid certificate")
17+
ErrCertAuthDisabled = errors.New("client cert auth disabled")
1718
)
1819

1920
type CbAuthAuthenticator struct {
@@ -101,6 +102,8 @@ func (a *CbAuthAuthenticator) ValidateConnStateForObo(ctx context.Context, connS
101102
if err != nil {
102103
if errors.Is(err, cbauthx.ErrInvalidAuth) {
103104
return "", "", ErrInvalidCertificate
105+
} else if errors.Is(err, cbauthx.ErrCertAuthDisabled) {
106+
return "", "", ErrCertAuthDisabled
104107
}
105108

106109
return "", "", fmt.Errorf("failed to check certificate with cbauth: %w", err)

gateway/dataimpl/server_v1/authhandler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ func (a AuthHandler) MaybeGetOboUserFromContext(ctx context.Context) (string, st
8888
if err != nil {
8989
if errors.Is(err, auth.ErrInvalidCertificate) {
9090
return "", "", a.ErrorHandler.NewInvalidCertificateStatus()
91+
} else if errors.Is(err, auth.ErrCertAuthDisabled) {
92+
return "", "", a.ErrorHandler.NewCertAuthDisabledStatus()
9193
}
9294

9395
a.Logger.Error("received an unexpected cert authentication error", zap.Error(err))

gateway/dataimpl/server_v1/errorhandler.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,16 @@ func (e ErrorHandler) NewInvalidCertificateStatus() *status.Status {
937937
return st
938938
}
939939

940+
func (e ErrorHandler) NewCertAuthDisabledStatus() *status.Status {
941+
st := status.New(codes.Unauthenticated, "Client cert auth disabled on the cluster.")
942+
st = e.tryAttachStatusDetails(st, &epb.ResourceInfo{
943+
ResourceType: "user",
944+
ResourceName: "",
945+
Description: "",
946+
})
947+
return st
948+
}
949+
940950
func (e ErrorHandler) NewUnexpectedAuthTypeStatus() *status.Status {
941951
st := status.New(codes.InvalidArgument, "Unexpected auth type.")
942952
return st

gateway/test/mtls_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"crypto/tls"
66
"time"
77

8+
"github.com/couchbase/gocbcorex/cbhttpx"
9+
"github.com/couchbase/gocbcorex/cbmgmtx"
810
"github.com/couchbase/goprotostellar/genproto/admin_query_v1"
911
"github.com/couchbase/goprotostellar/genproto/admin_search_v1"
1012
"github.com/couchbase/goprotostellar/genproto/kv_v1"
@@ -20,6 +22,13 @@ import (
2022

2123
func (s *GatewayOpsTestSuite) TestClientCertAuth() {
2224
testutils.SkipIfNoDinoCluster(s.T())
25+
26+
s.Run("KvService", s.KvService)
27+
28+
s.Run("ClientCertAuthDisabled", s.ClientCertConfiguration)
29+
}
30+
31+
func (s *GatewayOpsTestSuite) KvService() {
2332
dino := testutils.StartDinoTesting(s.T(), false)
2433

2534
indexClient := admin_search_v1.NewSearchAdminServiceClient(s.gatewayConn)
@@ -175,6 +184,123 @@ func (s *GatewayOpsTestSuite) TestClientCertAuth() {
175184
}
176185
}
177186

187+
func (s *GatewayOpsTestSuite) ClientCertConfiguration() {
188+
dino := testutils.StartDinoTesting(s.T(), false)
189+
username := "certConfig"
190+
conn := s.newClientCertConn(dino, username)
191+
kvClient := kv_v1.NewKvServiceClient(conn)
192+
193+
getFn := func() (*kv_v1.GetResponse, error) {
194+
return kvClient.Get(context.Background(), &kv_v1.GetRequest{
195+
BucketName: s.bucketName,
196+
ScopeName: s.scopeName,
197+
CollectionName: s.collectionName,
198+
Key: s.testDocId(),
199+
})
200+
}
201+
202+
enableReq := &cbmgmtx.ConfigureClientCertAuthRequest{
203+
State: "enable",
204+
Prefixes: []cbmgmtx.Prefix{
205+
{
206+
Path: "san.email",
207+
Prefix: "",
208+
Delimiter: "@",
209+
},
210+
},
211+
}
212+
213+
dino.AddWriteUser(username)
214+
215+
// Check that client cert auth is working as expected.
216+
s.Run("InitialSuccess", func() {
217+
resp, err := getFn()
218+
requireRpcSuccess(s.T(), resp, err)
219+
})
220+
221+
ep, err := s.testClusterInfo.AdminClient.GetMgmtEndpoint(context.Background())
222+
require.NoError(s.T(), err)
223+
mgmt := cbmgmtx.Management{
224+
Transport: ep.RoundTripper,
225+
UserAgent: "useragent",
226+
Endpoint: ep.Endpoint,
227+
Auth: &cbhttpx.BasicAuth{
228+
Username: ep.Username,
229+
Password: ep.Password,
230+
},
231+
}
232+
233+
// Change the path that cbauth will try and get the name from and check
234+
// that the old cert fails
235+
err = mgmt.ConfigureClientCertAuth(context.Background(), &cbmgmtx.ConfigureClientCertAuthRequest{
236+
State: "enable",
237+
Prefixes: []cbmgmtx.Prefix{
238+
{
239+
Path: "subject.cn",
240+
Prefix: "",
241+
Delimiter: "",
242+
},
243+
},
244+
})
245+
assert.NoError(s.T(), err)
246+
247+
s.Run("IncorrectUsernamePath", func() {
248+
require.Eventually(s.T(), func() bool {
249+
_, err := getFn()
250+
if err == nil {
251+
return false
252+
}
253+
254+
assertRpcStatus(s.T(), err, codes.PermissionDenied)
255+
return assert.Contains(s.T(), err.Error(), "Your certificate is invalid")
256+
}, time.Second*30, time.Second)
257+
})
258+
259+
// Restore intial settings and check that the original cert works again.
260+
err = mgmt.ConfigureClientCertAuth(context.Background(), enableReq)
261+
assert.NoError(s.T(), err)
262+
263+
s.Run("SuccessAfterSettingsReset", func() {
264+
require.Eventually(s.T(), func() bool {
265+
resp, err := getFn()
266+
if err != nil {
267+
return false
268+
}
269+
270+
requireRpcSuccess(s.T(), resp, err)
271+
return true
272+
}, time.Second*30, time.Second)
273+
})
274+
275+
// Disable client cert auth on the cluster and make sure op fails.
276+
err = mgmt.ConfigureClientCertAuth(context.Background(), &cbmgmtx.ConfigureClientCertAuthRequest{
277+
State: "disable",
278+
Prefixes: []cbmgmtx.Prefix{
279+
{
280+
Path: "san.email",
281+
Prefix: "",
282+
Delimiter: "@",
283+
},
284+
},
285+
})
286+
assert.NoError(s.T(), err)
287+
288+
s.Run("CertAuthDisabled", func() {
289+
require.Eventually(s.T(), func() bool {
290+
_, err := getFn()
291+
if err == nil {
292+
return false
293+
}
294+
295+
assertRpcStatus(s.T(), err, codes.Unauthenticated)
296+
return assert.Contains(s.T(), err.Error(), "Client cert auth disabled on the cluster")
297+
}, time.Second*30, time.Second)
298+
})
299+
300+
err = mgmt.ConfigureClientCertAuth(context.Background(), enableReq)
301+
assert.NoError(s.T(), err)
302+
}
303+
178304
func (s *GatewayOpsTestSuite) newClientCertConn(dino *testutils.DinoController, username string) *grpc.ClientConn {
179305
res := dino.GetClientCert(username)
180306

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.12.0
1111
github.com/aws/aws-sdk-go-v2/config v1.28.8
1212
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.9
13-
github.com/couchbase/gocbcorex v0.0.0-20251111022038-67fa47c27da2
13+
github.com/couchbase/gocbcorex v0.0.0-20251118081240-3f26da0f471e
1414
github.com/couchbase/goprotostellar v1.0.3-0.20251106001300-b09286f4d53d
1515
github.com/couchbaselabs/gocbconnstr v1.0.5
1616
github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
6767
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
6868
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
6969
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
70-
github.com/couchbase/gocbcorex v0.0.0-20251111022038-67fa47c27da2 h1:Cbw1gJs5V2z/SHPmiqVo7nlr7RWtMAusEoxIDpsPm+s=
71-
github.com/couchbase/gocbcorex v0.0.0-20251111022038-67fa47c27da2/go.mod h1:tlLBmDl/J9iUCImvGvzIr1sLhHm8+KWaSfkofmErCMQ=
70+
github.com/couchbase/gocbcorex v0.0.0-20251118081240-3f26da0f471e h1:3H82a+phltYGRicpdKogxt9mvDAe7kD66t9086vXh9k=
71+
github.com/couchbase/gocbcorex v0.0.0-20251118081240-3f26da0f471e/go.mod h1:tlLBmDl/J9iUCImvGvzIr1sLhHm8+KWaSfkofmErCMQ=
7272
github.com/couchbase/goprotostellar v1.0.3-0.20251106001300-b09286f4d53d h1:FZZcOTRLIaK3VGX0eVGmvCqc6teJ+BSuS3VYJMAjejs=
7373
github.com/couchbase/goprotostellar v1.0.3-0.20251106001300-b09286f4d53d/go.mod h1:X58ot5FRqlBTBkwG/oI4klunpu4MApjGktheqeRWQw0=
7474
github.com/couchbaselabs/gocbconnstr v1.0.5 h1:e0JokB5qbcz7rfnxEhNRTKz8q1svoRvDoZihsiwNigA=

0 commit comments

Comments
 (0)