summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsergey.galtsev <sergey.galtsev@mongodb.com>2021-09-28 22:43:09 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-09-28 23:11:18 +0000
commite5d32fc7b6811a659ac36f519bf697d776df4849 (patch)
tree6c2675bad0d3c64daf6c1dea26a738ea242b4921
parent9569a71e456821fe24030f59810f384d4a9b8b02 (diff)
downloadmongo-e5d32fc7b6811a659ac36f519bf697d776df4849.tar.gz
SERVER-56180 SELinux policy tests
-rw-r--r--.gitignore8
-rw-r--r--etc/evergreen.yml92
-rwxr-xr-xevergreen/selinux_run_test.sh78
-rwxr-xr-xevergreen/selinux_test_executor.sh102
-rwxr-xr-xevergreen/selinux_test_setup.sh44
-rw-r--r--jstests/selinux/core.js102
-rw-r--r--jstests/selinux/default.js24
-rw-r--r--jstests/selinux/lib/selinux_base_test.js23
8 files changed, 473 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 2434c53fb6f..acb29394d1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -211,3 +211,11 @@ resmoke.ini
default.profraw
/corpora
/corpora-merged
+
+# RPM build temps
+/distsrc.tar
+/selinux/tmp
+/dst
+/rpmbuild
+/repo
+/rpm/tmp
diff --git a/etc/evergreen.yml b/etc/evergreen.yml
index dee53d454a2..e3f564cabcd 100644
--- a/etc/evergreen.yml
+++ b/etc/evergreen.yml
@@ -1441,6 +1441,30 @@ functions:
args:
- "./src/evergreen/powercycle_setup_host.sh"
+ "run selinux tests":
+ - command: host.create
+ params:
+ provider: ec2
+ distro: ${distro}
+ timeout_teardown_secs: 86400 # 1 day
+ - command: host.list
+ params:
+ wait: true
+ timeout_seconds: 900 # 15 min
+ num_hosts: 1
+ path: src/hosts.yml
+ - *f_expansions_write
+ - command: subprocess.exec
+ type: test
+ params:
+ binary: bash
+ args:
+ - "./src/evergreen/selinux_run_test.sh"
+ env:
+ test_list: ${test_list}
+ user: ec2-user
+
+
### Process & archive remote EC2 artifacts ###
"save powercycle artifacts": &save_powercycle_artifacts
command: subprocess.exec
@@ -6047,6 +6071,69 @@ tasks:
- func: "run powercycle test"
timeout_secs: 1800 # 30 minute timeout for no output
+
+- name: selinux_rhel8_org
+ tags: []
+ depends_on:
+ - name: package
+ commands:
+ - command: manifest.load
+ - func: "git get project and add git tag"
+ - *f_expansions_write
+ - func: "set up venv"
+ - func: "fetch packages"
+ - func: "run selinux tests"
+ vars:
+ distro: rhel80-selinux
+ test_list: jstests/selinux/*.js
+
+- name: selinux_rhel8_enterprise
+ tags: []
+ depends_on:
+ - name: package
+ commands:
+ - command: manifest.load
+ - func: "git get project and add git tag"
+ - *f_expansions_write
+ - func: "set up venv"
+ - func: "fetch packages"
+ - func: "run selinux tests"
+ vars:
+ distro: rhel80-selinux
+ test_list: jstests/selinux/*.js src/mongo/db/modules/enterprise/jstests/selinux/*.js
+
+- name: selinux_rhel7_org
+ tags: []
+ depends_on:
+ - name: package
+ commands:
+ - command: manifest.load
+ - func: "git get project and add git tag"
+ - *f_expansions_write
+ - func: "set up venv"
+ - func: "fetch packages"
+ - func: "run selinux tests"
+ vars:
+ user: root
+ distro: rhel76-selinux
+ test_list: jstests/selinux/*.js
+
+- name: selinux_rhel7_enterprise
+ tags: []
+ depends_on:
+ - name: package
+ commands:
+ - command: manifest.load
+ - func: "git get project and add git tag"
+ - *f_expansions_write
+ - func: "set up venv"
+ - func: "fetch packages"
+ - func: "run selinux tests"
+ vars:
+ user: root
+ distro: rhel76-selinux
+ test_list: jstests/selinux/*.js src/mongo/db/modules/enterprise/jstests/selinux/*.js
+
- name: idl_tests
tags: []
depends_on:
@@ -9136,6 +9223,7 @@ buildvariants:
- name: test_packages
distros:
- ubuntu2004-package
+ - name: selinux_rhel8_enterprise
- name: .publish
- &enterprise-rhel-80-64-bit-dynamic-required-template
@@ -9830,6 +9918,7 @@ buildvariants:
- name: test_packages
distros:
- ubuntu2004-package
+ - name: selinux_rhel7_enterprise
- name: .publish
distros:
- rhel70-small
@@ -9912,6 +10001,7 @@ buildvariants:
- name: test_packages
distros:
- ubuntu2004-package
+ - name: selinux_rhel8_enterprise
- name: .publish
distros:
- rhel80-small
@@ -9967,6 +10057,7 @@ buildvariants:
- name: test_packages
distros:
- ubuntu2004-package
+ - name: selinux_rhel8_org
- name: .publish
distros:
- rhel80-small
@@ -10149,6 +10240,7 @@ buildvariants:
- name: test_packages
distros:
- ubuntu2004-package
+ - name: selinux_rhel7_org
- name: .publish
distros:
- rhel70-small
diff --git a/evergreen/selinux_run_test.sh b/evergreen/selinux_run_test.sh
new file mode 100755
index 00000000000..318d73adbe2
--- /dev/null
+++ b/evergreen/selinux_run_test.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+# Notes on how to run this manually:
+# - repo must be unpacked into source tree
+#
+# export ssh_key=$HOME/.ssh/id_rsa
+# export hostname=ec2-3-91-230-150.compute-1.amazonaws.com
+# export user=ec2-user
+# export bypass_prelude=yes
+# export workdir="$(dirname $(pwd) | tee /dev/stderr)"
+# export src="$(basename $(pwd) | tee /dev/stderr)"
+# export test_list='jstests/selinux/*.js'
+# export pkg_variant=mongodb-enterprise
+# evergreen/selinux_run_test.sh
+
+set -o errexit
+
+DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)"
+if [ "$bypass_prelude" != "yes" ]; then
+ . "$DIR/prelude.sh"
+ activate_venv
+ src="src"
+fi
+
+set -o xtrace
+
+if [ "$hostname" == "" ]; then
+ hostname="$(tr -d '"[]{}' < "$workdir"/$src/hosts.yml | cut -d , -f 1 | awk -F : '{print $2}')"
+fi
+
+if [ "$user" == "" ]; then
+ user=$USER
+fi
+
+host="${user}@${hostname}"
+python="${python:-python3}"
+
+if [ "$ssh_key" == "" ]; then
+ ssh_key="$workdir/selinux.pem"
+ "$workdir"/$src/buildscripts/yaml_key_value.py --yamlFile="$workdir"/expansions.yml \
+ --yamlKey=__project_aws_ssh_key_value > "$ssh_key"
+ chmod 600 "$ssh_key"
+ result="$(openssl rsa -in "$ssh_key" -check -noout | tee /dev/stderr)"
+ if [ "$result" != "RSA key ok" ]; then
+ exit 1
+ fi
+fi
+
+attempts=0
+connection_attempts=50
+
+# Check for remote connectivity
+set +o errexit
+ssh_options="-i $ssh_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=no"
+while ! ssh $ssh_options -o ConnectTimeout=10 "$host" echo "I am working"; do
+ if [ "$attempts" -ge "$connection_attempts" ]; then exit 1; fi
+ ((attempts++))
+ printf "SSH connection attempt %d/%d failed. Retrying...\n" "$attempts" "$connection_attempts"
+ sleep 10
+done
+
+set -o errexit
+echo "===> Copying sources to target..."
+rsync -ar -e "ssh $ssh_options" \
+ --exclude 'tmp' --exclude 'build' --exclude '.*' \
+ "$workdir"/$src/* "$host":
+
+echo "===> Configuring target machine..."
+ssh $ssh_options "$host" evergreen/selinux_test_setup.sh
+
+echo "===> Executing tests..."
+list="$(
+ cd src
+ for x in $test_list; do echo "$x"; done
+)"
+for test in $list; do
+ ssh $ssh_options "$host" evergreen/selinux_test_executor.sh "$test"
+done
diff --git a/evergreen/selinux_test_executor.sh b/evergreen/selinux_test_executor.sh
new file mode 100755
index 00000000000..1ba889a1c80
--- /dev/null
+++ b/evergreen/selinux_test_executor.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+
+set -o errexit
+set -o xtrace
+
+function print() {
+ echo "$@" >&2
+}
+
+function monitor_log() {
+ sed "s!^!mongod| $(date '+%F %H-%M-%S') !" <(sudo --non-interactive tail -f /var/log/mongodb/mongod.log)
+}
+
+TEST_PATH="$1"
+if [ ! -f "$TEST_PATH" ]; then
+ print "No test supplied or test file not found. Run:"
+ print " $(basename "${BASH_SOURCE[0]}") <path>"
+ exit 1
+fi
+
+# test file is even good before going on
+if ! mongo --nodb --norc --quiet "$TEST_PATH"; then
+ print "File $TEST_PATH has syntax errors"
+ exit 1
+fi
+
+# stop mongod, zero mongo log, clean up database, set all booleans to off
+sudo --non-interactive bash -c '
+ systemctl stop mongod
+
+ rm -f /var/log/mongodb/mongod.log
+ touch /var/log/mongodb/mongod.log
+ chown mongod /var/log/mongodb/mongod.log
+
+ rm -rf /var/lib/mongo/*
+
+ rm -rf /etc/sysconfig/mongod /etc/mongod
+
+ setsebool mongod_can_connect_snmp off
+ setsebool mongod_can_connect_ldap off
+ setsebool mongod_can_use_kerberos off
+'
+
+# create mongo config
+mongo --nodb --norc --quiet --eval='
+ assert(load("'"$TEST_PATH"'"));
+ const test = new TestDefinition();
+ print(typeof(test.config) === "string" ? test.config : JSON.stringify(test.config, null, 2));
+' | sudo --non-interactive tee /etc/mongod.conf
+
+# setup
+mongo --nodb --norc --quiet --eval='
+ assert(load("'"$TEST_PATH"'"));
+ const test = new TestDefinition();
+ jsTest.log("Running setup()");
+ test.setup();
+'
+
+# start log monitor, also kill it on exit
+monitor_log &
+MONITORPID="$!"
+trap "sudo --non-interactive pkill -P $MONITORPID" SIGINT SIGTERM ERR EXIT
+
+# start mongod and if it won't come up, log SELinux errors
+ts="$(date --utc --date='1 seconds ago' '+%x %H:%M:%S')"
+tsj="$(date --utc --date='1 seconds ago' +'%Y-%m-%d %H:%M:%S')"
+sudo --non-interactive systemctl start mongod \
+ && sudo --non-interactive systemctl status mongod || (
+ set +o errexit
+ echo "=== SELinux errors:"
+ sudo --non-interactive ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts $ts
+ echo "=== journalctl --unit=mongod:"
+ sudo --non-interactive journalctl --no-pager --since="$tsj" --unit=mongod --unit=systemd --catalog
+ echo "=== /var/log/mongodb/mongod.log:"
+ sudo --non-interactive cat /var/log/mongodb/mongod.log
+ echo "==== FAIL: mongod service was not started successfully"
+ exit 1
+)
+
+# run test and teardown
+mongo --norc --gssapiServiceName=mockservice --eval='
+ assert(load("'"$TEST_PATH"'"));
+ // name is such to prevent collisions
+ const test_812de7ce = new TestDefinition();
+ try {
+ jsTest.log("Running test");
+ test_812de7ce.run();
+ } finally {
+ test_812de7ce.teardown();
+ }
+' || (
+ echo "==== FAIL: test returned result: $?"
+ echo "=== SELinux errors:"
+ set +o errexit
+ sudo --non-interactive ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts $ts
+ echo "=== /var/log/mongodb/mongod.log:"
+ sudo --non-interactive cat /var/log/mongodb/mongod.log
+ exit 1
+)
+
+set +o xtrace
+echo "SUCCESS: $TEST_PATH"
diff --git a/evergreen/selinux_test_setup.sh b/evergreen/selinux_test_setup.sh
new file mode 100755
index 00000000000..ef2e70df2a9
--- /dev/null
+++ b/evergreen/selinux_test_setup.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# This script is loaded on the target machine, which is running tests
+# Purpose: install mongod and shell from packages
+
+set -o xtrace
+set -o errexit
+
+function apply_selinux_policy() {
+ echo "==== Applying SELinux policy now"
+ rm -rf mongodb-selinux
+ git clone https://github.com/mongodb/mongodb-selinux
+ cd mongodb-selinux
+ make
+ sudo make install
+}
+
+# on evergreen images /tmp is usually linked to /data/tmp, which interferes
+# with selinux, as it does not recognize it as tmp_t domain
+if [ -L /tmp ]; then
+ sudo --non-interactive rm /tmp
+ sudo --non-interactive mkdir /tmp
+ sudo --non-interactive systemctl start tmp.mount
+fi
+
+# selinux policy should work both when applied before and after install
+# we will randomly apply it before or after installation is completed
+SEORDER="$(($RANDOM % 2))"
+if [ "$SEORDER" == "0" ]; then
+ apply_selinux_policy
+fi
+
+# install shell using yum, so that dependencies are pulled
+pkg="$(find "$HOME"/repo -name 'mongodb-*-shell-*.x86_64.rpm' | tee /dev/stderr)"
+sudo --non-interactive yum install --assumeyes "$pkg" \
+ || if [ "$?" -gt "1" ]; then exit 1; fi # exit code 1 is OK
+
+pkg="$(find "$HOME"/repo -name 'mongodb-*-server-*.x86_64.rpm' | tee /dev/stderr)"
+sudo --non-interactive rpm --install --verbose --verbose --hash --nodeps "$pkg" \
+ || if [ "$?" -gt "1" ]; then exit 1; fi # exit code 1 is OK
+
+if [ "$SEORDER" == "1" ]; then
+ apply_selinux_policy
+fi
diff --git a/jstests/selinux/core.js b/jstests/selinux/core.js
new file mode 100644
index 00000000000..b4ac8652af7
--- /dev/null
+++ b/jstests/selinux/core.js
@@ -0,0 +1,102 @@
+
+'use strict';
+
+load('jstests/selinux/lib/selinux_base_test.js');
+
+class TestDefinition extends SelinuxBaseTest {
+ get config() {
+ return {
+ "systemLog":
+ {"destination": "file", "logAppend": true, "path": "/var/log/mongodb/mongod.log"},
+ "storage": {"dbPath": "/var/lib/mongo", "journal": {"enabled": true}},
+ "processManagement": {
+ "fork": true,
+ "pidFilePath": "/var/run/mongodb/mongod.pid",
+ "timeZoneInfo": "/usr/share/zoneinfo"
+ },
+ "net": {"port": 27017, "bindIp": "127.0.0.1"}
+ };
+ }
+
+ run() {
+ let dirs = ["jstests/core", "jstests/core_standalone"];
+
+ // Tests in jstests/core weren't specifically made to pass in this very scenario, so we
+ // will not be fixing what is not working, and instead exclude them from running as
+ // "known" to not work
+ const exclude = new Set([
+ "jstests/core/api_version_parameters.js",
+ "jstests/core/api_version_test_expression.js",
+ "jstests/core/basic6.js",
+ "jstests/core/capped_empty.js",
+ "jstests/core/capped_update.js",
+ "jstests/core/check_shard_index.js",
+ "jstests/core/collection_truncate.js",
+ "jstests/core/commands_namespace_parsing.js",
+ "jstests/core/comment_field.js",
+ "jstests/core/compound_index_max_fields.js",
+ "jstests/core/crud_ops_do_not_throw_locktimeout.js",
+ "jstests/core/currentop_cursors.js",
+ "jstests/core/currentop_shell.js",
+ "jstests/core/currentop_waiting_for_latch.js",
+ "jstests/core/datasize2.js",
+ "jstests/core/doc_validation_options.js",
+ "jstests/core/double_decimal_compare.js",
+ "jstests/core/drop_collection.js",
+ "jstests/core/explain_uuid.js",
+ "jstests/core/failcommand_failpoint.js",
+ "jstests/core/geo_near_point_query.js",
+ "jstests/core/getlog2.js",
+ "jstests/core/hash.js",
+ "jstests/core/indexj.js",
+ "jstests/core/jssymbol.js",
+ "jstests/core/latch_analyzer.js",
+ "jstests/core/list_all_sessions.js",
+ "jstests/core/list_sessions.js",
+ "jstests/core/logprocessdetails.js",
+ "jstests/core/mr_killop.js",
+ "jstests/core/profile_hide_index.js",
+ "jstests/core/rename_collection_capped.js",
+ "jstests/core/resume_query.js",
+ "jstests/core/splitvector.js",
+ "jstests/core/sort_with_update_between_getmores.js",
+ "jstests/core/stages_and_hash.js",
+ "jstests/core/stages_and_sorted.js",
+ "jstests/core/stages_collection_scan.js",
+ "jstests/core/stages_delete.js",
+ "jstests/core/stages_fetch.js",
+ "jstests/core/stages_ixscan.js",
+ "jstests/core/stages_limit_skip.js",
+ "jstests/core/stages_mergesort.js",
+ "jstests/core/stages_or.js",
+ "jstests/core/type8.js",
+ "jstests/core/validate_db_metadata_command.js",
+ "jstests/core/version_api_list_commands_verification.js",
+ "jstests/core/wildcard_index_distinct_scan.js",
+ "jstests/core/wildcard_index_projection.js"
+ ]);
+
+ for (let id = 0; id < dirs.length; ++id) {
+ const dir = dirs[id];
+ jsTest.log("Running tests in " + dir);
+
+ const all_tests = ls(dir).filter(d => !d.endsWith("/") && !exclude.has(d)).sort();
+ assert(all_tests);
+ assert(all_tests.length);
+
+ for (let i = 0; i < all_tests.length; ++i) {
+ let t = all_tests[i];
+ if (t.endsWith("/")) {
+ continue;
+ }
+ jsTest.log("Running test: " + t);
+ if (!load(t)) {
+ throw ("failed to load test " + t);
+ }
+ jsTest.log("Successful test: " + t);
+ }
+ }
+
+ jsTest.log("code test suite ran successfully");
+ }
+}
diff --git a/jstests/selinux/default.js b/jstests/selinux/default.js
new file mode 100644
index 00000000000..9252e84d9b7
--- /dev/null
+++ b/jstests/selinux/default.js
@@ -0,0 +1,24 @@
+// This test does not run any code. As long as mongod is
+// up and running, it is successful
+
+'use strict';
+
+load('jstests/selinux/lib/selinux_base_test.js');
+
+class TestDefinition extends SelinuxBaseTest {
+ get config() {
+ return cat("rpm/mongod.conf");
+ }
+
+ run() {
+ // The only things we are verifying here:
+ // - that we are connected
+ // - that process is running in correct SELinux context
+
+ assert(db);
+ assert.eq(0,
+ run("bash", "-c", "ps -efZ | grep -P 'system_u:system_r:mongod_t:s0[ ]+mongod'"));
+
+ jsTest.log("success");
+ }
+}
diff --git a/jstests/selinux/lib/selinux_base_test.js b/jstests/selinux/lib/selinux_base_test.js
new file mode 100644
index 00000000000..a5cccd451fc
--- /dev/null
+++ b/jstests/selinux/lib/selinux_base_test.js
@@ -0,0 +1,23 @@
+'use strict';
+
+class SelinuxBaseTest {
+ get config() {
+ return {};
+ }
+
+ // Notice: private definitions, e.g.: #sudo() are not
+ // recognized by js linter, so leaving this declaration public
+ sudo(script) {
+ return run("sudo", "--non-interactive", "bash", "-c", script);
+ }
+
+ setup() {
+ }
+
+ teardown() {
+ }
+
+ run() {
+ assert("override this function");
+ }
+}