Skip to content

Commit 6ec0218

Browse files
author
Krishan Gopal Saraswat
committed
Added code to enable Dynamic Key Guest Secure Boot
Dynamic Key Guest Secure Boot needs kernel config check, setting platform keys, checking signatures of GRUB and Kernel, added code to enable and disable DKGSB. Signed-off-by: Krishan Gopal Saraswat <krishang@linux.ibm.com>
1 parent 3d36abb commit 6ec0218

1 file changed

Lines changed: 350 additions & 0 deletions

File tree

testcases/OpTestDKGSB.py

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
#!/usr/bin/env python3
2+
# OpenPOWER Automated Test Project
3+
#
4+
# Contributors Listed Below - COPYRIGHT 2025
5+
# [+] International Business Machines Corp.
6+
# Author: Krishan Gopal Saraswat <krishang@linux.ibm.com>
7+
#
8+
# Licensed under the Apache License, Version 2.0 (the "License");
9+
# you may not use this file except in compliance with the License.
10+
# You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing, software
15+
# distributed under the License is distributed on an "AS IS" BASIS,
16+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
17+
# implied. See the License for the specific language governing
18+
# permissions and limitations under the License.
19+
20+
'''
21+
This test validates the Dynamic Key Guest Secure Boot process by
22+
checking kernel configurations, verifying kernel and GRUB signatures,
23+
setting up the required environment, and toggling the secure boot
24+
state on and off.
25+
'''
26+
27+
import unittest
28+
import os
29+
import time
30+
import OpTestConfiguration
31+
import OpTestLogger
32+
from common.OpTestUtil import OpTestUtil
33+
import shlex
34+
35+
log = OpTestLogger.optest_logger_glob.get_logger(__name__)
36+
37+
class DynamicKeyGuestSecureBoot(unittest.TestCase):
38+
def setUp(self):
39+
self.conf = OpTestConfiguration.conf
40+
self.util = OpTestUtil(OpTestConfiguration.conf)
41+
self.cv_HOST = self.conf.host()
42+
self.cv_SYSTEM = self.conf.system()
43+
# HMC object for performing HMC-level operations
44+
try:
45+
self.cv_HMC = self.cv_SYSTEM.hmc
46+
except Exception:
47+
self.cv_HMC = None
48+
self.connection = self.cv_SYSTEM.cv_HOST.get_ssh_connection()
49+
self.host_cmd_timeout = self.conf.args.host_cmd_timeout
50+
self.kernel_version = self.connection.run_command("cd /boot && uname -r")[0]
51+
52+
def check_kernel_config(self):
53+
try:
54+
# List of required kernel configurations
55+
required_configs = [
56+
"CONFIG_PPC_MEM_KEYS",
57+
"CONFIG_TRUSTED_KEYS",
58+
"CONFIG_PPC_SECURE_BOOT",
59+
"CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT",
60+
"CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS",
61+
"CONFIG_LOAD_PPC_KEYS",
62+
"CONFIG_INTEGRITY_TRUSTED_KEYRING",
63+
"CONFIG_INTEGRITY_PLATFORM_KEYRING",
64+
"CONFIG_INTEGRITY_MACHINE_KEYRING",
65+
"CONFIG_SECONDARY_TRUSTED_KEYRING",
66+
"CONFIG_SYSTEM_BLACKLIST_KEYRING",
67+
"CONFIG_MODULE_SIG_KEY_TYPE_RSA"
68+
]
69+
70+
# First check if kernel config file exists
71+
config_file = f"/boot/config-{self.kernel_version}"
72+
try:
73+
self.cv_HOST.host_run_command(f"test -e {config_file}")
74+
log.info(f"Found kernel config file: {config_file}")
75+
except Exception as e:
76+
self.fail(f"Kernel config file not found at {config_file}: {str(e)}")
77+
78+
# Check each required configuration
79+
missing_configs = []
80+
for config in required_configs:
81+
try:
82+
result = self.cv_HOST.host_check_config(self.kernel_version, config)
83+
if result not in ['y', 'm']:
84+
missing_configs.append(f"{config} (not set)")
85+
else:
86+
log.info(f"Found {config}={result}")
87+
except Exception as e:
88+
if "not set" in str(e):
89+
missing_configs.append(f"{config} (not set)")
90+
else:
91+
log.error(f"Error checking config {config}: {str(e)}")
92+
missing_configs.append(f"{config} (error: {str(e)})")
93+
94+
# If any configurations are missing, fail the test with detailed message
95+
if missing_configs:
96+
error_msg = "Missing or incorrectly configured kernel options:\n"
97+
for config in missing_configs:
98+
error_msg += f"- {config}\n"
99+
error_msg += "\nPlease ensure these kernel configurations are enabled (y) or built as modules (m)."
100+
self.fail(error_msg)
101+
102+
log.info("All required kernel configurations are present")
103+
104+
except Exception as e:
105+
self.fail(f"Error checking kernel configurations: {str(e)}")
106+
107+
108+
def kernel_grub_signature_check(self):
109+
"""
110+
Check kernel and grub signatures.
111+
"""
112+
try:
113+
if not getattr(self, 'util', None):
114+
self.util = OpTestUtil(self.conf)
115+
self.kernel_signature = self.util.check_kernel_signature()
116+
self.grub_filename = self.util.get_grub_file()
117+
try:
118+
self.grub_signature = self.util.check_grub_signature(self.grub_filename)
119+
except Exception as e:
120+
log.error("Grub signature check failed for '%s': %s", self.grub_filename, e)
121+
self.fail(f"Grub signature check failed for '{self.grub_filename}': {e}")
122+
log.info("Kernel signed: %s, Grub file: %s, Grub signed: %s",
123+
self.kernel_signature, self.grub_filename, self.grub_signature)
124+
return True
125+
126+
except Exception as e:
127+
log.error("Kernel/Grub signature check failed: %s", e)
128+
self.fail(f"Kernel/Grub signature check failed: {e}")
129+
130+
def execute_hmc_command(self, command):
131+
"""
132+
Execute HMC command and handle errors
133+
"""
134+
self.ensure_hmc_available()
135+
try:
136+
result = self.cv_HMC.run_command(command)
137+
log.info(f"Command executed successfully on HMC: {command}")
138+
return result
139+
except Exception as e:
140+
error_msg = f"Failed to execute HMC command on HMC '{command}': {str(e)}"
141+
log.error(error_msg)
142+
self.fail(error_msg)
143+
144+
def ensure_hmc_available(self):
145+
"""
146+
Ensure the HMC object is available.
147+
"""
148+
if not getattr(self, 'cv_HMC', None):
149+
msg = (
150+
"HMC object is not available (self.cv_HMC is None). "
151+
)
152+
log.error(msg)
153+
self.fail(msg)
154+
155+
def get_secure_boot_status(self, host_name, system_name):
156+
"""
157+
Get current secure boot and keystore status
158+
"""
159+
self.ensure_hmc_available()
160+
command = f"lssyscfg -r lpar -m {system_name} --filter \"lpar_names={host_name}\" | sed s/,/\\\n/g | grep \"secure_boot\\|keystore\""
161+
return self.execute_hmc_command(command)
162+
163+
def shutdown_lpar(self, host_name, system_name, sleep_time=5):
164+
"""
165+
Shutdown LPAR and wait
166+
"""
167+
self.ensure_hmc_available()
168+
command = f"chsysstate -o shutdown -r lpar -n {host_name} -m {system_name}"
169+
self.execute_hmc_command(command)
170+
time.sleep(sleep_time)
171+
172+
def start_lpar(self, host_name, system_name, sleep_time=5):
173+
"""
174+
Start LPAR and wait
175+
"""
176+
self.ensure_hmc_available()
177+
command = f"chsysstate -o on -r lpar -n {host_name} -m {system_name}"
178+
self.execute_hmc_command(command)
179+
time.sleep(sleep_time)
180+
181+
def configure_secure_boot(self, host_name, system_name, config_params):
182+
"""
183+
Configure secure boot parameters
184+
"""
185+
self.ensure_hmc_available()
186+
command = f"chsyscfg -r lpar -m {system_name} -i \"name={host_name}, {config_params}\""
187+
self.execute_hmc_command(command)
188+
189+
def setup_dynamic_secure_boot(self, host_name, system_name):
190+
"""
191+
Setup dynamic secure boot environment
192+
"""
193+
try:
194+
log.info(f"Starting dynamic secure boot setup for host: {host_name}, system: {system_name}")
195+
# Ensure HMC is available
196+
self.ensure_hmc_available()
197+
# Step: Shutdown LPAR and wait ~30s
198+
self.shutdown_lpar(host_name, system_name, sleep_time=30)
199+
# Configure combined secure-boot parameters
200+
config_params = (
201+
"keystore_signed_updates_without_verification=1,"
202+
"keystore_signed_updates=1,"
203+
"linux_dynamic_key_secure_boot=1,"
204+
"keystore_kbytes=64"
205+
)
206+
self.configure_secure_boot(host_name, system_name, config_params)
207+
# Allow the HMC to settle
208+
time.sleep(10)
209+
# Get and log current status
210+
status = self.get_secure_boot_status(host_name, system_name)
211+
log.info("Current secure boot status:")
212+
for line in status:
213+
log.info(line)
214+
# Start the LPAR
215+
self.start_lpar(host_name, system_name)
216+
log.info("Dynamic secure boot environment setup completed successfully")
217+
except Exception as e:
218+
error_msg = f"Failed to setup dynamic secure boot environment: {str(e)}"
219+
log.error(error_msg)
220+
self.fail(error_msg)
221+
222+
def reset_secure_boot(self, host_name, system_name):
223+
"""
224+
Reset all secure boot settings to zero by running HMC commands to
225+
configure the LPAR secure-boot-related parameters to zero.
226+
"""
227+
try:
228+
log.info(f"Resetting secure boot settings to zero for host: {host_name}, system: {system_name}")
229+
230+
# Run guest-side secvarctl to generate auth files.
231+
from testcases.OpTestSecvarctl import SecvarctlTest
232+
sec = SecvarctlTest()
233+
sec.connection = self.connection
234+
235+
# Search for the parent workspace that contains the cloned repo
236+
try_paths = []
237+
if getattr(self, 'home', None):
238+
try_paths.append(os.path.join(self.home, 'secvarctl_*'))
239+
try_paths.append('/home/secvarctl_*')
240+
241+
repo_root = None
242+
for p in try_paths:
243+
rc = self.connection.run_command(f"bash -c 'ls -d {p} 2>/dev/null | head -n1' || true")
244+
if rc and rc[0].strip():
245+
repo_root = rc[0].strip()
246+
break
247+
248+
auth_dir = None
249+
if repo_root:
250+
candidate = os.path.join(repo_root, 'secvarctl', 'test', 'testdata', 'guest', 'authfiles')
251+
rc = self.connection.run_command(f"bash -c 'ls -d {candidate} 2>/dev/null | head -n1' || true")
252+
if rc and rc[0].strip():
253+
auth_dir = rc[0].strip()
254+
255+
if auth_dir:
256+
log.info(f"Found authfiles at {auth_dir}; running secvarctl write PK reset_PK_by_PK.auth")
257+
try:
258+
cmd = f"bash -c 'cd {shlex.quote(auth_dir)} && secvarctl write PK reset_PK_by_PK.auth'"
259+
out = self.connection.run_command(cmd)
260+
for l in out:
261+
log.info(l)
262+
except Exception as e:
263+
self.fail(f"Failed to run secvarctl write on guest: {e}")
264+
else:
265+
log.warning("secvarctl authfiles directory not found")
266+
267+
# After PK reset, zero the HMC secure-boot parameters
268+
self.ensure_hmc_available()
269+
# Shutdown LPAR and wait ~30s
270+
self.shutdown_lpar(host_name, system_name, sleep_time=30)
271+
# Configure secure-boot related params to zero
272+
config_params = (
273+
"keystore_signed_updates_without_verification=0,"
274+
"keystore_signed_updates=0,"
275+
"linux_dynamic_key_secure_boot=0,"
276+
"keystore_kbytes=0"
277+
)
278+
self.configure_secure_boot(host_name, system_name, config_params)
279+
# Allow HMC to settle
280+
time.sleep(10)
281+
# Get and log current status
282+
output = self.get_secure_boot_status(host_name, system_name)
283+
log.info("Current secure boot status after resetting to zero:")
284+
for line in output:
285+
log.info(line)
286+
# Start the LPAR
287+
self.start_lpar(host_name, system_name)
288+
289+
# Cleanup via SecvarctlTest.tearDown()
290+
try:
291+
if repo_root:
292+
sec.home = repo_root
293+
sec.tearDown()
294+
except Exception as e:
295+
log.warning(f"secvarctl tearDown reported an error: {e}")
296+
297+
log.info("Secure boot reset completed and secvarctl artifacts cleaned up")
298+
except Exception as e:
299+
error_msg = f"Failed to reset secure boot settings: {str(e)}"
300+
log.error(error_msg)
301+
self.fail(error_msg)
302+
303+
def enable_secure_boot(self, host_name, system_name):
304+
"""
305+
Enable secure boot (set to mode 2)
306+
"""
307+
try:
308+
log.info(f"Enabling secure boot for host: {host_name}, system: {system_name}")
309+
self.shutdown_lpar(host_name, system_name)
310+
self.configure_secure_boot(host_name, system_name, "secure_boot=2")
311+
self.start_lpar(host_name, system_name)
312+
log.info("Secure boot enabled successfully")
313+
314+
except Exception as e:
315+
error_msg = f"Failed to enable secure boot: {str(e)}"
316+
log.error(error_msg)
317+
self.fail(error_msg)
318+
319+
def disable_secure_boot(self, host_name, system_name):
320+
"""
321+
Disable secure boot (set to mode 0)
322+
"""
323+
try:
324+
log.info(f"Disabling secure boot for host: {host_name}, system: {system_name}")
325+
self.shutdown_lpar(host_name, system_name, sleep_time=30)
326+
self.configure_secure_boot(host_name, system_name, "secure_boot=0")
327+
time.sleep(5)
328+
self.start_lpar(host_name, system_name)
329+
log.info("Secure boot disabled successfully")
330+
except Exception as e:
331+
error_msg = f"Failed to disable secure boot: {str(e)}"
332+
log.error(error_msg)
333+
self.fail(error_msg)
334+
335+
def runTest(self):
336+
# Get host and system names from configuration
337+
host_name = self.conf.args.lpar_name
338+
system_name = self.conf.args.system_name
339+
# Check required kernel configurations are present
340+
self.check_kernel_config()
341+
# Check kernel and grub signatures
342+
self.kernel_grub_signature_check()
343+
# Setup dynamic secure boot
344+
self.setup_dynamic_secure_boot(host_name, system_name)
345+
# Enable secure boot
346+
self.enable_secure_boot(host_name, system_name)
347+
# Reset secure boot
348+
self.reset_secure_boot(host_name, system_name)
349+
# Disable secure boot
350+
self.disable_secure_boot(host_name, system_name)

0 commit comments

Comments
 (0)