You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kube-bench/check/check.go

145 lines
3.2 KiB

// Copyright © 2017 Aqua Security Software Ltd. <info@aquasec.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"strings"
"github.com/joncalhoun/pipe"
)
// NodeType indicates the type of node (master, node, federated).
type NodeType string
// State is the state of a control check.
type State string
const (
// PASS check passed.
PASS State = "PASS"
// FAIL check failed.
FAIL = "FAIL"
// WARN could not carry out check.
WARN = "WARN"
// MASTER a master node
MASTER NodeType = "master"
// NODE a node
NODE NodeType = "node"
// FEDERATED a federated deployment.
FEDERATED NodeType = "federated"
)
// Check contains information about a recommendation in the
// CIS Kubernetes 1.6+ document.
type Check struct {
ID string `yaml:"id" json:"id"`
Text string
Audit string `json:"omit"`
Commands []*exec.Cmd `json:"omit"`
Tests *tests `json:"omit"`
Set bool `json:"omit"`
Remediation string
State
}
// Run executes the audit commands specified in a check and outputs
// the results.
func (c *Check) Run() {
var out string
// Check if command exists or exit with WARN.
for _, cmd := range c.Commands {
_, err := exec.LookPath(cmd.Path)
if err != nil {
c.State = WARN
return
}
}
// Run commands.
if len(c.Commands) == 0 {
// Likely a warning message.
c.State = WARN
return
}
p, err := pipe.New(c.Commands...)
if err != nil {
fmt.Fprintf(os.Stderr, "init: error creating command pipeline %s\n", err)
os.Exit(1)
}
pr, pw := io.Pipe()
p.Stdout = pw
defer pw.Close()
if err := p.Start(); err != nil {
fmt.Fprintf(os.Stderr, "start: error running audit command %s\n", err)
os.Exit(1)
}
// Read output of command chain into string for check.
go func() {
defer pr.Close()
scanner := bufio.NewScanner(pr)
for scanner.Scan() {
out += scanner.Text()
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "error accumulating output %s\n", err)
os.Exit(1)
}
}()
if err := p.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "wait: error running audit command %s\n", err)
os.Exit(1)
}
res := c.Tests.execute(out)
if res {
c.State = PASS
} else {
c.State = FAIL
}
}
// textToCommand transforms a text representation of commands to be
// run into a slice of commands.
// TODO: Make this more robust.
func textToCommand(s string) []*exec.Cmd {
cmds := []*exec.Cmd{}
cp := strings.Split(s, "|")
// fmt.Println("check.toCommand:", cp)
for _, v := range cp {
v = strings.Trim(v, " ")
cs := strings.Split(v, " ")
cmd := exec.Command(cs[0], cs[1:]...)
cmds = append(cmds, cmd)
}
return cmds
}