Skip to content

Commit

Permalink
feat: node-collector platfrom auto detection (#116)
Browse files Browse the repository at this point in the history
* feat: node-collector platfrom auto detection

Signed-off-by: chenk <[email protected]>
  • Loading branch information
chen-keinan committed May 8, 2024
1 parent 7c36831 commit 52b8728
Show file tree
Hide file tree
Showing 14 changed files with 599 additions and 153 deletions.
133 changes: 133 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Contributing

These guidelines will help you get started with the k8s-Node-Collector project.

## Table of Contents

- [Contributing](#contributing)
- [Table of Contents](#table-of-contents)
- [Contribution Workflow](#contribution-workflow)
- [Issues and Discussions](#issues-and-discussions)
- [Pull Requests](#pull-requests)
- [Conventional Commits](#conventional-commits)
- [Set up your Development Environment](#set-up-your-development-environment)
- [Build Binaries](#build-binaries)
- [running node-collector binary](#running-node-collector-binary)
- [Testing](#testing)
- [Run unit Tests](#run-unit-tests)

## Contribution Workflow

### Issues and Discussions

- Feel free to open issues for any reason as long as you make it clear what this issue is about: bug/feature/proposal/comment.
- For questions and general discussions, please do not open an issue, and instead create a discussion in the "Discussions" tab.
- Please spend a minimal amount of time giving due diligence to existing issues or discussions. Your topic might be a duplicate. If it is, please add your comment to the existing one.
- Please give your issue or discussion a meaningful title that will be clear for future users.
- The issue should clearly explain the reason for opening, the proposal if you have any, and any relevant technical information.
- For technical questions, please explain in detail what you were trying to do, provide an error message if applicable, and your versions of k8s-node-collector and your environment.

### Pull Requests

- Every Pull Request should have an associated Issue unless it is a trivial fix.
- Your PR is more likely to be accepted if it focuses on just one change.
- Describe what the PR does. There's no convention enforced, but please try to be concise and descriptive. Treat the PR description as a commit message. Titles that start with "fix"/"add"/"improve"/"remove" are good examples.
- There's no need to add or tag reviewers, if your PR is left unattended for too long, you can add a comment to bring it up to attention, optionally "@" mention one of the maintainers that was involved with the issue.
- If a reviewer commented on your code or asked for changes, please remember to mark the discussion as resolved after you address it and re-request a review.
- When addressing comments, try to fix each suggestion in a separate commit.
- Tests are not required at this point as k8s-node-collector is evolving fast, but if you can include tests that will be appreciated.

#### Conventional Commits

It is not that strict, but we use the [Conventional commits](https://www.conventionalcommits.org) in this repository.
Each commit message doesn't have to follow conventions as long as it is clear and descriptive since it will be squashed and merged.

## Set up your Development Environment

- Install Go

The project requires [Go 1.22.2][go-download] or later. We also assume that you're familiar with
Go's [GOPATH workspace][go-code] convention, and have the appropriate environment variables set.
- Get the source code:

```sh
git clone [email protected]:aquasecurity/k8s-node-collector.git
cd k8s-node-collector
```

- Access to a Kubernetes cluster. We assume that you're using a [KIND][kind] cluster. To create a single-node KIND
cluster, run:

```sh
kind create cluster
```

## Build Binaries

| Binary | Image | Description |
|----------------------|------------------------------------------------|---------------------------------------------------------------|
| `node-collector` | `ghcr.io/aquasecurity/node-collector:dev` | k8s-node-collector |

To build node-collector binary, run:

```sh
make build
```

### running node-collector binary

when running node-collector binary it will run cis-spec based on version mapping define in [config.yaml](./pkg/collector/config/config.yaml)
or you can define you on spec by using the flag `--spec k8s-cis` and `--version 1.23.0`

```sh
node-collector --help

A tool which provide a way to extract file info which is not accessible via pre-define commands

Usage:
node-collector [flags]
node-collector [command]

Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
k8s k8s-node-collector extract file system info from cluster Node

Flags:
-h, --help help for node-collector
-n, --node string node name
-o, --output string Output format. One of table|json (default "json")
-s, --spec string spec name. example: k8s-cis
-v, --version string spec version. example 1.23.0
```

This uses the `go build` command and builds binaries in the `./bin` directory.

To build all k8s-node-collector binary into Docker images, run:

copy `node-collector` binary to ./build/node-collector

```sh
mv ./cmd/node-collector/node-collector ./build/node-collector/node-collector
```

build docker image

```sh
make build:docker
```

## Testing

We generally require tests to be added for all, but the most trivial of changes. However, unit tests alone don't
provide guarantees about the behaviour of k8s-node-collector. To verify that each Go module correctly interacts with its
collaborators, more coarse grained integration tests might be required.

### Run unit Tests

To run all tests with code coverage enabled, run:

```sh
make test
```

10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ GOTEST=$(GOCMD) test


all:
$(info "completed running make file for go-opa-validate")
$(info "completed running make file for k8s node collector")
fmt:
@go fmt ./...
tidy:
$(GOMOD) tidy -v
test:
$(GOTEST) ./...

.PHONY: install-req fmt lint tidy test imports .
build:
cd ./cmd/node-collector && go build -o node-collector main.go

build-docker:
docker build -t ghcr.io/aquasecurity/node-collector:dev .

.PHONY: install-req fmt lint tidy test imports .
102 changes: 94 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,107 @@

# k8s-node-collector

[![GitHub Release][release-img]][release]
[![Build Action][action-build-img]][action-build]
[![Release snapshot Action][action-release-snapshot-img]][action-release-snapshot]
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/aquasecurity/k8s-node-collector/blob/main/LICENSE)

# k8s-node-collector
The k8s-Node-collector is an open-source collector that gathers Node information (file system and process data) from Kubernetes nodes and outputs it in a JSON format.

k8s-Node-collector is an open source collector who collect Node information (fs and process data) and output in a table/json format.
## Executing Collector specifications

## k8s-node-collector as job in k8s
The node-collector executes a collector specification,example [k8s-cis-1.23.0](./pkg/collector/config/specs/k8s-cis-1.23.0.yaml).
Each specification must include:

- simple k8s cluster run following job
- `name:` any other platfrom used, example (k8s-cis, aks-cis, gke-cis and etc)
- `version:` of the cis-benchmark it represent (example: 1.23.0)

for executing a specific spec need to pass the `--spec k8s-cis` and `--version 1.23.0` flags

If no collector spec has been specified. the node-collector will try to auto detect the matching spec by platrom type and version as define in [version_mapping data](./pkg/collector/config/config.yaml)
example:

```yaml
k8s:
- op: "="
cluster_version: "1.21"
spec: k8s-cis-1.21.0
- op: ">"
cluster_version: "1.21"
spec: k8s-cis-1.23.0
```

In the example provided, there are two rules; the first matching rule will obtain the appropriate specification.
Any native k8s cluser with version equal to 1.21 will obtain the `k8s-cis-1.21.0` collector specification it no match found
any native k8s cluser with version grather to 1.21 will obtain the `k8s-cis-1.23.0`

## Adding new collector specifications

In order to Add a new specifications, add a new yaml file to this path : `.pkg/collector/config/specs/`
with the following file naming convesion <`platform`-`cis`-`spec_version`>
example: `gke-cis-1.24.0`

Each collector specification audit includes the following fields

```yaml
---
version: "1.23.0"
name: aks-cis
title: Node Specification for info collector
collectors:
- key: < name to hold the audit command output>
title: <title of the audit command>
nodeType: <node type - master | worker>
audit: <audit shell command>
```

### General spec data

`name` - name of the spec (example: `aks-cis`)
`version` - version of the spec (example: `1.23.0`)
`title` - short description of the overall spec

### Specific audit data

`key` - parameter name to hold the audit shell command output
`title` - title of the audit shell command
`nodeType` - define the node type on which shell command should be executed (master | worker)
`audit` - a shell command that collect information and return the result (errors must be supressed)

## Config file

The k8s-node-collector use a config file which help to obtain binaries and config files path based on different platfrom (rancher, natinv k8s and etc)
for example:

```yaml
kubelet:
bins:
- kubelet
- hyperkube kubelet
confs:
- /etc/kubernetes/kubelet-config.yaml
- /var/lib/kubelet/config.yaml
```

The node collector will obtain the kubelet binary name and config file path based on the platfrom it runs on.
when writing an `audit` shell command the params from config files can be used to collect the appropriate data via config params
example, collect the kubelet config.yaml configuration file ownership:

```sh
stat -c %U:%G $kubelet.confs
```

## Run s k8s job

- simple k8s cluster run following job

```sh
kubectl apply -f job.yaml
```

- Check k8s pod status

```
```sh
kubectl get pods

NAME READY STATUS RESTARTS AGE
Expand All @@ -26,10 +110,12 @@ node-collector-ng2z7 0/1 Completed 0 6m1

- Check k8s pod audit output

```
```sh
kubectl logs node-collector-ng2z7
```

## k8s-node-collector output

- json output

```json
Expand Down Expand Up @@ -314,9 +400,9 @@ kubectl logs node-collector-ng2z7
}
```

* job cleanup
### job cleanup

```
```sh
kubectl delete -f job.yaml
```

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.0
toolchain go1.22.2

require (
github.com/Masterminds/semver v1.5.0
github.com/olekukonko/tablewriter v0.0.5
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
Expand Down
41 changes: 39 additions & 2 deletions pkg/collector/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ import (
"k8s.io/client-go/tools/clientcmd"
)

const (
native = "k8s"
gke = "gke"
aks = "aks"
eks = "eks"
rke2 = "rke2"
k3s = "k3s"
ocp = "ocp"
microk8s = "microk8s"
)

type Cluster struct {
clientSet *kubernetes.Clientset
cConfig clientcmd.ClientConfig
Expand Down Expand Up @@ -64,11 +75,30 @@ func (cluster *Cluster) Platfrom() (Platform, error) {
if len(v) != 0 {
return Platform{Name: "ocp", Version: majorVersion(v)}, nil
}
version, err := cluster.clientSet.ServerVersion()
nodeName := cluster.getNodeName()
semVersion, err := cluster.clientSet.ServerVersion()
if err != nil {
return Platform{}, err
}
return getPlatformInfoFromVersion(version.GitVersion), nil
p := getPlatformInfoFromVersion(semVersion.GitVersion)
var name string
switch {
case strings.Contains(p.Version, k3s):
name = k3s
case strings.Contains(p.Version, rke2):
name = rke2
case strings.Contains(p.Version, microk8s):
name = microk8s
case strings.Contains(nodeName, aks):
name = aks
case strings.Contains(nodeName, eks):
name = eks
case strings.Contains(nodeName, gke):
name = gke
default:
name = "k8s"
}
return Platform{Name: name, Version: p.Version}, nil
}

func getPlatformInfoFromVersion(s string) Platform {
Expand Down Expand Up @@ -103,6 +133,13 @@ func (cluster *Cluster) getOpenShiftVersion(ctx context.Context) string {
}
return version
}
func (cluster *Cluster) getNodeName() string {
nodes, err := cluster.clientSet.CoreV1().Nodes().List(context.Background(), v1.ListOptions{})
if err != nil {
return "k8s"
}
return nodes.Items[0].Name
}

func (cluster *Cluster) getDynamicClient(gvr schema.GroupVersionResource) dynamic.ResourceInterface {
return cluster.dynamicClient.Resource(gvr).Namespace("")
Expand Down
Loading

0 comments on commit 52b8728

Please sign in to comment.