Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ The following Rancher components are covered by the policy:
| Rancher Monitoring | [node-exporter] | `prom_node_exporter_t` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | Production |
| Rancher Monitoring | [pushprox] | `rke_kubereader_t` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | Production |
| Rancher Logging | [fluentbit] | `rke_logreader_t` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | Production |
| Rancher AI | [rancher-ai-agent] | `rancher_aiagent_container_t` | :white_check_mark: | :construction: | :construction: | :construction: | Testing |
| Rancher AI | [rancher-ai-mcp] | `rancher_aimcp_container_t` | :white_check_mark: | :construction: | :construction: | :construction: | Testing |
| Rancher AI | [rancher-ai-agent] | `rancher_aiagent_container_t` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | Production |
| Rancher AI | [rancher-ai-mcp] | `rancher_aimcp_container_t` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | Production |
| RKE1 | [flannel] | `rke_network_t` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | Production |
| RKE1 | [rke] `etcd`, `kube-apiserver`, etc. | `rke_container_t` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | Production |

Expand Down
158 changes: 109 additions & 49 deletions hack/e2e/setup-vm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@ function isSUSE(){

function verifyPolicyPresence() {
local pkgs=("rancher-selinux" "rke2-selinux")
local types=("prom_node_exporter_t" "rke2_service_t")

for i in "${!pkgs[@]}"; do
local p="${pkgs[$i]}"
local t="${types[$i]}"
local m="${p%-selinux}"
local types=(
"prom_node_exporter_t"
"rke2_service_t"
"rancher_aiagent_container_t"
"rancher_aimcp_container_t"
)

for p in "${pkgs[@]}"; do
rpm -q "$p" >/dev/null 2>&1 || { echo "ERROR: RPM $p not installed"; return 1; }
local m="${p%-selinux}"
semodule -l | grep -w "$m" || { echo "ERROR: Module $m not loaded"; return 1; }
done

for t in "${types[@]}"; do
seinfo -t "$t" >/dev/null 2>&1 || { echo "ERROR: Type $t unknown"; return 1; }
done

Expand Down Expand Up @@ -58,8 +63,8 @@ function enforceSELinux(){
}

function installDependencies(){
echo 'echo "export PATH=$PATH:/usr/local/bin"' >> ~/.bashrc
echo 'echo "export TERM=xterm"' >> ~/.bashrc
echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bashrc
echo 'export TERM=xterm' >> ~/.bashrc

if isSUSE; then
sudo zypper -n install jq git setools-console
Expand Down Expand Up @@ -138,36 +143,68 @@ function installRancher(){
kubectl wait --for=condition=ready -n cattle-system pod -l app=rancher-webhook --timeout=240s
}

# Example: installRancherChart "rancher-monitoring" "cattle-monitoring-system" "rancher-monitoring-prometheus-node-exporter" "app.kubernetes.io/name=prometheus-node-exporter" "--set ...".
# installRancherChart installs a Rancher chart and waits for its workloads to be ready.
#
# Positional arguments:
# $1 CHART_NAME Helm release name (also used as the CRD sibling chart name for HTTP repos).
# $2 NAMESPACE Target namespace.
# $3 WORKLOAD_KIND "daemonset" or "deployment".
# $4 WORKLOAD_NAMES Comma-separated list of workload names owned by this chart.
# $5 POD_LABEL_SELECTOR Label selector used to wait for pod readiness (e.g. "app=foo").
# For charts that own multiple workloads with different labels, pass a
# comma-separated list (e.g. "app=foo,app=bar"); each is waited on
# independently.
# $6 CHART_REF Helm chart reference, e.g. "rancher-charts/foo" (HTTP repo) or
# "oci://host/path/chart" (OCI registry).
# $7+ EXTRA_HELM_ARGS Additional arguments forwarded to `helm upgrade --install`.
# MUST include the chart's SELinux flag — the values key differs per chart
# (`--set global.seLinux.enabled=true` for rancher-monitoring/logging,
# `--set seLinux.enabled=true` for rancher-ai-agent).
# For OCI charts pulled without a pinned `--version`, include `--devel`
# so Helm resolves to the latest available chart including pre-releases.
#
# CRD sibling charts are installed only for the HTTP `rancher-charts/` source.
function installRancherChart() {
local CHART_NAME="$1"
local NAMESPACE="$2"
local DAEMONSET_NAME="$3"
local POD_LABEL_SELECTOR="$4"
local EXTRA_HELM_ARGS="${@:5}" # Collect any additional arguments.

# Add Rancher charts repository.
helm repo add rancher-charts https://charts.rancher.io/

echo "> Installing CRD chart ${CHART_NAME}-crd in namespace ${NAMESPACE}"
helm upgrade --install=true \
--labels=catalog.cattle.io/cluster-repo-name=rancher-charts \
--namespace="${NAMESPACE}" --timeout=20m0s --wait=true \
--create-namespace \
"${CHART_NAME}-crd" "rancher-charts/${CHART_NAME}-crd"
local WORKLOAD_KIND="$3"
local WORKLOAD_NAMES="$4"
local POD_LABEL_SELECTOR="$5"
local CHART_REF="$6"
local EXTRA_HELM_ARGS="${@:7}"

# Add the HTTP rancher-charts repo (and install a CRD sibling) only when this chart is sourced
# from it. OCI charts (e.g. rancher-ai-agent) have no -crd sibling chart.
if [[ "${CHART_REF}" == rancher-charts/* ]]; then
helm repo add rancher-charts https://charts.rancher.io/ >/dev/null 2>&1 || true

echo "> Installing CRD chart ${CHART_NAME}-crd in namespace ${NAMESPACE}"
helm upgrade --install=true \
--labels=catalog.cattle.io/cluster-repo-name=rancher-charts \
--namespace="${NAMESPACE}" --timeout=20m0s --wait=true \
--create-namespace \
"${CHART_NAME}-crd" "rancher-charts/${CHART_NAME}-crd"
fi

echo "> Installing main chart ${CHART_NAME} with SELinux enabled"
echo "> Installing main chart ${CHART_NAME} (${CHART_REF})"
helm upgrade --install=true \
--labels=catalog.cattle.io/cluster-repo-name=rancher-charts \
--namespace="${NAMESPACE}" --timeout=20m0s --wait=true \
--create-namespace \
"${CHART_NAME}" "rancher-charts/${CHART_NAME}" \
--set global.seLinux.enabled=true \
"${CHART_NAME}" "${CHART_REF}" \
${EXTRA_HELM_ARGS}

# Wait for DaemonSet creation and Pod readiness.
kubectl wait --for=create -n "${NAMESPACE}" daemonset/"${DAEMONSET_NAME}" --timeout=240s
kubectl wait --for=condition=ready -n "${NAMESPACE}" pod -l "${POD_LABEL_SELECTOR}" --timeout=240s
# Wait for every workload (DaemonSet or Deployment) the chart owns to be created.
local IFS_BAK="$IFS"; IFS=','
for w in ${WORKLOAD_NAMES}; do
kubectl wait --for=create -n "${NAMESPACE}" "${WORKLOAD_KIND}/${w}" --timeout=240s
done
# Wait for pod readiness for each provided label selector (supports multi-workload charts
# whose workloads don't share a single selector).
for sel in ${POD_LABEL_SELECTOR}; do
kubectl wait --for=condition=ready -n "${NAMESPACE}" pod -l "${sel}" --timeout=300s
done
IFS="$IFS_BAK"
}

function uninstallRancherChart() {
Expand All @@ -176,9 +213,13 @@ function uninstallRancherChart() {

echo "> Uninstalling main chart ${CHART_NAME} from namespace ${NAMESPACE}"
helm uninstall "${CHART_NAME}" -n "${NAMESPACE}" --wait

echo "> Uninstalling CRD chart ${CHART_NAME}-crd"
helm uninstall "${CHART_NAME}-crd" -n "${NAMESPACE}" --wait

# Best-effort CRD chart removal (only present when a -crd sibling was installed).
if helm status "${CHART_NAME}-crd" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "> Uninstalling CRD chart ${CHART_NAME}-crd"
helm uninstall "${CHART_NAME}-crd" -n "${NAMESPACE}" --wait
fi

echo "> Deleting namespace ${NAMESPACE}"
kubectl delete ns "${NAMESPACE}" --timeout=120s

Expand All @@ -189,10 +230,10 @@ function uninstallRancherChart() {

# Example: e2eSELinuxVerification "fluentbit" "fluent-bit" "cattle-logging-system" "rke_logreader_t".
function e2eSELinuxVerification(){
local CONTAINER_NAME="$1"
local POD_NAME_PREFIX="$1"
local CONTAINER_RUNNING_NAME="$2"
local POD_NAMESPACE="$3"
local POD_NAME=$(kubectl get pods -n ${POD_NAMESPACE} -o custom-columns=NAME:.metadata.name | grep "${CONTAINER_NAME}")
local POD_NAME=$(kubectl get pods -n ${POD_NAMESPACE} -o custom-columns=NAME:.metadata.name | grep "${POD_NAME_PREFIX}" | head -n1)
local CONTAINER_EXPECTED_SLTYPE="$4"
local CONTAINER_RUNNING_SLTYPE=""

Expand All @@ -203,8 +244,9 @@ function e2eSELinuxVerification(){
echo "SELinux type is not present: ${CONTAINER_EXPECTED_SLTYPE}"
fi

echo "> Verify expected SELinux context type ${CONTAINER_EXPECTED_SLTYPE} for container ${CONTAINER_NAME}"
CONTAINER_RUNNING_SLTYPE=$(kubectl get pod ${POD_NAME} -n ${POD_NAMESPACE} -o json | jq -r ".spec.containers[] | select(.name==\"${CONTAINER_RUNNING_NAME}\") | .securityContext.seLinuxOptions.type")
echo "> Verify expected SELinux context type ${CONTAINER_EXPECTED_SLTYPE} for container ${CONTAINER_RUNNING_NAME} in pod ${POD_NAME}"
# Get SELinux type from Pod-level securityContext, falling back to Container-level if empty
CONTAINER_RUNNING_SLTYPE=$(kubectl get pod ${POD_NAME} -n ${POD_NAMESPACE} -o json | jq -r ".spec.securityContext.seLinuxOptions.type // (.spec.containers[] | select(.name==\"${CONTAINER_RUNNING_NAME}\") | .securityContext.seLinuxOptions.type)")
if [[ "${CONTAINER_RUNNING_SLTYPE}" == "${CONTAINER_EXPECTED_SLTYPE}" ]]; then
echo "SELinux type is correct: ${CONTAINER_RUNNING_SLTYPE}"
else
Expand All @@ -230,32 +272,50 @@ function main(){
installRancher

# Note: Append this list with new components to install and test the rancher-selinux policy.
# Value: A space-separated list of arguments: Namespace DaemonSet PodLabel ContainerName ContainerRunningName SELinuxType ExtraHelmArgs.
#
# Per-component layout (space-separated):
# NAMESPACE WORKLOAD_KIND WORKLOAD_NAMES POD_LABELS VERIFY_TRIPLETS CHART_REF EXTRA_HELM_ARGS
#
# - WORKLOAD_KIND: daemonset|deployment
# - WORKLOAD_NAMES: comma-separated list of workload names owned by the chart.
# - POD_LABELS: comma-separated list of label selectors used for readiness waits
# (one entry per workload group that doesn't share a common selector).
# - VERIFY_TRIPLETS: ';'-separated list of 'podNamePrefix:containerName:expected_selinux_type'
# - CHART_REF: "rancher-charts/<name>" (HTTP repo) or "oci://host/path/chart" (OCI registry)
# - EXTRA_HELM_ARGS: forwarded verbatim to `helm upgrade --install`. MUST include the chart's
# SELinux flag (e.g. `--set global.seLinux.enabled=true` or
# `--set seLinux.enabled=true`). For OCI charts without a pinned version,
# include `--devel` so Helm resolves to the latest pre-release.
declare -A COMPONENTS=(
[rancher-monitoring]="cattle-monitoring-system rancher-monitoring-prometheus-node-exporter app.kubernetes.io/name=prometheus-node-exporter node-exporter node-exporter prom_node_exporter_t --set prometheus-node-exporter.seLinuxOptions.type=prom_node_exporter_t"
[rancher-logging]="cattle-logging-system rancher-logging-root-fluentbit app.kubernetes.io/name=fluentbit fluentbit fluent-bit rke_logreader_t"
[rancher-monitoring]="cattle-monitoring-system daemonset rancher-monitoring-prometheus-node-exporter app.kubernetes.io/name=prometheus-node-exporter node-exporter:node-exporter:prom_node_exporter_t rancher-charts/rancher-monitoring --set global.seLinux.enabled=true --set prometheus-node-exporter.hostRootFsMount.enabled=false"
[rancher-logging]="cattle-logging-system daemonset rancher-logging-root-fluentbit app.kubernetes.io/name=fluentbit fluentbit:fluent-bit:rke_logreader_t rancher-charts/rancher-logging --set global.seLinux.enabled=true"
[rancher-ai-agent]="cattle-ai-agent-system deployment rancher-ai-agent,rancher-mcp-server app=rancher-ai-agent,app=rancher-mcp-server rancher-ai-agent:agent:rancher_aiagent_container_t;rancher-mcp-server:mcp-server:rancher_aimcp_container_t oci://stgregistry.suse.com/rancher/charts/rancher-ai-agent --set seLinux.enabled=true --set insecureSkipTls=true"
Comment thread
andypitcher marked this conversation as resolved.
Outdated
)

for CHART_NAME in "${!COMPONENTS[@]}"; do
# Read the space-separated values into individual variables.
read -r NAMESPACE DAEMONSET_NAME POD_LABEL CONTAINER_NAME CONTAINER_RUNNING_NAME SELINUX_TYPE EXTRA_HELM_ARGS <<< "${COMPONENTS[${CHART_NAME}]}"
read -r NAMESPACE WORKLOAD_KIND WORKLOAD_NAMES POD_LABELS VERIFY_TRIPLETS CHART_REF EXTRA_HELM_ARGS <<< "${COMPONENTS[${CHART_NAME}]}"

echo "> Installing and testing Chart: ${CHART_NAME} in Namespace: ${NAMESPACE} with SELinux type ${SELINUX_TYPE}"
echo "> Installing and testing Chart: ${CHART_NAME} in Namespace: ${NAMESPACE}"

# 1. Install the chart (passing the collected variables).
# 1. Install the chart.
installRancherChart \
"${CHART_NAME}" \
"${NAMESPACE}" \
"${DAEMONSET_NAME}" \
"${POD_LABEL}" \
"${WORKLOAD_KIND}" \
"${WORKLOAD_NAMES}" \
"${POD_LABELS}" \
"${CHART_REF}" \
"${EXTRA_HELM_ARGS}"

# 2. Run E2E SELinux Verification.
e2eSELinuxVerification \
"${CONTAINER_NAME}" \
"${CONTAINER_RUNNING_NAME}" \
"${NAMESPACE}" \
"${SELINUX_TYPE}"
# 2. Run E2E SELinux verification for every (podPrefix,container,type) triplet.
IFS_BAK="$IFS"; IFS=';'
for triplet in ${VERIFY_TRIPLETS}; do
IFS=':' read -r POD_PREFIX C_NAME C_TYPE <<< "${triplet}"
echo "> Verifying pod ${POD_PREFIX} / container ${C_NAME} against SELinux type ${C_TYPE}"
e2eSELinuxVerification "${POD_PREFIX}" "${C_NAME}" "${NAMESPACE}" "${C_TYPE}"
done
IFS="$IFS_BAK"

# 3. Uninstall the chart (free some resources).
uninstallRancherChart \
Expand Down
2 changes: 1 addition & 1 deletion policy/centos10/rancher.te
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ corenet_tcp_connect_http_port(rancher_aiagent_container_t)
allow rancher_aiagent_container_t self:tcp_socket listen;

############################################################################
# type: rancher_aimcp_container_t #
# type: rancher_aimcp_container_t #
# project: rancher/rancher-ai-mcp #
# target: rancher-mcp-server container for Rancher AI #
############################################################################
Expand Down
2 changes: 1 addition & 1 deletion policy/centos9/rancher.te
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ corenet_tcp_connect_http_port(rancher_aiagent_container_t)
allow rancher_aiagent_container_t self:tcp_socket listen;

############################################################################
# type: rancher_aimcp_container_t #
# type: rancher_aimcp_container_t #
# project: rancher/rancher-ai-mcp #
# target: rancher-mcp-server container for Rancher AI #
############################################################################
Expand Down
24 changes: 24 additions & 0 deletions policy/fedora42/rancher.te
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,27 @@ corenet_tcp_bind_generic_port(prom_node_exporter_t)
init_read_state(prom_node_exporter_t)
selinux_read_security_files(prom_node_exporter_t)
allow prom_node_exporter_t self:tcp_socket listen;

############################################################################
# type: rancher_aiagent_container_t #
# project: rancher/rancher-ai-agent #
# target: rancher-ai-agent container for Rancher AI #
############################################################################

container_domain_template(rancher_aiagent_container, container)
corenet_tcp_bind_generic_node(rancher_aiagent_container_t)
corenet_tcp_bind_soundd_port(rancher_aiagent_container_t)
corenet_tcp_connect_http_port(rancher_aiagent_container_t)
allow rancher_aiagent_container_t self:tcp_socket listen;

############################################################################
# type: rancher_aimcp_container_t #
# project: rancher/rancher-ai-mcp #
# target: rancher-mcp-server container for Rancher AI #
############################################################################

container_domain_template(rancher_aimcp_container, container)
corenet_tcp_bind_generic_node(rancher_aimcp_container_t)
corenet_tcp_bind_generic_port(rancher_aimcp_container_t)
corenet_tcp_connect_http_port(rancher_aimcp_container_t)
allow rancher_aimcp_container_t self:tcp_socket listen;
24 changes: 24 additions & 0 deletions policy/microos/rancher.te
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,27 @@ corenet_tcp_bind_generic_port(prom_node_exporter_t)
init_read_state(prom_node_exporter_t)
selinux_read_security_files(prom_node_exporter_t)
allow prom_node_exporter_t self:tcp_socket listen;

############################################################################
# type: rancher_aiagent_container_t #
# project: rancher/rancher-ai-agent #
# target: rancher-ai-agent container for Rancher AI #
############################################################################

container_domain_template(rancher_aiagent_container, container)
corenet_tcp_bind_generic_node(rancher_aiagent_container_t)
corenet_tcp_bind_soundd_port(rancher_aiagent_container_t)
corenet_tcp_connect_http_port(rancher_aiagent_container_t)
allow rancher_aiagent_container_t self:tcp_socket listen;

############################################################################
# type: rancher_aimcp_container_t #
# project: rancher/rancher-ai-mcp #
# target: rancher-mcp-server container for Rancher AI #
############################################################################

container_domain_template(rancher_aimcp_container, container)
corenet_tcp_bind_generic_node(rancher_aimcp_container_t)
corenet_tcp_bind_generic_port(rancher_aimcp_container_t)
corenet_tcp_connect_http_port(rancher_aimcp_container_t)
allow rancher_aimcp_container_t self:tcp_socket listen;
Loading