Skip to content

Commit

Permalink
Add Windows Server 2019 Stand-alone v2.0.0 benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
ttousai committed Dec 30, 2024
1 parent fb9361f commit d795389
Show file tree
Hide file tree
Showing 11 changed files with 7,581 additions and 117 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ bin
.vscode
.idea
coverage.txt
dist/
dist/

# others
.pre-commit-config.yaml
7,462 changes: 7,462 additions & 0 deletions cfg/CIS_Microsoft_Windows_Server_2019_Stand-alone_v2.0.0.yaml

Large diffs are not rendered by default.

File renamed without changes.
35 changes: 0 additions & 35 deletions cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@
package cmd

import (
"fmt"
"os"
"path/filepath"

"github.com/aquasecurity/bench-common/check"
"github.com/aquasecurity/bench-common/util"
"github.com/golang/glog"
)

func runControls(controls *check.Controls, checkList string) check.Summary {
Expand Down Expand Up @@ -50,35 +47,3 @@ func getControls(bench check.Bench, path string, constraints []string) (*check.C

return controls, err
}

func getDefinitionFilePath(version, filename string) (string, error) {

glog.V(2).Info(fmt.Sprintf("Looking for config for version %s filename: %s\n", version, filename))
path := filepath.Join(cfgDir, version)
file := filepath.Join(path, filename)

glog.V(2).Info(fmt.Sprintf("Looking for config file: %s\n", file))

_, err := os.Stat(file)
if err != nil {
return "", err
}

return file, nil
}

// getConfigFilePath locates the config files we should be using based on either the specified
// version, or the running version of kubernetes if not specified
func getConfigFilePath(fileVersion string, filename string) (path string, err error) {

glog.V(2).Info(fmt.Sprintf("Looking for config for version %s", fileVersion))

path = filepath.Join(cfgDir, fileVersion)
file := filepath.Join(path, string(filename))
glog.V(2).Info(fmt.Sprintf("Looking for config file: %s\n", file))

if _, err = os.Stat(file); !os.IsNotExist(err) {
return path, err
}
return "", fmt.Errorf("file %s not found", filename)
}
52 changes: 6 additions & 46 deletions cmd/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ import (
)

var (
ver = "2.0.0"
testDefinitionFile = "definitions.yaml"
path string
testDefinitionFile = "CIS_Microsoft_Windows_Server_2019_Stand-alone_v2.0.0.yaml"
)

type mockPowerShell struct{}
Expand All @@ -38,51 +36,14 @@ func (p mockPowerShell) Execute(customConfig ...interface{}) (result string, err

func init() {
here, _ := os.Getwd()
// cfgDir is defined in root.go
cfgDir = fmt.Sprintf("%s/../cfg", here)
}

// Tests all standard windows-bench definition files
func TestGetDefinitionFilePath(t *testing.T) {
d, err := os.Open(cfgDir)
if err != nil {
t.Errorf("unexpected error: %s\n", err)
}

vers, err := d.Readdirnames(-1)
if err != nil {
t.Errorf("unexpected error: %s\n", err)
}

for _, ver := range vers {

verDir := fmt.Sprintf("%s/%s", cfgDir, ver)
cfvd, err := os.Open(verDir)
if err != nil {
t.Errorf("unexpected error: %s\n", err)
}
files, err := cfvd.Readdirnames(-1)
if err != nil {
t.Errorf("unexpected error: %s\n", err)
}

for _, file := range files {
_, err := getDefinitionFilePath(ver, file)
if err != nil {
t.Errorf("unexpected error: %s\n", err)
}
}

}
// cfgDir and rootDir are defined in root.go
rootDir = fmt.Sprintf("%s/..", here)
cfgDir = fmt.Sprintf("%s/cfg", rootDir)
}

func TestGetControls(t *testing.T) {
var err error
path, err = getDefinitionFilePath(ver, testDefinitionFile)
if err != nil {
t.Errorf("unexpected error: %s\n", err)
}

path := fmt.Sprintf("%s/%s", cfgDir, testDefinitionFile)
b := getMockBench()
_, err = getControls(b, path, nil)
if err != nil {
Expand All @@ -92,8 +53,7 @@ func TestGetControls(t *testing.T) {

func TestRunControls(t *testing.T) {
b := getMockBench()
path, err := loadConfig("2.0.0")
assert.NoError(t, err)
path := fmt.Sprintf("%s/%s", cfgDir, testDefinitionFile)
control, err := getControls(b, path, nil)
if err != nil {
t.Errorf("unexpected error: %s\n", err)
Expand Down
10 changes: 8 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ var (

windowsCisVersion string
cfgDir string
rootDir string
cfgFile string
checkList string
jsonFmt bool
includeTestOutput bool
outputFile string
definitionsFile = "definitions.yaml"
outputWriter io.Writer = os.Stdout
errWriter io.Writer = os.Stderr
)
Expand All @@ -63,7 +63,7 @@ var RootCmd = &cobra.Command{
glog.V(2).Info("Returning a PowerShell (Auditer) \n")
return ps
})
return runChecks(b, ps.OsType)
return runChecks(b, ps.OsType, ps.ServerCaption)
},
}

Expand Down Expand Up @@ -116,6 +116,12 @@ func init() {
RootCmd.PersistentFlags().AddGoFlag(goflag)
})

// Initialize global variables
var err error
rootDir, err = os.Getwd()
if err != nil {
glog.Fatalf("Failed to get the current working directory: %v", err)
}
}

// initConfig reads in config file and ENV variables if set.
Expand Down
43 changes: 32 additions & 11 deletions cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,36 @@ package cmd

import (
"fmt"
"os"
"path/filepath"
"regexp"

"github.com/aquasecurity/bench-common/check"
commonCheck "github.com/aquasecurity/bench-common/check"
"github.com/aquasecurity/bench-common/util"
"github.com/golang/glog"
"gopkg.in/yaml.v2"
)

func runChecks(b commonCheck.Bench, serverType string) error {
var version string
func runChecks(b commonCheck.Bench, serverType, serverCaption string) error {
var cisVersion string
var err error

if windowsCisVersion != "" {
version = windowsCisVersion
cisVersion = windowsCisVersion
} else {
version = "2.0.0"
cisVersion = "2.0.0"
}

if cfgFile == "" {
cfgFile, err = loadConfig(version)
sc := regexp.MustCompile(`Microsoft Windows Server (\d+)`)
match := sc.FindStringSubmatch(serverCaption)
if len(match) < 2 {
return fmt.Errorf("Invalid Microsoft Windows Server caption: %s.\nAre you running windows-bench on a Microsoft Windows Server?", serverCaption)
}
serverVersion := match[1]

cfgFile, err = loadConfig(cisVersion, serverVersion, serverType)
if err != nil {
return err
}
Expand Down Expand Up @@ -86,17 +96,28 @@ func getOsTypeAuditCommand(audit interface{}, serverType string) string {
return fmt.Sprintf("%v", audit)
}

// loadConfig finds the correct config dir based on the kubernetes version,
// merges any specific config.yaml file found with the main config
// and returns the benchmark file to use.
func loadConfig(version string) (string, error) {
// loadConfig finds the correct Window sbenchmark based on the Windows Server version,
// the Server type and the CIS version mapping in `version_map.yaml`.
func loadConfig(cisVersion, serverVersion, serverType string) (string, error) {
var err error
path, err := getConfigFilePath(version, definitionsFile)
versionMap := make(map[string]string)
key := fmt.Sprintf("%s_%s_%s", serverVersion, serverType, cisVersion)
path := filepath.Join(rootDir, "version_map.yaml")
in, err := os.ReadFile(path)
if err != nil {
return "", err
}

return filepath.Join(path, definitionsFile), nil
if err = yaml.Unmarshal(in, versionMap); err != nil {
return "", err
}

cfgFile, exists := versionMap[key]
if !exists {
return "", fmt.Errorf("No benchmark found for %s %s v%s", serverVersion, serverType, cisVersion)
}

return filepath.Join(cfgDir, cfgFile), nil
}

func outputResults(controls *commonCheck.Controls, summary commonCheck.Summary) error {
Expand Down
60 changes: 41 additions & 19 deletions cmd/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,66 @@ package cmd
import (
"bytes"
"encoding/json"
"fmt"
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestLoadConfig(t *testing.T) {
here, _ := os.Getwd()
// cfgDir is defined in root.go
type TestCase struct {
version string
cfgPath string
want string
expectError bool
cisVersion string
serverType string
serverVersion string
want string
}

testCases := []TestCase{
{
version: "2.0.0",
cfgPath: fmt.Sprintf("%s/../cfg", here),
want: "cfg/2.0.0/definitions.yaml",
expectError: false,
cisVersion: "2.0.0",
serverType: "Server",
serverVersion: "2019",
want: "CIS_Microsoft_Windows_Server_2019_Stand-alone_v2.0.0.yaml",
},
{
cisVersion: "2.0.0",
serverType: "MemberServer",
serverVersion: "2019",
want: "CIS_Microsoft_Windows_Server_2022_Benchmark_v2.0.0.yaml",
},
{
cisVersion: "2.0.0",
serverType: "DomainController",
serverVersion: "2019",
want: "CIS_Microsoft_Windows_Server_2022_Benchmark_v2.0.0.yaml",
},
{
cisVersion: "2.0.0",
serverType: "DomainController",
serverVersion: "2022",
want: "CIS_Microsoft_Windows_Server_2022_Benchmark_v2.0.0.yaml",
},
{
cisVersion: "2.0.0",
serverType: "Server",
serverVersion: "2022",
want: "",
},
{
cisVersion: "x.y.z",
serverType: "whois",
serverVersion: "bad",
want: "",
},
}
for _, tc := range testCases {
cfgDir = tc.cfgPath
got, _ := loadConfig(tc.version)
if tc.expectError {
assert.True(t, strings.Contains(got, tc.want))
}
out, _ := loadConfig(tc.cisVersion, tc.serverVersion, tc.serverType)
assert.Contains(t, out, tc.want)
}
}

func TestRunChecks(t *testing.T) {
b := getMockBench()
err := runChecks(b, "Server")
err := runChecks(b, "Server", "Microsoft Windows Server 2019")
var write bytes.Buffer
outputWriter = &write
if err != nil {
Expand Down
13 changes: 10 additions & 3 deletions shell/powershell.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ import (

const TypePowershell = "powershell"
const osTypePowershellCommand = `(Get-CimInstance -ClassName Win32_OperatingSystem).ProductType`
const serverCaptionPowershellCommand = `(Get-CimInstance -ClassName Win32_OperatingSystem).Caption`
const supressError = "$ErrorActionPreference = 'SilentlyContinue'"

var errWrongOSType = errors.New("wrongOSType")

type PowerShell struct {
Cmd map[string]string
sh ps.Shell
OsType string
Cmd map[string]string
sh ps.Shell
OsType string
ServerCaption string
}

type shellStarter interface {
Expand Down Expand Up @@ -62,6 +64,11 @@ func NewPowerShell() (*PowerShell, error) {
case "3":
p.OsType = "Server"
}

p.ServerCaption, err = p.performExec(serverCaptionPowershellCommand)
if err != nil {
return nil, fmt.Errorf("Failed to get server caption: %w", err)
}
return p, nil
}

Expand Down
2 changes: 2 additions & 0 deletions shell/powershell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func TestConstructShell(t *testing.T) {

for _, testCase := range testCases {
powerShell, err := constructShell(testCase.mss)
//nolint:gocritic
if testCase.mss.fail {
if err == nil {
t.Errorf("Expected Error")
Expand Down Expand Up @@ -166,6 +167,7 @@ func TestExecute(t *testing.T) {

for _, testCase := range testCases {
result, em, st := testCase.ps.Execute()
//nolint:gocritic
if testCase.fail {
if st != check.SKIP {
t.Errorf("Expected FAIL state but instead got %q", st)
Expand Down
Loading

0 comments on commit d795389

Please sign in to comment.