diff --git a/lib/config/configuration.go b/lib/config/configuration.go index 34c42d01f4562..8bd7680e3520a 100644 --- a/lib/config/configuration.go +++ b/lib/config/configuration.go @@ -1139,6 +1139,16 @@ func applyDatabasesConfig(fc *FileConfig, cfg *service.Config) error { Tags: matcher.Tags, }) } + for _, matcher := range fc.Databases.AzureMatchers { + cfg.Databases.AzureMatchers = append(cfg.Databases.AzureMatchers, + services.AzureMatcher{ + Subscriptions: matcher.Subscriptions, + ResourceGroups: matcher.ResourceGroups, + Types: matcher.Types, + Regions: matcher.Regions, + ResourceTags: matcher.ResourceTags, + }) + } for _, database := range fc.Databases.Databases { staticLabels := make(map[string]string) if database.StaticLabels != nil { diff --git a/lib/config/configuration_test.go b/lib/config/configuration_test.go index ffb800a8bb505..dc5b8805fb0b1 100644 --- a/lib/config/configuration_test.go +++ b/lib/config/configuration_test.go @@ -290,7 +290,7 @@ func TestConfigReading(t *testing.T) { require.True(t, conf.SSH.Enabled()) require.False(t, conf.Kube.Enabled()) - // static config + // good config conf, err = ReadFromFile(testConfigs.configFile) require.NoError(t, err) require.Empty(t, cmp.Diff(conf, &FileConfig{ @@ -437,6 +437,35 @@ func TestConfigReading(t *testing.T) { }, }, }, + AzureMatchers: []AzureMatcher{ + { + Subscriptions: []string{"sub1", "sub2"}, + ResourceGroups: []string{"rg1", "rg2"}, + Types: []string{"mysql"}, + Regions: []string{"eastus", "westus"}, + ResourceTags: map[string]apiutils.Strings{ + "a": {"b"}, + }, + }, + { + Subscriptions: []string{"sub3", "sub4"}, + ResourceGroups: []string{"rg3", "rg4"}, + Types: []string{"postgres"}, + Regions: []string{"centralus"}, + ResourceTags: map[string]apiutils.Strings{ + "c": {"d"}, + }, + }, + { + Subscriptions: nil, + ResourceGroups: nil, + Types: []string{"mysql", "postgres"}, + Regions: []string{"centralus"}, + ResourceTags: map[string]apiutils.Strings{ + "e": {"f"}, + }, + }, + }, }, Metrics: Metrics{ Service: Service{ @@ -783,6 +812,29 @@ SREzU8onbBsjMg9QDiSf5oJLKvd/Ren+zGY7 require.Equal(t, 1, *cfg.Auth.KeyStore.SlotNumber) require.Equal(t, "example_pin", cfg.Auth.KeyStore.Pin) require.ElementsMatch(t, []string{"ca-pin-from-string", "ca-pin-from-file1", "ca-pin-from-file2"}, cfg.CAPins) + + require.True(t, cfg.Databases.Enabled) + require.Empty(t, cmp.Diff(cfg.Databases.AzureMatchers, + []services.AzureMatcher{ + { + Subscriptions: []string{"sub1", "sub2"}, + ResourceGroups: []string{"group1", "group2"}, + Types: []string{"postgres", "mysql"}, + Regions: []string{"eastus", "centralus"}, + ResourceTags: map[string]apiutils.Strings{ + "a": {"b"}, + }, + }, + { + Subscriptions: nil, + ResourceGroups: nil, + Types: []string{"postgres", "mysql"}, + Regions: []string{"westus"}, + ResourceTags: map[string]apiutils.Strings{ + "c": {"d"}, + }, + }, + })) } // TestApplyConfigNoneEnabled makes sure that if a section is not enabled, @@ -1362,6 +1414,33 @@ func makeConfigFixture() string { Tags: map[string]apiutils.Strings{"c": {"d"}}, }, } + conf.Databases.AzureMatchers = []AzureMatcher{ + { + Subscriptions: []string{"sub1", "sub2"}, + ResourceGroups: []string{"rg1", "rg2"}, + Types: []string{"mysql"}, + Regions: []string{"eastus", "westus"}, + ResourceTags: map[string]apiutils.Strings{ + "a": {"b"}, + }, + }, + { + Subscriptions: []string{"sub3", "sub4"}, + ResourceGroups: []string{"rg3", "rg4"}, + Types: []string{"postgres"}, + Regions: []string{"centralus"}, + ResourceTags: map[string]apiutils.Strings{ + "c": {"d"}, + }, + }, + { + Types: []string{"mysql", "postgres"}, + Regions: []string{"centralus"}, + ResourceTags: map[string]apiutils.Strings{ + "e": {"f"}, + }, + }, + } // Metrics service. conf.Metrics.EnabledFlag = "yes" @@ -2020,6 +2099,12 @@ db_service: regions: ["us-east-1", "us-west-1"] tags: '*': '*' + azure: + - subscriptions: ["foo", "bar"] + types: ["mysql", "postgres"] + regions: ["eastus", "westus"] + tags: + '*': '*' databases: - name: foo protocol: postgres diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index 6aa4078e75da1..0b283e0b23f06 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -1197,6 +1197,8 @@ type Databases struct { ResourceMatchers []ResourceMatcher `yaml:"resources,omitempty"` // AWSMatchers match AWS hosted databases. AWSMatchers []AWSMatcher `yaml:"aws,omitempty"` + // AzureMatchers match Azure hosted databases. + AzureMatchers []AzureMatcher `yaml:"azure,omitempty"` } // ResourceMatcher matches cluster resources. @@ -1240,6 +1242,20 @@ type AWSSSM struct { DocumentName string `yaml:"document_name,omitempty"` } +// AzureMatcher matches Azure databases. +type AzureMatcher struct { + // Subscriptions are Azure subscriptions to query for resources. + Subscriptions []string `yaml:"subscriptions,omitempty"` + // ResourceGroups are Azure resource groups to query for resources. + ResourceGroups []string `yaml:"resource_groups,omitempty"` + // Types are Azure database types to match: "mysql", "postgres" + Types []string `yaml:"types,omitempty"` + // Regions are Azure locations to match for databases. + Regions []string `yaml:"regions,omitempty"` + // ResourceTags are Azure tags on resources to match. + ResourceTags map[string]apiutils.Strings `yaml:"tags,omitempty"` +} + // Database represents a single database proxied by the service. type Database struct { // Name is the name for the database proxy service. diff --git a/lib/config/testdata_test.go b/lib/config/testdata_test.go index dd01991a2d386..c4e22e8466532 100644 --- a/lib/config/testdata_test.go +++ b/lib/config/testdata_test.go @@ -164,6 +164,23 @@ proxy_service: mysql_public_addr: mysql.example:3306 mongo_listen_addr: webhost:27017 mongo_public_addr: mongo.example:27017 + +db_service: + enabled: yes + resources: + - labels: + "*": "*" + azure: + - subscriptions: ["sub1", "sub2"] + resource_groups: ["group1", "group2"] + types: ["postgres", "mysql"] + regions: ["eastus", "centralus"] + tags: + "a": "b" + - types: ["postgres", "mysql"] + regions: ["westus"] + tags: + "c": "d" ` // NoServicesConfigString is a configuration file with no services enabled diff --git a/lib/service/cfg.go b/lib/service/cfg.go index 075adbb539c61..d3acc4bc0551b 100644 --- a/lib/service/cfg.go +++ b/lib/service/cfg.go @@ -699,6 +699,8 @@ type DatabasesConfig struct { ResourceMatchers []services.ResourceMatcher // AWSMatchers match AWS hosted databases. AWSMatchers []services.AWSMatcher + // AzureMatchers match Azure hosted databases. + AzureMatchers []services.AzureMatcher // Limiter limits the connection and request rates. Limiter limiter.Config } diff --git a/lib/service/db.go b/lib/service/db.go index b008e9be251a5..dc99118e91180 100644 --- a/lib/service/db.go +++ b/lib/service/db.go @@ -34,7 +34,8 @@ func (process *TeleportProcess) shouldInitDatabases() bool { databasesCfg := len(process.Config.Databases.Databases) > 0 resourceMatchersCfg := len(process.Config.Databases.ResourceMatchers) > 0 awsMatchersCfg := len(process.Config.Databases.AWSMatchers) > 0 - anyCfg := databasesCfg || resourceMatchersCfg || awsMatchersCfg + azureMatchersCfg := len(process.Config.Databases.AzureMatchers) > 0 + anyCfg := databasesCfg || resourceMatchersCfg || awsMatchersCfg || azureMatchersCfg return process.Config.Databases.Enabled && anyCfg } @@ -215,6 +216,7 @@ func (process *TeleportProcess) initDatabaseService() (retErr error) { CloudLabels: process.cloudLabels, ResourceMatchers: process.Config.Databases.ResourceMatchers, AWSMatchers: process.Config.Databases.AWSMatchers, + AzureMatchers: process.Config.Databases.AzureMatchers, OnHeartbeat: process.onHeartbeat(teleport.ComponentDatabase), LockWatcher: lockWatcher, ConnectedProxyGetter: proxyGetter, diff --git a/lib/services/matchers.go b/lib/services/matchers.go index 4fe819f253576..17434bac109cd 100644 --- a/lib/services/matchers.go +++ b/lib/services/matchers.go @@ -59,6 +59,20 @@ type AWSMatcher struct { SSM *AWSSSM } +// AzureMatcher matches Azure databases. +type AzureMatcher struct { + // Subscriptions are Azure subscriptions to query for resources. + Subscriptions []string + // ResourceGroups are Azure resource groups to query for resources. + ResourceGroups []string + // Types are Azure resource types to match, for example "mysql" or "postgres". + Types []string + // Regions are Azure regions to query for databases. + Regions []string + // ResourceTags are Azure tags to match. + ResourceTags types.Labels +} + // MatchResourceLabels returns true if any of the provided selectors matches the provided database. func MatchResourceLabels(matchers []ResourceMatcher, resource types.ResourceWithLabels) bool { for _, matcher := range matchers { @@ -263,4 +277,8 @@ const ( AWSMatcherMemoryDB = "memorydb" // AWSMatcherEC2 is the AWS matcher type for EC2 instances. AWSMatcherEC2 = "ec2" + // AzureMatcherMySQL is the Azure matcher type for Azure MySQL databases. + AzureMatcherMySQL = "mysql" + // AzureMatcherPostgres is the Azure matcher type for Azure Postgres databases. + AzureMatcherPostgres = "postgres" ) diff --git a/lib/srv/db/server.go b/lib/srv/db/server.go index 3a8f32b3fb6e8..e17f1130463d1 100644 --- a/lib/srv/db/server.go +++ b/lib/srv/db/server.go @@ -89,6 +89,8 @@ type Config struct { ResourceMatchers []services.ResourceMatcher // AWSMatchers is a list of AWS databases matchers. AWSMatchers []services.AWSMatcher + // AzureMatchers is a list of Azure databases matchers. + AzureMatchers []services.AzureMatcher // Databases is a list of proxied databases from static configuration. Databases types.Databases // CloudLabels is a service that imports labels from a cloud provider. The labels are shared