Skip to content

Commit 24b192c

Browse files
committed
ING-1363: Ensure client cert auth fails when disabled
1 parent 7738912 commit 24b192c

6 files changed

Lines changed: 154 additions & 14 deletions

File tree

.github/actions/install-cbdinocluster/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ runs:
1111
shell: bash
1212
run: |
1313
mkdir -p "$HOME/bin"
14-
wget -nv -O $HOME/bin/cbdinocluster https://github.com/couchbaselabs/cbdinocluster/releases/download/v0.0.96/cbdinocluster-linux-amd64
14+
wget -nv -O $HOME/bin/cbdinocluster https://github.com/couchbaselabs/cbdinocluster/releases/download/v0.0.98/cbdinocluster-linux-amd64
1515
chmod +x $HOME/bin/cbdinocluster
1616
echo "$HOME/bin" >> $GITHUB_PATH
1717
- name: Initialize cbdinocluster

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/dapi_mtls_test.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,8 @@ import (
1111
"github.com/stretchr/testify/require"
1212
)
1313

14-
func (s *GatewayOpsTestSuite) TestDapiClientCertAuth() {
14+
func (s *GatewayOpsTestSuite) TestDapiClientCertAuthTools() {
1515
testutils.SkipIfNoDinoCluster(s.T())
16-
17-
s.Run("Tools", s.Tools)
18-
19-
s.Run("Proxy", s.Proxy)
20-
21-
s.Run("Crud", s.Crud)
22-
}
23-
24-
func (s *GatewayOpsTestSuite) Tools() {
2516
dino := testutils.StartDinoTesting(s.T(), false)
2617
username := "dapiUser"
2718
client := s.createMtlsClient(dino, username)
@@ -56,7 +47,8 @@ func (s *GatewayOpsTestSuite) Tools() {
5647
})
5748
}
5849

59-
func (s *GatewayOpsTestSuite) Proxy() {
50+
func (s *GatewayOpsTestSuite) TestDapiClientCertAuthProxy() {
51+
testutils.SkipIfNoDinoCluster(s.T())
6052
if !s.SupportsFeature(TestFeatureQuery) {
6153
s.T().Skip()
6254
}
@@ -113,7 +105,8 @@ func (s *GatewayOpsTestSuite) Proxy() {
113105
})
114106
}
115107

116-
func (s *GatewayOpsTestSuite) Crud() {
108+
func (s *GatewayOpsTestSuite) TestDapiClientCertAuthCrud() {
109+
testutils.SkipIfNoDinoCluster(s.T())
117110
dino := testutils.StartDinoTesting(s.T(), false)
118111
username := "crudUser"
119112
client := s.createMtlsClient(dino, username)

gateway/test/mtls_test.go

Lines changed: 133 additions & 1 deletion
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"
@@ -169,12 +171,142 @@ func (s *GatewayOpsTestSuite) TestClientCertAuth() {
169171
}
170172
requireRpcSuccess(s.T(), resp, err)
171173
return true
172-
}, time.Second*30, time.Second)
174+
}, time.Second*30, time.Second*5)
173175
})
174176
})
175177
}
176178
}
177179

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

0 commit comments

Comments
 (0)