diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 5a422296f2..b193cda1a3 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -222,8 +222,6 @@ type AgentFilters struct { ServiceID string // Return Agents with provided type. AgentType *AgentType - // Return only Agents that provide insights for that AWSAccessKey. - AWSAccessKey string // IgnoreNomad is used to ignore Nomad agents. IgnoreNomad bool // Disabled indicates whether to filter by disabled status. @@ -267,11 +265,6 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { args = append(args, *filters.AgentType) idx++ } - if filters.AWSAccessKey != "" { - conditions = append(conditions, fmt.Sprintf("(aws_options ? 'aws_access_key' AND aws_options->>'aws_access_key' = %s)", q.Placeholder(idx))) - args = append(args, filters.AWSAccessKey) - idx++ - } if filters.IgnoreNomad { conditions = append(conditions, "agent_type != "+q.Placeholder(idx)) args = append(args, NomadAgentType) diff --git a/managed/services/agents/roster.go b/managed/services/agents/roster.go index 0ed620951b..53c8794a3b 100644 --- a/managed/services/agents/roster.go +++ b/managed/services/agents/roster.go @@ -85,14 +85,19 @@ func (r *roster) get(groupID string) (string, []string, error) { agentIDs = []string{PMMAgentID} } else { awsAccessKey := strings.TrimPrefix(parts[1], rdsPrefix) - filters := models.AgentFilters{PMMAgentID: PMMAgentID, AgentType: new(models.RDSExporterType), AWSAccessKey: awsAccessKey} + // aws_access_key is encrypted at rest, and FindAgents decrypts rows only + // after the WHERE clause, so a SQL filter on the access key never matches. + // Match it in Go on the decrypted value instead. + filters := models.AgentFilters{PMMAgentID: PMMAgentID, AgentType: new(models.RDSExporterType)} agents, err := models.FindAgents(r.db.Querier, filters) if err != nil { return "", nil, err } agentIDs = make([]string, 0, len(agents)) for _, agent := range agents { - agentIDs = append(agentIDs, agent.AgentID) + if agent.AWSOptions.AWSAccessKey == awsAccessKey { + agentIDs = append(agentIDs, agent.AgentID) + } } } } diff --git a/managed/services/agents/roster_test.go b/managed/services/agents/roster_test.go index 7eda8a156b..7b158510fd 100644 --- a/managed/services/agents/roster_test.go +++ b/managed/services/agents/roster_test.go @@ -114,4 +114,40 @@ func TestRoster(t *testing.T) { assert.Equal(t, "pmm-server", PMMAgentID) assert.Equal(t, []string{}, agentIDs) }) + + // Regression test for the cold-cache DB fallback. aws_access_key is encrypted + // at rest, so resolving the group by an access key must work against the + // decrypted value. Seeding through the models API encrypts the key exactly as + // in production; get() runs on a cold roster (no prior add), exercising the + // fallback. This fails if get() filters the encrypted column in SQL and passes + // once the match is done in Go. + t.Run("GetFromDBEncryptedAccessKey", func(t *testing.T) { + r, teardown := setup(t) + defer teardown(t) + + const awsAccessKey = "test-access-key" + + node, err := models.CreateNode(r.db.Querier, models.RemoteRDSNodeType, &models.CreateNodeParams{ + NodeName: "test-rds-roster-node", + Address: "rds-roster-test.xyzzy.us-east-1.rds.amazonaws.com", + Region: new("us-east-1"), + AZ: "us-east-1a", + InstanceID: "rds-roster-test", + }) + require.NoError(t, err) + + agent, err := models.CreateAgent(r.db.Querier, models.RDSExporterType, &models.CreateAgentParams{ + PMMAgentID: models.PMMServerAgentID, + NodeID: node.NodeID, + AWSOptions: models.AWSOptions{AWSAccessKey: awsAccessKey}, + }) + require.NoError(t, err) + + // Cold roster: no prior add(), so get() resolves members from the DB. + groupID := models.PMMServerAgentID + ":" + rdsPrefix + awsAccessKey + PMMAgentID, agentIDs, err := r.get(groupID) + require.NoError(t, err) + assert.Equal(t, models.PMMServerAgentID, PMMAgentID) + assert.Equal(t, []string{agent.AgentID}, agentIDs) + }) }