-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #103751 from jaylim-crl/backport23.1-101864-103070…
…-103479-103487-103550-103625 release-23.1: ccl/sqlproxyccl: add CIDR Ranges and Private Endpoints ACL support
- Loading branch information
Showing
28 changed files
with
2,478 additions
and
510 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Licensed as a CockroachDB Enterprise file under the Cockroach Community | ||
// License (the "License"); you may not use this file except in compliance with | ||
// the License. You may obtain a copy of the License at | ||
// | ||
// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt | ||
|
||
package acl | ||
|
||
import ( | ||
"context" | ||
"net" | ||
|
||
"github.com/cockroachdb/errors" | ||
) | ||
|
||
// CIDRRanges represents the controller used to manage ACL rules for public | ||
// connections. It rejects connections if: | ||
// 1. the cluster does not allow public connections, or | ||
// 2. none of the allowed_cidr_ranges entries match the incoming connection's | ||
// IP. | ||
type CIDRRanges struct { | ||
LookupTenantFn lookupTenantFunc | ||
} | ||
|
||
var _ AccessController = &CIDRRanges{} | ||
|
||
// CheckConnection implements the AccessController interface. | ||
func (p *CIDRRanges) CheckConnection(ctx context.Context, conn ConnectionTags) error { | ||
tenantObj, err := p.LookupTenantFn(ctx, conn.TenantID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Private connections. This ACL is only responsible for public CIDR ranges. | ||
if conn.EndpointID != "" { | ||
return nil | ||
} | ||
|
||
// Cluster allows public connections, so we'll check allowed CIDR ranges. | ||
if tenantObj.AllowPublicConn() { | ||
ip := net.ParseIP(conn.IP) | ||
if ip == nil { | ||
return errors.Newf("could not parse IP address: '%s'", conn.IP) | ||
} | ||
for _, cidrRange := range tenantObj.AllowedCIDRRanges { | ||
// It is assumed that all public CIDR ranges are valid, so the | ||
// tenant directory server will have to enforce that. | ||
_, ipNetwork, err := net.ParseCIDR(cidrRange) | ||
if err != nil { | ||
return err | ||
} | ||
// A matching CIDR range was found. | ||
if ipNetwork.Contains(ip) { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
// By default, connections are rejected if the cluster does not allow public | ||
// connections, or if no ranges match the connection's IP. | ||
return errors.Newf( | ||
"connection to '%s' denied: cluster does not allow public connections from IP %s", | ||
conn.TenantID.String(), | ||
conn.IP, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Licensed as a CockroachDB Enterprise file under the Cockroach Community | ||
// License (the "License"); you may not use this file except in compliance with | ||
// the License. You may obtain a copy of the License at | ||
// | ||
// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt | ||
|
||
package acl_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/ccl/sqlproxyccl/acl" | ||
"github.com/cockroachdb/cockroach/pkg/ccl/sqlproxyccl/tenant" | ||
"github.com/cockroachdb/cockroach/pkg/roachpb" | ||
"github.com/cockroachdb/cockroach/pkg/util/leaktest" | ||
"github.com/cockroachdb/errors" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestCIDRRanges(t *testing.T) { | ||
defer leaktest.AfterTest(t)() | ||
ctx := context.Background() | ||
|
||
tenantID := roachpb.MustMakeTenantID(42) | ||
makeConn := func(endpoint string) acl.ConnectionTags { | ||
return acl.ConnectionTags{ | ||
IP: "127.0.0.1", | ||
TenantID: tenantID, | ||
EndpointID: endpoint, | ||
} | ||
} | ||
|
||
t.Run("lookup error", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return nil, errors.New("foo") | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, makeConn("")) | ||
require.EqualError(t, err, "foo") | ||
}) | ||
|
||
// Private connection should allow, despite not having any CIDR ranges. | ||
t.Run("private connection", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return &tenant.Tenant{ | ||
ConnectivityType: tenant.ALLOW_PUBLIC_ONLY, | ||
}, nil | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, makeConn("foo")) | ||
require.NoError(t, err) | ||
}) | ||
|
||
// CIDR ranges do not match. | ||
t.Run("bad public connection", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return &tenant.Tenant{ | ||
ConnectivityType: tenant.ALLOW_ALL, | ||
AllowedCIDRRanges: []string{"127.0.0.0/32", "10.0.0.8/16"}, | ||
}, nil | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, makeConn("")) | ||
require.EqualError(t, err, "connection to '42' denied: cluster does not allow public connections from IP 127.0.0.1") | ||
}) | ||
|
||
t.Run("default behavior if no entries", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return &tenant.Tenant{ | ||
ConnectivityType: tenant.ALLOW_ALL, | ||
AllowedCIDRRanges: []string{}, | ||
}, nil | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, makeConn("")) | ||
require.EqualError(t, err, "connection to '42' denied: cluster does not allow public connections from IP 127.0.0.1") | ||
}) | ||
|
||
t.Run("good public connection", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return &tenant.Tenant{ | ||
ConnectivityType: tenant.ALLOW_ALL, | ||
AllowedCIDRRanges: []string{"0.0.0.0/0"}, | ||
}, nil | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, makeConn("")) | ||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("disallow public connections", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return &tenant.Tenant{ | ||
ConnectivityType: tenant.ALLOW_PRIVATE_ONLY, | ||
AllowedCIDRRanges: []string{"127.0.0.1/32"}, | ||
}, nil | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, makeConn("")) | ||
require.EqualError(t, err, "connection to '42' denied: cluster does not allow public connections from IP 127.0.0.1") | ||
}) | ||
|
||
t.Run("could not parse connection IP", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return &tenant.Tenant{ | ||
ConnectivityType: tenant.ALLOW_ALL, | ||
AllowedCIDRRanges: []string{"127.0.0.1/32"}, | ||
}, nil | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, acl.ConnectionTags{ | ||
IP: "invalid-value", | ||
TenantID: tenantID, | ||
}) | ||
require.EqualError(t, err, "could not parse IP address: 'invalid-value'") | ||
}) | ||
|
||
t.Run("could not parse CIDR range", func(t *testing.T) { | ||
p := &acl.CIDRRanges{ | ||
LookupTenantFn: func(ctx context.Context, tenantID roachpb.TenantID) (*tenant.Tenant, error) { | ||
return &tenant.Tenant{ | ||
ConnectivityType: tenant.ALLOW_ALL, | ||
AllowedCIDRRanges: []string{"127.0.0.1"}, | ||
}, nil | ||
}, | ||
} | ||
err := p.CheckConnection(ctx, makeConn("")) | ||
require.EqualError(t, err, "invalid CIDR address: 127.0.0.1") | ||
}) | ||
} |
Oops, something went wrong.