Add initial support for kubequery (#6863)
Configuration and fixes for the Fleet server and frontend to add support for https://github.com/Uptycs/kubequery. Co-authored-by: Michal Nicpon <michal@fleetdm.com>
This commit is contained in:
co-authored by
Michal Nicpon
parent
0d16974fd6
commit
486b67caca
@@ -0,0 +1 @@
|
||||
* Add support for [kubequery](https://github.com/Uptycs/kubequery).
|
||||
@@ -584,7 +584,7 @@ export const inMilliseconds = (nanoseconds: number): number => {
|
||||
|
||||
export const humanHostLastRestart = (
|
||||
detailUpdatedAt: string,
|
||||
uptime: number
|
||||
uptime: number | string
|
||||
): string => {
|
||||
if (
|
||||
!detailUpdatedAt ||
|
||||
|
||||
@@ -495,6 +495,7 @@ func verifyDiscovery(t *testing.T, queries, discovery map[string]string) {
|
||||
hostDetailQueryPrefix + "mdm": {},
|
||||
hostDetailQueryPrefix + "munki_info": {},
|
||||
hostDetailQueryPrefix + "windows_update_history": {},
|
||||
hostDetailQueryPrefix + "kubequery_info": {},
|
||||
}
|
||||
for name := range queries {
|
||||
require.NotEmpty(t, discovery[name])
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
type DetailQuery struct {
|
||||
// Query is the SQL query string.
|
||||
Query string
|
||||
// Discovery is the SQL query that defines whether the query will run or the host or not.
|
||||
// Discovery is the SQL query that defines whether the query will run on the host or not.
|
||||
// If not set, Fleet makes sure the query will always run.
|
||||
Discovery string
|
||||
// Platforms is a list of platforms to run the query on. If this value is
|
||||
@@ -347,6 +347,11 @@ FROM logical_drives WHERE file_system = 'NTFS' LIMIT 1;`,
|
||||
Platforms: []string{"windows"},
|
||||
IngestFunc: ingestDiskSpace,
|
||||
},
|
||||
"kubequery_info": {
|
||||
Query: `SELECT * from kubernetes_info`,
|
||||
IngestFunc: ingestKubequeryInfo,
|
||||
Discovery: discoveryTable("kubernetes_info"),
|
||||
},
|
||||
}
|
||||
|
||||
// extraDetailQueries defines extra detail queries that should be run on the host, as
|
||||
@@ -1037,6 +1042,9 @@ func directIngestUsers(ctx context.Context, logger log.Logger, host *fleet.Host,
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
if len(users) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := ds.SaveHostUsers(ctx, host.ID, users); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "update host users")
|
||||
}
|
||||
@@ -1106,6 +1114,20 @@ func directIngestMunkiInfo(ctx context.Context, logger log.Logger, host *fleet.H
|
||||
return ds.SetOrUpdateMunkiInfo(ctx, host.ID, rows[0]["version"], errList, warnList)
|
||||
}
|
||||
|
||||
func ingestKubequeryInfo(ctx context.Context, logger log.Logger, host *fleet.Host, rows []map[string]string) error {
|
||||
if len(rows) != 1 {
|
||||
logger.Log("component", "service", "method", "ingestKubequeryInfo", "warn",
|
||||
fmt.Sprintf("kubernetes_info expected single result got %d", len(rows)))
|
||||
}
|
||||
|
||||
host.Hostname = fmt.Sprintf("kubequery %s", rows[0]["cluster_name"])
|
||||
|
||||
// These values are not provided by kubequery
|
||||
host.OsqueryVersion = "kubequery"
|
||||
host.Platform = "kubequery"
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDetailQueries(fleetConfig config.FleetConfig, features *fleet.Features) map[string]DetailQuery {
|
||||
generatedMap := make(map[string]DetailQuery)
|
||||
for key, query := range hostDetailQueries {
|
||||
|
||||
@@ -297,7 +297,7 @@ func sortedKeysCompare(t *testing.T, m map[string]DetailQuery, expectedKeys []st
|
||||
|
||||
func TestGetDetailQueries(t *testing.T) {
|
||||
queriesNoConfig := GetDetailQueries(config.FleetConfig{}, nil)
|
||||
require.Len(t, queriesNoConfig, 17)
|
||||
require.Len(t, queriesNoConfig, 18)
|
||||
|
||||
baseQueries := []string{
|
||||
"network_interface",
|
||||
@@ -317,18 +317,19 @@ func TestGetDetailQueries(t *testing.T) {
|
||||
"os_windows",
|
||||
"os_unix_like",
|
||||
"windows_update_history",
|
||||
"kubequery_info",
|
||||
}
|
||||
sortedKeysCompare(t, queriesNoConfig, baseQueries)
|
||||
|
||||
queriesWithoutWinOSVuln := GetDetailQueries(config.FleetConfig{Vulnerabilities: config.VulnerabilitiesConfig{DisableWinOSVulnerabilities: true}}, nil)
|
||||
require.Len(t, queriesWithoutWinOSVuln, 16)
|
||||
require.Len(t, queriesWithoutWinOSVuln, 17)
|
||||
|
||||
queriesWithUsers := GetDetailQueries(config.FleetConfig{App: config.AppConfig{EnableScheduledQueryStats: true}}, &fleet.Features{EnableHostUsers: true})
|
||||
require.Len(t, queriesWithUsers, 19)
|
||||
require.Len(t, queriesWithUsers, 20)
|
||||
sortedKeysCompare(t, queriesWithUsers, append(baseQueries, "users", "scheduled_query_stats"))
|
||||
|
||||
queriesWithUsersAndSoftware := GetDetailQueries(config.FleetConfig{App: config.AppConfig{EnableScheduledQueryStats: true}}, &fleet.Features{EnableHostUsers: true, EnableSoftwareInventory: true})
|
||||
require.Len(t, queriesWithUsersAndSoftware, 22)
|
||||
require.Len(t, queriesWithUsersAndSoftware, 23)
|
||||
sortedKeysCompare(t, queriesWithUsersAndSoftware,
|
||||
append(baseQueries, "users", "software_macos", "software_linux", "software_windows", "scheduled_query_stats"))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Kubequery and Fleet
|
||||
|
||||
Use the provided configuration file ([kubequery-fleet.yml](kubequery-fleet.yml)) to get a [kubequery](https://github.com/Uptycs/kubequery) instance connected to Fleet.
|
||||
|
||||
Before deploying, first retrieve the enroll secret from Fleet by opening a web browser to the Fleet URL, going to the Hosts page, and clicking on the "Manage enroll secret" button.
|
||||
Alternatively, you can get the enroll secret using `fleetctl` using `fleetctl get enroll-secret`.
|
||||
Update the `enroll.secret` in the `ConfigMap`. In production, you will also need to update the `tls_hostname` and `fleet.pem` to the appropriate values. Finally, deploy kubequery using `kubectl`
|
||||
|
||||
|
||||
```sh
|
||||
kubectl apply -f kubequery-fleet.yml
|
||||
```
|
||||
|
||||
Kubernetes clusters will show up in Fleet with hostnames like `kubequery <CLUSTER NAME>`.
|
||||
@@ -0,0 +1,181 @@
|
||||
# Copyright (c) 2020-present, The kubequery authors
|
||||
# Copyright (c) 2022-present, Fleet
|
||||
#
|
||||
# This source code is licensed as defined by the LICENSE file found in the
|
||||
# root directory of this source tree.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
||||
|
||||
# Adapted from original provided by Kubequery project.
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: kubequery
|
||||
labels:
|
||||
app.kubernetes.io/name: kubequery
|
||||
app.kubernetes.io/part-of: kubequery
|
||||
app.kubernetes.io/version: latest
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: kubequery-sa
|
||||
namespace: kubequery
|
||||
labels:
|
||||
app.kubernetes.io/name: kubequery-sa
|
||||
app.kubernetes.io/part-of: kubequery
|
||||
app.kubernetes.io/version: latest
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kubequery-clusterrole
|
||||
labels:
|
||||
app.kubernetes.io/name: kubequery-clusterrole
|
||||
app.kubernetes.io/part-of: kubequery
|
||||
app.kubernetes.io/version: latest
|
||||
rules:
|
||||
- apiGroups: ["", "admissionregistration.k8s.io", "apps", "autoscaling", "batch", "events.k8s.io", "networking.k8s.io", "policy", "rbac.authorization.k8s.io", "storage.k8s.io"]
|
||||
resources: ["*"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: kubequery-clusterrolebinding
|
||||
labels:
|
||||
app.kubernetes.io/name: kubequery-clusterrolebinding
|
||||
app.kubernetes.io/part-of: kubequery
|
||||
app.kubernetes.io/version: latest
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: kubequery-clusterrole
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kubequery-sa
|
||||
namespace: kubequery
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: kubequery-config
|
||||
namespace: kubequery
|
||||
labels:
|
||||
app.kubernetes.io/name: kubequery-config
|
||||
app.kubernetes.io/part-of: kubequery
|
||||
app.kubernetes.io/version: latest
|
||||
data:
|
||||
enroll.secret: TODO
|
||||
kubequery.flags: |
|
||||
# Server
|
||||
--tls_hostname=host.docker.internal:8080
|
||||
--tls_server_certs=/opt/uptycs/etc/fleet.pem
|
||||
# Enrollment
|
||||
--enroll_secret_path=/opt/uptycs/etc/enroll.secret
|
||||
--enroll_tls_endpoint=/api/v1/osquery/enroll
|
||||
# Configuration
|
||||
--config_plugin=tls
|
||||
--config_tls_endpoint=/api/v1/osquery/config
|
||||
--config_refresh=10
|
||||
# Live query
|
||||
--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
|
||||
# Logging
|
||||
--logger_plugin=tls
|
||||
--logger_tls_endpoint=/api/v1/osquery/log
|
||||
--logger_tls_period=10
|
||||
# File carving
|
||||
--disable_carver=false
|
||||
--carver_start_endpoint=/api/v1/osquery/carve/begin
|
||||
--carver_continue_endpoint=/api/v1/osquery/carve/block
|
||||
--carver_block_size=2000000
|
||||
kubequery.conf: |
|
||||
fleet.pem: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE5jCCAs6gAwIBAgIJAKq0+FAVArUhMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNV
|
||||
BAMMCkZsZWV0IFRlc3QwHhcNMjAxMDE1MTg1ODE5WhcNMzAwNzE1MTg1ODE5WjAV
|
||||
MRMwEQYDVQQDDApGbGVldCBUZXN0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
||||
CgKCAgEAttQ62lpMq48/XjQFxYg47D2fgKgTBMjDNSfCt9VpqE3xPnybmWo8VZtk
|
||||
jmrFM50IhagyYSjvl9iqdrnsl3ZV8KYbWEy6849zDYF1SudmC7/pJyH7QvpKgL7V
|
||||
4McM62jM905hyFy9KZTAlFiaeezWSWJre7kHsK2u5tsqS6ElatEZmF59sInixaRw
|
||||
RqVxOhtDm7Sl1c5xKWM6phWoJfykspFpu5J6N2jRQXCfzBYoQWN76OohGouit/BQ
|
||||
C1xvm+f7wgGZgbbfDjUoHAe9Yhd3XzZsYTgMDt/SRJRDnxFZwo8BAkY9yJm7f3dQ
|
||||
AEhgJ66KbyoxITdgma1hgmeWibZY4hVymcRxB1B3RNN2at19RNy2J+brMxlG0KZk
|
||||
nD77EqidrwLAlYcdeU3yLjt0vYPxT+RW7l1jiZlVi/oaykAmVfOhWnTnTwbsYs7O
|
||||
UMyMyYHQECEs98ex7wrjThIBJScqhsSN1ipAxr5RgaDr+U5IR+tLhMewBy8So+nf
|
||||
2YuMhLfkCgoY80ELhz5F8avts5hksB0hqnNYr+Nlwm6eXqEPZSzFJmdc1IbmWzq2
|
||||
7UH1OQmBFF2qr2j/8dcM+oPNgjrEEQjtyW0S4j2PhjSEbINgcwu0AaABssLI80Vm
|
||||
Gp1TjUGA92rMwIjlAtcUUB5FOKSS8vAXb1VcDWMkybh9sHj4Z0ECAwEAAaM5MDcw
|
||||
NQYDVR0RBC4wLIIJbG9jYWxob3N0ghRob3N0LmRvY2tlci5pbnRlcm5hbIIJMTI3
|
||||
LjAuMC4xMA0GCSqGSIb3DQEBCwUAA4ICAQBZOY++LNRTVG8XlQGVlOloEKA2WY3P
|
||||
gXKJLSM7xWSxj2co0v+noyhoInHT7ysub8en59Et+vN53/0OobxNIdEKDUdqs38R
|
||||
F++Oy6s/MhFHSo87F06t+W91/60ub4iFRHfev1qeNFV6Yzv9sFJ5LpXLFk+qVDb8
|
||||
pPyFFE1bXjctDPjD5gUj+Y34XikVKzMb7xddWCNs34v1KCaCBW7kkfefxiZiDR6g
|
||||
lCEkDzp6xaLS898oCbfFakjr4bvOgBP1IqXLIDLPMhivaxNAooHTtu/3ezp7puix
|
||||
TSDkjlkStDtEFw/wjyaMcEkk51Gs1ponBbADLRxQ50AHDWk/4vy8GcIVc6CdVEOA
|
||||
Zw12FN06C4Jviiiv6uCXZ6iZ+V+pjGiGmSNYF+kruUs8BfrJIB89lqxpdQ4Kx01j
|
||||
AuSFvjRRvIPmvApSdKEjLcY3AYRivXsB/hASMBbjh/p1f/JzSJdxoqSvONhNQJuh
|
||||
+wcdNVQhGAv3kkLn/HMHTBl2Ur+9tQaJrnR1tWl1IzwLRJIi0Soyp/q5ZjQyFglj
|
||||
32xW83DZhtpQ2SI1QGy4AvWIPnGHZhMfav02KnKRhZdOMW4oekXRMrwiyXCqIazc
|
||||
xXzAlCq8dHdP2Y9uvfFxVFyE+uSfkcPxX+DG/ZnpgCS27oKA/qLCybJamlqtveNs
|
||||
RSjNe5qwGi0ifA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kubequery
|
||||
namespace: kubequery
|
||||
labels:
|
||||
app.kubernetes.io/name: kubequery
|
||||
app.kubernetes.io/part-of: kubequery
|
||||
app.kubernetes.io/version: latest
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: kubequery
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: kubequery
|
||||
app.kubernetes.io/part-of: kubequery
|
||||
app.kubernetes.io/version: latest
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
terminationGracePeriodSeconds: 10
|
||||
serviceAccountName: kubequery-sa
|
||||
containers:
|
||||
- name: kubequery
|
||||
image: uptycs/kubequery:latest
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /opt/uptycs/config
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: kubequery-config
|
||||
Reference in New Issue
Block a user