2017-05-26 09:25:29 +00:00
// 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 cmd
import (
2017-07-25 00:34:07 +00:00
goflag "flag"
2017-05-26 09:25:29 +00:00
"fmt"
"os"
2017-06-20 10:10:11 +00:00
"github.com/aquasecurity/kube-bench/check"
2019-03-07 17:02:43 +00:00
"github.com/golang/glog"
2017-05-26 09:25:29 +00:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
2019-04-29 10:17:06 +00:00
type FilterOpts struct {
CheckList string
GroupList string
Scored bool
Unscored bool
}
2017-05-26 09:25:29 +00:00
var (
2020-03-03 14:51:48 +00:00
envVarsPrefix = "KUBE_BENCH"
2020-08-04 13:40:12 +00:00
defaultKubeVersion = "1.18"
2020-03-03 14:51:48 +00:00
kubeVersion string
benchmarkVersion string
cfgFile string
cfgDir = "./cfg/"
jsonFmt bool
junitFmt bool
pgSQL bool
2020-11-23 19:43:53 +00:00
aSFF bool
2020-03-03 14:51:48 +00:00
masterFile = "master.yaml"
nodeFile = "node.yaml"
etcdFile = "etcd.yaml"
controlplaneFile = "controlplane.yaml"
policiesFile = "policies.yaml"
managedservicesFile = "managedservices.yaml"
2020-10-29 10:12:45 +00:00
exitCode int
2020-03-03 14:51:48 +00:00
noResults bool
noSummary bool
noRemediations bool
2020-10-29 10:03:41 +00:00
skipIds string
2020-12-21 11:18:54 +00:00
noTotals bool
2020-03-03 14:51:48 +00:00
filterOpts FilterOpts
includeTestOutput bool
outputFile string
configFileError error
2020-06-24 09:13:10 +00:00
controlsCollection [ ] * check . Controls
2017-05-26 09:25:29 +00:00
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = & cobra . Command {
2017-06-20 08:52:53 +00:00
Use : os . Args [ 0 ] ,
2017-06-19 20:17:19 +00:00
Short : "Run CIS Benchmarks checks against a Kubernetes deployment" ,
2018-08-10 19:55:02 +00:00
Long : ` This tool runs the CIS Kubernetes Benchmark (https://www.cisecurity.org/benchmark/kubernetes/) ` ,
2019-03-07 17:02:43 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2020-08-04 15:04:02 +00:00
bv , err := getBenchmarkVersion ( kubeVersion , benchmarkVersion , viper . GetViper ( ) )
2019-12-05 20:55:44 +00:00
if err != nil {
2019-12-13 15:09:30 +00:00
exitWithError ( fmt . Errorf ( "unable to determine benchmark version: %v" , err ) )
2019-12-05 20:55:44 +00:00
}
2020-08-30 07:16:21 +00:00
glog . V ( 1 ) . Infof ( "Running checks for benchmark %v" , bv )
2019-12-05 20:55:44 +00:00
2019-03-07 17:02:43 +00:00
if isMaster ( ) {
2020-08-30 07:16:21 +00:00
glog . V ( 1 ) . Info ( "== Running master checks ==" )
2020-08-04 15:04:02 +00:00
runChecks ( check . MASTER , loadConfig ( check . MASTER , bv ) )
2019-12-05 20:55:44 +00:00
// Control Plane is only valid for CIS 1.5 and later,
// this a gatekeeper for previous versions
2020-08-30 07:16:21 +00:00
valid , err := validTargets ( bv , [ ] string { string ( check . CONTROLPLANE ) } , viper . GetViper ( ) )
if err != nil {
exitWithError ( fmt . Errorf ( "error validating targets: %v" , err ) )
}
if valid {
glog . V ( 1 ) . Info ( "== Running control plane checks ==" )
2020-08-04 15:04:02 +00:00
runChecks ( check . CONTROLPLANE , loadConfig ( check . CONTROLPLANE , bv ) )
2019-12-05 20:55:44 +00:00
}
2020-12-21 11:18:54 +00:00
} else {
glog . V ( 1 ) . Info ( "== Skipping master checks ==" )
2019-12-05 20:55:44 +00:00
}
// Etcd is only valid for CIS 1.5 and later,
// this a gatekeeper for previous versions.
2020-08-30 07:16:21 +00:00
valid , err := validTargets ( bv , [ ] string { string ( check . ETCD ) } , viper . GetViper ( ) )
if err != nil {
exitWithError ( fmt . Errorf ( "error validating targets: %v" , err ) )
}
if valid && isEtcd ( ) {
glog . V ( 1 ) . Info ( "== Running etcd checks ==" )
2020-08-04 15:04:02 +00:00
runChecks ( check . ETCD , loadConfig ( check . ETCD , bv ) )
2020-12-21 11:18:54 +00:00
} else {
glog . V ( 1 ) . Info ( "== Skipping etcd checks ==" )
2019-03-07 17:02:43 +00:00
}
2019-12-05 20:55:44 +00:00
2020-08-30 07:16:21 +00:00
glog . V ( 1 ) . Info ( "== Running node checks ==" )
2020-08-04 15:04:02 +00:00
runChecks ( check . NODE , loadConfig ( check . NODE , bv ) )
2019-12-05 20:55:44 +00:00
// Policies is only valid for CIS 1.5 and later,
// this a gatekeeper for previous versions.
2020-08-30 07:16:21 +00:00
valid , err = validTargets ( bv , [ ] string { string ( check . POLICIES ) } , viper . GetViper ( ) )
if err != nil {
exitWithError ( fmt . Errorf ( "error validating targets: %v" , err ) )
}
if valid {
glog . V ( 1 ) . Info ( "== Running policies checks ==" )
2020-08-04 15:04:02 +00:00
runChecks ( check . POLICIES , loadConfig ( check . POLICIES , bv ) )
2020-12-21 11:18:54 +00:00
} else {
glog . V ( 1 ) . Info ( "== Skipping policies checks ==" )
2019-12-05 20:55:44 +00:00
}
2020-03-03 14:51:48 +00:00
// Managedservices is only valid for GKE 1.0 and later,
// this a gatekeeper for previous versions.
2020-08-30 07:16:21 +00:00
valid , err = validTargets ( bv , [ ] string { string ( check . MANAGEDSERVICES ) } , viper . GetViper ( ) )
if err != nil {
exitWithError ( fmt . Errorf ( "error validating targets: %v" , err ) )
}
if valid {
glog . V ( 1 ) . Info ( "== Running managed services checks ==" )
2020-08-04 15:04:02 +00:00
runChecks ( check . MANAGEDSERVICES , loadConfig ( check . MANAGEDSERVICES , bv ) )
2020-12-21 11:18:54 +00:00
} else {
glog . V ( 1 ) . Info ( "== Skipping managed services checks ==" )
2020-03-03 14:51:48 +00:00
}
2020-06-24 09:13:10 +00:00
writeOutput ( controlsCollection )
2020-10-29 10:12:45 +00:00
exitCode := exitCodeSelection ( controlsCollection )
os . Exit ( exitCode )
2019-03-07 17:02:43 +00:00
} ,
2017-05-26 09:25:29 +00:00
}
// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute ( ) {
2017-07-25 00:34:07 +00:00
goflag . CommandLine . Parse ( [ ] string { } )
2017-05-26 09:25:29 +00:00
if err := RootCmd . Execute ( ) ; err != nil {
fmt . Println ( err )
2019-07-01 08:49:46 +00:00
// flush before exit non-zero
glog . Flush ( )
2017-05-26 09:25:29 +00:00
os . Exit ( - 1 )
}
2019-07-01 08:49:46 +00:00
// flush before exit
glog . Flush ( )
2017-05-26 09:25:29 +00:00
}
func init ( ) {
cobra . OnInitialize ( initConfig )
2018-04-10 19:58:19 +00:00
// Output control
2020-10-29 10:12:45 +00:00
RootCmd . PersistentFlags ( ) . IntVar ( & exitCode , "exit-code" , 0 , "Specify the exit code for when checks fail" )
2018-04-10 23:04:24 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & noResults , "noresults" , false , "Disable printing of results section" )
2018-04-10 19:58:19 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & noSummary , "nosummary" , false , "Disable printing of summary section" )
RootCmd . PersistentFlags ( ) . BoolVar ( & noRemediations , "noremediations" , false , "Disable printing of remediations section" )
2020-12-21 11:18:54 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & noTotals , "nototals" , false , "Disable printing of totals for failed, passed, ... checks across all sections" )
2017-06-21 19:45:50 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & jsonFmt , "json" , false , "Prints the results as JSON" )
2019-11-13 13:03:04 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & junitFmt , "junit" , false , "Prints the results as JUnit" )
2018-01-11 18:01:58 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & pgSQL , "pgsql" , false , "Save the results to PostgreSQL" )
2020-11-23 19:43:53 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & aSFF , "asff" , false , "Send the results to AWS Security Hub" )
2019-05-01 19:43:06 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & filterOpts . Scored , "scored" , true , "Run the scored CIS checks" )
RootCmd . PersistentFlags ( ) . BoolVar ( & filterOpts . Unscored , "unscored" , true , "Run the unscored CIS checks" )
2020-10-29 10:03:41 +00:00
RootCmd . PersistentFlags ( ) . StringVar ( & skipIds , "skip" , "" , "List of comma separated values of checks to be skipped" )
2019-05-05 07:52:28 +00:00
RootCmd . PersistentFlags ( ) . BoolVar ( & includeTestOutput , "include-test-output" , false , "Prints the actual result when test fails" )
2019-05-29 15:05:55 +00:00
RootCmd . PersistentFlags ( ) . StringVar ( & outputFile , "outputfile" , "" , "Writes the JSON results to output file" )
2018-04-10 19:58:19 +00:00
2017-07-10 00:02:39 +00:00
RootCmd . PersistentFlags ( ) . StringVarP (
2019-04-29 10:17:06 +00:00
& filterOpts . CheckList ,
2017-05-26 09:25:29 +00:00
"check" ,
"c" ,
"" ,
` A comma-delimited list of checks to run as specified in CIS document. Example --check="1.1.1,1.1.2" ` ,
)
2017-07-10 00:02:39 +00:00
RootCmd . PersistentFlags ( ) . StringVarP (
2019-04-29 10:17:06 +00:00
& filterOpts . GroupList ,
2017-05-26 09:25:29 +00:00
"group" ,
"g" ,
"" ,
` Run all the checks under this comma-delimited list of groups. Example --group="1.1" ` ,
)
2017-06-22 14:34:21 +00:00
RootCmd . PersistentFlags ( ) . StringVar ( & cfgFile , "config" , "" , "config file (default is ./cfg/config.yaml)" )
2019-12-12 21:51:35 +00:00
RootCmd . PersistentFlags ( ) . StringVarP ( & cfgDir , "config-dir" , "D" , cfgDir , "config directory" )
2018-04-12 18:22:50 +00:00
RootCmd . PersistentFlags ( ) . StringVar ( & kubeVersion , "version" , "" , "Manually specify Kubernetes version, automatically detected if unset" )
2019-11-05 21:31:27 +00:00
RootCmd . PersistentFlags ( ) . StringVar ( & benchmarkVersion , "benchmark" , "" , "Manually specify CIS benchmark version. It would be an error to specify both --version and --benchmark flags" )
2017-07-25 00:34:07 +00:00
2020-09-09 12:46:35 +00:00
if err := goflag . Set ( "logtostderr" , "true" ) ; err != nil {
fmt . Printf ( "unable to set logtostderr: %+v\n" , err )
os . Exit ( - 1 )
}
2017-07-25 00:34:07 +00:00
goflag . CommandLine . VisitAll ( func ( goflag * goflag . Flag ) {
RootCmd . PersistentFlags ( ) . AddGoFlag ( goflag )
} )
2017-05-26 09:25:29 +00:00
}
// initConfig reads in config file and ENV variables if set.
func initConfig ( ) {
if cfgFile != "" { // enable ability to specify config file via flag
viper . SetConfigFile ( cfgFile )
2017-06-22 14:34:21 +00:00
} else {
viper . SetConfigName ( "config" ) // name of config file (without extension)
viper . AddConfigPath ( cfgDir ) // adding ./cfg as first search path
2017-05-26 09:25:29 +00:00
}
2019-08-27 08:04:11 +00:00
// Read flag values from environment variables.
// Precedence: Command line flags take precedence over environment variables.
2017-10-31 20:08:46 +00:00
viper . SetEnvPrefix ( envVarsPrefix )
2019-08-27 08:04:11 +00:00
viper . AutomaticEnv ( )
2019-10-12 23:00:26 +00:00
2019-08-27 08:04:11 +00:00
if kubeVersion == "" {
if env := viper . Get ( "version" ) ; env != nil {
kubeVersion = env . ( string )
}
2019-10-12 23:00:26 +00:00
}
2017-05-26 09:25:29 +00:00
// If a config file is found, read it in.
2017-06-23 09:48:49 +00:00
if err := viper . ReadInConfig ( ) ; err != nil {
2019-08-22 12:43:09 +00:00
if _ , ok := err . ( viper . ConfigFileNotFoundError ) ; ok {
// Config file not found; ignore error for now to prevent commands
// which don't need the config file exiting.
configFileError = err
} else {
// Config file was found but another error was produced
colorPrint ( check . FAIL , fmt . Sprintf ( "Failed to read config file: %v\n" , err ) )
os . Exit ( 1 )
}
2017-05-26 09:25:29 +00:00
}
}