mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Add CIS checks for 5.4 and 5.5 (#9747)
#9260 - [X] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - ~[ ] Documented any API changes (docs/Using-Fleet/REST-API.md or docs/Contributing/API-for-contributors.md)~ - ~[ ] Documented any permissions changes~ - ~[ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements)~ - ~[ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features.~ - [X] Added/updated tests - [X] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [X] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - ~[ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)).~
This commit is contained in:
parent
a23ce1b0a2
commit
7fc3613dc5
@ -220,7 +220,7 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: policy
|
||||
spec:
|
||||
name: CIS - Ensure Time Is Set Within Appropriate Limits (fleetd required)
|
||||
name: CIS - Ensure Time Is Set Within Appropriate Limits (Fleetd Required)
|
||||
platforms: macOS
|
||||
platform: darwin
|
||||
description: |
|
||||
@ -1033,7 +1033,7 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: policy
|
||||
spec:
|
||||
name: CIS - Ensure Users' Accounts Do Not Have a Password Hint (fleetd required)
|
||||
name: CIS - Ensure Users' Accounts Do Not Have a Password Hint (Fleetd Required)
|
||||
platforms: macOS
|
||||
platform: darwin
|
||||
description: |
|
||||
@ -1640,7 +1640,7 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: policy
|
||||
spec:
|
||||
name: CIS - Ensure Password Age Is Configured (fleetd required)
|
||||
name: CIS - Ensure Password Age Is Configured (Fleetd Required)
|
||||
platforms: macOS
|
||||
platform: darwin
|
||||
description: |
|
||||
@ -1665,7 +1665,7 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: policy
|
||||
spec:
|
||||
name: CIS - Ensure Password History Is Configured (fleetd required)
|
||||
name: CIS - Ensure Password History Is Configured (Fleetd Required)
|
||||
platforms: macOS
|
||||
platform: darwin
|
||||
description: |
|
||||
@ -1683,6 +1683,60 @@ spec:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: policy
|
||||
spec:
|
||||
name: CIS - Ensure the Sudo Timeout Period Is Set to Zero (Fleetd Required)
|
||||
platforms: macOS
|
||||
platform: darwin
|
||||
description: |
|
||||
The sudo command stays logged in as the root user for five minutes before timing out and re-requesting a password.
|
||||
This five-minute window should be eliminated since it leaves the system extremely vulnerable.
|
||||
resolution: |
|
||||
Automated method:
|
||||
Ask your system administrator to deploy the following script:
|
||||
echo 'Defaults timestamp_timeout=0' | sudo tee -a /etc/sudoers.d/CIS_54_sudoconfiguration
|
||||
/usr/bin/sudo /usr/sbin/chown -R root:wheel /etc/sudoers.d/
|
||||
query: |
|
||||
SELECT 1 WHERE EXISTS(
|
||||
SELECT * FROM file WHERE path = '/etc/sudoers.d' AND uid = 0 AND gid = 0
|
||||
) AND EXISTS(
|
||||
SELECT
|
||||
COALESCE(JSON_EXTRACT(
|
||||
json_result, '$.Authentication timestamp timeout'
|
||||
), '') AS authentication_timestamp_timeout
|
||||
FROM sudo_info WHERE authentication_timestamp_timeout = '0.0 minutes'
|
||||
);
|
||||
purpose: Informational
|
||||
tags: compliance, CIS, CIS_Level1, CIS5.4
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: policy
|
||||
spec:
|
||||
name: CIS - Ensure a Separate Timestamp Is Enabled for Each User/tty (Fleetd Required)
|
||||
platforms: macOS
|
||||
platform: darwin
|
||||
description: |
|
||||
Using tty tickets ensures that a user must enter the sudo password in each Terminal session.
|
||||
With sudo versions 1.8 and higher, introduced in 10.12, the default value is to have tty tickets
|
||||
for each interface so that root access is limited to a specific terminal.
|
||||
The default configuration can be overwritten or not configured correctly on earlier versions of macOS.
|
||||
resolution: |
|
||||
Automated method:
|
||||
Ask your system administrator to deploy the following script:
|
||||
echo 'Defaults timestamp_type=tty' | sudo tee /etc/sudoers.d/CIS_55_sudoconfiguration
|
||||
query: |
|
||||
SELECT 1 WHERE EXISTS(
|
||||
SELECT
|
||||
COALESCE(JSON_EXTRACT(
|
||||
json_result, '$.Type of authentication timestamp record'
|
||||
), '') AS type_of_auth_timestamp_record
|
||||
FROM sudo_info WHERE type_of_auth_timestamp_record = 'tty'
|
||||
);
|
||||
purpose: Informational
|
||||
tags: compliance, CIS, CIS_Level1, CIS5.5
|
||||
contributors: lucasmrod
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: policy
|
||||
spec:
|
||||
name: CIS - Ensure an Administrator Account Cannot Login to Another User's Active and Locked Session (Fleetd Required)
|
||||
platforms: macOS
|
||||
|
4
ee/cis/macos-13/test/scripts/CIS_5.4.sh
Normal file
4
ee/cis/macos-13/test/scripts/CIS_5.4.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo 'Defaults timestamp_timeout=0' | sudo tee /etc/sudoers.d/CIS_54_sudoconfiguration
|
||||
/usr/bin/sudo /usr/sbin/chown -R root:wheel /etc/sudoers.d/
|
3
ee/cis/macos-13/test/scripts/CIS_5.5.sh
Normal file
3
ee/cis/macos-13/test/scripts/CIS_5.5.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo 'Defaults timestamp_type=tty' | sudo tee /etc/sudoers.d/CIS_55_sudoconfiguration
|
1
orbit/changes/9260-add-sudo_info-table-cis-5.X
Normal file
1
orbit/changes/9260-add-sudo_info-table-cis-5.X
Normal file
@ -0,0 +1 @@
|
||||
* Add `sudo_info` table to Orbit for CIS checks 5.4 and 5.5 on macOS.
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/pmset"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/privaterelay"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/pwd_policy"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/sudo_info"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/table/user_login_settings"
|
||||
"github.com/macadmins/osquery-extension/tables/filevaultusers"
|
||||
"github.com/macadmins/osquery-extension/tables/macos_profiles"
|
||||
@ -29,6 +30,7 @@ func platformTables() []osquery.OsqueryPlugin {
|
||||
table.NewPlugin("nvram_info", nvram_info.Columns(), nvram_info.Generate),
|
||||
table.NewPlugin("authdb", authdb.Columns(), authdb.Generate),
|
||||
table.NewPlugin("pmset", pmset.Columns(), pmset.Generate),
|
||||
table.NewPlugin("sudo_info", sudo_info.Columns(), sudo_info.Generate),
|
||||
|
||||
// Macadmins extension tables
|
||||
table.NewPlugin("filevault_users", filevaultusers.FileVaultUsersColumns(), filevaultusers.FileVaultUsersGenerate),
|
||||
|
76
orbit/pkg/table/sudo_info/sudo_info_darwin.go
Normal file
76
orbit/pkg/table/sudo_info/sudo_info_darwin.go
Normal file
@ -0,0 +1,76 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package sudo_info
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/osquery/osquery-go/plugin/table"
|
||||
)
|
||||
|
||||
// Columns is the schema of the table.
|
||||
func Columns() []table.ColumnDefinition {
|
||||
return []table.ColumnDefinition{
|
||||
table.TextColumn("json_result"),
|
||||
}
|
||||
}
|
||||
|
||||
// Generate is called to return the results for the table at query time.
|
||||
//
|
||||
// Constraints for generating can be retrieved from the queryContext.
|
||||
func Generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
|
||||
out, err := exec.Command("/usr/bin/sudo", "-V").Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("command failed: %w", err)
|
||||
}
|
||||
result := parseSudoVOutput(out)
|
||||
|
||||
jsonResult, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []map[string]string{{
|
||||
"json_result": string(jsonResult),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func parseSudoVOutput(output []byte) map[string]interface{} {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(output))
|
||||
|
||||
result := make(map[string]interface{})
|
||||
curKey := ""
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
if line[0] == ' ' || line[0] == '\t' {
|
||||
if result[curKey] == nil {
|
||||
result[curKey] = []string{}
|
||||
}
|
||||
result[curKey] = append(result[curKey].([]string), strings.TrimSpace(line))
|
||||
continue
|
||||
}
|
||||
colonIndex := strings.IndexByte(line, ':')
|
||||
if colonIndex == -1 {
|
||||
curKey = line
|
||||
result[line] = nil
|
||||
continue
|
||||
}
|
||||
if colonIndex == len(line)-1 {
|
||||
curKey = line[:len(line)-1]
|
||||
continue
|
||||
}
|
||||
result[line[0:colonIndex]] = line[colonIndex+2:] // +2 because of space character after colon
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
216
orbit/pkg/table/sudo_info/sudo_info_darwin_test.go
Normal file
216
orbit/pkg/table/sudo_info/sudo_info_darwin_test.go
Normal file
@ -0,0 +1,216 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package sudo_info
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseSudoVOutput(t *testing.T) {
|
||||
const sample = `Sudo version 1.9.5p2
|
||||
Configure options: --with-password-timeout=0 --disable-setreuid --with-env-editor --with-pam --with-libraries=bsm --with-noexec=no --sysconfdir=/private/etc --without-lecture --enable-static-sudoers --with-rundir=/var/db/sudo
|
||||
Sudoers policy plugin version 1.9.5p2
|
||||
Sudoers file grammar version 48
|
||||
|
||||
Sudoers path: /etc/sudoers
|
||||
Authentication methods: 'pam'
|
||||
Syslog facility if syslog is being used for logging: authpriv
|
||||
Syslog priority to use when user authenticates successfully: notice
|
||||
Syslog priority to use when user authenticates unsuccessfully: alert
|
||||
Send mail if the user is not in sudoers
|
||||
Lecture user the first time they run sudo
|
||||
File containing the sudo lecture: /etc/sudo_lecture
|
||||
Require users to authenticate by default
|
||||
Root may run sudo
|
||||
Allow some information gathering to give useful error messages
|
||||
Visudo will honor the EDITOR environment variable
|
||||
Set the LOGNAME and USER environment variables
|
||||
Length at which to wrap log file lines (0 for no wrap): 80
|
||||
Authentication timestamp timeout: 5.0 minutes
|
||||
Password prompt timeout: 0.0 minutes
|
||||
Number of tries to enter a password: 3
|
||||
Umask to use or 0777 to use user's: 022
|
||||
Path to mail program: /usr/sbin/sendmail
|
||||
Flags for mail program: -t
|
||||
Address to send mail to: root
|
||||
Subject line for mail messages: *** SECURITY information for %h ***
|
||||
Incorrect password message: Sorry, try again.
|
||||
Path to lecture status dir: /var/db/sudo/lectured
|
||||
Path to authentication timestamp dir: /var/db/sudo/ts
|
||||
Default password prompt: Password:
|
||||
Default user to run commands as: root
|
||||
Path to the editor for use by visudo: /usr/bin/vi
|
||||
When to require a password for 'list' pseudocommand: any
|
||||
When to require a password for 'verify' pseudocommand: all
|
||||
File descriptors >= 3 will be closed before executing a command
|
||||
Reset the environment to a default set of variables
|
||||
Environment variables to check for safety:
|
||||
TZ
|
||||
TERM
|
||||
LINGUAS
|
||||
LC_*
|
||||
LANGUAGE
|
||||
LANG
|
||||
COLORTERM
|
||||
Environment variables to remove:
|
||||
*=()*
|
||||
RUBYOPT
|
||||
RUBYLIB
|
||||
PYTHONUSERBASE
|
||||
PYTHONINSPECT
|
||||
PYTHONPATH
|
||||
PYTHONHOME
|
||||
TMPPREFIX
|
||||
ZDOTDIR
|
||||
READNULLCMD
|
||||
NULLCMD
|
||||
FPATH
|
||||
PERL5DB
|
||||
PERL5OPT
|
||||
PERL5LIB
|
||||
PERLLIB
|
||||
PERLIO_DEBUG
|
||||
JAVA_TOOL_OPTIONS
|
||||
SHELLOPTS
|
||||
BASHOPTS
|
||||
GLOBIGNORE
|
||||
PS4
|
||||
BASH_ENV
|
||||
ENV
|
||||
TERMCAP
|
||||
TERMPATH
|
||||
TERMINFO_DIRS
|
||||
TERMINFO
|
||||
DYLD_*
|
||||
_RLD*
|
||||
LD_*
|
||||
PATH_LOCALE
|
||||
NLSPATH
|
||||
HOSTALIASES
|
||||
RES_OPTIONS
|
||||
LOCALDOMAIN
|
||||
CDPATH
|
||||
IFS
|
||||
Environment variables to preserve:
|
||||
MAIL
|
||||
HOME
|
||||
VISUAL
|
||||
EDITOR
|
||||
TZ
|
||||
SSH_AUTH_SOCK
|
||||
LSCOLORS
|
||||
COLUMNS
|
||||
LINES
|
||||
LC_TIME
|
||||
LC_NUMERIC
|
||||
LC_MONETARY
|
||||
LC_MESSAGES
|
||||
LC_CTYPE
|
||||
LC_COLLATE
|
||||
LC_ALL
|
||||
LANGUAGE
|
||||
LANG
|
||||
CHARSET
|
||||
__CF_USER_TEXT_ENCODING
|
||||
COLORTERM
|
||||
COLORFGBG
|
||||
BLOCKSIZE
|
||||
XAUTHORIZATION
|
||||
XAUTHORITY
|
||||
PS2
|
||||
PS1
|
||||
PATH
|
||||
LS_COLORS
|
||||
KRB5CCNAME
|
||||
HOSTNAME
|
||||
DISPLAY
|
||||
COLORS
|
||||
Locale to use while parsing sudoers: C
|
||||
Compress I/O logs using zlib
|
||||
Directory in which to store input/output logs: /var/log/sudo-io
|
||||
File in which to store the input/output log: %{seq}
|
||||
Add an entry to the utmp/utmpx file when allocating a pty
|
||||
PAM service name to use: sudo
|
||||
PAM service name to use for login shells: sudo
|
||||
Attempt to establish PAM credentials for the target user
|
||||
Create a new PAM session for the command to run in
|
||||
Perform PAM account validation management
|
||||
Enable sudoers netgroup support
|
||||
Check parent directories for writability when editing files with sudoedit
|
||||
Allow commands to be run even if sudo cannot write to the audit log
|
||||
Allow commands to be run even if sudo cannot write to the log file
|
||||
Log entries larger than this value will be split into multiple syslog messages: 960
|
||||
File mode to use for the I/O log files: 0600
|
||||
Execute commands by file descriptor instead of by path: digest_only
|
||||
Type of authentication timestamp record: tty
|
||||
Ignore case when matching user names
|
||||
Ignore case when matching group names
|
||||
Log when a command is allowed by sudoers
|
||||
Log when a command is denied by sudoers
|
||||
Sudo log server timeout in seconds: 30
|
||||
Enable SO_KEEPALIVE socket option on the socket connected to the logserver
|
||||
Verify that the log server's certificate is valid
|
||||
Set the pam remote user to the user running sudo
|
||||
The format of logs to produce: sudo
|
||||
|
||||
Local IP address and netmask pairs:
|
||||
fe80::aede:48ff:fe00:1122/ffff:ffff:ffff:ffff::
|
||||
fe80::142f:6d87:1e52:591d/ffff:ffff:ffff:ffff::
|
||||
192.168.0.230/255.255.255.0
|
||||
fe80::70f9:67ff:fe50:983f/ffff:ffff:ffff:ffff::
|
||||
fe80::70f9:67ff:fe50:983f/ffff:ffff:ffff:ffff::
|
||||
fe80::8372:c8a:ecf8:40b5/ffff:ffff:ffff:ffff::
|
||||
fe80::612f:3c9d:f33a:9e7d/ffff:ffff:ffff:ffff::
|
||||
fe80::ce81:b1c:bd2c:69e/ffff:ffff:ffff:ffff::
|
||||
192.168.103.1/255.255.255.0
|
||||
fe80::682f:67ff:fee8:7464/ffff:ffff:ffff:ffff::
|
||||
172.16.132.1/255.255.255.0
|
||||
fe80::682f:67ff:fee8:7465/ffff:ffff:ffff:ffff::
|
||||
|
||||
Sudoers I/O plugin version 1.9.5p2
|
||||
Sudoers audit plugin version 1.9.5p2`
|
||||
result := parseSudoVOutput([]byte(sample))
|
||||
_, err := json.Marshal(result)
|
||||
require.NoError(t, err)
|
||||
|
||||
// First line.
|
||||
require.Contains(t, result, "Sudo version 1.9.5p2")
|
||||
require.Nil(t, result["Sudo version 1.9.5p2"])
|
||||
|
||||
// Key without value.
|
||||
require.Contains(t, result, "Compress I/O logs using zlib")
|
||||
require.Nil(t, result["Compress I/O logs using zlib"])
|
||||
|
||||
// Key and value pairs defined in one line.
|
||||
require.Equal(t, "C", result["Locale to use while parsing sudoers"])
|
||||
require.Equal(t, "tty", result["Type of authentication timestamp record"])
|
||||
require.Equal(t, "5.0 minutes", result["Authentication timestamp timeout"])
|
||||
|
||||
// Last line.
|
||||
require.Contains(t, result, "Sudoers audit plugin version 1.9.5p2")
|
||||
require.Nil(t, result["Sudoers audit plugin version 1.9.5p2"])
|
||||
|
||||
// Key and value paris defined in multiple lines
|
||||
v := result["Local IP address and netmask pairs"]
|
||||
require.NotNil(t, v)
|
||||
localIPAddrAndNetmaskPairs, ok := v.([]string)
|
||||
require.True(t, ok)
|
||||
require.Len(t, localIPAddrAndNetmaskPairs, 12)
|
||||
require.Contains(t, localIPAddrAndNetmaskPairs, "fe80::aede:48ff:fe00:1122/ffff:ffff:ffff:ffff::")
|
||||
require.Contains(t, localIPAddrAndNetmaskPairs, "192.168.103.1/255.255.255.0")
|
||||
require.Contains(t, localIPAddrAndNetmaskPairs, "fe80::682f:67ff:fee8:7465/ffff:ffff:ffff:ffff::")
|
||||
v = result["Environment variables to preserve"]
|
||||
require.NotNil(t, v)
|
||||
envVariablesToPreserve, ok := v.([]string)
|
||||
require.True(t, ok)
|
||||
require.Len(t, envVariablesToPreserve, 33)
|
||||
require.Contains(t, envVariablesToPreserve, "MAIL")
|
||||
require.Contains(t, envVariablesToPreserve, "COLORS")
|
||||
|
||||
// Line with multiple colons:
|
||||
require.Equal(t, "Password: ", result["Default password prompt"])
|
||||
}
|
12
schema/tables/sudo_info.yml
Normal file
12
schema/tables/sudo_info.yml
Normal file
@ -0,0 +1,12 @@
|
||||
name: sudo_info
|
||||
platforms:
|
||||
- darwin
|
||||
description: Returns the output of `sudo -V` in JSON format.
|
||||
columns:
|
||||
- name: json_result
|
||||
type: text
|
||||
required: false
|
||||
description: A JSON document with the key value pairs parsed from `sudo -V` output.
|
||||
notes: >-
|
||||
- This table is not a core osquery table. It is included as part of Fleetd, the osquery manager from Fleet.
|
||||
evented: false
|
@ -24,7 +24,7 @@ if [[ -d "$TUF_PATH" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
OSQUERY_MACOS_APP_BUNDLE_VERSION=5.6.0
|
||||
OSQUERY_MACOS_APP_BUNDLE_VERSION=5.7.0
|
||||
SYSTEMS=${SYSTEMS:-macos linux windows}
|
||||
|
||||
mkdir -p $TUF_PATH/tmp
|
||||
|
Loading…
Reference in New Issue
Block a user