2022-06-01 23:05:05 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2023-04-27 11:44:39 +00:00
|
|
|
"crypto/tls"
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
"io"
|
2022-06-01 23:05:05 +00:00
|
|
|
"net/http"
|
2023-04-27 11:44:39 +00:00
|
|
|
"net/http/httptest"
|
|
|
|
"path/filepath"
|
2022-09-26 17:39:56 +00:00
|
|
|
"strings"
|
2022-06-01 23:05:05 +00:00
|
|
|
"testing"
|
|
|
|
|
2023-04-27 11:44:39 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/pkg/certificate"
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2022-06-01 23:05:05 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestUrlGeneration(t *testing.T) {
|
|
|
|
t.Run("without prefix", func(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, fleet.CapabilityMap{})
|
2022-06-01 23:05:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "https://test.com/test/path", bc.url("test/path", "").String())
|
|
|
|
require.Equal(t, "https://test.com/test/path?raw=query", bc.url("test/path", "raw=query").String())
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("with prefix", func(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "prefix/", nil, fleet.CapabilityMap{})
|
2022-06-01 23:05:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "https://test.com/prefix/test/path", bc.url("test/path", "").String())
|
|
|
|
require.Equal(t, "https://test.com/prefix/test/path?raw=query", bc.url("test/path", "raw=query").String())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseResponseKnownErrors(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
message string
|
|
|
|
code int
|
|
|
|
out error
|
|
|
|
}{
|
|
|
|
{"not found errors", http.StatusNotFound, notFoundErr{}},
|
|
|
|
{"unauthenticated errors", http.StatusUnauthorized, ErrUnauthenticated},
|
|
|
|
{"license errors", http.StatusPaymentRequired, ErrMissingLicense},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
t.Run(c.message, func(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, fleet.CapabilityMap{})
|
2022-06-01 23:05:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
response := &http.Response{
|
|
|
|
StatusCode: c.code,
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
Body: io.NopCloser(bytes.NewBufferString(`{"test": "ok"}`)),
|
2022-06-01 23:05:05 +00:00
|
|
|
}
|
|
|
|
err = bc.parseResponse("GET", "", response, &struct{}{})
|
|
|
|
require.ErrorIs(t, err, c.out)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseResponseOK(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, fleet.CapabilityMap{})
|
2022-06-01 23:05:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
response := &http.Response{
|
|
|
|
StatusCode: http.StatusOK,
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
Body: io.NopCloser(bytes.NewBufferString(`{"test": "ok"}`)),
|
2022-06-01 23:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var resDest struct{ Test string }
|
|
|
|
err = bc.parseResponse("", "", response, &resDest)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "ok", resDest.Test)
|
|
|
|
}
|
|
|
|
|
2024-01-11 20:55:35 +00:00
|
|
|
func TestParseResponseOKNoContent(t *testing.T) {
|
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, fleet.CapabilityMap{})
|
|
|
|
require.NoError(t, err)
|
|
|
|
response := &http.Response{
|
|
|
|
StatusCode: http.StatusNoContent,
|
|
|
|
Body: io.NopCloser(bytes.NewBufferString("")),
|
|
|
|
}
|
|
|
|
|
|
|
|
var resDest struct{ Err error }
|
|
|
|
err = bc.parseResponse("", "", response, &resDest)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Nil(t, resDest.Err)
|
|
|
|
}
|
|
|
|
|
2022-06-01 23:05:05 +00:00
|
|
|
func TestParseResponseGeneralErrors(t *testing.T) {
|
|
|
|
t.Run("general HTTP errors", func(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, fleet.CapabilityMap{})
|
2022-06-01 23:05:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
response := &http.Response{
|
|
|
|
StatusCode: http.StatusBadRequest,
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
Body: io.NopCloser(bytes.NewBufferString(`{"test": "ok"}`)),
|
2022-06-01 23:05:05 +00:00
|
|
|
}
|
|
|
|
err = bc.parseResponse("GET", "", response, &struct{}{})
|
|
|
|
require.Error(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("parse errors", func(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, fleet.CapabilityMap{})
|
2022-06-01 23:05:05 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
response := &http.Response{
|
|
|
|
StatusCode: http.StatusBadRequest,
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
Body: io.NopCloser(bytes.NewBufferString(`invalid json`)),
|
2022-06-01 23:05:05 +00:00
|
|
|
}
|
|
|
|
err = bc.parseResponse("GET", "", response, &struct{}{})
|
|
|
|
require.Error(t, err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewBaseClient(t *testing.T) {
|
|
|
|
t.Run("invalid addresses are an error", func(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
_, err := newBaseClient("http://foo\x7f.com/", true, "", "", nil, fleet.CapabilityMap{})
|
2022-06-01 23:05:05 +00:00
|
|
|
require.Error(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("http is only valid in development", func(t *testing.T) {
|
2022-10-28 17:27:21 +00:00
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
address string
|
|
|
|
insecureSkipVerify bool
|
|
|
|
expectedErr error
|
|
|
|
}{
|
|
|
|
{"http non-local URL without insecureSkipVerify", "http://test.com", false, errInvalidScheme},
|
|
|
|
{"http non-local URL with insecureSkipVerify", "http://test.com", true, nil},
|
|
|
|
{"https", "https://test.com", false, nil},
|
|
|
|
{"http localhost with insecureSkipVerify", "http://localhost:8080", true, nil},
|
|
|
|
{"http localhost without insecureSkipVerify", "http://localhost:8080", false, nil},
|
|
|
|
{"http local ip with insecureSkipVerify", "http://127.0.0.1:8080", true, nil},
|
|
|
|
{"http local ip without insecureSkipVerify", "http://127.0.0.1:8080", false, nil},
|
|
|
|
}
|
2022-06-01 23:05:05 +00:00
|
|
|
|
2022-10-28 17:27:21 +00:00
|
|
|
for _, c := range cases {
|
2023-04-27 11:44:39 +00:00
|
|
|
_, err := newBaseClient(c.address, c.insecureSkipVerify, "", "", nil, fleet.CapabilityMap{})
|
2022-10-28 17:27:21 +00:00
|
|
|
require.Equal(t, c.expectedErr, err, c.name)
|
|
|
|
}
|
2022-06-01 23:05:05 +00:00
|
|
|
})
|
|
|
|
}
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
|
|
|
|
func TestClientCapabilities(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
name string
|
|
|
|
capabilities fleet.CapabilityMap
|
|
|
|
expected string
|
|
|
|
}{
|
|
|
|
{"no capabilities", fleet.CapabilityMap{}, ""},
|
|
|
|
{"one capability", fleet.CapabilityMap{fleet.Capability("test_capability"): {}}, "test_capability"},
|
|
|
|
{
|
|
|
|
"multiple capabilities",
|
|
|
|
fleet.CapabilityMap{
|
|
|
|
fleet.Capability("test_capability"): {},
|
|
|
|
fleet.Capability("test_capability_2"): {},
|
|
|
|
},
|
2023-04-27 11:44:39 +00:00
|
|
|
"test_capability,test_capability_2",
|
|
|
|
},
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
t.Run(c.name, func(t *testing.T) {
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, c.capabilities)
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var req http.Request
|
|
|
|
bc.setClientCapabilitiesHeader(&req)
|
2022-09-26 17:39:56 +00:00
|
|
|
require.ElementsMatch(t, strings.Split(c.expected, ","), strings.Split(req.Header.Get(fleet.CapabilitiesHeader), ","))
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestServerCapabilities(t *testing.T) {
|
|
|
|
// initial response has a single capability
|
|
|
|
response := &http.Response{
|
|
|
|
StatusCode: http.StatusOK,
|
|
|
|
Body: io.NopCloser(bytes.NewBufferString(`{}`)),
|
|
|
|
Header: http.Header{fleet.CapabilitiesHeader: []string{"test_capability"}},
|
|
|
|
}
|
2023-04-27 11:44:39 +00:00
|
|
|
bc, err := newBaseClient("https://test.com", true, "", "", nil, fleet.CapabilityMap{})
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
testCapability := fleet.Capability("test_capability")
|
|
|
|
|
|
|
|
err = bc.parseResponse("", "", response, &struct{}{})
|
|
|
|
require.NoError(t, err)
|
2022-09-26 14:44:09 +00:00
|
|
|
require.True(t, bc.GetServerCapabilities().Has(testCapability))
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
|
|
|
|
// later on, the server is downgraded and no longer has the capability
|
|
|
|
response = &http.Response{
|
|
|
|
StatusCode: http.StatusOK,
|
|
|
|
Body: io.NopCloser(bytes.NewBufferString(`{}`)),
|
|
|
|
Header: http.Header{},
|
|
|
|
}
|
|
|
|
err = bc.parseResponse("", "", response, &struct{}{})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, fleet.CapabilityMap{}, bc.serverCapabilities)
|
2022-09-26 14:44:09 +00:00
|
|
|
require.False(t, bc.GetServerCapabilities().Has(testCapability))
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
|
|
|
|
// after an upgrade, the server has many capabilities
|
|
|
|
response = &http.Response{
|
|
|
|
StatusCode: http.StatusOK,
|
|
|
|
Body: io.NopCloser(bytes.NewBufferString(`{}`)),
|
|
|
|
Header: http.Header{fleet.CapabilitiesHeader: []string{"test_capability,test_capability_2"}},
|
|
|
|
}
|
|
|
|
err = bc.parseResponse("", "", response, &struct{}{})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, fleet.CapabilityMap{
|
|
|
|
testCapability: {},
|
|
|
|
fleet.Capability("test_capability_2"): {},
|
|
|
|
}, bc.serverCapabilities)
|
2022-09-26 14:44:09 +00:00
|
|
|
require.True(t, bc.GetServerCapabilities().Has(testCapability))
|
|
|
|
require.True(t, bc.GetServerCapabilities().Has(fleet.Capability("test_capability")))
|
add headers denoting capabilities between fleet server / desktop / orbit (#7833)
This adds a new mechanism to allow us to handle compatibility issues between Orbit, Fleet Server and Fleet Desktop.
The general idea is to _always_ send a custom header of the form:
```
fleet-capabilities-header = "X-Fleet-Capabilities:" capabilities
capabilities = capability * (,)
capability = string
```
Both from the server to the clients (Orbit, Fleet Desktop) and vice-versa. For an example, see: https://github.com/fleetdm/fleet/commit/8c0bbdd291f54e03e19766bcdfead0fb8067f60c
Also, the following applies:
- Backwards compat: if the header is not present, assume that orbit/fleet doesn't have the capability
- The current capabilities endpoint will be removed
### Motivation
This solution is trying to solve the following problems:
- We have three independent processes communicating with each other (Fleet Desktop, Orbit and Fleet Server). Each process can be updated independently, and therefore we need a way for each process to know what features are supported by its peers.
- We originally implemented a dedicated API endpoint in the server that returned a list of the capabilities (or "features") enabled, we found this, and any other server-only solution (like API versioning) to be insufficient because:
- There are cases in which the server also needs to know which features are supported by its clients
- Clients needed to poll for changes to detect if the capabilities supported by the server change, by sending the capabilities on each request we have a much cleaner way to handling different responses.
- We are also introducing an unauthenticated endpoint to get the server features, this gives us flexibility if we need to implement different authentication mechanisms, and was one of the pitfalls of the first implementation.
Related to https://github.com/fleetdm/fleet/issues/7929
2022-09-26 10:53:53 +00:00
|
|
|
}
|
2023-04-27 11:44:39 +00:00
|
|
|
|
|
|
|
func TestClientCertificateAuth(t *testing.T) {
|
|
|
|
httpRequestReceived := false
|
|
|
|
|
|
|
|
clientCAs, err := certificate.LoadPEM(filepath.Join("testdata", "client-ca.crt"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
httpRequestReceived = true
|
|
|
|
}))
|
|
|
|
ts.TLS = &tls.Config{
|
|
|
|
MinVersion: tls.VersionTLS12,
|
|
|
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
|
|
|
ClientCAs: clientCAs,
|
|
|
|
}
|
|
|
|
|
|
|
|
ts.StartTLS()
|
|
|
|
t.Cleanup(func() {
|
|
|
|
ts.Close()
|
|
|
|
})
|
|
|
|
|
|
|
|
// Try connecting without setting TLS client certificates.
|
|
|
|
bc, err := newBaseClient(ts.URL, true, "", "", nil, fleet.CapabilityMap{})
|
|
|
|
require.NoError(t, err)
|
|
|
|
request, err := http.NewRequest("GET", ts.URL, nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
_, err = bc.http.Do(request)
|
|
|
|
require.Error(t, err)
|
|
|
|
require.False(t, httpRequestReceived)
|
|
|
|
|
|
|
|
// Now try connecting by setting the correct TLS client certificates.
|
|
|
|
clientCrt, err := certificate.LoadClientCertificateFromFiles(filepath.Join("testdata", "client.crt"), filepath.Join("testdata", "client.key"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, clientCrt)
|
|
|
|
bc, err = newBaseClient(ts.URL, true, "", "", &clientCrt.Crt, fleet.CapabilityMap{})
|
|
|
|
require.NoError(t, err)
|
|
|
|
request, err = http.NewRequest("GET", ts.URL, nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
_, err = bc.http.Do(request)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, httpRequestReceived)
|
|
|
|
}
|