Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace secret refs #2863

Merged
merged 17 commits into from
Aug 10, 2023
Merged

replace secret refs #2863

merged 17 commits into from
Aug 10, 2023

Conversation

juliaElastic
Copy link
Contributor

@juliaElastic juliaElastic commented Aug 7, 2023

What is the problem this PR solves?

Agent policy secrets are sent to agents in agent policy.

How does this PR solve the problem?

On checkin, read secret values of secret references in the agent policy, and replace occurrences in input and stream variables.

How to test this PR locally

  • Start local es, kibana, fleet server
  • Enable secrets feature flag in kibana.dev.yml: xpack.fleet.enableExperimental: ['secretsStorage']
  • Start local registry with secret package here: [Fleet] View & Edit Secret Variables kibana#157176
  • Open kibana, add integration: secret package, add values for package, input and stream secret vars
  • When the agent policy with the secret integration is created, the agent policy should have 3 secret references
  • Enroll an agent locally
  • Download diagnostics and check in computed-config.yml that the inputs contain the secret values, not references
  • Note: the agent is unhealthy with the error message input not supported is not related to secrets, looks like a validation changed on agent side, so that the test package doesn't have a valid input type. Can be ignored for now.

Example:

inputs:
- data_stream:
    namespace: default
  id: test_input-secrets-7c6b08d8-dbe5-4278-8929-dfb26ea8e579
  input_var_secret: input
  meta:
    package:
      name: secrets
      version: 1.0.0
  name: secrets-1
  package_policy_id: 7c6b08d8-dbe5-4278-8929-dfb26ea8e579
  package_var_secret: pkg
  policy:
    revision: 5
  revision: 1
  streams:
  - config.version: "2"
    data_stream:
      dataset: secrets.log
      type: logs
    id: test_input-secrets.log-7c6b08d8-dbe5-4278-8929-dfb26ea8e579
    input_var_secret: input
    package_var_secret: pkg
    stream_var_secret: stream
  type: test_input

Design Checklist

  • I have ensured my design is stateless and will work when multiple fleet-server instances are behind a load balancer.
  • I have or intend to scale test my changes, ensuring it will work reliably with 100K+ agents connected.
  • I have included fail safe mechanisms to limit the load on fleet-server: rate limiting, circuit breakers, caching, load shedding, etc.

Checklist

  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have made corresponding change to the default configuration files
  • I have added tests that prove my fix is effective or that my feature works
  • I have added an entry in ./changelog/fragments using the changelog tool

Related issues

@juliaElastic juliaElastic added the enhancement New feature or request label Aug 7, 2023
@juliaElastic juliaElastic self-assigned this Aug 7, 2023
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return result, nil
}

func getPolicyInputsWithSecrets(inputsRaw json.RawMessage, secretReferences map[string]string) ([]map[string]interface{}, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to define a type in Go where some fields are known (streams) and some are dynamic (e.g. package_var_secret)?
Example inputs and secret refs:

 "inputs": [
              {
                "id": "test_input-secrets-7c6b08d8-dbe5-4278-8929-dfb26ea8e579",
                "revision": 1,
                "name": "secrets-1",
                "type": "test_input",
                "data_stream": {
                  "namespace": "default"
                },
                "use_output": "default",
                "package_policy_id": "7c6b08d8-dbe5-4278-8929-dfb26ea8e579",
                "package_var_secret": "$co.elastic.secret{2LnzzokBsuDBJ5hx_Zo_}",
                "input_var_secret": "$co.elastic.secret{2bnzzokBsuDBJ5hx_Zo_}",
                "streams": [
                  {
                    "id": "test_input-secrets.log-7c6b08d8-dbe5-4278-8929-dfb26ea8e579",
                    "data_stream": {
                      "type": "logs",
                      "dataset": "secrets.log"
                    },
                    "config.version": "2",
                    "package_var_secret": "$co.elastic.secret{2LnzzokBsuDBJ5hx_Zo_}",
                    "input_var_secret": "$co.elastic.secret{2bnzzokBsuDBJ5hx_Zo_}",
                    "stream_var_secret": "$co.elastic.secret{2rnzzokBsuDBJ5hx_Zo_}"
                  }
                ],
                "meta": {
                  "package": {
                    "name": "secrets",
                    "version": "1.0.0"
                  }
                }
              }
            ],
            "secret_references": [
              {
                "id": "2LnzzokBsuDBJ5hx_Zo_"
              },
              {
                "id": "2bnzzokBsuDBJ5hx_Zo_"
              },
              {
                "id": "2rnzzokBsuDBJ5hx_Zo_"
              }
            ],

Example of replaced values:

 "inputs": [
  {
    "data_stream": {
      "namespace": "default"
    },
    "id": "test_input-secrets-7c6b08d8-dbe5-4278-8929-dfb26ea8e579",
    "input_var_secret": "input",
    "meta": {
      "package": {
        "name": "secrets",
        "version": "1.0.0"
      }
    },
    "name": "secrets-1",
    "package_policy_id": "7c6b08d8-dbe5-4278-8929-dfb26ea8e579",
    "package_var_secret": "pkg",
    "revision": 1,
    "streams": [
      {
        "config.version": "2",
        "data_stream": {
          "dataset": "secrets.log",
          "type": "logs"
        },
        "id": "test_input-secrets.log-7c6b08d8-dbe5-4278-8929-dfb26ea8e579",
        "input_var_secret": "input",
        "package_var_secret": "pkg",
        "stream_var_secret": "stream"
      }
    ],
    "type": "test_input",
    "use_output": "default"
  }
]

@elasticmachine
Copy link
Contributor

elasticmachine commented Aug 7, 2023

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview preview

Expand to view the summary

Build stats

  • Start Time: 2023-08-10T11:48:29.527+0000

  • Duration: 37 min 5 sec

Test stats 🧪

Test Results
Failed 0
Passed 751
Skipped 1
Total 752

💚 Flaky test report

Tests succeeded.

🤖 GitHub comments

Expand to view the GitHub comments

To re-run your PR in the CI, just comment with:

  • /test : Re-trigger the build.

@juliaElastic juliaElastic marked this pull request as ready for review August 8, 2023 11:03
@juliaElastic juliaElastic requested a review from a team as a code owner August 8, 2023 11:03
@juliaElastic
Copy link
Contributor Author

juliaElastic commented Aug 8, 2023

I'm planning to add an integration or e2e test for this, though not sure how to create a secret as it requires privileges that only kibana_system user has. I would try to create a kibana user, unless someone has a better idea.

internal/pkg/policy/secret.go Outdated Show resolved Hide resolved
if len(policies) == 0 {
return nil
}

latest := m.groupByLatest(policies)
for _, policy := range latest {
pp, err := NewParsedPolicy(policy)
pp, err := NewParsedPolicy(ctx, m.bulker, policy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah we get policies through the monitor - so I think this will be shared with all agents current subscribed to checkin. That should minimize the amount of re-fetching we have to do here.

internal/pkg/policy/secret.go Outdated Show resolved Hide resolved
internal/pkg/policy/secret.go Outdated Show resolved Hide resolved
internal/pkg/policy/secret.go Outdated Show resolved Hide resolved
internal/pkg/policy/secret.go Outdated Show resolved Hide resolved
@juliaElastic juliaElastic requested a review from joshdover August 9, 2023 15:23
Copy link
Contributor

@joshdover joshdover left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks for the e2e test!

@juliaElastic
Copy link
Contributor Author

I'm seeing some flakyness with integration tests using fake checkin request:

FAIL: TestServerInstrumentation (6.16s) | 2s
-- | --
  | === RUN   Test_SmokeTest_Agent_Calls
  | fleet_integration_test.go:598: Enroll an agent
  | fleet_integration_test.go:608: Agent enrollment successful, verify body
  | fleet_integration_test.go:633: Fake a checkin for agent d56ff08b-bad3-452d-8bb5-f012c5e90127
  | fleet_integration_test.go:640:
  | Error Trace:	/opt/buildkite-agent/builds/bk-agent-prod-gcp-1691594516224871631/elastic/fleet-server/internal/pkg/server/fleet_integration_test.go:640
  | Error:      	Received unexpected error:
  | Post "http://localhost:46833/api/fleet/agents/d56ff08b-bad3-452d-8bb5-f012c5e90127/checkin": EOF


@juliaElastic
Copy link
Contributor Author

juliaElastic commented Aug 10, 2023

The integration tests all pass now, but there is still an issue that I don't know how to fix:

--- PASS: TestCheckCompatibilityInternal/supported_elasticsearch_715-800a1 (0.00s)
--
  | PASS
  | ok  	github.com/elastic/fleet-server/v7/internal/pkg/ver	0.062s
  | ?   	github.com/elastic/fleet-server/v7/pkg/api	[no test files]
  | ?   	github.com/elastic/fleet-server/v7/pkg/api/versions/2022_06_01/api	[no test files]
  | ?   	github.com/elastic/fleet-server/v7/version	[no test files]
  | FAIL
  | make[1]: *** [Makefile:328: test-int-set] Error 1
  | make[1]: Leaving directory '/opt/buildkite-agent/builds/bk-agent-prod-gcp-1691664493893470492/elastic/fleet-server'
  | make: *** [Makefile:317: test-int] Error 2
  | 🚨 Error: The command exited with status 2
  | user command error: exit status 2

Line 328 is this:

ELASTICSEARCH_SERVICE_TOKEN=$(shell ./dev-tools/integration/get-elasticsearch-servicetoken.sh ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD}@${TEST_ELASTICSEARCH_HOSTS}) \

Trying to revert the last 3 commits as I saw a passing built at that point.

@joshdover
Copy link
Contributor

Did the last commit fix? Seems like it should. Otherwise I can dig in to help

@juliaElastic
Copy link
Contributor Author

juliaElastic commented Aug 10, 2023

Did the last commit fix? Seems like it should. Otherwise I can dig in to help

I had a passing build before this commit, which excludes some unit tests from the integration tests run, not sure if this is flakyness, or something breaks when I exclude those.

I think I got it, it seems that an integration test referenced a variable from a unit test and it was missing when I excluded the unit test.

# github.com/elastic/fleet-server/v7/internal/pkg/policy [github.com/elastic/fleet-server/v7/internal/pkg/policy.test]
--
  | internal/pkg/policy/policy_output_integration_test.go:146:10: undefined: TestPayload
  | FAIL	github.com/elastic/fleet-server/v7/internal/pkg/policy [build failed]

// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

//go:build !integration
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand though we we have to exclude unit tests like this explicitly, as integration tests are tagged also.
Are we missing something?
the Makefile has this command to tigger integration tests:
go test -v -tags=integration -count=1 -race -p 1 ./...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that the idea is to don't run unit tests when running integration tests, so the same tests are not executed at two different stages of testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I mean shouldn't it be enough to mark integration tests with //go:build integration? I think it's easy to forget when adding new unit tests to add the negated condition, and I'm trying to understand why it is needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Files without any build tag are always included, so -tags=integration wouldn't discard unit test files if they don't have the negated tag. So we need !integration for test files that we don't want to execute during integration tests.

I agree that this is easy to forget. Tough I wouldn't see as a great problem to execute some fast unit tests during integration test execution.

If we strongly want to have their execution separated, a possible strategy to don't forget it is to have only unit tests in packages, and move the integration tests to their own package/s, in a similar fashion to what we do for e2e tests. This also help to validate interfaces.
Or we could have some check in CI to ensure that all test files have tags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
4 participants