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

[Fleet] Prevent agents from enrolling in a managed policy #90458

Merged

Conversation

jfsiii
Copy link
Contributor

@jfsiii jfsiii commented Feb 5, 2021

Summary

Add guard to /agents/enroll API preventing agents from enrolling in managed policies

closes #90435

  • No Agents can be enrolled into this policy by the user.
  • The install & enroll commands should print an error to the console if the enroll command fails (due to being a managed policy or any other reason)

So how do you associate an agent with a managed policy?

Enroll in an unmanaged policy then set that policy to managed.

We don't restrict the agent policy, only what other things (agents, integrations) can do if they're associated with a managed policy.

A force flag has been mentioned for some other actions. It might work here as well, but I'd like to handle discussion & implementation of those later.

Manual testing

Prevent enroll for managed policies
  1. Created a managed agent policy
    curl --user elastic:changeme -X POST localhost:5601/api/fleet/agent_policies -H 'Content-Type: application/json' -d'{ "name": "User created MANAGED", "namespace": "default", "is_managed": true}' -H 'kbn-xsrf: true'
    {"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"User created MANAGED","namespace":"default","is_managed":true,"revision":1,"updated_at":"2021-02-05T16:36:01.931Z","updated_by":"elastic"}}
    
  2. Try install command show in the UI
    sudo ./elastic-agent install -f --kibana-url=http://localhost:5601 --enrollment-token=WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
    Password:
    The Elastic Agent is currently in BETA and should not be used in production
    
    Error: fail to enroll: fail to execute request to Kibana: Status code: 400, Kibana returned an error: Bad Request, message: Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a
    Error: enroll command failed with exit code: 1
    
  3. Observe Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a error
  4. Try enroll instead:
    sudo ./elastic-agent enroll http://localhost:5601 WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
    The Elastic Agent is currently in BETA and should not be used in production
    
    This will replace your current settings. Do you want to continue? [Y/n]:
    Error: fail to enroll: fail to execute request to Kibana: Status code: 400, Kibana returned an error: Bad Request, message: Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a
    
  5. Observe same Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a error
Enroll in unmanaged policy, then update it to managed

Agent policies are is_managed: false by default, or we can update the policy to is_managed: false, like:

curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": false, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":false,"revision":4,"updated_at":"2021-02-05T17:42:05.610Z","updated_by":"elastic","package_policies":[]}}

then enroll

 sudo ./elastic-agent install -f --kibana-url=http://localhost:5601 --enrollment-token=WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
The Elastic Agent is currently in BETA and should not be used in production

Successfully enrolled the Elastic Agent.
Installation was successful and Elastic Agent is running.

and set the policy back to managed

curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": true, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":true,"revision":5,"updated_at":"2021-02-05T17:44:18.757Z","updated_by":"elastic","package_policies":[]}}

with all the restrictions that entails (cannot unenroll, reassign, etc)

curl --user elastic:changeme -X PUT 'http://localhost:5601/api/fleet/agents/8169f0a0-67d9-11eb-80f2-73dd45e7318e/reassign'   -X 'PUT'  -H 'kbn-xsrf: abc'  -H 'Content-Type: application/json'   --data-raw '{"policy_id":"729f8440-67cf-11eb-b656-21ad68ebfa8a"}'   
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot reassign an agent from managed agent policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a"
}

Checklist

@jfsiii jfsiii added Feature:Fleet Fleet team's agent central management project Team:Fleet Team label for Observability Data Collection Fleet team v7.12.0 labels Feb 5, 2021
@jfsiii jfsiii self-assigned this Feb 5, 2021
@elasticmachine
Copy link
Contributor

Pinging @elastic/fleet (Team:Fleet)

@elasticmachine
Copy link
Contributor

Pinging @elastic/fleet (Feature:Fleet)

@jfsiii jfsiii added the release_note:skip Skip the PR/issue when compiling release notes label Feb 5, 2021
@nchaulet
Copy link
Member

nchaulet commented Feb 5, 2021

Maybe I am missing something but how agents are going to run a policy if they cannot enroll?

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 5, 2021

@nchaulet I had the same reaction for a while. That's why I didn't add it at first. What I'm doing now is setting the policy to managed after enrollment.

We don't restrict the agent policy, only what other things (agents, integrations) can do if they're associated with a managed policy.

I'll update the description to include this, but

Agent policies are is_managed: false by default, or we can update the policy from the description to is_managed: false, like:

curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": false, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":false,"revision":4,"updated_at":"2021-02-05T17:42:05.610Z","updated_by":"elastic","package_policies":[]}}

Then you can enroll it

 sudo ./elastic-agent install -f --kibana-url=http://localhost:5601 --enrollment-token=WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
The Elastic Agent is currently in BETA and should not be used in production

Successfully enrolled the Elastic Agent.
Installation was successful and Elastic Agent is running.

and set the policy back to managed

curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": true, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":true,"revision":5,"updated_at":"2021-02-05T17:44:18.757Z","updated_by":"elastic","package_policies":[]}}

with all the restrictions that entails (cannot unenroll, reassign, etc)

@nchaulet
Copy link
Member

nchaulet commented Feb 5, 2021

Oh okay, so managed = cannot perform any agent actions except updating the policy.

@nchaulet
Copy link
Member

nchaulet commented Feb 5, 2021

How hard is to had a functionnal test for that?

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 5, 2021

How hard is it to add a functional test for that?

Should be an issue, I did it for the others actions. I plan to circle back to it before I ship merge it. I mainly wanted to get confirmation on the behavior.

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 6, 2021

@elasticmachine merge upstream

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 6, 2021

@elasticmachine merge upstream

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 7, 2021

@nchaulet added a test in 054f19f

LMK you have ideas for more or different ways

Copy link
Member

@nchaulet nchaulet left a comment

Choose a reason for hiding this comment

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

🚀 thanks for adding the test

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 8, 2021

@elasticmachine merge upstream

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 8, 2021

@elasticmachine merge upstream

@jfsiii jfsiii added the v8.0.0 label Feb 8, 2021
@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 9, 2021

@elasticmachine merge upstream

@jfsiii
Copy link
Contributor Author

jfsiii commented Feb 9, 2021

@elasticmachine merge upstream

@kibanamachine
Copy link
Contributor

💛 Build succeeded, but was flaky


Test Failures

Kibana Pipeline / general / X-Pack API Integration Tests.x-pack/test/api_integration/apis/security_solution/uncommon_processes·ts.apis SecuritySolution Endpoints uncommon_processes should return a single data set with pagination of 1

Link to Jenkins

Standard Out

Failed Tests Reporter:
  - Test has failed 2 times on tracked branches: https://github.com/elastic/kibana/issues/90416

[00:00:00]       │
[00:00:00]         └-: apis
[00:00:00]           └-> "before all" hook in "apis"
[00:05:25]           └-: SecuritySolution Endpoints
[00:05:25]             └-> "before all" hook in "SecuritySolution Endpoints"
[00:06:33]             └-: uncommon_processes
[00:06:33]               └-> "before all" hook for "should return an edge of length 1 when given a pagination of length 1"
[00:06:33]               └-> "before all" hook for "should return an edge of length 1 when given a pagination of length 1"
[00:06:33]                 │ info [auditbeat/hosts] Loading "mappings.json"
[00:06:33]                 │ info [auditbeat/hosts] Loading "data.json.gz"
[00:06:33]                 │ info [o.e.c.m.MetadataCreateIndexService] [kibana-ci-immutable-centos-tests-xxl-1612868210805894513] [auditbeat-8.0.0-2019.02.19-000001] creating index, cause [api], templates [], shards [1]/[1]
[00:06:33]                 │ info [auditbeat/hosts] Created index "auditbeat-8.0.0-2019.02.19-000001"
[00:06:33]                 │ debg [auditbeat/hosts] "auditbeat-8.0.0-2019.02.19-000001" settings {"index":{"lifecycle":{"name":"auditbeat-8.0.0","rollover_alias":"auditbeat-8.0.0"},"mapping":{"total_fields":{"limit":"10000"}},"number_of_replicas":"1","number_of_shards":"1","query":{"default_field":["tags","message","agent.version","agent.name","agent.type","agent.id","agent.ephemeral_id","client.address","client.mac","client.domain","client.geo.continent_name","client.geo.country_name","client.geo.region_name","client.geo.city_name","client.geo.country_iso_code","client.geo.region_iso_code","client.geo.name","cloud.provider","cloud.availability_zone","cloud.region","cloud.instance.id","cloud.instance.name","cloud.machine.type","cloud.account.id","container.runtime","container.id","container.image.name","container.image.tag","container.name","destination.address","destination.mac","destination.domain","destination.geo.continent_name","destination.geo.country_name","destination.geo.region_name","destination.geo.city_name","destination.geo.country_iso_code","destination.geo.region_iso_code","destination.geo.name","ecs.version","error.id","error.message","error.code","event.id","event.kind","event.category","event.action","event.outcome","event.type","event.module","event.dataset","event.hash","event.timezone","file.path","file.target_path","file.extension","file.type","file.device","file.inode","file.uid","file.owner","file.gid","file.group","file.mode","group.id","group.name","host.hostname","host.name","host.id","host.mac","host.type","host.architecture","host.os.platform","host.os.name","host.os.full","host.os.family","host.os.version","host.os.kernel","host.geo.continent_name","host.geo.country_name","host.geo.region_name","host.geo.city_name","host.geo.country_iso_code","host.geo.region_iso_code","host.geo.name","http.request.method","http.request.body.content","http.request.referrer","http.response.body.content","http.version","log.level","network.name","network.type","network.iana_number","network.transport","network.application","network.protocol","network.direction","network.community_id","observer.mac","observer.hostname","observer.vendor","observer.version","observer.serial_number","observer.type","observer.os.platform","observer.os.name","observer.os.full","observer.os.family","observer.os.version","observer.os.kernel","observer.geo.continent_name","observer.geo.country_name","observer.geo.region_name","observer.geo.city_name","observer.geo.country_iso_code","observer.geo.region_iso_code","observer.geo.name","organization.name","organization.id","os.platform","os.name","os.full","os.family","os.version","os.kernel","process.name","process.args","process.executable","process.title","process.working_directory","server.address","server.mac","server.domain","server.geo.continent_name","server.geo.country_name","server.geo.region_name","server.geo.city_name","server.geo.country_iso_code","server.geo.region_iso_code","server.geo.name","service.id","service.name","service.type","service.state","service.version","service.ephemeral_id","source.address","source.mac","source.domain","source.geo.continent_name","source.geo.country_name","source.geo.region_name","source.geo.city_name","source.geo.country_iso_code","source.geo.region_iso_code","source.geo.name","url.original","url.full","url.scheme","url.domain","url.path","url.query","url.fragment","url.username","url.password","user.id","user.name","user.full_name","user.email","user.hash","user.group.id","user.group.name","user_agent.original","user_agent.name","user_agent.version","user_agent.device.name","user_agent.os.platform","user_agent.os.name","user_agent.os.full","user_agent.os.family","user_agent.os.version","user_agent.os.kernel","agent.hostname","error.type","cloud.project.id","kubernetes.pod.name","kubernetes.pod.uid","kubernetes.namespace","kubernetes.node.name","kubernetes.container.name","kubernetes.container.image","file.origin","raw","file.selinux.user","file.selinux.role","file.selinux.domain","file.selinux.level","user.audit.id","user.audit.name","user.effective.id","user.effective.name","user.effective.group.id","user.effective.group.name","user.filesystem.id","user.filesystem.name","user.filesystem.group.id","user.filesystem.group.name","user.saved.id","user.saved.name","user.saved.group.id","user.saved.group.name","user.selinux.user","user.selinux.role","user.selinux.domain","user.selinux.level","user.selinux.category","source.path","destination.path","auditd.message_type","auditd.session","auditd.result","auditd.summary.actor.primary","auditd.summary.actor.secondary","auditd.summary.object.type","auditd.summary.object.primary","auditd.summary.object.secondary","auditd.summary.how","auditd.paths.inode","auditd.paths.dev","auditd.paths.obj_user","auditd.paths.obj_role","auditd.paths.obj_domain","auditd.paths.obj_level","auditd.paths.objtype","auditd.paths.ouid","auditd.paths.rdev","auditd.paths.nametype","auditd.paths.ogid","auditd.paths.item","auditd.paths.mode","auditd.paths.name","auditd.data.action","auditd.data.minor","auditd.data.acct","auditd.data.addr","auditd.data.cipher","auditd.data.id","auditd.data.entries","auditd.data.kind","auditd.data.ksize","auditd.data.spid","auditd.data.arch","auditd.data.argc","auditd.data.major","auditd.data.unit","auditd.data.table","auditd.data.terminal","auditd.data.grantors","auditd.data.direction","auditd.data.op","auditd.data.tty","auditd.data.syscall","auditd.data.data","auditd.data.family","auditd.data.mac","auditd.data.pfs","auditd.data.items","auditd.data.a0","auditd.data.a1","auditd.data.a2","auditd.data.a3","auditd.data.hostname","auditd.data.lport","auditd.data.rport","auditd.data.exit","auditd.data.fp","auditd.data.laddr","auditd.data.sport","auditd.data.capability","auditd.data.nargs","auditd.data.new-enabled","auditd.data.audit_backlog_limit","auditd.data.dir","auditd.data.cap_pe","auditd.data.model","auditd.data.new_pp","auditd.data.old-enabled","auditd.data.oauid","auditd.data.old","auditd.data.banners","auditd.data.feature","auditd.data.vm-ctx","auditd.data.opid","auditd.data.seperms","auditd.data.seresult","auditd.data.new-rng","auditd.data.old-net","auditd.data.sigev_signo","auditd.data.ino","auditd.data.old_enforcing","auditd.data.old-vcpu","auditd.data.range","auditd.data.res","auditd.data.added","auditd.data.fam","auditd.data.nlnk-pid","auditd.data.subj","auditd.data.a[0-3]","auditd.data.cgroup","auditd.data.kernel","auditd.data.ocomm","auditd.data.new-net","auditd.data.permissive","auditd.data.class","auditd.data.compat","auditd.data.fi","auditd.data.changed","auditd.data.msg","auditd.data.dport","auditd.data.new-seuser","auditd.data.invalid_context","auditd.data.dmac","auditd.data.ipx-net","auditd.data.iuid","auditd.data.macproto","auditd.data.obj","auditd.data.ipid","auditd.data.new-fs","auditd.data.vm-pid","auditd.data.cap_pi","auditd.data.old-auid","auditd.data.oses","auditd.data.fd","auditd.data.igid","auditd.data.new-disk","auditd.data.parent","auditd.data.len","auditd.data.oflag","auditd.data.uuid","auditd.data.code","auditd.data.nlnk-grp","auditd.data.cap_fp","auditd.data.new-mem","auditd.data.seperm","auditd.data.enforcing","auditd.data.new-chardev","auditd.data.old-rng","auditd.data.outif","auditd.data.cmd","auditd.data.hook","auditd.data.new-level","auditd.data.sauid","auditd.data.sig","auditd.data.audit_backlog_wait_time","auditd.data.printer","auditd.data.old-mem","auditd.data.perm","auditd.data.old_pi","auditd.data.state","auditd.data.format","auditd.data.new_gid","auditd.data.tcontext","auditd.data.maj","auditd.data.watch","auditd.data.device","auditd.data.grp","auditd.data.bool","auditd.data.icmp_type","auditd.data.new_lock","auditd.data.old_prom","auditd.data.acl","auditd.data.ip","auditd.data.new_pi","auditd.data.default-context","auditd.data.inode_gid","auditd.data.new-log_passwd","auditd.data.new_pe","auditd.data.selected-context","auditd.data.cap_fver","auditd.data.file","auditd.data.net","auditd.data.virt","auditd.data.cap_pp","auditd.data.old-range","auditd.data.resrc","auditd.data.new-range","auditd.data.obj_gid","auditd.data.proto","auditd.data.old-disk","auditd.data.audit_failure","auditd.data.inif","auditd.data.vm","auditd.data.flags","auditd.data.nlnk-fam","auditd.data.old-fs","auditd.data.old-ses","auditd.data.seqno","auditd.data.fver","auditd.data.qbytes","auditd.data.seuser","auditd.data.cap_fe","auditd.data.new-vcpu","auditd.data.old-level","auditd.data.old_pp","auditd.data.daddr","auditd.data.old-role","auditd.data.ioctlcmd","auditd.data.smac","auditd.data.apparmor","auditd.data.fe","auditd.data.perm_mask","auditd.data.ses","auditd.data.cap_fi","auditd.data.obj_uid","auditd.data.reason","auditd.data.list","auditd.data.old_lock","auditd.data.bus","auditd.data.old_pe","auditd.data.new-role","auditd.data.prom","auditd.data.uri","auditd.data.audit_enabled","auditd.data.old-log_passwd","auditd.data.old-seuser","auditd.data.per","auditd.data.scontext","auditd.data.tclass","auditd.data.ver","auditd.data.new","auditd.data.val","auditd.data.img-ctx","auditd.data.old-chardev","auditd.data.old_val","auditd.data.success","auditd.data.inode_uid","auditd.data.removed","auditd.data.socket.port","auditd.data.socket.saddr","auditd.data.socket.addr","auditd.data.socket.family","auditd.data.socket.path","geoip.continent_name","geoip.city_name","geoip.region_name","geoip.country_iso_code","hash.blake2b_256","hash.blake2b_384","hash.blake2b_512","hash.md5","hash.sha1","hash.sha224","hash.sha256","hash.sha384","hash.sha3_224","hash.sha3_256","hash.sha3_384","hash.sha3_512","hash.sha512","hash.sha512_224","hash.sha512_256","hash.xxh64","event.origin","user.entity_id","user.terminal","process.entity_id","socket.entity_id","system.audit.host.timezone.name","system.audit.host.hostname","system.audit.host.id","system.audit.host.architecture","system.audit.host.mac","system.audit.host.os.platform","system.audit.host.os.name","system.audit.host.os.family","system.audit.host.os.version","system.audit.host.os.kernel","system.audit.package.entity_id","system.audit.package.name","system.audit.package.version","system.audit.package.release","system.audit.package.arch","system.audit.package.license","system.audit.package.summary","system.audit.package.url","system.audit.user.name","system.audit.user.uid","system.audit.user.gid","system.audit.user.dir","system.audit.user.shell","system.audit.user.user_information","system.audit.user.password.type","fields.*"]},"refresh_interval":"5s"}}
[00:06:33]                 │ info [o.e.i.b.HierarchyCircuitBreakerService] [kibana-ci-immutable-centos-tests-xxl-1612868210805894513] attempting to trigger G1GC due to high heap usage [1027044104]
[00:06:33]                 │ info [o.e.i.b.HierarchyCircuitBreakerService] [kibana-ci-immutable-centos-tests-xxl-1612868210805894513] GC did bring memory usage down, before [1027044104], after [674665736], allocations [7], duration [16]
[00:06:33]                 │ info [auditbeat/hosts] Indexed 1751 docs into "auditbeat-8.0.0-2019.02.19-000001"
[00:06:33]                 │ info [auditbeat/hosts] Indexed 1 docs into "winlogbeat-8.0.0-2019.02.19-000001"
[00:06:33]               └-> should return an edge of length 1 when given a pagination of length 1
[00:06:33]                 └-> "before each" hook: global before each for "should return an edge of length 1 when given a pagination of length 1"
[00:06:33]                 └- ✓ pass  (50ms) "apis SecuritySolution Endpoints uncommon_processes should return an edge of length 1 when given a pagination of length 1"
[00:06:33]               └-> should return an edge of length 2 when given a pagination of length 2
[00:06:33]                 └-> "before each" hook: global before each for "should return an edge of length 2 when given a pagination of length 2"
[00:06:33]                 └- ✓ pass  (29ms) "apis SecuritySolution Endpoints uncommon_processes should return an edge of length 2 when given a pagination of length 2"
[00:06:33]               └-> should return a total count of elements
[00:06:33]                 └-> "before each" hook: global before each for "should return a total count of elements"
[00:06:33]                 └- ✓ pass  (77ms) "apis SecuritySolution Endpoints uncommon_processes should return a total count of elements"
[00:06:33]               └-> should return a single data set with pagination of 1
[00:06:33]                 └-> "before each" hook: global before each for "should return a single data set with pagination of 1"
[00:06:33]                 └- ✖ fail: apis SecuritySolution Endpoints uncommon_processes should return a single data set with pagination of 1
[00:06:33]                 │      Error: expected 200 "OK", got 429 "Too Many Requests"
[00:06:33]                 │       at Test._assertStatus (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:268:12)
[00:06:33]                 │       at Test._assertFunction (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:283:11)
[00:06:33]                 │       at Test.assert (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:173:18)
[00:06:33]                 │       at assert (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:131:12)
[00:06:33]                 │       at /dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:128:5
[00:06:33]                 │       at Test.Request.callback (/dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/index.js:718:3)
[00:06:33]                 │       at /dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/index.js:906:18
[00:06:33]                 │       at Stream.<anonymous> (/dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/parsers/json.js:19:7)
[00:06:33]                 │       at Unzip.<anonymous> (/dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/unzip.js:55:12)
[00:06:33]                 │       at endReadableNT (internal/streams/readable.js:1327:12)
[00:06:33]                 │       at processTicksAndRejections (internal/process/task_queues.js:80:21)
[00:06:33]                 │ 
[00:06:33]                 │ 

Stack Trace

Error: expected 200 "OK", got 429 "Too Many Requests"
    at Test._assertStatus (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:268:12)
    at Test._assertFunction (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:283:11)
    at Test.assert (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:173:18)
    at assert (/dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:131:12)
    at /dev/shm/workspace/kibana/node_modules/supertest/lib/test.js:128:5
    at Test.Request.callback (/dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/index.js:718:3)
    at /dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/index.js:906:18
    at Stream.<anonymous> (/dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/parsers/json.js:19:7)
    at Unzip.<anonymous> (/dev/shm/workspace/kibana/node_modules/supertest/node_modules/superagent/lib/node/unzip.js:55:12)
    at endReadableNT (internal/streams/readable.js:1327:12)
    at processTicksAndRejections (internal/process/task_queues.js:80:21)

Metrics [docs]

✅ unchanged

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@jfsiii jfsiii merged commit 810e4ab into elastic:master Feb 9, 2021
jfsiii pushed a commit to jfsiii/kibana that referenced this pull request Feb 9, 2021
)

## Summary
Add guard to `/agents/enroll` API preventing agents from enrolling in managed policies

closes elastic#90435
- [x] No Agents can be enrolled into this policy by the user.
- [x] The install & enroll commands should print an error to the console if the enroll command fails (due to being a managed policy or any other reason)



#### So how do you associate an agent with a managed policy?
Enroll in an unmanaged policy then set that policy to managed. 

We don't restrict the agent policy, only what other things (agents, integrations) can do if they're associated with a managed policy.

A _force flag_ has been mentioned for some other actions. It might work here as well, but I'd like to handle discussion & implementation of those later.

### Manual testing
<details><summary>Prevent enroll for managed policies</summary>

1. Created a managed agent policy
    ```
    curl --user elastic:changeme -X POST localhost:5601/api/fleet/agent_policies -H 'Content-Type: application/json' -d'{ "name": "User created MANAGED", "namespace": "default", "is_managed": true}' -H 'kbn-xsrf: true'
    {"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"User created MANAGED","namespace":"default","is_managed":true,"revision":1,"updated_at":"2021-02-05T16:36:01.931Z","updated_by":"elastic"}}
    ```
2. Try `install` command show in the UI
    ```
    sudo ./elastic-agent install -f --kibana-url=http://localhost:5601 --enrollment-token=WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
    Password:
    The Elastic Agent is currently in BETA and should not be used in production

    Error: fail to enroll: fail to execute request to Kibana: Status code: 400, Kibana returned an error: Bad Request, message: Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a
    Error: enroll command failed with exit code: 1
    ```
3. Observe `Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a` error
4. Try `enroll` instead:
    ```
    sudo ./elastic-agent enroll http://localhost:5601 WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
    The Elastic Agent is currently in BETA and should not be used in production

    This will replace your current settings. Do you want to continue? [Y/n]:
    Error: fail to enroll: fail to execute request to Kibana: Status code: 400, Kibana returned an error: Bad Request, message: Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a
    ```
5. Observe same `Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a` error

</details>

<details><summary>Enroll in unmanaged policy, then update it to managed</summary>

Agent policies are `is_managed: false` by default, or we can update the policy to `is_managed: false`, like:

```
curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": false, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":false,"revision":4,"updated_at":"2021-02-05T17:42:05.610Z","updated_by":"elastic","package_policies":[]}}
```

then enroll

```
 sudo ./elastic-agent install -f --kibana-url=http://localhost:5601 --enrollment-token=WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
The Elastic Agent is currently in BETA and should not be used in production

Successfully enrolled the Elastic Agent.
Installation was successful and Elastic Agent is running.
```

and set the policy back to managed

```
curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": true, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":true,"revision":5,"updated_at":"2021-02-05T17:44:18.757Z","updated_by":"elastic","package_policies":[]}}
```

with all the restrictions that entails (cannot unenroll, reassign, etc)

```
curl --user elastic:changeme -X PUT 'http://localhost:5601/api/fleet/agents/8169f0a0-67d9-11eb-80f2-73dd45e7318e/reassign'   -X 'PUT'  -H 'kbn-xsrf: abc'  -H 'Content-Type: application/json'   --data-raw '{"policy_id":"729f8440-67cf-11eb-b656-21ad68ebfa8a"}'   
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot reassign an agent from managed agent policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a"
}
```

</details>

### Checklist
- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
jfsiii pushed a commit that referenced this pull request Feb 9, 2021
…90780)

## Summary
Add guard to `/agents/enroll` API preventing agents from enrolling in managed policies

closes #90435
- [x] No Agents can be enrolled into this policy by the user.
- [x] The install & enroll commands should print an error to the console if the enroll command fails (due to being a managed policy or any other reason)



#### So how do you associate an agent with a managed policy?
Enroll in an unmanaged policy then set that policy to managed. 

We don't restrict the agent policy, only what other things (agents, integrations) can do if they're associated with a managed policy.

A _force flag_ has been mentioned for some other actions. It might work here as well, but I'd like to handle discussion & implementation of those later.

### Manual testing
<details><summary>Prevent enroll for managed policies</summary>

1. Created a managed agent policy
    ```
    curl --user elastic:changeme -X POST localhost:5601/api/fleet/agent_policies -H 'Content-Type: application/json' -d'{ "name": "User created MANAGED", "namespace": "default", "is_managed": true}' -H 'kbn-xsrf: true'
    {"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"User created MANAGED","namespace":"default","is_managed":true,"revision":1,"updated_at":"2021-02-05T16:36:01.931Z","updated_by":"elastic"}}
    ```
2. Try `install` command show in the UI
    ```
    sudo ./elastic-agent install -f --kibana-url=http://localhost:5601 --enrollment-token=WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
    Password:
    The Elastic Agent is currently in BETA and should not be used in production

    Error: fail to enroll: fail to execute request to Kibana: Status code: 400, Kibana returned an error: Bad Request, message: Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a
    Error: enroll command failed with exit code: 1
    ```
3. Observe `Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a` error
4. Try `enroll` instead:
    ```
    sudo ./elastic-agent enroll http://localhost:5601 WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
    The Elastic Agent is currently in BETA and should not be used in production

    This will replace your current settings. Do you want to continue? [Y/n]:
    Error: fail to enroll: fail to execute request to Kibana: Status code: 400, Kibana returned an error: Bad Request, message: Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a
    ```
5. Observe same `Cannot enroll in managed policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a` error

</details>

<details><summary>Enroll in unmanaged policy, then update it to managed</summary>

Agent policies are `is_managed: false` by default, or we can update the policy to `is_managed: false`, like:

```
curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": false, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":false,"revision":4,"updated_at":"2021-02-05T17:42:05.610Z","updated_by":"elastic","package_policies":[]}}
```

then enroll

```
 sudo ./elastic-agent install -f --kibana-url=http://localhost:5601 --enrollment-token=WmcwTWMzY0IzWlBUUWJJUjZqRDA6UGRZelVlaS1STml1cVdjSUVwSkJRQQ== --insecure
The Elastic Agent is currently in BETA and should not be used in production

Successfully enrolled the Elastic Agent.
Installation was successful and Elastic Agent is running.
```

and set the policy back to managed

```
curl --user elastic:changeme -X PUT localhost:5601/api/fleet/agent_policies/3bd07db0-67d0-11eb-b656-21ad68ebfa8a -H 'Content-Type: application/json' -d'{ "is_managed": true, "name": "xyz", "namespace": "default" }' -H 'kbn-xsrf: true'
{"item":{"id":"3bd07db0-67d0-11eb-b656-21ad68ebfa8a","name":"xyz","namespace":"default","is_managed":true,"revision":5,"updated_at":"2021-02-05T17:44:18.757Z","updated_by":"elastic","package_policies":[]}}
```

with all the restrictions that entails (cannot unenroll, reassign, etc)

```
curl --user elastic:changeme -X PUT 'http://localhost:5601/api/fleet/agents/8169f0a0-67d9-11eb-80f2-73dd45e7318e/reassign'   -X 'PUT'  -H 'kbn-xsrf: abc'  -H 'Content-Type: application/json'   --data-raw '{"policy_id":"729f8440-67cf-11eb-b656-21ad68ebfa8a"}'   
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Cannot reassign an agent from managed agent policy 3bd07db0-67d0-11eb-b656-21ad68ebfa8a"
}
```

</details>

### Checklist
- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
@jfsiii jfsiii deleted the 90435-prevent-enroll-in-managed-policies branch April 6, 2021 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Fleet Fleet team's agent central management project release_note:skip Skip the PR/issue when compiling release notes Team:Fleet Team label for Observability Data Collection Fleet team v7.12.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Fleet] Agents cannot enroll in managed policies
4 participants