Skip to content

Commit 40b00a9

Browse files
craig[bot]angles-n-daemons
andcommitted
Merge #151804
151804: sql: gate access to crdb_internal tables based on session var r=angles-n-daemons a=angles-n-daemons The epic below outlines a set of work to prevent users from unauthorized access to what we deem unsafe internals. These unsafe internals lie mostly in the crdb_internal schema and the system database. This PR makes it so that crdb_internal access is limited to a slim set of "allowed" internal objects. Fixes: #151803 Epic: CRDB-24527 Release note (ops change): restricts access to all but allowed list of internal tables in the crdb_internal schema if the session variable `allow_unsafe_internals` is set to true, or if the caller is internal. Co-authored-by: Brian Dillmann <[email protected]>
2 parents 71fcf72 + 8e688ef commit 40b00a9

File tree

3 files changed

+80
-24
lines changed

3 files changed

+80
-24
lines changed

pkg/sql/authorization.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,11 @@ func (p *planner) HasPrivilege(
122122
return false, errors.AssertionFailedf("cannot use CheckPrivilege without a txn")
123123
}
124124

125-
// Check for system table access restrictions before any admin bypasses
126-
if d, ok := privilegeObject.(catalog.TableDescriptor); ok {
127-
// Check for system table access restrictions before any admin bypasses
128-
if catalog.IsSystemDescriptor(d) {
129-
if err := unsafesql.CheckInternalsAccess(p.SessionData()); err != nil {
130-
return false, err
131-
}
125+
// Do a safety check on the object, if it is considered unsafe
126+
// does the caller have the appropriate session data to access it?
127+
if p.objectIsUnsafe(ctx, privilegeObject) {
128+
if err := unsafesql.CheckInternalsAccess(p.SessionData()); err != nil {
129+
return false, err
132130
}
133131
}
134132

@@ -951,6 +949,29 @@ func (p *planner) HasViewActivityOrViewActivityRedactedRole(
951949
return false, false, nil
952950
}
953951

952+
// objectIsUnsafe checks if the privilege object is considered unsafe for external usage.
953+
// Unsafe objects are any system tables, and crdb_internal tables which are not listed as externally supported.
954+
func (p *planner) objectIsUnsafe(ctx context.Context, privilegeObject privilege.Object) bool {
955+
d, ok := privilegeObject.(catalog.TableDescriptor)
956+
if !ok {
957+
return true
958+
}
959+
960+
// All system descriptors are considered unsafe.
961+
if catalog.IsSystemDescriptor(d) {
962+
return true
963+
}
964+
965+
// Unsupported crdb_internal tables are considered unsafe.
966+
if d.GetParentSchemaID() == catconstants.CrdbInternalID {
967+
if _, ok := SupportedCRDBInternalTables[d.GetName()]; !ok {
968+
return true
969+
}
970+
}
971+
972+
return false
973+
}
974+
954975
func insufficientPrivilegeError(
955976
user username.SQLUsername, kind privilege.Kind, object privilege.Object,
956977
) error {

pkg/sql/authorization_test.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ func checkUnsafeErr(t *testing.T, err error) {
311311
t.Fatal("expected unsafe access error, got", err)
312312
}
313313

314-
func TestCheckUnsafeInternalsAccess(t *testing.T) {
314+
func TestCheckUnsafeSystemAccess(t *testing.T) {
315315
defer leaktest.AfterTest(t)()
316316
defer log.Scope(t).Close(t)
317317

@@ -375,3 +375,38 @@ func TestCheckUnsafeInternalsAccess(t *testing.T) {
375375
})
376376

377377
}
378+
379+
func TestCheckUnsafeCRDBInternalAccess(t *testing.T) {
380+
defer leaktest.AfterTest(t)()
381+
defer log.Scope(t).Close(t)
382+
383+
ctx := context.Background()
384+
s := serverutils.StartServerOnly(t, base.TestServerArgs{
385+
DefaultTestTenant: base.TestControlsTenantsExplicitly,
386+
})
387+
defer s.Stopper().Stop(ctx)
388+
389+
// Test that with allow_unsafe_internals = false:
390+
// - Supported tables (zones) are allowed
391+
// - Unsupported tables (gossip_alerts) are denied
392+
393+
t.Run("supported table allowed", func(t *testing.T) {
394+
conn := s.SQLConn(t)
395+
_, err := conn.Exec("SET allow_unsafe_internals = false")
396+
require.NoError(t, err)
397+
398+
// Supported crdb_internal tables should be allowed even when allow_unsafe_internals = false
399+
_, err = conn.Query("SELECT * FROM crdb_internal.zones")
400+
require.NoError(t, err, "supported crdb_internal table (zones) should be accessible when allow_unsafe_internals = false")
401+
})
402+
403+
t.Run("unsupported table denied", func(t *testing.T) {
404+
conn := s.SQLConn(t)
405+
_, err := conn.Exec("SET allow_unsafe_internals = false")
406+
require.NoError(t, err)
407+
408+
// Unsupported crdb_internal tables should be denied when allow_unsafe_internals = false
409+
_, err = conn.Query("SELECT * FROM crdb_internal.gossip_alerts")
410+
checkUnsafeErr(t, err)
411+
})
412+
}

pkg/sql/crdb_internal.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -235,29 +235,29 @@ var crdbInternal = virtualSchema{
235235
validWithNoDatabaseContext: true,
236236
}
237237

238-
// SupportedVTables are the crdb_internal tables that are "supported" for real
238+
// SupportedCRDBInternal are the crdb_internal tables that are "supported" for real
239239
// customer use in production for legacy reasons. Avoid adding to this list if
240240
// possible and prefer to add new customer-facing tables that should be public
241241
// under the non-"internal" namespace of information_schema.
242-
var SupportedVTables = map[string]struct{}{
243-
`"".crdb_internal.cluster_contended_indexes`: {},
244-
`"".crdb_internal.cluster_contended_keys`: {},
245-
`"".crdb_internal.cluster_contended_tables`: {},
246-
`"".crdb_internal.cluster_contention_events`: {},
247-
`"".crdb_internal.cluster_locks`: {},
248-
`"".crdb_internal.cluster_queries`: {},
249-
`"".crdb_internal.cluster_sessions`: {},
250-
`"".crdb_internal.cluster_transactions`: {},
251-
`"".crdb_internal.index_usage_statistics`: {},
252-
`"".crdb_internal.statement_statistics`: {},
253-
`"".crdb_internal.transaction_contention_events`: {},
254-
`"".crdb_internal.transaction_statistics`: {},
255-
`"".crdb_internal.zones`: {},
242+
var SupportedCRDBInternalTables = map[string]struct{}{
243+
`cluster_contended_indexes`: {},
244+
`cluster_contended_keys`: {},
245+
`cluster_contended_tables`: {},
246+
`cluster_contention_events`: {},
247+
`cluster_locks`: {},
248+
`cluster_queries`: {},
249+
`cluster_sessions`: {},
250+
`cluster_transactions`: {},
251+
`index_usage_statistics`: {},
252+
`statement_statistics`: {},
253+
`transaction_contention_events`: {},
254+
`transaction_statistics`: {},
255+
`zones`: {},
256256
}
257257

258258
// Note that this map is currently unused but serves to document which vtables
259259
// are expected to be used in production setting.
260-
var _ = SupportedVTables
260+
var _ = SupportedCRDBInternalTables
261261

262262
var crdbInternalBuildInfoTable = virtualSchemaTable{
263263
comment: `detailed identification strings (RAM, local node only)`,

0 commit comments

Comments
 (0)