Skip to content

Commit 4f7d553

Browse files
committed
Add selinux-policy migration test PED-12492
1 parent 96d9307 commit 4f7d553

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: selinux-migration
2+
description: >
3+
Verify that moving the SELinux modules path from /var/lib/selinux
4+
to /etc does not affect SELinux functionality.
5+
more details https://jira.suse.com/browse/PED-12492
6+
schedule:
7+
- boot/boot_to_desktop
8+
- security/selinux/selinux_migration
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# SUSE's openQA tests
2+
#
3+
# Copyright 2025 SUSE LLC
4+
#
5+
# SUSE's openQA tests
6+
# Package: selinux-policy
7+
# Summary: test selinux-policy migration
8+
# Maintainer: Gayane Osipyan <[email protected]>
9+
10+
use base 'selinuxtest';
11+
use testapi;
12+
use serial_terminal 'select_serial_terminal';
13+
use utils;
14+
use version_utils qw(is_sle is_microos is_leap is_tumbleweed is_sle_micro has_selinux);
15+
use transactional qw(process_reboot trup_call);
16+
use Utils::Architectures;
17+
18+
sub run {
19+
select_serial_terminal;
20+
initial_state();
21+
check_dir();
22+
my $rollback_number = create_snapshot();
23+
update_system();
24+
check_paths();
25+
check_after_update();
26+
check_custom_policy();
27+
rollback_and_verify_state($rollback_number);
28+
cleanup_test_artifacts();
29+
}
30+
31+
# set up the initial test environment with packages, users, and custom policies
32+
sub initial_state {
33+
record_info('Verifying SELinux status and installing packages.');
34+
assert_script_run('sestatus | grep "SELinux status:" | awk "{print $3}" | grep -q "enabled"');
35+
# install selinux packages
36+
if (is_microos) {
37+
trup_call('pkg install selinux-policy-targeted selinux-policy-targeted-gaming policycoreutils policycoreutils-python-utils setools-console podman');
38+
process_reboot(trigger => 1);
39+
}
40+
else {
41+
zypper_call('install selinux-policy-targeted selinux-policy-targeted-gaming policycoreutils policycoreutils-python-utils setools-console podman');
42+
}
43+
record_info('Creating test user and custom policy.');
44+
assert_script_run('useradd testselinux');
45+
assert_script_run('echo testselinux:testpasswd | chpasswd');
46+
assert_script_run('semanage login -a -s staff_u testselinux');
47+
assert_script_run('semanage port -a -t http_port_t -p tcp 8888');
48+
assert_script_run('semanage port -l | grep -E "(^SELinux Port Type|http_port_t).*8888"');
49+
assert_script_run('podman run -d --name test1 busybox sleep infinity');
50+
assert_script_run('podman ps --filter name=test1');
51+
create_custom_module('mycustom', 'httpd_t', 'tcp_socket name_connect', 'allow httpd_t self:tcp_socket name_connect;');
52+
enable_boolean('httpd_can_network_connect_db');
53+
capture_current_state();
54+
}
55+
56+
# create a new SELinux module from a template
57+
sub create_custom_module {
58+
my ($name, $type, $class_perm, $policy_rule) = @_;
59+
record_info("Creating and installing custom module: $name");
60+
my $te_content = "module $name 1.0;\n\nrequire {\n type $type;\n class $class_perm;\n};\n\n$policy_rule";
61+
assert_script_run("echo '$te_content' > ${name}.te");
62+
assert_script_run("checkmodule -M -m -o ${name}.mod ${name}.te");
63+
assert_script_run("semodule_package -o ${name}.pp -m ${name}.mod");
64+
assert_script_run("semodule -i ${name}.pp");
65+
assert_script_run("semodule -l | grep -q '$name'");
66+
}
67+
68+
# enable a specific SELinux boolean
69+
sub enable_boolean {
70+
my ($boolean_name) = @_;
71+
record_info("Enabling boolean: $boolean_name");
72+
assert_script_run("setsebool -P $boolean_name on");
73+
assert_script_run("getsebool $boolean_name | grep -q 'on'");
74+
}
75+
76+
# save the current system state for later verification
77+
sub capture_current_state {
78+
record_info('Capturing system state before update.');
79+
script_run('semodule -l > semodule_list_before_migration.txt');
80+
script_run('semanage boolean -l > semanage_booleans_before_migration.txt');
81+
script_run('semanage login -l > semanage_login_before_migration.txt');
82+
script_run('semanage port -l > semanage_ports_before_migration.txt');
83+
script_run('semanage fcontext -l > semanage_fcontexts_before_migration.txt');
84+
}
85+
86+
# create a snapper snapshot
87+
sub create_snapshot {
88+
record_info('Creating snapshot for rollback.');
89+
my $rollback_number = script_output('snapper create -d "Before SELinux update" -p');
90+
script_output('snapper list');
91+
return $rollback_number;
92+
}
93+
94+
# SELinux policy package update
95+
sub update_system {
96+
record_info('Updating environment');
97+
zypper_call("--gpg-auto-import-keys ref");
98+
if (is_microos) {
99+
validate_script_output('sestatus', sub { m/SELinux status: .*enabled/ && m/Current mode: .*enforcing/ }, fail_message => 'SELinux is NOT enabled and set to enforcing');
100+
trup_call('dup', timeout => 600);
101+
process_reboot(trigger => 1);
102+
}
103+
else {
104+
zypper_call('dup --force-resolution --allow-vendor-change --no-confirm');
105+
}
106+
zypper_call('info selinux-policy selinux-policy-targeted selinux-policy-targeted-gaming libsemanage-conf libsemanage2 policycoreutils policycoreutils-python-utils setools-console container-selinux');
107+
record_info('Adding a second custom module after update.');
108+
create_custom_module('mycustom2', 'sshd_t', 'process setrlimit', 'allow sshd_t self:process setrlimit;');
109+
check_cleanoldspoldir_service();
110+
}
111+
112+
# check the system state after the update
113+
sub check_after_update {
114+
record_info('Verifying system state after update.');
115+
script_run('diff -q semodule_list_before_migration.txt <(semodule -l)');
116+
script_run('diff -q semanage_booleans_before_migration.txt <(semanage boolean -l)');
117+
script_run('diff -q semanage_login_before_migration.txt <(semanage login -l)');
118+
my $module_list = script_output('semodule -l');
119+
for my $m (qw(mycustom mycustom2)) {
120+
if ($module_list =~ /^\Q$m\E\b/m) {
121+
print "$m present";
122+
} else {
123+
record_info("Module '$m' not found in semodule -l output\n", result => "fail");
124+
}
125+
}
126+
check_gaming_boolean();
127+
}
128+
129+
# roll back the system to the pre-update state and verify
130+
sub rollback_and_verify_state {
131+
my ($rollback_number) = @_;
132+
record_info("Rolling back to snapshot $rollback_number.");
133+
assert_script_run("snapper rollback $rollback_number");
134+
record_info('Verifying system state after rollback.');
135+
my $module_list = script_output('semodule -l');
136+
# check that 'mycustom' present
137+
for my $m (qw(mycustom)) {
138+
if ($module_list =~ /^\Q$m\E\b/m) {
139+
print "$m present";
140+
} else {
141+
record_info("Module '$m' not found in semodule -l output\n", result => "fail");
142+
}
143+
}
144+
assert_script_run('semanage boolean -l | grep -q "httpd_can_network_connect_db.* on"');
145+
assert_script_run('semanage login -l | grep -q "testselinux.*staff_u"');
146+
assert_script_run('semanage port -l | grep -E "(^SELinux Port Type|http_port_t).*8888"');
147+
assert_script_run('podman ps --filter name=test1');
148+
if (script_run('ps -eZ | grep $(podman inspect -f "{{.State.Pid}}" test1) | grep -q "container_t"') == 0) {
149+
record_info("test1 has correct selinux label");
150+
}
151+
else {
152+
record_info("wrong selinux label", result => "fail");
153+
}
154+
}
155+
156+
# test selinux-policy-targeted-gaming boolean
157+
sub check_gaming_boolean {
158+
record_info('Verify gaming boolean');
159+
zypper_call('in selinux-policy-targeted-gaming');
160+
for my $boolean (qw(selinuxuser_execstack selinuxuser_execmod)) {
161+
my $out = script_output("getsebool $boolean");
162+
if ($out =~ /on$/) {
163+
record_info($boolean, "Is on");
164+
} else {
165+
die "$boolean, Is off or missing";
166+
}
167+
}
168+
}
169+
170+
# no packages install in /var/lib/selinux
171+
sub check_paths {
172+
my @packages = @_;
173+
@packages = qw(selinux-policy selinux-policy-targeted selinux-policy-targeted-gaming libsemanage-conf libsemanage2 policycoreutils policycoreutils-python-utils setools-console) unless @packages;
174+
foreach my $package (@packages) {
175+
if (script_run("rpm -qvl $package | grep -q '/var/lib/selinux'") == 0) {
176+
record_info("[FAIL]", "$package contain /var/lib/selinux paths", result => "fail");
177+
}
178+
else {
179+
script_run('echo "[PASS] no /var/lib/selinux paths found"');
180+
record_info("[PASS] no /var/lib/selinux paths found");
181+
}
182+
}
183+
}
184+
185+
## check /var/lib/selinux deleted
186+
sub check_dir {
187+
if (script_run("grep -rq '/var/lib/selinux/' /.snapshots/*") == 0) {
188+
record_info("[FAIL]", "/var/lib/selinux exist in old snapshots", result => "fail");
189+
}
190+
elsif (-d "/var/lib/selinux") {
191+
record_info("[FAIL]", "/var/lib/selinux not deleted", result => "fail");
192+
}
193+
else {
194+
record_info("[PASS]", "/var/lib/selinux not present");
195+
}
196+
}
197+
198+
# check cleanoldsepoldir.service
199+
sub check_cleanoldspoldir_service {
200+
if (script_run("systemctl list-unit-files --type=service | grep -qw cleanoldsepoldir.service") == 0 && script_output("systemctl is-enabled cleanoldsepoldir.service"=="enabled")){
201+
record_info("[PASS]", "cleanoldsepoldir.service enabled", result => "fail");
202+
} else {
203+
record_info("[FAIL]", "cleanoldsepoldir.service not detected", result => "fail");
204+
}
205+
}
206+
207+
# add custom modules
208+
sub check_custom_policy {
209+
# get list of SELinux specific modules
210+
my $modules = script_output('zypper -n se -s | awk "{print \$2}" | grep -E -- "-selinux$" | sort -u', timeout => 300);
211+
my @packages;
212+
foreach my $module (split /\n/, $modules) {
213+
next unless $module;
214+
next if ($module eq 'forgejo-selinux' || $module eq 'rke2-selinux');
215+
push @packages, $module;
216+
}
217+
return unless @packages;
218+
print(@packages);
219+
my $package_list = join(' ', @packages);
220+
record_info("Installing packages", $package_list);
221+
if (is_microos) {
222+
trup_call("pkg install $package_list");
223+
process_reboot(trigger => 1);
224+
}
225+
else {
226+
script_run("zypper -n in -y $package_list", timeout => 600);
227+
}
228+
check_paths(@packages);
229+
}
230+
231+
# clean up all temporary files, users, and policy modules.
232+
sub cleanup_test_artifacts {
233+
record_info('Cleaning up test environment.');
234+
script_run('semodule -r mycustom || true');
235+
script_run('semodule -r mycustom2 || true');
236+
script_run('userdel -r testselinux || true');
237+
script_run('semanage login -d testselinux || true');
238+
script_run('rm -f mycustom*.{te,mod,pp}');
239+
script_run('rm -f semodule_list_before_migration.txt semanage_booleans_before_migration.txt semanage_login_before_migration.txt');
240+
script_run('rm -f semanage_ports_before_migration.txt semanage_fcontexts_before_migration.txt');
241+
script_run('snapper list | grep "Before SELinux update" | awk "{print \$1}" | xargs -r snapper delete');
242+
}
243+
1;

0 commit comments

Comments
 (0)