Introduce API version 2022-04, deprecate use of /global in paths (#4731)

This commit is contained in:
Martin Angers 2022-04-05 11:35:53 -04:00 committed by GitHub
parent 57816592ba
commit 90b15071a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 7411 additions and 1053 deletions

View file

@ -0,0 +1 @@
* Introduce new API version (`/api/2022-04/...`, aliased as `/api/latest/...`) to introduce breaking changes that remove `/global` sections from the paths (the deprecated API is still available under `/api/v1/...`)

View file

@ -492,7 +492,7 @@ the way that the Fleet server works.
defaultWritetimeout := 40 * time.Second
writeTimeout := defaultWritetimeout
// The "GET /api/v1/fleet/queries/run" API requires
// The "GET /api/latest/fleet/queries/run" API requires
// WriteTimeout to be higher than the live query rest period
// (otherwise the response is not sent back to the client).
//
@ -680,6 +680,8 @@ func cronDB(ctx context.Context, ds fleet.Datastore, logger kitlog.Logger, ident
sentry.CaptureException(err)
}
// NOTE(mna): this is not a route from the fleet server (not in server/service/handler.go) so it
// will not automatically support the /latest/ versioning. Leaving it as /v1/ for that reason.
err = trySendStatistics(ctx, ds, fleet.StatisticsFrequency, "https://fleetdm.com/api/v1/webhooks/receive-usage-analytics", license)
if err != nil {
level.Error(logger).Log("err", "sending statistics", "details", err)

View file

@ -269,7 +269,7 @@ spec:
`)
runAppCheckErr(t, []string{"apply", "-f", name},
"applying fleet config: PATCH /api/v1/fleet/config received status 400 Bad request: json: unknown field \"enabled_software_inventory\"",
"applying fleet config: PATCH /api/latest/fleet/config received status 400 Bad request: json: unknown field \"enabled_software_inventory\"",
)
require.Nil(t, savedAppConfig)
}

View file

@ -464,7 +464,7 @@ or provide an <address> argument to debug: fleetctl debug connection localhost:8
}
// Check that the server responds with expected responses (by
// making a POST to /api/v1/osquery/enroll with an invalid
// making a POST to /api/osquery/enroll with an invalid
// secret).
if err := checkAPIEndpoint(c.Context, timeoutPerCheck, baseURL, cli); err != nil {
return fmt.Errorf("Fail: agent API endpoint: %w", err)
@ -681,7 +681,7 @@ func checkAPIEndpoint(ctx context.Context, timeout time.Duration, baseURL *url.U
"Accept": "application/json",
}
baseURL.Path = "/api/v1/osquery/enroll"
baseURL.Path = "/api/osquery/enroll"
req, err := http.NewRequestWithContext(
ctx,
"POST",

View file

@ -279,7 +279,7 @@ func (a *agent) enroll(i int, onlyAlreadyEnrolled bool) error {
req.Header.SetMethod("POST")
req.Header.SetContentType("application/json")
req.Header.Add("User-Agent", "osquery/4.6.0")
req.SetRequestURI(a.serverAddress + "/api/v1/osquery/enroll")
req.SetRequestURI(a.serverAddress + "/api/osquery/enroll")
res := fasthttp.AcquireResponse()
a.waitingDo(req, res)
@ -314,7 +314,7 @@ func (a *agent) config() {
req.Header.SetMethod("POST")
req.Header.SetContentType("application/json")
req.Header.Add("User-Agent", "osquery/4.6.0")
req.SetRequestURI(a.serverAddress + "/api/v1/osquery/config")
req.SetRequestURI(a.serverAddress + "/api/osquery/config")
res := fasthttp.AcquireResponse()
a.waitingDo(req, res)
@ -435,7 +435,7 @@ func (a *agent) DistributedRead() (*distributedReadResponse, error) {
req.Header.SetMethod("POST")
req.Header.SetContentType("application/json")
req.Header.Add("User-Agent", "osquery/4.6.0")
req.SetRequestURI(a.serverAddress + "/api/v1/osquery/distributed/read")
req.SetRequestURI(a.serverAddress + "/api/osquery/distributed/read")
res := fasthttp.AcquireResponse()
a.waitingDo(req, res)
@ -587,7 +587,7 @@ func (a *agent) DistributedWrite(queries map[string]string) {
req.Header.SetMethod("POST")
req.Header.SetContentType("application/json")
req.Header.Add("User-Agent", "osquery/5.0.1")
req.SetRequestURI(a.serverAddress + "/api/v1/osquery/distributed/write")
req.SetRequestURI(a.serverAddress + "/api/osquery/distributed/write")
res := fasthttp.AcquireResponse()
a.waitingDo(req, res)

View file

@ -143,7 +143,7 @@ services:
image: fleetdm/docker-idp:latest
environment:
SIMPLESAMLPHP_SP_ENTITY_ID: "https://localhost:8080"
SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: "https://localhost:8080/api/v1/fleet/sso/callback"
SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: "https://localhost:8080/api/latest/fleet/sso/callback"
volumes:
- ./tools/saml/users.php:/var/www/simplesamlphp/config/authsources.php
ports:

View file

@ -27,7 +27,7 @@ Unlike the [Fleet REST API documentation](../Using-Fleet/REST-API.md), the API r
Returns a list of all queries in the Fleet instance. Each item returned includes the name, description, and SQL of the query.
`GET /api/v1/fleet/spec/queries`
`GET /api/latest/fleet/spec/queries`
#### Parameters
@ -35,7 +35,7 @@ None.
#### Example
`GET /api/v1/fleet/spec/queries`
`GET /api/latest/fleet/spec/queries`
##### Default response
@ -62,7 +62,7 @@ None.
Returns the name, description, and SQL of the query specified by name.
`GET /api/v1/fleet/spec/queries/{name}`
`GET /api/latest/fleet/spec/queries/{name}`
#### Parameters
@ -72,7 +72,7 @@ Returns the name, description, and SQL of the query specified by name.
#### Example
`GET /api/v1/fleet/spec/queries/query1`
`GET /api/latest/fleet/spec/queries/query1`
##### Default response
@ -92,7 +92,7 @@ Returns the name, description, and SQL of the query specified by name.
Creates and/or modifies the queries included in the specs list. To modify an existing query, the name of the query included in `specs` must already be used by an existing query. If a query with the specified name doesn't exist in Fleet, a new query will be created.
`POST /api/v1/fleet/spec/queries`
`POST /api/latest/fleet/spec/queries`
#### Parameters
@ -102,7 +102,7 @@ Creates and/or modifies the queries included in the specs list. To modify an exi
#### Example
`POST /api/v1/fleet/spec/queries`
`POST /api/latest/fleet/spec/queries`
##### Request body
@ -131,11 +131,11 @@ Creates and/or modifies the queries included in the specs list. To modify an exi
Returns the specs for all packs in the Fleet instance.
`GET /api/v1/fleet/spec/packs`
`GET /api/latest/fleet/spec/packs`
#### Example
`GET /api/v1/fleet/spec/packs`
`GET /api/latest/fleet/spec/packs`
##### Default response
@ -247,7 +247,7 @@ Returns the specs for all packs in the Fleet instance.
Returns the specs for all packs in the Fleet instance.
`POST /api/v1/fleet/spec/packs`
`POST /api/latest/fleet/spec/packs`
#### Parameters
@ -257,7 +257,7 @@ Returns the specs for all packs in the Fleet instance.
#### Example
`POST /api/v1/fleet/spec/packs`
`POST /api/latest/fleet/spec/packs`
##### Request body
@ -356,7 +356,7 @@ Returns the specs for all packs in the Fleet instance.
Returns the spec for the specified pack by pack name.
`GET /api/v1/fleet/spec/packs/{name}`
`GET /api/latest/fleet/spec/packs/{name}`
#### Parameters
@ -366,7 +366,7 @@ Returns the spec for the specified pack by pack name.
#### Example
`GET /api/v1/fleet/spec/packs/pack_1`
`GET /api/latest/fleet/spec/packs/pack_1`
##### Default response
@ -447,19 +447,19 @@ If the `name` specified is associated with an existing team, this API route, com
If the `name` is not already associated with an existing team, this API route creates a new team with the specified `name`, `agent_options`, and `secrets`.
`POST /api/v1/fleet/spec/teams`
`POST /api/latest/fleet/spec/teams`
#### Parameters
| Name | Type | In | Description |
| ------------- | ------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | body | **Required.** The team's name. |
| agent_options | string | body | **Required.** The agent options spec that is applied to the hosts assigned to the specified to team. These agent agent options completely override the global agent options specified in the [`GET /api/v1/fleet/config API route`](#get-configuration) |
| agent_options | string | body | **Required.** The agent options spec that is applied to the hosts assigned to the specified to team. These agent agent options completely override the global agent options specified in the [`GET /api/latest/fleet/config API route`](#get-configuration) |
| secrets | list | body | **Required.** A list of plain text strings used as the enroll secrets. |
#### Example
`POST /api/v1/fleet/spec/teams`
`POST /api/latest/fleet/spec/teams`
##### Request body
@ -477,7 +477,7 @@ If the `name` is not already associated with an existing team, this API route cr
"logger_tls_period": 10,
"distributed_plugin": "tls",
"disable_distributed": false,
"logger_tls_endpoint": "/api/v1/osquery/log",
"logger_tls_endpoint": "/api/osquery/log",
"distributed_interval": 10,
"distributed_tls_max_attempts": 3
},
@ -516,7 +516,7 @@ If the `label_membership_type` is set to `dynamic`, the `query` property must al
If the `label_membership_type` is set to `manual`, the `hosts` property must also be specified with the value set to a list of hostnames.
`POST /api/v1/fleet/spec/labels`
`POST /api/latest/fleet/spec/labels`
#### Parameters
@ -526,7 +526,7 @@ If the `label_membership_type` is set to `manual`, the `hosts` property must als
#### Example
`POST /api/v1/fleet/spec/labels`
`POST /api/latest/fleet/spec/labels`
##### Request body
@ -555,7 +555,7 @@ If the `label_membership_type` is set to `manual`, the `hosts` property must als
### Get labels spec
`GET /api/v1/fleet/spec/labels`
`GET /api/latest/fleet/spec/labels`
#### Parameters
@ -563,7 +563,7 @@ None.
#### Example
`GET /api/v1/fleet/spec/labels`
`GET /api/latest/fleet/spec/labels`
##### Default response
@ -630,7 +630,7 @@ None.
Returns the spec for the label specified by name.
`GET /api/v1/fleet/spec/labels/{name}`
`GET /api/latest/fleet/spec/labels/{name}`
#### Parameters
@ -638,7 +638,7 @@ None.
#### Example
`GET /api/v1/fleet/spec/labels/local_machine`
`GET /api/latest/fleet/spec/labels/local_machine`
##### Default response
@ -660,7 +660,7 @@ None.
Returns the valid global enroll secrets.
`GET /api/v1/fleet/spec/enroll_secret`
`GET /api/latest/fleet/spec/enroll_secret`
#### Parameters
@ -668,7 +668,7 @@ None.
#### Example
`GET /api/v1/fleet/spec/enroll_secret`
`GET /api/latest/fleet/spec/enroll_secret`
##### Default response
@ -695,7 +695,7 @@ None.
Replaces the active global enroll secrets with the secrets specified.
`POST /api/v1/fleet/spec/enroll_secret`
`POST /api/latest/fleet/spec/enroll_secret`
#### Parameters
@ -719,7 +719,7 @@ Replaces the active global enroll secrets with the secrets specified.
}
```
`POST /api/v1/fleet/spec/enroll_secret`
`POST /api/latest/fleet/spec/enroll_secret`
##### Default response
@ -729,7 +729,7 @@ Replaces the active global enroll secrets with the secrets specified.
Checks the status of the Fleet's ability to run a live query. If an error is present in the response, Fleet won't be able to successfully run a live query. This endpoint is used by the Fleet UI to make sure that the Fleet instance is correctly configured to run live queries.
`GET /api/v1/fleet/status/live_query`
`GET /api/latest/fleet/status/live_query`
#### Parameters
@ -737,7 +737,7 @@ None.
#### Example
`GET /api/v1/fleet/status/live_query`
`GET /api/latest/fleet/status/live_query`
##### Default response
@ -747,7 +747,7 @@ None.
Checks the status of the Fleet's result store. If an error is present in the response, Fleet won't be able to successfully run a live query. This endpoint is used by the Fleet UI to make sure that the Fleet instance is correctly configured to run live queries.
`GET /api/v1/fleet/status/result_store`
`GET /api/latest/fleet/status/result_store`
#### Parameters
@ -755,7 +755,7 @@ None.
#### Example
`GET /api/v1/fleet/status/result_store`
`GET /api/latest/fleet/status/result_store`
##### Default response
@ -767,7 +767,7 @@ Runs the specified query as a live query on the specified hosts or group of host
After the query has been initiated, [get results via WebSocket](#retrieve-live-query-results-standard-websocket-api).
`POST /api/v1/fleet/queries/run`
`POST /api/latest/fleet/queries/run`
#### Parameters
@ -781,7 +781,7 @@ One of `query` and `query_id` must be specified.
#### Example with one host targeted by ID
`POST /api/v1/fleet/queries/run`
`POST /api/latest/fleet/queries/run`
##### Request body
@ -820,7 +820,7 @@ One of `query` and `query_id` must be specified.
#### Example with multiple hosts targeted by label ID
`POST /api/v1/fleet/queries/run`
`POST /api/latest/fleet/queries/run`
##### Request body
@ -863,7 +863,7 @@ Runs the specified saved query as a live query on the specified targets. Returns
After the query has been initiated, [get results via WebSocket](#retrieve-live-query-results-standard-websocket-api).
`POST /api/v1/fleet/queries/run_by_names`
`POST /api/latest/fleet/queries/run_by_names`
#### Parameters
@ -877,7 +877,7 @@ One of `query` and `query_id` must be specified.
#### Example with one host targeted by hostname
`POST /api/v1/fleet/queries/run_by_names`
`POST /api/latest/fleet/queries/run_by_names`
##### Request body
@ -916,7 +916,7 @@ One of `query` and `query_id` must be specified.
#### Example with multiple hosts targeted by label name
`POST /api/v1/fleet/queries/run_by_names`
`POST /api/latest/fleet/queries/run_by_names`
##### Request body
@ -1238,7 +1238,7 @@ Creates and/or modifies the policies included in the specs list. To modify an ex
NOTE: when updating a policy, team and platform will be ignored.
`POST /api/v1/fleet/spec/policies`
`POST /api/latest/fleet/spec/policies`
#### Parameters
@ -1248,7 +1248,7 @@ NOTE: when updating a policy, team and platform will be ignored.
#### Example
`POST /api/v1/fleet/spec/policies`
`POST /api/latest/fleet/spec/policies`
##### Request body

View file

@ -2299,10 +2299,10 @@ _**Note that the email being used in the SAML Assertion must match a user that a
Setting up the service provider (Fleet) with an identity provider generally requires the following information:
- _Assertion Consumer Service_ - This is the call back URL that the identity provider
will use to send security assertions to Fleet. In Okta, this field is called _Single sign on URL_. On Google it is "ACS URL". The value that you supply will be a fully qualified URL consisting of your Fleet web address and the callback path `/api/v1/fleet/sso/callback`. For example, if your Fleet web address is https://fleet.example.com, then the value you would use in the identity provider configuration would be:
will use to send security assertions to Fleet. In Okta, this field is called _Single sign on URL_. On Google it is "ACS URL". The value that you supply will be a fully qualified URL consisting of your Fleet web address and the callback path `/api/latest/fleet/sso/callback`. For example, if your Fleet web address is https://fleet.example.com, then the value you would use in the identity provider configuration would be:
```
https://fleet.example.com/api/v1/fleet/sso/callback
https://fleet.example.com/api/latest/fleet/sso/callback
```
- _Entity ID_ - This value is an identifier that you choose. It identifies your Fleet instance as the service provider that issues authorization requests. The value must exactly match the Entity ID that you define in the Fleet SSO configuration.
@ -2390,7 +2390,7 @@ Follow these steps to configure Fleet SSO with Google Workspace. This will requi
5. In Google Workspace, configure the _Service provider details_.
- For _ACS URL_, use `https://<your_fleet_url>/api/v1/fleet/sso/callback` (eg. `https://fleet.example.com/api/v1/fleet/sso/callback`).
- For _ACS URL_, use `https://<your_fleet_url>/api/latest/fleet/sso/callback` (eg. `https://fleet.example.com/api/latest/fleet/sso/callback`).
- For Entity ID, use **the same unique identifier from step 4** (eg. `fleet.example.com`).
- For _Name ID format_ choose `EMAIL`.
- For _Name ID_ choose `Basic Information > Primary email`.

View file

@ -53,7 +53,7 @@ Osquery requires that all communication between the agent and Fleet are over a s
- Try specifying the path to the full certificate chain used by the server using the `--tls_server_certs` flag in `osqueryd`. This is often unnecessary when using a certificate signed by an authority trusted by the system, but is mandatory when working with self-signed certificates. In all cases it can be a useful debugging step.
- Ensure that the CNAME or one of the Subject Alternate Names (SANs) on the certificate matches the address at which the server is being accessed. If osquery connects via `https://localhost:443`, but the certificate is for `https://fleet.example.com`, the verification will fail.
- Is Fleet behind a load-balancer? Ensure that if the load-balancer is terminating TLS, this is the certificate provided to osquery.
- Does the certificate verify with `curl`? Try `curl -v -X POST https://fleetserver:port/api/v1/osquery/enroll`.
- Does the certificate verify with `curl`? Try `curl -v -X POST https://fleetserver:port/api/osquery/enroll`.
## What do I need to do to change the Fleet server TLS certificate?
@ -237,7 +237,7 @@ Check out the [documentation on running database migrations](./Upgrading-Fleet.m
## What API endpoints should I expose to the public internet?
If you would like to manage hosts that can travel outside your VPN or intranet we recommend only exposing the "/api/v1/osquery" endpoint to the public internet.
If you would like to manage hosts that can travel outside your VPN or intranet we recommend only exposing the "/api/osquery" endpoint to the public internet.
## What is the minimum version of MySQL required by Fleet?

View file

@ -225,18 +225,18 @@ sudo /usr/bin/osqueryd \
--tls_server_certs=/var/osquery/server.pem \
--tls_hostname=localhost:8080 \
--host_identifier=instance \
--enroll_tls_endpoint=/api/v1/osquery/enroll \
--enroll_tls_endpoint=/api/osquery/enroll \
--config_plugin=tls \
--config_tls_endpoint=/api/v1/osquery/config \
--config_tls_endpoint=/api/osquery/config \
--config_refresh=10 \
--disable_distributed=false \
--distributed_plugin=tls \
--distributed_interval=3 \
--distributed_tls_max_attempts=3 \
--distributed_tls_read_endpoint=/api/v1/osquery/distributed/read \
--distributed_tls_write_endpoint=/api/v1/osquery/distributed/write \
--distributed_tls_read_endpoint=/api/osquery/distributed/read \
--distributed_tls_write_endpoint=/api/osquery/distributed/write \
--logger_plugin=tls \
--logger_tls_endpoint=/api/v1/osquery/log \
--logger_tls_endpoint=/api/osquery/log \
--logger_tls_period=10
```
@ -398,18 +398,18 @@ sudo /usr/bin/osqueryd \
--tls_server_certs=/var/osquery/server.pem \
--tls_hostname=localhost:8080 \
--host_identifier=instance \
--enroll_tls_endpoint=/api/v1/osquery/enroll \
--enroll_tls_endpoint=/api/osquery/enroll \
--config_plugin=tls \
--config_tls_endpoint=/api/v1/osquery/config \
--config_tls_endpoint=/api/osquery/config \
--config_refresh=10 \
--disable_distributed=false \
--distributed_plugin=tls \
--distributed_interval=3 \
--distributed_tls_max_attempts=3 \
--distributed_tls_read_endpoint=/api/v1/osquery/distributed/read \
--distributed_tls_write_endpoint=/api/v1/osquery/distributed/write \
--distributed_tls_read_endpoint=/api/osquery/distributed/read \
--distributed_tls_write_endpoint=/api/osquery/distributed/write \
--logger_plugin=tls \
--logger_tls_endpoint=/api/v1/osquery/log \
--logger_tls_endpoint=/api/osquery/log \
--logger_tls_period=10
```
@ -601,4 +601,4 @@ Below are some projects created by Fleet community members. These projects provi
- [CptOfEvilMinions/FleetDM-Automation](https://github.com/CptOfEvilMinions/FleetDM-Automation) - Ansible and Docker code to set up Fleet
<meta name="pageOrderInSection" value="200">
<meta name="pageOrderInSection" value="200">

View file

@ -136,18 +136,18 @@ sudo osqueryd \
--tls_server_certs=/etc/osquery/fleet.crt \
--tls_hostname=fleet.example.com \
--host_identifier=uuid \
--enroll_tls_endpoint=/api/v1/osquery/enroll \
--enroll_tls_endpoint=/api/osquery/enroll \
--config_plugin=tls \
--config_tls_endpoint=/api/v1/osquery/config \
--config_tls_endpoint=/api/osquery/config \
--config_refresh=10 \
--disable_distributed=false \
--distributed_plugin=tls \
--distributed_interval=10 \
--distributed_tls_max_attempts=3 \
--distributed_tls_read_endpoint=/api/v1/osquery/distributed/read \
--distributed_tls_write_endpoint=/api/v1/osquery/distributed/write \
--distributed_tls_read_endpoint=/api/osquery/distributed/read \
--distributed_tls_write_endpoint=/api/osquery/distributed/write \
--logger_plugin=tls \
--logger_tls_endpoint=/api/v1/osquery/log \
--logger_tls_endpoint=/api/osquery/log \
--logger_tls_period=10
```
@ -206,4 +206,4 @@ installation should appear as the same host in the Fleet UI. If other settings a
entries will appear in the Fleet UI. The older entries can be automatically cleaned up with the host
expiration functionality configured in the application settings (UI or fleetctl).
<meta name="pageOrderInSection" value="500">
<meta name="pageOrderInSection" value="500">

View file

@ -126,17 +126,17 @@ Live query results are never logged to the filesystem of the Fleet server. See [
You cannot. Scheduled query results are logged to whatever logging plugin you have configured and are not stored in the Fleet DB.
However, the Fleet API exposes a significant amount of host information via the [`api/v1/fleet/hosts`](./REST-API.md#list-hosts) and the [`api/v1/fleet/hosts/{id}`](./REST-API.md#get-host) API endpoints. The `api/v1/fleet/hosts` [can even be configured to return additional host information](https://github.com/fleetdm/fleet/blob/9fb9da31f5462fa7dda4819a114bbdbc0252c347/docs/1-Using-Fleet/2-fleetctl-CLI.md#fleet-configuration-options).
However, the Fleet API exposes a significant amount of host information via the [`api/latest/fleet/hosts`](./REST-API.md#list-hosts) and the [`api/latest/fleet/hosts/{id}`](./REST-API.md#get-host) API endpoints. The `api/latest/fleet/hosts` [can even be configured to return additional host information](https://github.com/fleetdm/fleet/blob/9fb9da31f5462fa7dda4819a114bbdbc0252c347/docs/1-Using-Fleet/2-fleetctl-CLI.md#fleet-configuration-options).
As an example, let's say you want to retrieve a host's OS version, installed software, and kernel version:
Each hosts OS version is available using the `api/v1/fleet/hosts` API endpoint. [Check out the API documentation for this endpoint](./REST-API.md#list-hosts).
Each hosts OS version is available using the `api/latest/fleet/hosts` API endpoint. [Check out the API documentation for this endpoint](./REST-API.md#list-hosts).
The ability to view each hosts installed software was released behind a feature flag in Fleet 3.11.0 and called Software inventory. [Check out the feature flag documentation for instructions on turning on Software inventory in Fleet](../Deploying/Configuration.md#feature-flags).
Once the Software inventory feature is turned on, a list of a specific hosts installed software is available using the `api/v1/fleet/hosts/{id}` endpoint. [Check out the documentation for this endpoint](./REST-API.md#get-host).
Once the Software inventory feature is turned on, a list of a specific hosts installed software is available using the `api/latest/fleet/hosts/{id}` endpoint. [Check out the documentation for this endpoint](./REST-API.md#get-host).
Its possible in Fleet to retrieve each hosts kernel version, using the Fleet API, through `additional_queries`. The Fleet configuration options yaml file includes an `additional_queries` property that allows you to append custom query results to the host details returned by the `api/v1/fleet/hosts` endpoint. [Check out an example configuration file with the additional_queries field](./fleetctl-CLI.md#fleet-configuration-options).
Its possible in Fleet to retrieve each hosts kernel version, using the Fleet API, through `additional_queries`. The Fleet configuration options yaml file includes an `additional_queries` property that allows you to append custom query results to the host details returned by the `api/latest/fleet/hosts` endpoint. [Check out an example configuration file with the additional_queries field](./fleetctl-CLI.md#fleet-configuration-options).
## How do I automatically add hosts to packs when the hosts enroll to Fleet?

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -19,8 +19,8 @@ to detect the CVEs that match the defined CPEs.
If matches are found, they are exposed on each host's
**Host details** page and on the **Home** page in the Fleet UI. The CVEs are also exposed in the
`fleetctl get software` command and the `GET api/v1/fleet/hosts/{id}` and `GET
api/v1/fleet/software` API routes.
`fleetctl get software` command and the `GET api/latest/fleet/hosts/{id}` and `GET
api/latest/fleet/software` API routes.
Vulnerability processing happens on the Fleet instance and not on the host machine. Because of this,
detected vulnerabilities cannot be used in the same way you would use an osquery query (e.g. you wouldn't

View file

@ -191,7 +191,7 @@ spec:
distributed_plugin: tls
distributed_tls_max_attempts: 3
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 10
pack_delimiter: /
overrides: {}
@ -218,10 +218,10 @@ spec:
distributed_interval: 10
distributed_plugin: tls
distributed_tls_max_attempts: 3
distributed_tls_read_endpoint: /api/v1/osquery/distributed/read
distributed_tls_write_endpoint: /api/v1/osquery/distributed/write
distributed_tls_read_endpoint: /api/osquery/distributed/read
distributed_tls_write_endpoint: /api/osquery/distributed/write
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 10
pack_delimiter: /
overrides: {}
@ -266,7 +266,7 @@ spec:
failing_policies_webhook:
enable_failing_policies_webhook: true
destination_url": https://server.com
policy_ids:
policy_ids:
- 1
- 2
- 3
@ -304,7 +304,7 @@ spec:
distributed_interval: 3
distributed_tls_max_attempts: 3
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 10
decorators:
load:
@ -327,7 +327,7 @@ spec:
distributed_interval: 10
distributed_tls_max_attempts: 10
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 300
disable_tables: chrome_extensions
docker_socket: /var/run/docker.sock
@ -343,7 +343,7 @@ spec:
distributed_interval: 10
distributed_tls_max_attempts: 3
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 60
schedule_timeout: 60
docker_socket: /etc/run/docker.sock

View file

@ -14,7 +14,7 @@ spec:
distributed_plugin: tls
distributed_tls_max_attempts: 3
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 10
pack_delimiter: /
overrides: {}

View file

@ -16,7 +16,7 @@ spec:
distributed_plugin: tls
distributed_tls_max_attempts: 3
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 10
pack_delimiter: /
overrides: {}

View file

@ -14,7 +14,7 @@ spec:
distributed_plugin: tls
distributed_tls_max_attempts: 3
logger_plugin: tls
logger_tls_endpoint: /api/v1/osquery/log
logger_tls_endpoint: /api/osquery/log
logger_tls_period: 10
pack_delimiter: /
overrides: {}

View file

@ -26,12 +26,12 @@ Fleetctl also provides a quick way to work with all the data exposed by Fleet wi
## Using fleetctl
You can use `fleetctl` to accomplish many tasks you would typically need to do through the UI(User Interface). You can even set up or apply configuration files to the Fleet server.
You can use `fleetctl` to accomplish many tasks you would typically need to do through the UI(User Interface). You can even set up or apply configuration files to the Fleet server.
### Available commands
Much of the functionality available in the Fleet UI is also available in `fleetctl`. You can run queries, add and remove users, generate install packages to add new hosts, get information about existing hosts, and more! The following commands are available for use with `fleetctl`:
| Command | Description |
|:---------------------------|:-------------------------------------------------------------------|
| apply | Apply files to declaratively manage osquery configurations |
@ -208,7 +208,7 @@ Note the token can also be set with `fleetctl config set --token`, but this may
## Using fleetctl to configure Fleet
A Fleet configuration is defined using one or more declarative "messages" in yaml syntax.
A Fleet configuration is defined using one or more declarative "messages" in yaml syntax.
Fleet configuration can be retrieved and applied using the `fleetctl` tool.
@ -271,7 +271,7 @@ fleetctl user create --name "API User" --email api@example.com --password temp!p
### Reset the password
When a new user is created, a password reset is needed before that user can perform queries. Since an API-only user cannot log in to the Fleet UI, this is done through the REST API. We'll be doing this through the terminal using `curl`.
When a new user is created, a password reset is needed before that user can perform queries. Since an API-only user cannot log in to the Fleet UI, this is done through the REST API. We'll be doing this through the terminal using `curl`.
First, log in to the new user account using `fleetctl login`. Once you're logged in successfully to the API-only user, set up a variable to hold the user's token:
@ -313,7 +313,7 @@ unset token
### Use fleetctl as the new user
Now that the password is reset, you will need to log in again using the updated password with `fleetctl login`. You'll now be able to perform tasks using `fleetctl` as your new API-only user.
Now that the password is reset, you will need to log in again using the updated password with `fleetctl login`. You'll now be able to perform tasks using `fleetctl` as your new API-only user.
### Switching users
@ -346,8 +346,8 @@ The user creation failed because the API-only user doesn't have the right permis
```
$ fleetctl user create --email test@example.com --name "New User" --context admin
Enter password for user:
Enter password for user (confirm):
Enter password for user:
Enter password for user (confirm):
```
## File carving
@ -363,8 +363,8 @@ Given a working flagfile for connecting osquery agents to Fleet, add the followi
```
--disable_carver=false
--carver_disable_function=false
--carver_start_endpoint=/api/v1/osquery/carve/begin
--carver_continue_endpoint=/api/v1/osquery/carve/block
--carver_start_endpoint=/api/osquery/carve/begin
--carver_continue_endpoint=/api/osquery/carve/block
--carver_block_size=2097152
```

View file

@ -12,9 +12,9 @@ func FleetFlags(fleetURL *url.URL) []string {
// Use uuid as the default identifier -- users can override this in their flagfile
"--host_identifier=uuid",
"--tls_hostname=" + hostname,
"--enroll_tls_endpoint=" + path.Join(prefix, "/api/v1/osquery/enroll"),
"--enroll_tls_endpoint=" + path.Join(prefix, "/api/osquery/enroll"),
"--config_plugin=tls",
"--config_tls_endpoint=" + path.Join(prefix, "/api/v1/osquery/config"),
"--config_tls_endpoint=" + path.Join(prefix, "/api/osquery/config"),
// Osquery defaults config_refresh to 0 which is probably not ideal for
// a client connected to Fleet. Users can always override this in the
// config they serve via Fleet.
@ -22,16 +22,16 @@ func FleetFlags(fleetURL *url.URL) []string {
"--disable_distributed=false",
"--distributed_plugin=tls",
"--distributed_tls_max_attempts=10",
"--distributed_tls_read_endpoint=" + path.Join(prefix, "/api/v1/osquery/distributed/read"),
"--distributed_tls_write_endpoint=" + path.Join(prefix, "/api/v1/osquery/distributed/write"),
"--distributed_tls_read_endpoint=" + path.Join(prefix, "/api/osquery/distributed/read"),
"--distributed_tls_write_endpoint=" + path.Join(prefix, "/api/osquery/distributed/write"),
"--logger_plugin=tls,filesystem",
"--logger_tls_endpoint=" + path.Join(prefix, "/api/v1/osquery/log"),
"--logger_tls_endpoint=" + path.Join(prefix, "/api/osquery/log"),
"--disable_carver=false",
// carver_disable_function is separate from disable_carver as it controls the use of file
// carving as a SQL function (eg. `SELECT carve(path) FROM processes`).
"--carver_disable_function=false",
"--carver_start_endpoint=" + path.Join(prefix, "/api/v1/osquery/carve/begin"),
"--carver_continue_endpoint=" + path.Join(prefix, "/api/v1/osquery/carve/block"),
"--carver_start_endpoint=" + path.Join(prefix, "/api/osquery/carve/begin"),
"--carver_continue_endpoint=" + path.Join(prefix, "/api/osquery/carve/block"),
"--carver_block_size=2000000",
}
}

View file

@ -260,7 +260,7 @@ func TestCachedTeamAgentOptions(t *testing.T) {
"logger_tls_period": 10,
"distributed_plugin": "tls",
"disable_distributed": false,
"logger_tls_endpoint": "/api/v1/osquery/log",
"logger_tls_endpoint": "/api/osquery/log",
"distributed_interval": 10,
"distributed_tls_max_attempts": 3
},

View file

@ -0,0 +1,62 @@
package tables
import (
"bytes"
"database/sql"
"encoding/json"
"github.com/pkg/errors"
)
func init() {
MigrationClient.AddMigration(Up_20220404091216, Down_20220404091216)
}
func Up_20220404091216(tx *sql.Tx) error {
const selectStmt = `SELECT json_value FROM app_config_json LIMIT 1`
var raw json.RawMessage
var config map[string]*json.RawMessage
row := tx.QueryRow(selectStmt)
if err := row.Scan(&raw); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil
}
return errors.Wrap(err, "select app_config_json")
}
if err := json.Unmarshal(raw, &config); err != nil {
return errors.Wrap(err, "unmarshal appconfig")
}
var (
oldPath = []byte(`"/api/v1/osquery/log"`)
newPath = []byte(`"/api/osquery/log"`)
updated = false
)
if opts := config["agent_options"]; opts != nil {
oldOpts := []byte(*opts)
newOpts := json.RawMessage(bytes.ReplaceAll(oldOpts, oldPath, newPath))
config["agent_options"] = &newOpts
updated = !bytes.Equal(oldOpts, newOpts)
}
if !updated {
return nil
}
b, err := json.Marshal(config)
if err != nil {
return errors.Wrap(err, "marshal updated appconfig")
}
const updateStmt = `UPDATE app_config_json SET json_value = ? WHERE id = 1`
if _, err := tx.Exec(updateStmt, b); err != nil {
return errors.Wrap(err, "update app_config_json")
}
return nil
}
func Down_20220404091216(tx *sql.Tx) error {
return nil
}

View file

@ -0,0 +1,64 @@
package tables
import (
"database/sql"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestUp_20220404091216(t *testing.T) {
db := applyUpToPrev(t) // must be done in top-level test as the migration comes from the test name
t.Run("no entry", func(t *testing.T) {
_, err := db.Exec(`DELETE FROM app_config_json`)
require.NoError(t, err)
// Apply current migration.
applyNext(t, db)
var count int
err = db.Get(&count, `SELECT 1 FROM app_config_json`)
require.Error(t, err)
require.ErrorIs(t, err, sql.ErrNoRows)
})
db = applyUpToPrev(t)
t.Run("required update", func(t *testing.T) {
var raw string
err := db.Get(&raw, `SELECT json_value FROM app_config_json`)
require.NoError(t, err)
require.Contains(t, raw, "/api/v1/osquery/log")
require.NotContains(t, raw, "/api/osquery/log")
oriLen := len(raw)
// Apply current migration.
applyNext(t, db)
err = db.Get(&raw, `SELECT json_value FROM app_config_json`)
require.NoError(t, err)
require.NotContains(t, raw, "/api/v1/osquery/log")
require.Contains(t, raw, "/api/osquery/log")
require.Len(t, raw, oriLen-3) // ensure all the rest is left as-is, only "/v1" is removed
})
db = applyUpToPrev(t)
t.Run("no update required", func(t *testing.T) {
var raw string
err := db.Get(&raw, `SELECT json_value FROM app_config_json`)
require.NoError(t, err)
oriLen := len(raw)
raw = strings.ReplaceAll(raw, "/api/v1/osquery/log", "/api/v2/osquery/log")
_, err = db.Exec(`UPDATE app_config_json SET json_value = ? WHERE id = 1`, raw)
require.NoError(t, err)
// Apply current migration.
applyNext(t, db)
err = db.Get(&raw, `SELECT json_value FROM app_config_json`)
require.NoError(t, err)
require.NotContains(t, raw, "/api/osquery/log")
require.Contains(t, raw, "/api/v2/osquery/log")
require.Len(t, raw, oriLen) // left untouched
})
}

File diff suppressed because one or more lines are too long

View file

@ -123,7 +123,7 @@ type AppConfig struct {
// EnrichedAppConfig contains the AppConfig along with additional fleet
// instance configuration settings as returned by the
// "GET /api/v1/fleet/config" API endpoint (and fleetctl get config).
// "GET /api/latest/fleet/config" API endpoint (and fleetctl get config).
type EnrichedAppConfig struct {
AppConfig
@ -235,7 +235,7 @@ func (c *AppConfig) ApplyDefaultsForNewInstalls() {
c.SMTPSettings.SMTPVerifySSLCerts = true
c.SMTPSettings.SMTPEnableTLS = true
agentOptions := json.RawMessage(`{"config": {"options": {"logger_plugin": "tls", "pack_delimiter": "/", "logger_tls_period": 10, "distributed_plugin": "tls", "disable_distributed": false, "logger_tls_endpoint": "/api/v1/osquery/log", "distributed_interval": 10, "distributed_tls_max_attempts": 3}, "decorators": {"load": ["SELECT uuid AS host_uuid FROM system_info;", "SELECT hostname AS hostname FROM system_info;"]}}, "overrides": {}}`)
agentOptions := json.RawMessage(`{"config": {"options": {"logger_plugin": "tls", "pack_delimiter": "/", "logger_tls_period": 10, "distributed_plugin": "tls", "disable_distributed": false, "logger_tls_endpoint": "/api/osquery/log", "distributed_interval": 10, "distributed_tls_max_attempts": 3}, "decorators": {"load": ["SELECT uuid AS host_uuid FROM system_info;", "SELECT hostname AS hostname FROM system_info;"]}}, "overrides": {}}`)
c.AgentOptions = &agentOptions
c.HostSettings.EnableSoftwareInventory = true

View file

@ -7,14 +7,14 @@ import (
// ApplyAppConfig sends the application config to be applied to the Fleet instance.
func (c *Client) ApplyAppConfig(payload interface{}) error {
verb, path := "PATCH", "/api/v1/fleet/config"
verb, path := "PATCH", "/api/latest/fleet/config"
var responseBody appConfigResponse
return c.authenticatedRequest(payload, verb, path, &responseBody)
}
// GetAppConfig fetches the application config from the server API
func (c *Client) GetAppConfig() (*fleet.EnrichedAppConfig, error) {
verb, path := "GET", "/api/v1/fleet/config"
verb, path := "GET", "/api/latest/fleet/config"
var responseBody fleet.EnrichedAppConfig
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return &responseBody, err
@ -22,7 +22,7 @@ func (c *Client) GetAppConfig() (*fleet.EnrichedAppConfig, error) {
// GetEnrollSecretSpec fetches the enroll secrets stored on the server
func (c *Client) GetEnrollSecretSpec() (*fleet.EnrollSecretSpec, error) {
verb, path := "GET", "/api/v1/fleet/spec/enroll_secret"
verb, path := "GET", "/api/latest/fleet/spec/enroll_secret"
var responseBody getEnrollSecretSpecResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Spec, err
@ -31,13 +31,13 @@ func (c *Client) GetEnrollSecretSpec() (*fleet.EnrollSecretSpec, error) {
// ApplyEnrollSecretSpec applies the enroll secrets.
func (c *Client) ApplyEnrollSecretSpec(spec *fleet.EnrollSecretSpec) error {
req := applyEnrollSecretSpecRequest{Spec: spec}
verb, path := "POST", "/api/v1/fleet/spec/enroll_secret"
verb, path := "POST", "/api/latest/fleet/spec/enroll_secret"
var responseBody applyEnrollSecretSpecResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}
func (c *Client) Version() (*version.Info, error) {
verb, path := "GET", "/api/v1/fleet/version"
verb, path := "GET", "/api/latest/fleet/version"
var responseBody versionResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Info, err

View file

@ -11,14 +11,14 @@ import (
// ListCarves lists the file carving sessions
func (c *Client) ListCarves(opt fleet.CarveListOptions) ([]*fleet.CarveMetadata, error) {
endpoint := "/api/v1/fleet/carves"
endpoint := "/api/latest/fleet/carves"
rawQuery := ""
if opt.Expired {
rawQuery = "expired=1"
}
response, err := c.AuthenticatedDo("GET", endpoint, rawQuery, nil)
if err != nil {
return nil, fmt.Errorf("GET /api/v1/fleet/carves: %w", err)
return nil, fmt.Errorf("GET /api/latest/fleet/carves: %w", err)
}
defer response.Body.Close()
@ -49,7 +49,7 @@ func (c *Client) ListCarves(opt fleet.CarveListOptions) ([]*fleet.CarveMetadata,
}
func (c *Client) GetCarve(carveId int64) (*fleet.CarveMetadata, error) {
endpoint := fmt.Sprintf("/api/v1/fleet/carves/%d", carveId)
endpoint := fmt.Sprintf("/api/latest/fleet/carves/%d", carveId)
response, err := c.AuthenticatedDo("GET", endpoint, "", nil)
if err != nil {
return nil, fmt.Errorf("GET "+endpoint+": %w", err)
@ -77,7 +77,7 @@ func (c *Client) GetCarve(carveId int64) (*fleet.CarveMetadata, error) {
func (c *Client) getCarveBlock(carveId, blockId int64) ([]byte, error) {
path := fmt.Sprintf(
"/api/v1/fleet/carves/%d/block/%d",
"/api/latest/fleet/carves/%d/block/%d",
carveId,
blockId,
)
@ -160,7 +160,7 @@ func (r *carveReader) Read(p []byte) (n int, err error) {
// DownloadCarve creates a Reader downloading a carve (by ID)
func (c *Client) DownloadCarve(id int64) (io.Reader, error) {
path := fmt.Sprintf("/api/v1/fleet/carves/%d", id)
path := fmt.Sprintf("/api/latest/fleet/carves/%d", id)
response, err := c.AuthenticatedDo("GET", path, "", nil)
if err != nil {
return nil, fmt.Errorf("GET %s: %w", path, err)

View file

@ -7,7 +7,7 @@ import (
// GetHosts retrieves the list of all Hosts
func (c *Client) GetHosts(query string) ([]HostResponse, error) {
verb, path := "GET", "/api/v1/fleet/hosts"
verb, path := "GET", "/api/latest/fleet/hosts"
var responseBody listHostsResponse
err := c.authenticatedRequestWithQuery(nil, verb, path, &responseBody, query)
return responseBody.Hosts, err
@ -16,14 +16,14 @@ func (c *Client) GetHosts(query string) ([]HostResponse, error) {
// HostByIdentifier retrieves a host by the uuid, osquery_host_id, hostname, or
// node_key.
func (c *Client) HostByIdentifier(identifier string) (*HostDetailResponse, error) {
verb, path := "GET", "/api/v1/fleet/hosts/identifier/"+identifier
verb, path := "GET", "/api/latest/fleet/hosts/identifier/"+identifier
var responseBody getHostResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Host, err
}
func (c *Client) translateTransferHostsToIDs(hosts []string, label string, team string) ([]uint, uint, uint, error) {
verb, path := "POST", "/api/v1/fleet/translate"
verb, path := "POST", "/api/latest/fleet/translate"
var responseBody translatorResponse
var translatePayloads []fleet.TranslatePayload
@ -88,7 +88,7 @@ func (c *Client) TransferHosts(hosts []string, label string, status, searchQuery
}
if len(hosts) != 0 {
verb, path := "POST", "/api/v1/fleet/hosts/transfer"
verb, path := "POST", "/api/latest/fleet/hosts/transfer"
var responseBody addHostsToTeamResponse
params := addHostsToTeamRequest{TeamID: ptr.Uint(teamID), HostIDs: hostIDs}
return c.authenticatedRequest(params, verb, path, &responseBody)
@ -99,7 +99,7 @@ func (c *Client) TransferHosts(hosts []string, label string, status, searchQuery
labelIDPtr = &labelID
}
verb, path := "POST", "/api/v1/fleet/hosts/transfer/filter"
verb, path := "POST", "/api/latest/fleet/hosts/transfer/filter"
var responseBody addHostsToTeamByFilterResponse
params := addHostsToTeamByFilterRequest{TeamID: ptr.Uint(teamID), Filters: struct {
MatchQuery string `json:"query"`

View file

@ -10,14 +10,14 @@ import (
// Fleet instance.
func (c *Client) ApplyLabels(specs []*fleet.LabelSpec) error {
req := applyLabelSpecsRequest{Specs: specs}
verb, path := "POST", "/api/v1/fleet/spec/labels"
verb, path := "POST", "/api/latest/fleet/spec/labels"
var responseBody applyLabelSpecsResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}
// GetLabel retrieves information about a label by name
func (c *Client) GetLabel(name string) (*fleet.LabelSpec, error) {
verb, path := "GET", "/api/v1/fleet/spec/labels/"+url.PathEscape(name)
verb, path := "GET", "/api/latest/fleet/spec/labels/"+url.PathEscape(name)
var responseBody getLabelSpecResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Spec, err
@ -25,7 +25,7 @@ func (c *Client) GetLabel(name string) (*fleet.LabelSpec, error) {
// GetLabels retrieves the list of all Labels.
func (c *Client) GetLabels() ([]*fleet.LabelSpec, error) {
verb, path := "GET", "/api/v1/fleet/spec/labels"
verb, path := "GET", "/api/latest/fleet/spec/labels"
var responseBody getLabelSpecsResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Specs, err
@ -33,7 +33,7 @@ func (c *Client) GetLabels() ([]*fleet.LabelSpec, error) {
// DeleteLabel deletes the label with the matching name.
func (c *Client) DeleteLabel(name string) error {
verb, path := "DELETE", "/api/v1/fleet/labels/"+url.PathEscape(name)
verb, path := "DELETE", "/api/latest/fleet/labels/"+url.PathEscape(name)
var responseBody deleteLabelResponse
return c.authenticatedRequest(nil, verb, path, &responseBody)
}

View file

@ -71,7 +71,7 @@ func (c *Client) LiveQueryWithContext(ctx context.Context, query string, labels
QuerySQL: query,
Selected: distributedQueryCampaignTargetsByNames{Labels: labels, Hosts: hosts},
}
verb, path := "POST", "/api/v1/fleet/queries/run_by_names"
verb, path := "POST", "/api/latest/fleet/queries/run_by_names"
var responseBody createDistributedQueryCampaignResponse
err := c.authenticatedRequest(req, verb, path, &responseBody)
if err != nil {

View file

@ -20,7 +20,7 @@ func TestLiveQueryWithContext(t *testing.T) {
upgrader := websocket.Upgrader{}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v1/fleet/queries/run_by_names":
case "/api/latest/fleet/queries/run_by_names":
resp := createDistributedQueryCampaignResponse{
Campaign: &fleet.DistributedQueryCampaign{
UpdateCreateTimestamps: fleet.UpdateCreateTimestamps{

View file

@ -10,14 +10,14 @@ import (
// Fleet instance.
func (c *Client) ApplyPacks(specs []*fleet.PackSpec) error {
req := applyPackSpecsRequest{Specs: specs}
verb, path := "POST", "/api/v1/fleet/spec/packs"
verb, path := "POST", "/api/latest/fleet/spec/packs"
var responseBody applyPackSpecsResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}
// GetPack retrieves information about a pack
func (c *Client) GetPack(name string) (*fleet.PackSpec, error) {
verb, path := "GET", "/api/v1/fleet/spec/packs/"+url.PathEscape(name)
verb, path := "GET", "/api/latest/fleet/spec/packs/"+url.PathEscape(name)
var responseBody getPackSpecResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Spec, err
@ -25,7 +25,7 @@ func (c *Client) GetPack(name string) (*fleet.PackSpec, error) {
// GetPacks retrieves the list of all Packs.
func (c *Client) GetPacks() ([]*fleet.PackSpec, error) {
verb, path := "GET", "/api/v1/fleet/spec/packs"
verb, path := "GET", "/api/latest/fleet/spec/packs"
var responseBody getPackSpecsResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Specs, err
@ -33,7 +33,7 @@ func (c *Client) GetPacks() ([]*fleet.PackSpec, error) {
// DeletePack deletes the pack with the matching name.
func (c *Client) DeletePack(name string) error {
verb, path := "DELETE", "/api/v1/fleet/packs/"+url.PathEscape(name)
verb, path := "DELETE", "/api/latest/fleet/packs/"+url.PathEscape(name)
var responseBody deletePackResponse
return c.authenticatedRequest(nil, verb, path, &responseBody)
}

View file

@ -8,7 +8,7 @@ func (c *Client) CreateGlobalPolicy(name, query, description, resolution, platfo
Resolution: resolution,
Platform: platform,
}
verb, path := "POST", "/api/v1/fleet/global/policies"
verb, path := "POST", "/api/latest/fleet/global/policies"
var responseBody globalPolicyResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}

View file

@ -10,14 +10,14 @@ import (
// Fleet instance.
func (c *Client) ApplyQueries(specs []*fleet.QuerySpec) error {
req := applyQuerySpecsRequest{Specs: specs}
verb, path := "POST", "/api/v1/fleet/spec/queries"
verb, path := "POST", "/api/latest/fleet/spec/queries"
var responseBody applyQuerySpecsResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}
// GetQuery retrieves the list of all Queries.
func (c *Client) GetQuery(name string) (*fleet.QuerySpec, error) {
verb, path := "GET", "/api/v1/fleet/spec/queries/"+url.PathEscape(name)
verb, path := "GET", "/api/latest/fleet/spec/queries/"+url.PathEscape(name)
var responseBody getQuerySpecResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Spec, err
@ -25,7 +25,7 @@ func (c *Client) GetQuery(name string) (*fleet.QuerySpec, error) {
// GetQueries retrieves the list of all Queries.
func (c *Client) GetQueries() ([]*fleet.QuerySpec, error) {
verb, path := "GET", "/api/v1/fleet/spec/queries"
verb, path := "GET", "/api/latest/fleet/spec/queries"
var responseBody getQuerySpecsResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
return responseBody.Specs, err
@ -33,7 +33,7 @@ func (c *Client) GetQueries() ([]*fleet.QuerySpec, error) {
// DeleteQuery deletes the query with the matching name.
func (c *Client) DeleteQuery(name string) error {
verb, path := "DELETE", "/api/v1/fleet/queries/"+url.PathEscape(name)
verb, path := "DELETE", "/api/latest/fleet/queries/"+url.PathEscape(name)
var responseBody deleteQueryResponse
return c.authenticatedRequest(nil, verb, path, &responseBody)
}

View file

@ -14,9 +14,9 @@ func (c *Client) Login(email, password string) (string, error) {
Password: password,
}
response, err := c.Do("POST", "/api/v1/fleet/login", "", params)
response, err := c.Do("POST", "/api/latest/fleet/login", "", params)
if err != nil {
return "", fmt.Errorf("POST /api/v1/fleet/login: %w", err)
return "", fmt.Errorf("POST /api/latest/fleet/login: %w", err)
}
defer response.Body.Close()
@ -47,7 +47,7 @@ func (c *Client) Login(email, password string) (string, error) {
// Logout attempts to logout to the current Fleet instance.
func (c *Client) Logout() error {
verb, path := "POST", "/api/v1/fleet/logout"
verb, path := "POST", "/api/latest/fleet/logout"
var responseBody logoutResponse
return c.authenticatedRequest(nil, verb, path, &responseBody)
}

View file

@ -8,7 +8,7 @@ import (
// ListSoftware retrieves the software running across hosts.
func (c *Client) ListSoftware(teamID *uint) ([]fleet.Software, error) {
verb, path := "GET", "/api/v1/fleet/software"
verb, path := "GET", "/api/latest/fleet/software"
query := ""
if teamID != nil {
query = fmt.Sprintf("team_id=%d", *teamID)

View file

@ -16,7 +16,7 @@ func (c *Client) SearchTargets(query string, hostIDs, labelIDs []uint) (*fleet.T
// TODO handle TeamIDs
},
}
verb, path := "POST", "/api/v1/fleet/targets"
verb, path := "POST", "/api/latest/fleet/targets"
var responseBody searchTargetsResponse
err := c.authenticatedRequest(req, verb, path, &responseBody)
if err != nil {

View file

@ -6,7 +6,7 @@ import (
// ListTeams retrieves the list of teams.
func (c *Client) ListTeams(query string) ([]fleet.Team, error) {
verb, path := "GET", "/api/v1/fleet/teams"
verb, path := "GET", "/api/latest/fleet/teams"
var responseBody listTeamsResponse
err := c.authenticatedRequestWithQuery(nil, verb, path, &responseBody, query)
if err != nil {
@ -19,7 +19,7 @@ func (c *Client) ListTeams(query string) ([]fleet.Team, error) {
// Fleet instance.
func (c *Client) ApplyTeams(specs []*fleet.TeamSpec) error {
req := applyTeamSpecsRequest{Specs: specs}
verb, path := "POST", "/api/v1/fleet/spec/teams"
verb, path := "POST", "/api/latest/fleet/spec/teams"
var responseBody applyTeamSpecsResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}
@ -28,7 +28,7 @@ func (c *Client) ApplyTeams(specs []*fleet.TeamSpec) error {
// Fleet instance.
func (c *Client) ApplyPolicies(specs []*fleet.PolicySpec) error {
req := applyPolicySpecsRequest{Specs: specs}
verb, path := "POST", "/api/v1/fleet/spec/policies"
verb, path := "POST", "/api/latest/fleet/spec/policies"
var responseBody applyPolicySpecsResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}

View file

@ -9,7 +9,7 @@ import (
// CreateUser creates a new user, skipping the invitation process.
func (c *Client) CreateUser(p fleet.UserPayload) error {
verb, path := "POST", "/api/v1/fleet/users/admin"
verb, path := "POST", "/api/latest/fleet/users/admin"
var responseBody createUserResponse
return c.authenticatedRequest(p, verb, path, &responseBody)
@ -17,7 +17,7 @@ func (c *Client) CreateUser(p fleet.UserPayload) error {
// ListUsers retrieves the list of users.
func (c *Client) ListUsers() ([]fleet.User, error) {
verb, path := "GET", "/api/v1/fleet/users"
verb, path := "GET", "/api/latest/fleet/users"
var responseBody listUsersResponse
err := c.authenticatedRequest(nil, verb, path, &responseBody)
@ -30,13 +30,13 @@ func (c *Client) ListUsers() ([]fleet.User, error) {
// ApplyUsersRoleSecretSpec applies the global and team roles for users.
func (c *Client) ApplyUsersRoleSecretSpec(spec *fleet.UsersRoleSpec) error {
req := applyUserRoleSpecsRequest{Spec: spec}
verb, path := "POST", "/api/v1/fleet/users/roles/spec"
verb, path := "POST", "/api/latest/fleet/users/roles/spec"
var responseBody applyUserRoleSpecsResponse
return c.authenticatedRequest(req, verb, path, &responseBody)
}
func (c *Client) userIdFromEmail(email string) (uint, error) {
verb, path := "POST", "/api/v1/fleet/translate"
verb, path := "POST", "/api/latest/fleet/translate"
var responseBody translatorResponse
params := translatorRequest{List: []fleet.TranslatePayload{
@ -63,7 +63,7 @@ func (c *Client) DeleteUser(email string) error {
return err
}
verb, path := "DELETE", fmt.Sprintf("/api/v1/fleet/users/%d", userID)
verb, path := "DELETE", fmt.Sprintf("/api/latest/fleet/users/%d", userID)
var responseBody deleteUserResponse
return c.authenticatedRequest(nil, verb, path, &responseBody)
}

View file

@ -102,6 +102,7 @@ func MakeHandler(svc fleet.Service, config config.FleetConfig, logger kitlog.Log
attachFleetAPIRoutes(r, svc, config, logger, limitStore, fleetAPIOptions)
// Results endpoint is handled different due to websockets use
// TODO: this would not work once v1 is deprecated - note that the handler too uses the /v1/ path
// and this routes on path prefix, not exact path (unlike the authendpointer struct).
r.PathPrefix("/api/v1/fleet/results/").
@ -212,8 +213,11 @@ var (
func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetConfig,
logger kitlog.Logger, limitStore throttled.GCRAStore, opts []kithttp.ServerOption,
) {
apiVersions := []string{"v1", "2022-04"}
// user-authenticated endpoints
ue := newUserAuthenticatedEndpointer(svc, opts, r, "v1")
ue := newUserAuthenticatedEndpointer(svc, opts, r, apiVersions...)
ue.GET("/api/_version_/fleet/me", meEndpoint, nil)
ue.GET("/api/_version_/fleet/sessions/{id:[0-9]+}", getInfoAboutSessionEndpoint, getInfoAboutSessionRequest{})
@ -241,12 +245,6 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.DELETE("/api/_version_/fleet/teams/{id:[0-9]+}/users", deleteTeamUsersEndpoint, modifyTeamUsersRequest{})
ue.GET("/api/_version_/fleet/teams/{id:[0-9]+}/secrets", teamEnrollSecretsEndpoint, teamEnrollSecretsRequest{})
// Alias /api/_version_/fleet/team/ -> /api/_version_/fleet/teams/
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule").GET("/api/_version_/fleet/teams/{team_id}/schedule", getTeamScheduleEndpoint, getTeamScheduleRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule").POST("/api/_version_/fleet/teams/{team_id}/schedule", teamScheduleQueryEndpoint, teamScheduleQueryRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule/{scheduled_query_id}").PATCH("/api/_version_/fleet/teams/{team_id}/schedule/{scheduled_query_id}", modifyTeamScheduleEndpoint, modifyTeamScheduleRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule/{scheduled_query_id}").DELETE("/api/_version_/fleet/teams/{team_id}/schedule/{scheduled_query_id}", deleteTeamScheduleEndpoint, deleteTeamScheduleRequest{})
ue.GET("/api/_version_/fleet/users", listUsersEndpoint, listUsersRequest{})
ue.POST("/api/_version_/fleet/users/admin", createUserEndpoint, createUserRequest{})
ue.GET("/api/_version_/fleet/users/{id:[0-9]+}", getUserEndpoint, getUserRequest{})
@ -265,17 +263,26 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.DELETE("/api/_version_/fleet/invites/{id:[0-9]+}", deleteInviteEndpoint, deleteInviteRequest{})
ue.PATCH("/api/_version_/fleet/invites/{id:[0-9]+}", updateInviteEndpoint, updateInviteRequest{})
ue.POST("/api/_version_/fleet/global/policies", globalPolicyEndpoint, globalPolicyRequest{})
ue.GET("/api/_version_/fleet/global/policies", listGlobalPoliciesEndpoint, nil)
ue.GET("/api/_version_/fleet/global/policies/{policy_id}", getPolicyByIDEndpoint, getPolicyByIDRequest{})
ue.POST("/api/_version_/fleet/global/policies/delete", deleteGlobalPoliciesEndpoint, deleteGlobalPoliciesRequest{})
ue.PATCH("/api/_version_/fleet/global/policies/{policy_id}", modifyGlobalPolicyEndpoint, modifyGlobalPolicyRequest{})
ue.EndingAtVersion("v1").POST("/api/_version_/fleet/global/policies", globalPolicyEndpoint, globalPolicyRequest{})
ue.StartingAtVersion("2022-04").POST("/api/_version_/fleet/policies", globalPolicyEndpoint, globalPolicyRequest{})
ue.EndingAtVersion("v1").GET("/api/_version_/fleet/global/policies", listGlobalPoliciesEndpoint, nil)
ue.StartingAtVersion("2022-04").GET("/api/_version_/fleet/policies", listGlobalPoliciesEndpoint, nil)
ue.EndingAtVersion("v1").GET("/api/_version_/fleet/global/policies/{policy_id}", getPolicyByIDEndpoint, getPolicyByIDRequest{})
ue.StartingAtVersion("2022-04").GET("/api/_version_/fleet/policies/{policy_id}", getPolicyByIDEndpoint, getPolicyByIDRequest{})
ue.EndingAtVersion("v1").POST("/api/_version_/fleet/global/policies/delete", deleteGlobalPoliciesEndpoint, deleteGlobalPoliciesRequest{})
ue.StartingAtVersion("2022-04").POST("/api/_version_/fleet/policies/delete", deleteGlobalPoliciesEndpoint, deleteGlobalPoliciesRequest{})
ue.EndingAtVersion("v1").PATCH("/api/_version_/fleet/global/policies/{policy_id}", modifyGlobalPolicyEndpoint, modifyGlobalPolicyRequest{})
ue.StartingAtVersion("2022-04").PATCH("/api/_version_/fleet/policies/{policy_id}", modifyGlobalPolicyEndpoint, modifyGlobalPolicyRequest{})
// Alias /api/_version_/fleet/team/ -> /api/_version_/fleet/teams/
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies").POST("/api/_version_/fleet/teams/{team_id}/policies", teamPolicyEndpoint, teamPolicyRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies").GET("/api/_version_/fleet/teams/{team_id}/policies", listTeamPoliciesEndpoint, listTeamPoliciesRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies/{policy_id}").GET("/api/_version_/fleet/teams/{team_id}/policies/{policy_id}", getTeamPolicyByIDEndpoint, getTeamPolicyByIDRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies/delete").POST("/api/_version_/fleet/teams/{team_id}/policies/delete", deleteTeamPoliciesEndpoint, deleteTeamPoliciesRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies").
POST("/api/_version_/fleet/teams/{team_id}/policies", teamPolicyEndpoint, teamPolicyRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies").
GET("/api/_version_/fleet/teams/{team_id}/policies", listTeamPoliciesEndpoint, listTeamPoliciesRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies/{policy_id}").
GET("/api/_version_/fleet/teams/{team_id}/policies/{policy_id}", getTeamPolicyByIDEndpoint, getTeamPolicyByIDRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/policies/delete").
POST("/api/_version_/fleet/teams/{team_id}/policies/delete", deleteTeamPoliciesEndpoint, deleteTeamPoliciesRequest{})
ue.PATCH("/api/_version_/fleet/teams/{team_id}/policies/{policy_id}", modifyTeamPolicyEndpoint, modifyTeamPolicyRequest{})
ue.POST("/api/_version_/fleet/spec/policies", applyPolicySpecsEndpoint, applyPolicySpecsRequest{})
@ -290,12 +297,6 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.GET("/api/_version_/fleet/spec/queries", getQuerySpecsEndpoint, nil)
ue.GET("/api/_version_/fleet/spec/queries/{name}", getQuerySpecEndpoint, getGenericSpecRequest{})
ue.GET("/api/_version_/fleet/packs/{id:[0-9]+}/scheduled", getScheduledQueriesInPackEndpoint, getScheduledQueriesInPackRequest{})
ue.POST("/api/_version_/fleet/schedule", scheduleQueryEndpoint, scheduleQueryRequest{})
ue.GET("/api/_version_/fleet/schedule/{id:[0-9]+}", getScheduledQueryEndpoint, getScheduledQueryRequest{})
ue.PATCH("/api/_version_/fleet/schedule/{id:[0-9]+}", modifyScheduledQueryEndpoint, modifyScheduledQueryRequest{})
ue.DELETE("/api/_version_/fleet/schedule/{id:[0-9]+}", deleteScheduledQueryEndpoint, deleteScheduledQueryRequest{})
ue.GET("/api/_version_/fleet/packs/{id:[0-9]+}", getPackEndpoint, getPackRequest{})
ue.POST("/api/_version_/fleet/packs", createPackEndpoint, createPackRequest{})
ue.PATCH("/api/_version_/fleet/packs/{id:[0-9]+}", modifyPackEndpoint, modifyPackRequest{})
@ -340,10 +341,33 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.GET("/api/_version_/fleet/activities", listActivitiesEndpoint, listActivitiesRequest{})
ue.GET("/api/_version_/fleet/global/schedule", getGlobalScheduleEndpoint, getGlobalScheduleRequest{})
ue.POST("/api/_version_/fleet/global/schedule", globalScheduleQueryEndpoint, globalScheduleQueryRequest{})
ue.PATCH("/api/_version_/fleet/global/schedule/{id:[0-9]+}", modifyGlobalScheduleEndpoint, modifyGlobalScheduleRequest{})
ue.DELETE("/api/_version_/fleet/global/schedule/{id:[0-9]+}", deleteGlobalScheduleEndpoint, deleteGlobalScheduleRequest{})
ue.GET("/api/_version_/fleet/packs/{id:[0-9]+}/scheduled", getScheduledQueriesInPackEndpoint, getScheduledQueriesInPackRequest{})
ue.EndingAtVersion("v1").POST("/api/_version_/fleet/schedule", scheduleQueryEndpoint, scheduleQueryRequest{})
ue.StartingAtVersion("2022-04").POST("/api/_version_/fleet/packs/schedule", scheduleQueryEndpoint, scheduleQueryRequest{})
ue.GET("/api/_version_/fleet/schedule/{id:[0-9]+}", getScheduledQueryEndpoint, getScheduledQueryRequest{})
ue.EndingAtVersion("v1").PATCH("/api/_version_/fleet/schedule/{id:[0-9]+}", modifyScheduledQueryEndpoint, modifyScheduledQueryRequest{})
ue.StartingAtVersion("2022-04").PATCH("/api/_version_/fleet/packs/schedule/{id:[0-9]+}", modifyScheduledQueryEndpoint, modifyScheduledQueryRequest{})
ue.EndingAtVersion("v1").DELETE("/api/_version_/fleet/schedule/{id:[0-9]+}", deleteScheduledQueryEndpoint, deleteScheduledQueryRequest{})
ue.StartingAtVersion("2022-04").DELETE("/api/_version_/fleet/packs/schedule/{id:[0-9]+}", deleteScheduledQueryEndpoint, deleteScheduledQueryRequest{})
ue.EndingAtVersion("v1").GET("/api/_version_/fleet/global/schedule", getGlobalScheduleEndpoint, getGlobalScheduleRequest{})
ue.StartingAtVersion("2022-04").GET("/api/_version_/fleet/schedule", getGlobalScheduleEndpoint, getGlobalScheduleRequest{})
ue.EndingAtVersion("v1").POST("/api/_version_/fleet/global/schedule", globalScheduleQueryEndpoint, globalScheduleQueryRequest{})
ue.StartingAtVersion("2022-04").POST("/api/_version_/fleet/schedule", globalScheduleQueryEndpoint, globalScheduleQueryRequest{})
ue.EndingAtVersion("v1").PATCH("/api/_version_/fleet/global/schedule/{id:[0-9]+}", modifyGlobalScheduleEndpoint, modifyGlobalScheduleRequest{})
ue.StartingAtVersion("2022-04").PATCH("/api/_version_/fleet/schedule/{id:[0-9]+}", modifyGlobalScheduleEndpoint, modifyGlobalScheduleRequest{})
ue.EndingAtVersion("v1").DELETE("/api/_version_/fleet/global/schedule/{id:[0-9]+}", deleteGlobalScheduleEndpoint, deleteGlobalScheduleRequest{})
ue.StartingAtVersion("2022-04").DELETE("/api/_version_/fleet/schedule/{id:[0-9]+}", deleteGlobalScheduleEndpoint, deleteGlobalScheduleRequest{})
// Alias /api/_version_/fleet/team/ -> /api/_version_/fleet/teams/
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule").
GET("/api/_version_/fleet/teams/{team_id}/schedule", getTeamScheduleEndpoint, getTeamScheduleRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule").
POST("/api/_version_/fleet/teams/{team_id}/schedule", teamScheduleQueryEndpoint, teamScheduleQueryRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule/{scheduled_query_id}").
PATCH("/api/_version_/fleet/teams/{team_id}/schedule/{scheduled_query_id}", modifyTeamScheduleEndpoint, modifyTeamScheduleRequest{})
ue.WithAltPaths("/api/_version_/fleet/team/{team_id}/schedule/{scheduled_query_id}").
DELETE("/api/_version_/fleet/teams/{team_id}/schedule/{scheduled_query_id}", deleteTeamScheduleEndpoint, deleteTeamScheduleRequest{})
ue.GET("/api/_version_/fleet/carves", listCarvesEndpoint, listCarvesRequest{})
ue.GET("/api/_version_/fleet/carves/{id:[0-9]+}", getCarveEndpoint, getCarveRequest{})
@ -356,30 +380,45 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.GET("/api/_version_/fleet/status/live_query", statusLiveQueryEndpoint, nil)
// device-authenticated endpoints
de := newDeviceAuthenticatedEndpointer(svc, logger, opts, r, "v1")
de := newDeviceAuthenticatedEndpointer(svc, logger, opts, r, apiVersions...)
de.GET("/api/_version_/fleet/device/{token}", getDeviceHostEndpoint, getDeviceHostRequest{})
de.POST("/api/_version_/fleet/device/{token}/refetch", refetchDeviceHostEndpoint, refetchDeviceHostRequest{})
de.GET("/api/_version_/fleet/device/{token}/device_mapping", listDeviceHostDeviceMappingEndpoint, listDeviceHostDeviceMappingRequest{})
de.GET("/api/_version_/fleet/device/{token}/macadmins", getDeviceMacadminsDataEndpoint, getDeviceMacadminsDataRequest{})
// host-authenticated endpoints
he := newHostAuthenticatedEndpointer(svc, logger, opts, r, "v1")
he.POST("/api/_version_/osquery/config", getClientConfigEndpoint, getClientConfigRequest{})
he.POST("/api/_version_/osquery/distributed/read", getDistributedQueriesEndpoint, getDistributedQueriesRequest{})
he.POST("/api/_version_/osquery/distributed/write", submitDistributedQueryResultsEndpoint, submitDistributedQueryResultsRequestShim{})
he.POST("/api/_version_/osquery/carve/begin", carveBeginEndpoint, carveBeginRequest{})
he.POST("/api/_version_/osquery/log", submitLogsEndpoint, submitLogsRequest{})
he := newHostAuthenticatedEndpointer(svc, logger, opts, r, apiVersions...)
// Note that the /osquery/ endpoints are *not* versioned, i.e. there is no
// `_version_` placeholder in the path. This is deliberate, see
// https://github.com/fleetdm/fleet/pull/4731#discussion_r838931732 For now
// we add an alias to `/api/v1/osquery` so that it is backwards compatible,
// but even that `v1` is *not* part of the standard versioning, it will still
// work even after we remove support for the `v1` version for the rest of the
// API. This allows us to deprecate osquery endpoints separately.
he.WithAltPaths("/api/v1/osquery/config").
POST("/api/osquery/config", getClientConfigEndpoint, getClientConfigRequest{})
he.WithAltPaths("/api/v1/osquery/distributed/read").
POST("/api/osquery/distributed/read", getDistributedQueriesEndpoint, getDistributedQueriesRequest{})
he.WithAltPaths("/api/v1/osquery/distributed/write").
POST("/api/osquery/distributed/write", submitDistributedQueryResultsEndpoint, submitDistributedQueryResultsRequestShim{})
he.WithAltPaths("/api/v1/osquery/carve/begin").
POST("/api/osquery/carve/begin", carveBeginEndpoint, carveBeginRequest{})
he.WithAltPaths("/api/v1/osquery/log").
POST("/api/osquery/log", submitLogsEndpoint, submitLogsRequest{})
// unauthenticated endpoints - most of those are either login-related,
// invite-related or host-enrolling. So they typically do some kind of
// one-time authentication by verifying that a valid secret token is provided
// with the request.
ne := newNoAuthEndpointer(svc, opts, r, "v1")
ne.POST("/api/_version_/osquery/enroll", enrollAgentEndpoint, enrollAgentRequest{})
ne := newNoAuthEndpointer(svc, opts, r, apiVersions...)
ne.WithAltPaths("/api/v1/osquery/enroll").
POST("/api/osquery/enroll", enrollAgentEndpoint, enrollAgentRequest{})
// For some reason osquery does not provide a node key with the block data.
// Instead the carve session ID should be verified in the service method.
ne.POST("/api/_version_/osquery/carve/block", carveBlockEndpoint, carveBlockRequest{})
// For some reason osquery does not provide a node key with the block data.
// Instead the carve session ID should be verified in the service method.
ne.WithAltPaths("/api/v1/osquery/carve/block").
POST("/api/osquery/carve/block", carveBlockEndpoint, carveBlockRequest{})
ne.POST("/api/_version_/fleet/perform_required_password_reset", performRequiredPasswordResetEndpoint, performRequiredPasswordResetRequest{})
ne.POST("/api/_version_/fleet/users", createUserFromInviteEndpoint, createUserRequest{})

View file

@ -43,21 +43,10 @@ func TestAPIRoutesConflicts(t *testing.T) {
// is used to name the sub-test for that route.
status := 200
err := router.Walk(func(route *mux.Route, router *mux.Router, ancestores []*mux.Route) error {
routeStatus := status
_, path, err := mockRouteHandler(route, status)
if path == "" || err != nil { // failure or no method set
return err
}
path = reSimpleVar.ReplaceAllString(path, "$1")
// for now at least, the only times we use regexp-constrained vars is
// for numeric arguments.
path = reNumVar.ReplaceAllStringFunc(path, func(s string) string {
if strings.Index(s, "fleetversion") != -1 {
parts := strings.Split(strings.TrimPrefix(s, "{fleetversion:(?:"), "|")
return strings.TrimSuffix(parts[0], ")}")
}
return "1"
})
meths, _ := route.GetMethods()
for _, meth := range meths {
@ -65,7 +54,7 @@ func TestAPIRoutesConflicts(t *testing.T) {
name: route.GetName(),
path: path,
verb: meth,
want: routeStatus,
want: status,
})
}
@ -312,11 +301,13 @@ func mockRouteHandler(route *mux.Route, status int) (verb, path string, err erro
path = reSimpleVar.ReplaceAllString(path, "$1")
// for now at least, the only times we use regexp-constrained vars is
// for numeric arguments.
// for numeric arguments or the fleetversion specifier.
path = reNumVar.ReplaceAllStringFunc(path, func(s string) string {
if strings.Index(s, "fleetversion") != -1 {
if strings.Contains(s, "fleetversion") {
parts := strings.Split(strings.TrimPrefix(s, "{fleetversion:(?:"), "|")
return strings.TrimSuffix(parts[0], ")}")
// test with "latest" if not deprecated, or last supported version for that route
// (for either case, this will be in the last part)
return strings.TrimSuffix(parts[len(parts)-1], ")}")
}
return "1"
})

View file

@ -63,7 +63,7 @@ func TestLogin(t *testing.T) {
assert.Nil(t, err)
requestBody := io.NopCloser(bytes.NewBuffer(j))
resp, err := http.Post(server.URL+"/api/v1/fleet/login", "application/json", requestBody)
resp, err := http.Post(server.URL+"/api/latest/fleet/login", "application/json", requestBody)
require.Nil(t, err)
assert.Equal(t, tt.status, resp.StatusCode)
@ -92,7 +92,7 @@ func TestLogin(t *testing.T) {
assert.NotEqual(t, "", sessions[0].Key)
// test logout
req, _ := http.NewRequest("POST", server.URL+"/api/v1/fleet/logout", nil)
req, _ := http.NewRequest("POST", server.URL+"/api/latest/fleet/logout", nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", jsn.Token))
client := fleethttp.NewClient()
resp, err = client.Do(req)
@ -186,7 +186,7 @@ func getTestAdminToken(t *testing.T, server *httptest.Server) string {
assert.Nil(t, err)
requestBody := io.NopCloser(bytes.NewBuffer(j))
resp, err := http.Post(server.URL+"/api/v1/fleet/login", "application/json", requestBody)
resp, err := http.Post(server.URL+"/api/latest/fleet/login", "application/json", requestBody)
require.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
@ -204,7 +204,7 @@ func getTestAdminToken(t *testing.T, server *httptest.Server) string {
func TestNoHeaderErrorsDifferently(t *testing.T) {
_, _, server := setupAuthTest(t)
req, _ := http.NewRequest("GET", server.URL+"/api/v1/fleet/users", nil)
req, _ := http.NewRequest("GET", server.URL+"/api/latest/fleet/users", nil)
client := fleethttp.NewClient()
resp, err := client.Do(req)
require.Nil(t, err)
@ -223,7 +223,7 @@ func TestNoHeaderErrorsDifferently(t *testing.T) {
}
`, string(bodyBytes))
req, _ = http.NewRequest("GET", server.URL+"/api/v1/fleet/users", nil)
req, _ = http.NewRequest("GET", server.URL+"/api/latest/fleet/users", nil)
req.Header.Add("Authorization", "Bearer AAAA")
resp, err = client.Do(req)
require.Nil(t, err)

File diff suppressed because it is too large Load diff

View file

@ -50,7 +50,7 @@ func (s *integrationDSTestSuite) TestLicenseExpiration() {
ts.s = &s.Suite
ts.token = ts.getTestAdminToken()
resp := ts.Do("GET", "/api/v1/fleet/config", nil, http.StatusOK)
resp := ts.Do("GET", "/api/latest/fleet/config", nil, http.StatusOK)
if tt.shouldHaveHeader {
require.Equal(t, fleet.HeaderLicenseValueExpired, resp.Header.Get(fleet.HeaderLicenseKey))
} else {

View file

@ -47,12 +47,12 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
Description: "desc team1",
}
s.Do("POST", "/api/v1/fleet/teams", team, http.StatusOK)
s.Do("POST", "/api/latest/fleet/teams", team, http.StatusOK)
// updates a team
agentOpts := json.RawMessage(`{"config": {"foo": "bar"}, "overrides": {"platforms": {"darwin": {"foo": "override"}}}}`)
teamSpecs := applyTeamSpecsRequest{Specs: []*fleet.TeamSpec{{Name: teamName, AgentOptions: &agentOpts}}}
s.Do("POST", "/api/v1/fleet/spec/teams", teamSpecs, http.StatusOK)
s.Do("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusOK)
team, err := s.ds.TeamByName(context.Background(), teamName)
require.NoError(t, err)
@ -69,7 +69,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
require.True(t, len(teams) >= 1)
teamSpecs = applyTeamSpecsRequest{Specs: []*fleet.TeamSpec{{Name: "team2"}}}
s.Do("POST", "/api/v1/fleet/spec/teams", teamSpecs, http.StatusOK)
s.Do("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusOK)
teams, err = s.ds.ListTeams(context.Background(), fleet.TeamFilter{User: user}, fleet.ListOptions{})
require.NoError(t, err)
@ -78,14 +78,14 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
team, err = s.ds.TeamByName(context.Background(), "team2")
require.NoError(t, err)
defaultOpts := `{"config": {"options": {"logger_plugin": "tls", "pack_delimiter": "/", "logger_tls_period": 10, "distributed_plugin": "tls", "disable_distributed": false, "logger_tls_endpoint": "/api/v1/osquery/log", "distributed_interval": 10, "distributed_tls_max_attempts": 3}, "decorators": {"load": ["SELECT uuid AS host_uuid FROM system_info;", "SELECT hostname AS hostname FROM system_info;"]}}, "overrides": {}}`
defaultOpts := `{"config": {"options": {"logger_plugin": "tls", "pack_delimiter": "/", "logger_tls_period": 10, "distributed_plugin": "tls", "disable_distributed": false, "logger_tls_endpoint": "/api/osquery/log", "distributed_interval": 10, "distributed_tls_max_attempts": 3}, "decorators": {"load": ["SELECT uuid AS host_uuid FROM system_info;", "SELECT hostname AS hostname FROM system_info;"]}}, "overrides": {}}`
assert.Len(t, team.Secrets, 0)
require.NotNil(t, team.Config.AgentOptions)
require.JSONEq(t, defaultOpts, string(*team.Config.AgentOptions))
// updates secrets
teamSpecs = applyTeamSpecsRequest{Specs: []*fleet.TeamSpec{{Name: "team2", Secrets: []fleet.EnrollSecret{{Secret: "ABC"}}}}}
s.Do("POST", "/api/v1/fleet/spec/teams", teamSpecs, http.StatusOK)
s.Do("POST", "/api/latest/fleet/spec/teams", teamSpecs, http.StatusOK)
team, err = s.ds.TeamByName(context.Background(), "team2")
require.NoError(t, err)
@ -105,7 +105,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamSchedule() {
require.NoError(t, err)
ts := getTeamScheduleResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
require.Len(t, ts.Scheduled, 0)
qr, err := s.ds.NewQuery(
@ -116,10 +116,10 @@ func (s *integrationEnterpriseTestSuite) TestTeamSchedule() {
gsParams := teamScheduleQueryRequest{ScheduledQueryPayload: fleet.ScheduledQueryPayload{QueryID: &qr.ID, Interval: ptr.Uint(42)}}
r := teamScheduleQueryResponse{}
s.DoJSON("POST", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule", team1.ID), gsParams, http.StatusOK, &r)
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule", team1.ID), gsParams, http.StatusOK, &r)
ts = getTeamScheduleResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
require.Len(t, ts.Scheduled, 1)
assert.Equal(t, uint(42), ts.Scheduled[0].Interval)
assert.Equal(t, "TestQueryTeamPolicy", ts.Scheduled[0].Name)
@ -128,21 +128,21 @@ func (s *integrationEnterpriseTestSuite) TestTeamSchedule() {
modifyResp := modifyTeamScheduleResponse{}
modifyParams := modifyTeamScheduleRequest{ScheduledQueryPayload: fleet.ScheduledQueryPayload{Interval: ptr.Uint(55)}}
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule/%d", team1.ID, id), modifyParams, http.StatusOK, &modifyResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule/%d", team1.ID, id), modifyParams, http.StatusOK, &modifyResp)
// just to satisfy my paranoia, wanted to make sure the contents of the json would work
s.DoRaw("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule/%d", team1.ID, id), []byte(`{"interval": 77}`), http.StatusOK)
s.DoRaw("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule/%d", team1.ID, id), []byte(`{"interval": 77}`), http.StatusOK)
ts = getTeamScheduleResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
require.Len(t, ts.Scheduled, 1)
assert.Equal(t, uint(77), ts.Scheduled[0].Interval)
deleteResp := deleteTeamScheduleResponse{}
s.DoJSON("DELETE", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule/%d", team1.ID, id), nil, http.StatusOK, &deleteResp)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule/%d", team1.ID, id), nil, http.StatusOK, &deleteResp)
ts = getTeamScheduleResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/schedule", team1.ID), nil, http.StatusOK, &ts)
require.Len(t, ts.Scheduled, 0)
}
@ -182,7 +182,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamPolicies() {
s.token = s.getTestToken(email, password)
ts := listTeamPoliciesResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/policies", team1.ID), nil, http.StatusOK, &ts)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), nil, http.StatusOK, &ts)
require.Len(t, ts.Policies, 0)
qr, err := s.ds.NewQuery(context.Background(), &fleet.Query{Name: "TestQuery2", Description: "Some description", Query: "select * from osquery;", ObserverCanRun: true})
@ -193,10 +193,10 @@ func (s *integrationEnterpriseTestSuite) TestTeamPolicies() {
Resolution: "some team resolution",
}
r := teamPolicyResponse{}
s.DoJSON("POST", fmt.Sprintf("/api/v1/fleet/teams/%d/policies", team1.ID), tpParams, http.StatusOK, &r)
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), tpParams, http.StatusOK, &r)
ts = listTeamPoliciesResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/policies", team1.ID), nil, http.StatusOK, &ts)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), nil, http.StatusOK, &ts)
require.Len(t, ts.Policies, 1)
assert.Equal(t, "TestQuery2", ts.Policies[0].Name)
assert.Equal(t, "select * from osquery;", ts.Policies[0].Query)
@ -206,10 +206,10 @@ func (s *integrationEnterpriseTestSuite) TestTeamPolicies() {
deletePolicyParams := deleteTeamPoliciesRequest{IDs: []uint{ts.Policies[0].ID}}
deletePolicyResp := deleteTeamPoliciesResponse{}
s.DoJSON("POST", fmt.Sprintf("/api/v1/fleet/teams/%d/policies/delete", team1.ID), deletePolicyParams, http.StatusOK, &deletePolicyResp)
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/delete", team1.ID), deletePolicyParams, http.StatusOK, &deletePolicyResp)
ts = listTeamPoliciesResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/policies", team1.ID), nil, http.StatusOK, &ts)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), nil, http.StatusOK, &ts)
require.Len(t, ts.Policies, 0)
}
@ -224,7 +224,7 @@ func (s *integrationEnterpriseTestSuite) TestModifyTeamEnrollSecrets() {
Secrets: []*fleet.EnrollSecret{{Secret: "initialSecret"}},
}
s.Do("POST", "/api/v1/fleet/teams", team, http.StatusOK)
s.Do("POST", "/api/latest/fleet/teams", team, http.StatusOK)
team, err := s.ds.TeamByName(context.Background(), teamName)
require.NoError(t, err)
@ -234,7 +234,7 @@ func (s *integrationEnterpriseTestSuite) TestModifyTeamEnrollSecrets() {
req := json.RawMessage(`{"secrets": [{"secret": "testSecret1"},{"secret": "testSecret2"}]}`)
var resp teamEnrollSecretsResponse
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/secrets", team.ID), req, http.StatusOK, &resp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/secrets", team.ID), req, http.StatusOK, &resp)
require.Len(t, resp.Secrets, 2)
team, err = s.ds.TeamByName(context.Background(), teamName)
@ -243,12 +243,12 @@ func (s *integrationEnterpriseTestSuite) TestModifyTeamEnrollSecrets() {
assert.Equal(t, "testSecret2", team.Secrets[1].Secret)
// Test delete all enroll secrets
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/secrets", team.ID), json.RawMessage(`{"secrets": []}`), http.StatusOK, &resp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/secrets", team.ID), json.RawMessage(`{"secrets": []}`), http.StatusOK, &resp)
require.Len(t, resp.Secrets, 0)
// Test bad requests
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/secrets", team.ID), json.RawMessage(`{"foo": [{"secret": "testSecret3"}]}`), http.StatusUnprocessableEntity, &resp)
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/secrets", team.ID), json.RawMessage(`{}`), http.StatusUnprocessableEntity, &resp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/secrets", team.ID), json.RawMessage(`{"foo": [{"secret": "testSecret3"}]}`), http.StatusUnprocessableEntity, &resp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/secrets", team.ID), json.RawMessage(`{}`), http.StatusUnprocessableEntity, &resp)
}
func (s *integrationEnterpriseTestSuite) TestAvailableTeams() {
@ -260,7 +260,7 @@ func (s *integrationEnterpriseTestSuite) TestAvailableTeams() {
Description: "Available Team description",
}
s.Do("POST", "/api/v1/fleet/teams", team, http.StatusOK)
s.Do("POST", "/api/latest/fleet/teams", team, http.StatusOK)
team, err := s.ds.TeamByName(context.Background(), "Available Team")
require.NoError(t, err)
@ -278,7 +278,7 @@ func (s *integrationEnterpriseTestSuite) TestAvailableTeams() {
// test available teams for user assigned to global role
var getResp getUserResponse
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/users/%d", user.ID), nil, http.StatusOK, &getResp)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/users/%d", user.ID), nil, http.StatusOK, &getResp)
assert.Equal(t, user.ID, getResp.User.ID)
assert.Equal(t, ptr.String("observer"), getResp.User.GlobalRole)
assert.Len(t, getResp.User.Teams, 0) // teams is empty if user has a global role
@ -292,7 +292,7 @@ func (s *integrationEnterpriseTestSuite) TestAvailableTeams() {
require.NoError(t, err)
// test available teams for user assigned to team role
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/users/%d", user.ID), nil, http.StatusOK, &getResp)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/users/%d", user.ID), nil, http.StatusOK, &getResp)
assert.Equal(t, user.ID, getResp.User.ID)
assert.Nil(t, getResp.User.GlobalRole)
assert.Len(t, getResp.User.Teams, 1)
@ -305,7 +305,7 @@ func (s *integrationEnterpriseTestSuite) TestAvailableTeams() {
sessionKey := base64.StdEncoding.EncodeToString(key)
_, err = s.ds.NewSession(context.Background(), user.ID, sessionKey)
require.NoError(t, err)
resp := s.DoRawWithHeaders("GET", "/api/v1/fleet/me", []byte(""), http.StatusOK, map[string]string{
resp := s.DoRawWithHeaders("GET", "/api/latest/fleet/me", []byte(""), http.StatusOK, map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", sessionKey),
})
err = json.NewDecoder(resp.Body).Decode(&getResp)
@ -330,7 +330,7 @@ func (s *integrationEnterpriseTestSuite) TestTeamEndpoints() {
}
var tmResp teamResponse
s.DoJSON("POST", "/api/v1/fleet/teams", team, http.StatusOK, &tmResp)
s.DoJSON("POST", "/api/latest/fleet/teams", team, http.StatusOK, &tmResp)
assert.Equal(t, team.Name, tmResp.Team.Name)
require.Len(t, tmResp.Team.Secrets, 1)
assert.Equal(t, "DEF", tmResp.Team.Secrets[0].Secret)
@ -342,38 +342,38 @@ func (s *integrationEnterpriseTestSuite) TestTeamEndpoints() {
Secrets: []*fleet.EnrollSecret{{Secret: "GHI"}},
}
tmResp.Team = nil
s.DoJSON("POST", "/api/v1/fleet/teams", team2, http.StatusConflict, &tmResp)
s.DoJSON("POST", "/api/latest/fleet/teams", team2, http.StatusConflict, &tmResp)
// list teams
var listResp listTeamsResponse
s.DoJSON("GET", "/api/v1/fleet/teams", nil, http.StatusOK, &listResp, "query", name, "per_page", "2")
s.DoJSON("GET", "/api/latest/fleet/teams", nil, http.StatusOK, &listResp, "query", name, "per_page", "2")
require.Len(t, listResp.Teams, 1)
assert.Equal(t, team.Name, listResp.Teams[0].Name)
tm1ID := listResp.Teams[0].ID
// get team
var getResp getTeamResponse
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d", tm1ID), nil, http.StatusOK, &getResp)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d", tm1ID), nil, http.StatusOK, &getResp)
assert.Equal(t, team.Name, getResp.Team.Name)
// modify team
team.Description = "Alt " + team.Description
tmResp.Team = nil
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d", tm1ID), team, http.StatusOK, &tmResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", tm1ID), team, http.StatusOK, &tmResp)
assert.Contains(t, tmResp.Team.Description, "Alt ")
// modify non-existing team
tmResp.Team = nil
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d", tm1ID+1), team, http.StatusNotFound, &tmResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", tm1ID+1), team, http.StatusNotFound, &tmResp)
// list team users
var usersResp listUsersResponse
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID), nil, http.StatusOK, &usersResp)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID), nil, http.StatusOK, &usersResp)
assert.Len(t, usersResp.Users, 0)
// list team users - non-existing team
usersResp.Users = nil
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID+1), nil, http.StatusNotFound, &usersResp)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID+1), nil, http.StatusNotFound, &usersResp)
// create a new user
user := &fleet.User{
@ -387,71 +387,71 @@ func (s *integrationEnterpriseTestSuite) TestTeamEndpoints() {
// add a team user
tmResp.Team = nil
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: *user, Role: fleet.RoleObserver}}}, http.StatusOK, &tmResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: *user, Role: fleet.RoleObserver}}}, http.StatusOK, &tmResp)
require.Len(t, tmResp.Team.Users, 1)
assert.Equal(t, user.ID, tmResp.Team.Users[0].ID)
// add a team user - non-existing team
tmResp.Team = nil
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID+1), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: *user, Role: fleet.RoleObserver}}}, http.StatusNotFound, &tmResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID+1), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: *user, Role: fleet.RoleObserver}}}, http.StatusNotFound, &tmResp)
// add a team user - invalid user role
tmResp.Team = nil
s.DoJSON("PATCH", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: *user, Role: "foobar"}}}, http.StatusUnprocessableEntity, &tmResp)
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: *user, Role: "foobar"}}}, http.StatusUnprocessableEntity, &tmResp)
// search for that user
usersResp.Users = nil
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID), nil, http.StatusOK, &usersResp, "query", "user")
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID), nil, http.StatusOK, &usersResp, "query", "user")
require.Len(t, usersResp.Users, 1)
assert.Equal(t, user.ID, usersResp.Users[0].ID)
// search for unknown user
usersResp.Users = nil
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID), nil, http.StatusOK, &usersResp, "query", "notauser")
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID), nil, http.StatusOK, &usersResp, "query", "notauser")
require.Len(t, usersResp.Users, 0)
// delete team user
tmResp.Team = nil
s.DoJSON("DELETE", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: fleet.User{ID: user.ID}}}}, http.StatusOK, &tmResp)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: fleet.User{ID: user.ID}}}}, http.StatusOK, &tmResp)
require.Len(t, tmResp.Team.Users, 0)
// delete team user - unknown user
tmResp.Team = nil
s.DoJSON("DELETE", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: fleet.User{ID: user.ID + 1}}}}, http.StatusOK, &tmResp)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: fleet.User{ID: user.ID + 1}}}}, http.StatusOK, &tmResp)
require.Len(t, tmResp.Team.Users, 0)
// delete team user - unknown team
tmResp.Team = nil
s.DoJSON("DELETE", fmt.Sprintf("/api/v1/fleet/teams/%d/users", tm1ID+1), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: fleet.User{ID: user.ID}}}}, http.StatusNotFound, &tmResp)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/teams/%d/users", tm1ID+1), modifyTeamUsersRequest{Users: []fleet.TeamUser{{User: fleet.User{ID: user.ID}}}}, http.StatusNotFound, &tmResp)
// modify team agent options (options for orbit/osquery)
tmResp.Team = nil
opts := map[string]string{"x": "y"}
s.DoJSON("POST", fmt.Sprintf("/api/v1/fleet/teams/%d/agent_options", tm1ID), opts, http.StatusOK, &tmResp)
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/agent_options", tm1ID), opts, http.StatusOK, &tmResp)
var m map[string]string
require.NoError(t, json.Unmarshal(*tmResp.Team.Config.AgentOptions, &m))
assert.Equal(t, opts, m)
// modify team agent options - unknown team
tmResp.Team = nil
s.DoJSON("POST", fmt.Sprintf("/api/v1/fleet/teams/%d/agent_options", tm1ID+1), opts, http.StatusNotFound, &tmResp)
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/teams/%d/agent_options", tm1ID+1), opts, http.StatusNotFound, &tmResp)
// get team enroll secrets
var secResp teamEnrollSecretsResponse
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/secrets", tm1ID), nil, http.StatusOK, &secResp)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/secrets", tm1ID), nil, http.StatusOK, &secResp)
require.Len(t, secResp.Secrets, 1)
assert.Equal(t, team.Secrets[0].Secret, secResp.Secrets[0].Secret)
// get team enroll secrets- unknown team: does not return 404 because reads directly
// the secrets table, does not load the team first (which would be unnecessary except
// for checking that it exists)
s.DoJSON("GET", fmt.Sprintf("/api/v1/fleet/teams/%d/secrets", tm1ID+1), nil, http.StatusOK, &secResp)
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/secrets", tm1ID+1), nil, http.StatusOK, &secResp)
assert.Len(t, secResp.Secrets, 0)
// delete team
var delResp deleteTeamResponse
s.DoJSON("DELETE", fmt.Sprintf("/api/v1/fleet/teams/%d", tm1ID), nil, http.StatusOK, &delResp)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/teams/%d", tm1ID), nil, http.StatusOK, &delResp)
// delete team again, now an unknown team
s.DoJSON("DELETE", fmt.Sprintf("/api/v1/fleet/teams/%d", tm1ID), nil, http.StatusNotFound, &delResp)
s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/teams/%d", tm1ID), nil, http.StatusNotFound, &delResp)
}

View file

@ -94,7 +94,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestOneHostOneQuery() {
wg.Add(1)
go func() {
defer wg.Done()
s.DoJSON("GET", "/api/v1/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
s.DoJSON("GET", "/api/latest/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
}()
// Give the above call a couple of seconds to create the campaign
@ -118,7 +118,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestOneHostOneQuery() {
},
}
distributedResp := submitDistributedQueryResultsResponse{}
s.DoJSON("POST", "/api/v1/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
wg.Wait()
@ -160,7 +160,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestOneHostMultipleQuery() {
wg.Add(1)
go func() {
defer wg.Done()
s.DoJSON("GET", "/api/v1/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
s.DoJSON("GET", "/api/latest/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
}()
// Give the above call a couple of seconds to create the campaign
@ -185,7 +185,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestOneHostMultipleQuery() {
},
}
distributedResp := submitDistributedQueryResultsResponse{}
s.DoJSON("POST", "/api/v1/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
wg.Wait()
@ -259,7 +259,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestMultipleHostMultipleQuery() {
wg.Add(1)
go func() {
defer wg.Done()
s.DoJSON("GET", "/api/v1/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
s.DoJSON("GET", "/api/latest/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
}()
// Give the above call a couple of seconds to create the campaign
@ -283,7 +283,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestMultipleHostMultipleQuery() {
},
}
distributedResp := submitDistributedQueryResultsResponse{}
s.DoJSON("POST", "/api/v1/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
}
wg.Wait()
@ -323,7 +323,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestFailsToCreateCampaign() {
}
liveQueryResp := runLiveQueryResponse{}
s.DoJSON("GET", "/api/v1/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
s.DoJSON("GET", "/api/latest/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
require.Len(t, liveQueryResp.Results, 1)
assert.Equal(t, 0, liveQueryResp.Summary.RespondedHostCount)
@ -356,7 +356,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestFailsOnSomeHost() {
wg.Add(1)
go func() {
defer wg.Done()
s.DoJSON("GET", "/api/v1/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
s.DoJSON("GET", "/api/latest/fleet/queries/run", liveQueryRequest, http.StatusOK, &liveQueryResp)
}()
// Give the above call a couple of seconds to create the campaign
@ -375,7 +375,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestFailsOnSomeHost() {
},
}
distributedResp := submitDistributedQueryResultsResponse{}
s.DoJSON("POST", "/api/v1/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
distributedReq = submitDistributedQueryResultsRequestShim{
NodeKey: h2.NodeKey,
@ -390,7 +390,7 @@ func (s *liveQueriesTestSuite) TestLiveQueriesRestFailsOnSomeHost() {
},
}
distributedResp = submitDistributedQueryResultsResponse{}
s.DoJSON("POST", "/api/v1/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp)
wg.Wait()
@ -420,13 +420,13 @@ func (s *liveQueriesTestSuite) TestCreateDistributedQueryCampaign() {
// create with no payload
var createResp createDistributedQueryCampaignResponse
s.DoJSON("POST", "/api/v1/fleet/queries/run", nil, http.StatusUnprocessableEntity, &createResp)
s.DoJSON("POST", "/api/latest/fleet/queries/run", nil, http.StatusUnprocessableEntity, &createResp)
// create with unknown query
s.DoJSON("POST", "/api/v1/fleet/queries/run", createDistributedQueryCampaignRequest{QueryID: ptr.Uint(9999)}, http.StatusNotFound, &createResp)
s.DoJSON("POST", "/api/latest/fleet/queries/run", createDistributedQueryCampaignRequest{QueryID: ptr.Uint(9999)}, http.StatusNotFound, &createResp)
// create with new query
s.DoJSON("POST", "/api/v1/fleet/queries/run", createDistributedQueryCampaignRequest{QuerySQL: "SELECT 1"}, http.StatusOK, &createResp)
s.DoJSON("POST", "/api/latest/fleet/queries/run", createDistributedQueryCampaignRequest{QuerySQL: "SELECT 1"}, http.StatusOK, &createResp)
assert.NotZero(t, createResp.Campaign.ID)
assert.Equal(t, fleet.QueryWaiting, createResp.Campaign.Status)
assert.Equal(t, uint(0), createResp.Campaign.Metrics.TotalHosts)
@ -436,7 +436,7 @@ func (s *liveQueriesTestSuite) TestCreateDistributedQueryCampaign() {
time.Sleep(time.Second)
// create with new query for specific hosts
s.DoJSON("POST", "/api/v1/fleet/queries/run", createDistributedQueryCampaignRequest{QuerySQL: "SELECT 2", Selected: fleet.HostTargets{HostIDs: []uint{h1.ID, h2.ID}}}, http.StatusOK, &createResp)
s.DoJSON("POST", "/api/latest/fleet/queries/run", createDistributedQueryCampaignRequest{QuerySQL: "SELECT 2", Selected: fleet.HostTargets{HostIDs: []uint{h1.ID, h2.ID}}}, http.StatusOK, &createResp)
assert.NotEqual(t, camp1.ID, createResp.Campaign.ID)
assert.Equal(t, uint(2), createResp.Campaign.Metrics.TotalHosts)
@ -444,7 +444,7 @@ func (s *liveQueriesTestSuite) TestCreateDistributedQueryCampaign() {
time.Sleep(time.Second)
// create by host name
s.DoJSON("POST", "/api/v1/fleet/queries/run_by_names", createDistributedQueryCampaignByNamesRequest{
s.DoJSON("POST", "/api/latest/fleet/queries/run_by_names", createDistributedQueryCampaignByNamesRequest{
QuerySQL: "SELECT 3", Selected: distributedQueryCampaignTargetsByNames{Hosts: []string{h1.Hostname}}},
http.StatusOK, &createResp)
assert.NotEqual(t, camp1.ID, createResp.Campaign.ID)
@ -454,7 +454,7 @@ func (s *liveQueriesTestSuite) TestCreateDistributedQueryCampaign() {
time.Sleep(time.Second)
// create by unknown host name - it ignores the unknown names
s.DoJSON("POST", "/api/v1/fleet/queries/run_by_names", createDistributedQueryCampaignByNamesRequest{
s.DoJSON("POST", "/api/latest/fleet/queries/run_by_names", createDistributedQueryCampaignByNamesRequest{
QuerySQL: "SELECT 3", Selected: distributedQueryCampaignTargetsByNames{Hosts: []string{h1.Hostname + "ZZZZZ"}}},
http.StatusOK, &createResp)
}
@ -467,12 +467,12 @@ func (s *liveQueriesTestSuite) TestOsqueryDistributedRead() {
req := getDistributedQueriesRequest{NodeKey: s.hosts[1].NodeKey}
var resp getDistributedQueriesResponse
s.DoJSON("POST", "/api/v1/osquery/distributed/read", req, http.StatusOK, &resp)
s.DoJSON("POST", "/api/osquery/distributed/read", req, http.StatusOK, &resp)
assert.Contains(t, resp.Queries, hostDistributedQueryPrefix+fmt.Sprintf("%d", hostID))
// test with invalid node key
var errRes map[string]interface{}
req.NodeKey += "zzzz"
s.DoJSON("POST", "/api/v1/osquery/distributed/read", req, http.StatusUnauthorized, &errRes)
s.DoJSON("POST", "/api/osquery/distributed/read", req, http.StatusUnauthorized, &errRes)
assert.Contains(t, errRes["error"], "invalid node key")
}

View file

@ -64,7 +64,7 @@ func (s *integrationLoggerTestSuite) TestLogger() {
Query: ptr.String("select 1 from osquery;"),
}
var createResp createQueryResponse
s.DoJSON("POST", "/api/v1/fleet/queries", params, http.StatusOK, &createResp)
s.DoJSON("POST", "/api/latest/fleet/queries", params, http.StatusOK, &createResp)
logs := s.buf.String()
parts := strings.Split(strings.TrimSpace(logs), "\n")
@ -80,16 +80,16 @@ func (s *integrationLoggerTestSuite) TestLogger() {
case 0:
assert.Equal(t, "info", kv["level"])
assert.Equal(t, "POST", kv["method"])
assert.Equal(t, "/api/v1/fleet/login", kv["uri"])
assert.Equal(t, "/api/latest/fleet/login", kv["uri"])
case 1:
assert.Equal(t, "debug", kv["level"])
assert.Equal(t, "GET", kv["method"])
assert.Equal(t, "/api/v1/fleet/config", kv["uri"])
assert.Equal(t, "/api/latest/fleet/config", kv["uri"])
assert.Equal(t, "admin1@example.com", kv["user"])
case 2:
assert.Equal(t, "debug", kv["level"])
assert.Equal(t, "POST", kv["method"])
assert.Equal(t, "/api/v1/fleet/queries", kv["uri"])
assert.Equal(t, "/api/latest/fleet/queries", kv["uri"])
assert.Equal(t, "admin1@example.com", kv["user"])
assert.Equal(t, "somequery", kv["name"])
assert.Equal(t, "select 1 from osquery;", kv["sql"])
@ -117,13 +117,13 @@ func (s *integrationLoggerTestSuite) TestOsqueryEndpointsLogErrors() {
require.NoError(t, err)
requestBody := io.NopCloser(bytes.NewBuffer([]byte(`{"node_key":"1234","log_type":"status","data":[}`)))
req, _ := http.NewRequest("POST", s.server.URL+"/api/v1/osquery/log", requestBody)
req, _ := http.NewRequest("POST", s.server.URL+"/api/osquery/log", requestBody)
client := fleethttp.NewClient()
_, err = client.Do(req)
require.Nil(t, err)
logString := s.buf.String()
assert.Contains(t, logString, `invalid character '}' looking for beginning of value","level":"info","path":"/api/v1/osquery/log"}
assert.Contains(t, logString, `invalid character '}' looking for beginning of value","level":"info","path":"/api/osquery/log"}
`, logString)
}
@ -151,7 +151,7 @@ func (s *integrationLoggerTestSuite) TestSubmitLog() {
Data: nil,
}
res := submitLogsResponse{}
s.DoJSON("POST", "/api/v1/osquery/log", req, http.StatusOK, &res)
s.DoJSON("POST", "/api/osquery/log", req, http.StatusOK, &res)
logString := s.buf.String()
assert.Equal(t, 1, strings.Count(logString, `"ip_addr"`))
@ -165,7 +165,7 @@ func (s *integrationLoggerTestSuite) TestSubmitLog() {
Data: nil,
}
res = submitLogsResponse{}
s.DoJSON("POST", "/api/v1/osquery/log", req, http.StatusOK, &res)
s.DoJSON("POST", "/api/osquery/log", req, http.StatusOK, &res)
logString = s.buf.String()
assert.Equal(t, 1, strings.Count(logString, `"ip_addr"`))
@ -179,7 +179,7 @@ func (s *integrationLoggerTestSuite) TestSubmitLog() {
Data: nil,
}
var errRes map[string]string
s.DoJSON("POST", "/api/v1/osquery/log", req, http.StatusInternalServerError, &errRes)
s.DoJSON("POST", "/api/osquery/log", req, http.StatusInternalServerError, &errRes)
assert.Contains(t, errRes["error"], "unknown log type")
s.buf.Reset()
@ -194,13 +194,13 @@ func (s *integrationLoggerTestSuite) TestSubmitLog() {
require.NoError(t, err)
require.NoError(t, gw.Close())
s.DoRawWithHeaders("POST", "/api/v1/osquery/log", body.Bytes(), http.StatusOK, map[string]string{"Content-Encoding": "gzip"})
s.DoRawWithHeaders("POST", "/api/osquery/log", body.Bytes(), http.StatusOK, map[string]string{"Content-Encoding": "gzip"})
logString = s.buf.String()
assert.Equal(t, 1, strings.Count(logString, `"ip_addr"`))
assert.Equal(t, 1, strings.Count(logString, "x_for_ip_addr"))
// submit same payload without specifying gzip encoding fails
s.DoRawWithHeaders("POST", "/api/v1/osquery/log", body.Bytes(), http.StatusInternalServerError, nil)
s.DoRawWithHeaders("POST", "/api/osquery/log", body.Bytes(), http.StatusInternalServerError, nil)
}
func (s *integrationLoggerTestSuite) TestEnrollAgentLogsErrors() {
@ -225,7 +225,7 @@ func (s *integrationLoggerTestSuite) TestEnrollAgentLogsErrors() {
})
require.NoError(t, err)
s.DoRawNoAuth("POST", "/api/v1/osquery/enroll", j, http.StatusUnauthorized)
s.DoRawNoAuth("POST", "/api/osquery/enroll", j, http.StatusUnauthorized)
parts := strings.Split(strings.TrimSpace(s.buf.String()), "\n")
require.Len(t, parts, 1)

View file

@ -61,12 +61,12 @@ func (s *integrationSSOTestSuite) TestGetSSOSettings() {
// double-check the settings
var resGet ssoSettingsResponse
s.DoJSON("GET", "/api/v1/fleet/sso", nil, http.StatusOK, &resGet)
s.DoJSON("GET", "/api/latest/fleet/sso", nil, http.StatusOK, &resGet)
require.True(t, resGet.Settings.SSOEnabled)
// initiate an SSO auth
var resIni initiateSSOResponse
s.DoJSON("POST", "/api/v1/fleet/sso", map[string]string{}, http.StatusOK, &resIni)
s.DoJSON("POST", "/api/latest/fleet/sso", map[string]string{}, http.StatusOK, &resIni)
require.NotEmpty(t, resIni.URL)
parsed, err := url.Parse(resIni.URL)

View file

@ -286,7 +286,7 @@ func (svc *Service) InitiateSSO(ctx context.Context, redirectURL string) (string
settings := sso.Settings{
Metadata: metadata,
// Construct call back url to send to idp
AssertionConsumerServiceURL: serverURL + svc.config.Server.URLPrefix + "/api/v1/fleet/sso/callback",
AssertionConsumerServiceURL: serverURL + svc.config.Server.URLPrefix + "/api/latest/fleet/sso/callback",
SessionStore: svc.ssoSessionStore,
OriginalURL: redirectURL,
}
@ -425,7 +425,7 @@ func (svc *Service) CallbackSSO(ctx context.Context, auth fleet.Auth) (*fleet.SS
validator, err := sso.NewValidator(*metadata, sso.WithExpectedAudience(
appConfig.SSOSettings.EntityID,
appConfig.ServerSettings.ServerURL,
appConfig.ServerSettings.ServerURL+svc.config.Server.URLPrefix+"/api/v1/fleet/sso/callback", // ACS
appConfig.ServerSettings.ServerURL+svc.config.Server.URLPrefix+"/api/latest/fleet/sso/callback", // ACS
))
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "create validator from metadata")

View file

@ -144,7 +144,7 @@ func (ts *withServer) getTestToken(email string, password string) string {
require.NoError(ts.s.T(), err)
requestBody := io.NopCloser(bytes.NewBuffer(j))
resp, err := http.Post(ts.server.URL+"/api/v1/fleet/login", "application/json", requestBody)
resp, err := http.Post(ts.server.URL+"/api/latest/fleet/login", "application/json", requestBody)
require.NoError(ts.s.T(), err)
defer resp.Body.Close()
assert.Equal(ts.s.T(), http.StatusOK, resp.StatusCode)
@ -166,11 +166,11 @@ func (ts *withServer) applyConfig(spec []byte) {
err := yaml.Unmarshal(spec, &appConfigSpec)
require.NoError(ts.s.T(), err)
ts.Do("PATCH", "/api/v1/fleet/config", appConfigSpec, http.StatusOK)
ts.Do("PATCH", "/api/latest/fleet/config", appConfigSpec, http.StatusOK)
}
func (ts *withServer) getConfig() *appConfigResponse {
var responseBody *appConfigResponse
ts.DoJSON("GET", "/api/v1/fleet/config", nil, http.StatusOK, &responseBody)
ts.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &responseBody)
return responseBody
}

View file

@ -34,7 +34,7 @@ func TestCreateAuthorizationRequest(t *testing.T) {
},
},
// Construct call back url to send to idp
AssertionConsumerServiceURL: "http://localhost:8001/api/v1/fleet/sso/callback",
AssertionConsumerServiceURL: "http://localhost:8001/api/latest/fleet/sso/callback",
SessionStore: store,
OriginalURL: "/redir",
}

View file

@ -1,4 +1,4 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/me"
endpoint="api/latest/fleet/me"
curl $CURL_FLAGS -H "Authorization: Bearer $TOKEN" "$SERVER_URL/$endpoint"

View file

@ -1,6 +1,6 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/packs"
endpoint="api/latest/fleet/packs"
jq -n \
--arg name "$1" \
'.name = $name

View file

@ -1,4 +1,4 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/packs"
endpoint="api/latest/fleet/packs"
curl $CURL_FLAGS -H "Authorization: Bearer $TOKEN" "$SERVER_URL/$endpoint"

View file

@ -1,4 +1,4 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/packs/$1/scheduled"
endpoint="api/latest/fleet/packs/$1/scheduled"
curl $CURL_FLAGS -H "Authorization: Bearer $TOKEN" "$SERVER_URL/$endpoint"

View file

@ -1,6 +1,6 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/queries"
endpoint="api/latest/fleet/queries"
jq -n \
--arg name "$1" \
--arg query "$2" \

View file

@ -1,4 +1,4 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/queries"
endpoint="api/latest/fleet/queries"
curl $CURL_FLAGS -H "Authorization: Bearer $TOKEN" "$SERVER_URL/$endpoint"

View file

@ -1,6 +1,6 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/queries/run"
endpoint="api/latest/fleet/queries/run"
echo "By default, $endpoint will take 90 seconds..."
jq -n \
--argjson query_ids "$1" \

View file

@ -1,6 +1,6 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/schedule"
endpoint="api/latest/fleet/schedule"
jq -n \
--arg pack_id "$1" \
--arg query_id "$2" \

View file

@ -1,4 +1,4 @@
#!/bin/bash
source $FLEET_ENV_PATH
endpoint="api/v1/fleet/schedule/$1"
endpoint="api/latest/fleet/schedule/$1"
curl $CURL_FLAGS -X DELETE -H "Authorization: Bearer $TOKEN" "$SERVER_URL/$endpoint"

View file

@ -5,7 +5,7 @@
source $FLEET_ENV_PATH
# Create teams
create_team_endpoint="api/v1/fleet/teams"
create_team_endpoint="api/latest/fleet/teams"
# Create Client Platform Engineering
data='{
@ -26,7 +26,7 @@ data='{
curl -X POST $CURL_FLAGS -H "Authorization: Bearer $TOKEN" "$SERVER_URL/$create_team_endpoint" -d "$data" --insecure
# Create users
create_user_endpoint="api/v1/fleet/users/admin"
create_user_endpoint="api/latest/fleet/users/admin"
# Create Andre Verot
data='{

View file

@ -5,7 +5,7 @@
source $FLEET_ENV_PATH
# Create users
create_user_endpoint="api/v1/fleet/users/admin"
create_user_endpoint="api/latest/fleet/users/admin"
# Create Anna
data='{

View file

@ -5,7 +5,7 @@
source $FLEET_ENV_PATH
# Create teams
create_team_endpoint="api/v1/fleet/teams"
create_team_endpoint="api/latest/fleet/teams"
# Create Apples
data='{
@ -20,7 +20,7 @@ data='{
curl -X POST $CURL_FLAGS -H "Authorization: Bearer $TOKEN" "$SERVER_URL/$create_team_endpoint" -d "$data" --insecure
# Create users
create_user_endpoint="api/v1/fleet/users/admin"
create_user_endpoint="api/latest/fleet/users/admin"
# Create Anna
data='{

View file

@ -1,129 +1,130 @@
<html>
<head>
<style>
div.columns {
width: 900px;
padding-top: 10px;
padding-bottom: 10px;
}
div.columns div {
width: 300px;
float: left;
}
div.clear {
clear: both;
}
div.content {
width: 900px;
padding-top: 10px;
padding-bottom: 10px;
}
</style>
<script
src="https://code.jquery.com/jquery-3.2.1.js"
integrity="sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE="
crossorigin="anonymous"
></script>
<script>
$(document).ready(function () {
// User agent handling for SSO
// Check for existing session token indicating user has already started SSO process.
// If the token exists, it is used to fetch the same user info/token as the
// normal login process, albeit via the different SSO login endpoint. Note the session token only
// persists for a few minutes on the server side, and, when we're done
// we always delete the token in the user agent. We use the session token
// to keep track of state from the user agent (Kolide SPA), the service provider
// (Kolide back end), to the identity provider (IDP) and back.
var sessionToken = localStorage.getItem("FLEET::auth_token");
if (sessionToken != null) {
console.log(
"user should be authenticated, fetching user with token " +
sessionToken
);
$.ajax({
type: "GET",
url: "https://localhost:8080/api/v1/fleet/me",
headers: { Authorization: "Bearer " + sessionToken },
contentType: "text/plain;",
dataType: "json",
success: function (data) {
// We've successfully created a login session with a token that
// we can use in subsequent api calls to Kolide.
console.log("sso login succeeded " + data);
$("#displayarea").empty();
$("#displayarea").append(
"<h3>Authentication succeeded</h3>" +
"<p>Token: " +
localStorage.getItem("FLEET::auth_token").substring(0, 16) +
"..." +
"</p>" +
"<p>User: " +
data.user.email +
"</p>"
);
// print user stuff
},
error: function (err) {
console.log("sso login failed " + data);
$("#displayarea").empty();
$("#displayarea").append("<h3>Auth failed</h3>");
},
});
console.log("removing token " + localStorage.getItem("ssoSession"));
localStorage.removeItem("ssoSession");
}
// Single sign on invocation. User agent chooses single sign on for a particular
// IDP trigger the following post.
$(".clicker").click(function (e) {
e.preventDefault();
<head>
<style>
div.columns {
width: 900px;
padding-top: 10px;
padding-bottom: 10px;
}
$.ajax({
type: "POST",
url: "https://localhost:8080/api/v1/fleet/sso",
data: JSON.stringify({
// supply the url of the resource user was trying to access when
// prompted for login
relay_url: $("#relay").val(),
}),
contentType: "text/plain;",
dataType: "json",
success: function (data) {
console.log(data);
// on success we redirect to IDP URL which is in response
window.location.href = data.url;
},
error: function (errMsg) {
console.log(errMsg);
},
});
div.columns div {
width: 300px;
float: left;
}
div.clear {
clear: both;
}
div.content {
width: 900px;
padding-top: 10px;
padding-bottom: 10px;
}
</style>
<script src="https://code.jquery.com/jquery-3.2.1.js" integrity="sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE="
crossorigin="anonymous"></script>
<script>
$(document).ready(function () {
// User agent handling for SSO
// Check for existing session token indicating user has already started SSO process.
// If the token exists, it is used to fetch the same user info/token as the
// normal login process, albeit via the different SSO login endpoint. Note the session token only
// persists for a few minutes on the server side, and, when we're done
// we always delete the token in the user agent. We use the session token
// to keep track of state from the user agent (Kolide SPA), the service provider
// (Kolide back end), to the identity provider (IDP) and back.
var sessionToken = localStorage.getItem("FLEET::auth_token");
if (sessionToken != null) {
console.log(
"user should be authenticated, fetching user with token " +
sessionToken
);
$.ajax({
type: "GET",
url: "https://localhost:8080/api/latest/fleet/me",
headers: {Authorization: "Bearer " + sessionToken},
contentType: "text/plain;",
dataType: "json",
success: function (data) {
// We've successfully created a login session with a token that
// we can use in subsequent api calls to Kolide.
console.log("sso login succeeded " + data);
$("#displayarea").empty();
$("#displayarea").append(
"<h3>Authentication succeeded</h3>" +
"<p>Token: " +
localStorage.getItem("FLEET::auth_token").substring(0, 16) +
"..." +
"</p>" +
"<p>User: " +
data.user.email +
"</p>"
);
// print user stuff
},
error: function (err) {
console.log("sso login failed " + data);
$("#displayarea").empty();
$("#displayarea").append("<h3>Auth failed</h3>");
},
});
console.log("removing token " + localStorage.getItem("ssoSession"));
localStorage.removeItem("ssoSession");
}
// Single sign on invocation. User agent chooses single sign on for a particular
// IDP trigger the following post.
$(".clicker").click(function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: "https://localhost:8080/api/latest/fleet/sso",
data: JSON.stringify({
// supply the url of the resource user was trying to access when
// prompted for login
relay_url: $("#relay").val(),
}),
contentType: "text/plain;",
dataType: "json",
success: function (data) {
console.log(data);
// on success we redirect to IDP URL which is in response
window.location.href = data.url;
},
error: function (errMsg) {
console.log(errMsg);
},
});
});
</script>
</head>
<body>
<h3>Single sign on test page</h3>
<div class="content">
This page is used to test single sign on identity providers. The
<strong>Relay URL</strong> field contains the URL of the Kolide resource
to invoke after authentication with the IDP. It defaults to this page.
Click <a class="clicker" href="#">Request Authorization</a> to trigger the
authorization process. The browser's javascript console may contain useful
debugging information. The
<a href="https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/"
>SAML Tracer</a
>
add-on for Firefox is also useful in diagnosing problems with a particular
identity provider.
});
</script>
</head>
<body>
<h3>Single sign on test page</h3>
<div class="content">
This page is used to test single sign on identity providers. The
<strong>Relay URL</strong> field contains the URL of the Kolide resource
to invoke after authentication with the IDP. It defaults to this page.
Click <a class="clicker" href="#">Request Authorization</a> to trigger the
authorization process. The browser's javascript console may contain useful
debugging information. The
<a href="https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/">SAML Tracer</a>
add-on for Firefox is also useful in diagnosing problems with a particular
identity provider.
</div>
<div class="columns">
<div>Relay URL:</div>
<div>
<input type="text" name="relay" id="relay" value="/test" />
</div>
<div class="columns">
<div>Relay URL:</div>
<div>
<input type="text" name="relay" id="relay" value="/test" />
</div>
</div>
<div class="clear"></div>
<p><a class="clicker" href="#">Request Authorization</a></p>
<div id="displayarea"></div>
</body>
</div>
<div class="clear"></div>
<p><a class="clicker" href="#">Request Authorization</a></p>
<div id="displayarea"></div>
</body>
</html>

View file

@ -7,24 +7,24 @@
--tls_server_certs=/etc/osquery/fleet.crt
--enroll_secret_env=ENROLL_SECRET
--enroll_tls_endpoint=/api/v1/osquery/enroll
--enroll_tls_endpoint=/api/osquery/enroll
--config_plugin=tls
--config_tls_endpoint=/api/v1/osquery/config
--config_tls_endpoint=/api/osquery/config
--config_refresh=10
--disable_distributed=false
--distributed_plugin=tls
--distributed_interval=10
--distributed_tls_max_attempts=3
--distributed_tls_read_endpoint=/api/v1/osquery/distributed/read
--distributed_tls_write_endpoint=/api/v1/osquery/distributed/write
--distributed_tls_read_endpoint=/api/osquery/distributed/read
--distributed_tls_write_endpoint=/api/osquery/distributed/write
--logger_plugin=tls
--logger_tls_endpoint=/api/v1/osquery/log
--logger_tls_endpoint=/api/osquery/log
--logger_tls_period=10
--disable_carver=false
--carver_start_endpoint=/api/v1/osquery/carve/begin
--carver_continue_endpoint=/api/v1/osquery/carve/block
--carver_start_endpoint=/api/osquery/carve/begin
--carver_continue_endpoint=/api/osquery/carve/block
--carver_block_size=2000000