mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
update SSH SELinux module to work with cilium/ebpf (#65049)
Also ensure the specified Teleport binary has the proper context restored when using the SELinux module install script.
This commit is contained in:
@@ -15,7 +15,9 @@
|
||||
|
||||
# This script installs the SELinux module for Teleport SSH.
|
||||
|
||||
set -euo pipefail
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
if [[ ! -f "/usr/share/selinux/devel/Makefile" ]]; then
|
||||
echo "SELinux Makefile not found, please install selinux-policy-devel"
|
||||
@@ -23,35 +25,55 @@ if [[ ! -f "/usr/share/selinux/devel/Makefile" ]]; then
|
||||
fi
|
||||
|
||||
TELEPORT="teleport"
|
||||
TELEPORT_ARGS=""
|
||||
TELEPORT_SET=false
|
||||
TELEPORT_ARGS=()
|
||||
|
||||
while getopts "c:t:" opt; do
|
||||
USAGE="Usage: $0 [-c config_path] [-t teleport_path]"
|
||||
|
||||
while getopts "c:t:h" opt; do
|
||||
case "${opt}" in
|
||||
c)
|
||||
TELEPORT_ARGS="-c ${OPTARG}"
|
||||
TELEPORT_ARGS=(-c "${OPTARG}")
|
||||
;;
|
||||
t)
|
||||
TELEPORT="${OPTARG}"
|
||||
TELEPORT_SET=true
|
||||
if ! type -p "${TELEPORT}" >/dev/null; then
|
||||
echo "Teleport binary ${TELEPORT} not found"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
h)
|
||||
echo "${USAGE}"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [-c config_path] [-t teleport_path]"
|
||||
echo "${USAGE}"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! ${TELEPORT_SET} && ! type -p teleport >/dev/null; then
|
||||
echo "Teleport binary not found, please specify with -t"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Write SELinux module source to a temporary directory
|
||||
WORK_DIR="$(mktemp -d -t teleport-selinux.XXXXXXXX)"
|
||||
trap 'rm -rf "${WORK_DIR}"' EXIT INT TERM
|
||||
|
||||
"${TELEPORT}" selinux-ssh module-source > "${WORK_DIR}/teleport_ssh.te"
|
||||
"${TELEPORT}" selinux-ssh file-contexts ${TELEPORT_ARGS} > "${WORK_DIR}/teleport_ssh.fc"
|
||||
DIRS=$(${TELEPORT} selinux-ssh dirs ${TELEPORT_ARGS})
|
||||
"${TELEPORT}" selinux-ssh file-contexts "${TELEPORT_ARGS[@]}" > "${WORK_DIR}/teleport_ssh.fc"
|
||||
DIRS=$("${TELEPORT}" selinux-ssh dirs "${TELEPORT_ARGS[@]}")
|
||||
|
||||
# Build SELinux module
|
||||
pushd "${WORK_DIR}"
|
||||
make -f /usr/share/selinux/devel/Makefile teleport_ssh.pp
|
||||
# Install the module first so that the file contexts are available so
|
||||
# we can label the directories and binary correctly.
|
||||
semodule -i teleport_ssh.pp
|
||||
popd
|
||||
|
||||
# Ensure necessary directories exist and are labeled correctly
|
||||
while IFS= read -r dir; do
|
||||
@@ -60,5 +82,5 @@ while IFS= read -r dir; do
|
||||
restorecon -rv "${dir}"
|
||||
done <<< "$DIRS"
|
||||
|
||||
popd
|
||||
rm -rf "${WORK_DIR}"
|
||||
# Label the Teleport binary
|
||||
restorecon -v "$(type -p "${TELEPORT}")"
|
||||
|
||||
+21
-6
@@ -173,8 +173,7 @@ func testBPFRecording(t *testing.T, srv Server, bpfSrv bpf.BPF) {
|
||||
newFilePath := filepath.Join(cmdDir, "newfile")
|
||||
|
||||
// Lookup paths of programs that are also shell builtins.
|
||||
echoPath, err := exec.LookPath("echo")
|
||||
require.NoError(t, err, "echo command is required for these tests but was not found")
|
||||
echoPath := lookResolvedPath(t, "echo")
|
||||
|
||||
// Create a TCP listener for each IP family. Register the listeners
|
||||
// to be closed in case the test fails before the connection
|
||||
@@ -573,8 +572,7 @@ func runBPFTestCase(t *testing.T, srv Server, bpfSrv bpf.BPF, tt bpfTestCase, cl
|
||||
// free, which can be useful to catch rare bugs in the
|
||||
// BPF disk tracing program.
|
||||
if info.cmdInfo.scriptPath == "" {
|
||||
programPath, err := exec.LookPath(cmdInfo.program)
|
||||
require.NoError(t, err, "%s command is required for these tests but was not found", cmdInfo.program)
|
||||
programPath := lookResolvedPath(t, cmdInfo.program)
|
||||
programPaths[cmdInfo.program] = programPath
|
||||
|
||||
_, ok := programLibs[cmdInfo.program]
|
||||
@@ -586,8 +584,7 @@ func runBPFTestCase(t *testing.T, srv Server, bpfSrv bpf.BPF, tt bpfTestCase, cl
|
||||
// show the script as the program, but it will act
|
||||
// like the interpreter.
|
||||
programPaths[cmdInfo.program] = info.cmdInfo.scriptPath
|
||||
interpPath, err := exec.LookPath(cmdInfo.interpreter)
|
||||
require.NoError(t, err)
|
||||
interpPath := lookResolvedPath(t, cmdInfo.interpreter)
|
||||
|
||||
_, ok := programLibs[cmdInfo.program]
|
||||
if !ok {
|
||||
@@ -1219,6 +1216,24 @@ func checkEvent[T comparable](program string, eventField T, expectedFields map[s
|
||||
return false
|
||||
}
|
||||
|
||||
// lookResolvedPath looks up the given file in PATH and resolves any
|
||||
// symlinks in the resulting path. The resolved path is returned.
|
||||
// This is necessary when testing on Fedora based distros where
|
||||
// /bin is symlinked to /usr/bin. This causes exec.LookPath to return
|
||||
// paths in /bin, but the binaries actually reside in /usr/bin and will
|
||||
// be correctly reported as being opened from /usr/bin in events.
|
||||
func lookResolvedPath(t *testing.T, file string) string {
|
||||
t.Helper()
|
||||
|
||||
path, err := exec.LookPath(file)
|
||||
require.NoError(t, err, "%s command is required for these tests but was not found", file)
|
||||
|
||||
resolved, err := filepath.EvalSymlinks(path)
|
||||
require.NoError(t, err, path)
|
||||
|
||||
return resolved
|
||||
}
|
||||
|
||||
func quoteStrings(s []string) string {
|
||||
var quoted []string
|
||||
for _, v := range s {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
policy_module(teleport_ssh, 1.0.0)
|
||||
policy_module(teleport_ssh, 1.0.1)
|
||||
|
||||
require {
|
||||
attribute can_change_process_identity;
|
||||
attribute can_change_process_role;
|
||||
attribute userdomain;
|
||||
|
||||
type bin_t;
|
||||
type config_home_t;
|
||||
type default_t;
|
||||
type default_context_t;
|
||||
type devlog_t;
|
||||
type devpts_t;
|
||||
@@ -22,7 +22,6 @@ require {
|
||||
type passwd_exec_t;
|
||||
type passwd_file_t;
|
||||
type ptmx_t;
|
||||
type root_t;
|
||||
type security_t;
|
||||
type selinux_config_t;
|
||||
type semanage_store_t;
|
||||
@@ -43,11 +42,11 @@ require {
|
||||
type var_run_t;
|
||||
type wtmp_t;
|
||||
|
||||
class capability { audit_control audit_write chown dac_read_search dac_override fowner fsetid net_admin net_bind_service setgid setuid sys_admin sys_resource };
|
||||
class capability { audit_control audit_write chown dac_read_search dac_override fowner fsetid kill net_admin net_bind_service setgid setuid sys_admin sys_resource };
|
||||
class chr_file { append read write };
|
||||
class dbus { send_msg };
|
||||
class file { create getattr ioctl map open read remove_name setattr unlink write };
|
||||
class filesystem { getattr mount unmount };
|
||||
class filesystem { getattr unmount };
|
||||
class process { setsched transition };
|
||||
class service { status };
|
||||
class sock_file { append create getattr open read setattr unlink write };
|
||||
@@ -184,6 +183,19 @@ allow teleport_ssh_t http_port_t:tcp_socket name_connect;
|
||||
##
|
||||
type_transition teleport_ssh_t teleport_ssh_exec_t:process teleport_ssh_t;
|
||||
allow teleport_ssh_t self:process { transition setexec setkeycreate setrlimit setsched };
|
||||
allow teleport_ssh_t self:capability kill;
|
||||
|
||||
# Allow Teleport to send SIGKILL to child processes in the user domain,
|
||||
# as well as unconfined processes. This is needed to ensure that when a
|
||||
# user exits an SSH session, all child processes are killed properly
|
||||
# regardless of their SELinux context.
|
||||
#
|
||||
# Note that SELinux doesn't allow this to be scoped to child processes
|
||||
# only, so this allows Teleport to SIGKILL any process in a user or
|
||||
# unconfined domain. This does not allow Teleport to SIGKILL system
|
||||
# processes in other domains though.
|
||||
allow teleport_ssh_t userdomain:process sigkill;
|
||||
allow teleport_ssh_t unconfined_t:process sigkill;
|
||||
|
||||
##
|
||||
# handling SSH connections
|
||||
@@ -221,17 +233,19 @@ allow teleport_ssh_t wtmp_t:file rw_file_perms;
|
||||
allow teleport_ssh_t lastlog_t:file read_file_perms;
|
||||
|
||||
# enhanced session recording
|
||||
allow teleport_ssh_t cgroup_t:filesystem { mount unmount };
|
||||
# TODO(capnspacehook) REMOVE IN v20: cleaning up cgroup dirs won't be needed by that point
|
||||
allow teleport_ssh_t cgroup_t:filesystem unmount;
|
||||
allow teleport_ssh_t cgroup_t:dir manage_dir_perms;
|
||||
allow teleport_ssh_t default_t:dir mounton;
|
||||
|
||||
allow teleport_ssh_t self:bpf { map_create map_read map_write prog_load prog_run };
|
||||
allow teleport_ssh_t self:perf_event { cpu kernel open write };
|
||||
allow teleport_ssh_t sysfs_t:file map;
|
||||
allow teleport_ssh_t tracefs_t:filesystem getattr;
|
||||
allow teleport_ssh_t tracefs_t:file read_file_perms;
|
||||
allow teleport_ssh_t root_t:dir { add_name create mounton write };
|
||||
allow teleport_ssh_t self:perf_event { cpu kernel open tracepoint write };
|
||||
allow teleport_ssh_t self:capability sys_admin;
|
||||
# not available on RHEL 8
|
||||
optional_policy(`
|
||||
require {
|
||||
require {
|
||||
class capability2 { bpf perfmon };
|
||||
};
|
||||
allow teleport_ssh_t self:capability2 { bpf perfmon };
|
||||
|
||||
Reference in New Issue
Block a user