diff --git a/debian/control b/debian/control
index 890100fd8..d1d1602ae 100644
--- a/debian/control
+++ b/debian/control
@@ -1,381 +1,384 @@
 Source: vyos-1x
 Section: contrib/net
 Priority: extra
 Maintainer: VyOS Package Maintainers <maintainers@vyos.net>
 Build-Depends:
   debhelper (>= 9),
   dh-python,
   fakeroot,
   gcc,
   iproute2,
   libvyosconfig0 (>= 0.0.7),
   libzmq3-dev,
   python3 (>= 3.10),
 # For QA
   pylint,
 # For generating command definitions
   python3-lxml,
   python3-xmltodict,
 # For running tests
   python3-coverage,
   python3-hurry.filesize,
   python3-netaddr,
   python3-netifaces,
   python3-nose,
   python3-jinja2,
   python3-paramiko,
   python3-passlib,
   python3-psutil,
   python3-requests,
   python3-setuptools,
   python3-tabulate,
   python3-zmq,
   quilt,
   whois
 Standards-Version: 3.9.6
 
 Package: vyos-1x
 Architecture: amd64 arm64
 Pre-Depends:
   libpam-runtime [amd64],
   libnss-tacplus [amd64],
   libpam-tacplus [amd64],
   libpam-radius-auth [amd64]
 Depends:
 ## Fundamentals
   ${python3:Depends} (>= 3.10),
   dialog,
   libvyosconfig0,
   libpam-cap,
   bash-completion,
   ipvsadm,
   udev,
   less,
   at,
   rsync,
   vyatta-bash,
   vyatta-biosdevname,
   vyatta-cfg,
   vyos-http-api-tools,
   vyos-utils,
 ## End of Fundamentals
 ## Python libraries used in multiple modules and scripts
   python3,
   python3-cryptography,
   python3-hurry.filesize,
   python3-inotify,
   python3-jinja2,
   python3-jmespath,
   python3-netaddr,
   python3-netifaces,
   python3-paramiko,
   python3-passlib,
   python3-pyroute2,
   python3-psutil,
   python3-pyhumps,
   python3-pystache,
   python3-pyudev,
   python3-six,
   python3-tabulate,
   python3-voluptuous,
   python3-xmltodict,
   python3-zmq,
 ## End of Python libraries
 ## Basic System services and utilities
   coreutils,
   sudo,
   systemd,
   bsdmainutils,
   openssl,
   curl,
   dbus,
   file,
   iproute2 (>= 6.0.0),
   linux-cpupower,
 # ipaddrcheck is widely used in IP value validators
   ipaddrcheck,
   ethtool,
   lm-sensors,
   procps,
   netplug,
   sed,
   ssl-cert,
   tuned,
   beep,
   wide-dhcpv6-client,
 # Generic colorizer
   grc,
 ## End of System services and utilities
 ## For the installer
   fdisk,
   gdisk,
   mdadm,
   efibootmgr,
   libefivar1,
   dosfstools,
-  grub-efi-amd64-bin [amd64],
+  grub-efi-amd64-signed [amd64],
   grub-efi-arm64-bin [arm64],
+  mokutil [amd64],
+  shim-signed [amd64],
+  sbsigntool [amd64],
 # Image signature verification tool
   minisign,
 # Live filesystem tools
   squashfs-tools,
   fuse-overlayfs,
 ## End installer
   auditd,
   iputils-arping,
   iputils-ping,
   isc-dhcp-client,
 # For "vpn pptp", "vpn l2tp", "vpn sstp", "service ipoe-server"
   accel-ppp,
 # End "vpn pptp", "vpn l2tp", "vpn sstp", "service ipoe-server"
   avahi-daemon,
   conntrack,
   conntrackd,
 ## Conf mode features
 # For "interfaces wireless"
   hostapd,
   hsflowd,
   iw,
   wireless-regdb,
   wpasupplicant (>= 0.6.7),
 # End "interfaces wireless"
 # For "interfaces wwan"
   modemmanager,
   usb-modeswitch,
   libqmi-utils,
 # End "interfaces wwan"
 # For "interfaces openvpn"
   openvpn,
   openvpn-auth-ldap,
   openvpn-auth-radius,
   openvpn-otp,
   openvpn-dco,
   libpam-google-authenticator,
 # End "interfaces openvpn"
 # For "interfaces wireguard"
   wireguard-tools,
   qrencode,
 # End "interfaces wireguard"
 # For "interfaces pppoe"
   pppoe,
 # End "interfaces pppoe"
 # For "interfaces sstpc"
   sstp-client,
 # End "interfaces sstpc"
 # For "protocols *"
   frr (>= 9.1),
   frr-pythontools,
   frr-rpki-rtrlib,
   frr-snmp,
 # End "protocols *"
 # For "protocols nhrp" (part of DMVPN)
   opennhrp,
 # End "protocols nhrp"
 # For "protocols igmp-proxy"
   igmpproxy,
 # End "protocols igmp-proxy"
 # For "pki"
   certbot,
 # End "pki"
 # For "service console-server"
   conserver-client,
   conserver-server,
   console-data,
   dropbear,
 # End "service console-server"
 # For "service aws glb"
   aws-gwlbtun,
 # For "service dns dynamic"
   ddclient (>= 3.11.1),
 # End "service dns dynamic"
 # # For "service ids"
   fastnetmon [amd64],
   suricata,
   suricata-update,
 # End "service ids"
 # # For "service ndp-proxy"
   ndppd,
 # End "service ndp-proxy"
 # For "service router-advert"
   radvd,
 # End "service route-advert"
 # For "load-balancing reverse-proxy"
   haproxy,
 # End "load-balancing reverse-proxy"
 # For "load-balancing wan"
   vyatta-wanloadbalance,
 # End "load-balancing wan"
 # For "service dhcp-relay"
   isc-dhcp-relay,
 # For "service dhcp-server"
   kea,
 # End "service dhcp-server"
 # For "service lldp"
   lldpd,
 # End "service lldp"
 # For "service https"
   nginx-light,
 # End "service https"
 # For "service ssh"
   openssh-server,
   sshguard,
 # End "service ssh"
 # For "service salt-minion"
   salt-minion,
 # End "service salt-minion"
 # For "service snmp"
   snmp,
   snmpd,
 # End "service snmp"
 # For "service webproxy"
   squid,
   squidclient,
   squidguard,
 # End "service webproxy"
 # For "service monitoring telegraf"
   telegraf (>= 1.20),
 # End "service monitoring telegraf"
 # For "service monitoring zabbix-agent"
   zabbix-agent2,
 # End "service monitoring zabbix-agent"
 # For "service tftp-server"
   tftpd-hpa,
 # End "service tftp-server"
 # For "service dns forwarding"
   pdns-recursor,
 # End "service dns forwarding"
 # For "service sla owamp"
   owamp-client,
   owamp-server,
 # End "service sla owamp"
 # For "service sla twamp"
   twamp-client,
   twamp-server,
 # End "service sla twamp"
 # For "service broadcast-relay"
   udp-broadcast-relay,
 # End "service broadcast-relay"
 # For "high-availability vrrp"
   keepalived (>=2.0.5),
 # End "high-availability-vrrp"
 # For "system console"
   util-linux,
 # End "system console"
 # For "system task-scheduler"
   cron,
 # End "system task-scheduler"
 # For "system lcd"
   lcdproc,
   lcdproc-extra-drivers,
 # End "system lcd"
 # For "system config-management commit-archive"
   git,
 # End "system config-management commit-archive"
 # For firewall
   libndp-tools,
   libnetfilter-conntrack3,
   libnfnetlink0,
   nfct,
   nftables (>= 0.9.3),
 # For "vpn ipsec"
   strongswan (>= 5.9),
   strongswan-swanctl (>= 5.9),
   charon-systemd,
   libcharon-extra-plugins (>=5.9),
   libcharon-extauth-plugins (>=5.9),
   libstrongswan-extra-plugins (>=5.9),
   libstrongswan-standard-plugins (>=5.9),
   python3-vici (>= 5.7.2),
 # End "vpn ipsec"
 # For "nat64"
   jool,
 # End "nat64"
 # For "system conntrack modules rtsp"
   nat-rtsp,
 # End "system conntrack modules rtsp"
 # For "service ntp"
   chrony,
 # End "system ntp"
 # For "vpn openconnect"
   ocserv,
 # End "vpn openconnect"
 # For "system flow-accounting"
   pmacct (>= 1.6.0),
 # End "system flow-accounting"
 # For "system syslog"
   rsyslog,
 # End "system syslog"
 # For "system option keyboard-layout"
   kbd,
 # End "system option keyboard-layout"
 # For "container"
   podman (>=4.9.5),
   netavark,
   aardvark-dns,
 # iptables is only used for containers now, not the the firewall CLI
   iptables,
 # End container
 ## End Configuration mode
 ## Operational mode
 # Used for hypervisor model in "run show version"
   hvinfo,
 # For "run traceroute"
   traceroute,
 # For "run monitor traffic"
   tcpdump,
 # End "run monitor traffic"
 # For "show hardware dmi"
   dmidecode,
 # For "run show hardware storage smart"
   smartmontools,
 # For "run show hardware scsi"
   lsscsi,
 # For "run show hardware pci"
   pciutils,
 # For "show hardware usb"
   usbutils,
 # For "run show hardware storage nvme"
   nvme-cli,
 # For "run monitor bandwidth-test"
   iperf,
   iperf3,
 # End "run monitor bandwidth-test"
 # For "run wake-on-lan"
   etherwake,
 # For "run force ipv6-nd"
   ndisc6,
 # For "run monitor bandwidth"
   bmon,
 # For "run format disk"
   parted,
 # End Operational mode
 ## TPM tools
   cryptsetup,
   tpm2-tools,
 ## End TPM tools
 ## Optional utilities
   easy-rsa,
   tcptraceroute,
   mtr-tiny,
   telnet,
   stunnel4,
   uidmap
 ## End optional utilities
 Description: VyOS configuration scripts and data
  VyOS configuration scripts, interface definitions, and everything
 
 Package: vyos-1x-vmware
 Architecture: amd64
 Depends:
  vyos-1x,
  open-vm-tools
 Description: VyOS configuration scripts and data for VMware
  Adds configuration files required for VyOS running on VMware hosts.
 
 Package: vyos-1x-smoketest
 Architecture: all
 Depends:
  skopeo,
  snmp,
  vyos-1x
 Description: VyOS build sanity checking toolkit
diff --git a/op-mode-definitions/install-mok.xml.in b/op-mode-definitions/install-mok.xml.in
new file mode 100644
index 000000000..18526a354
--- /dev/null
+++ b/op-mode-definitions/install-mok.xml.in
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+  <node name="install">
+    <children>
+      <leafNode name="mok">
+        <properties>
+          <help>Install Secure Boot MOK (Machine Owner Key)</help>
+        </properties>
+        <command>if test -f /var/lib/shim-signed/mok/MOK.der; then sudo mokutil --ignore-keyring --import /var/lib/shim-signed/mok/MOK.der; else echo "Secure Boot Machine Owner Key not found"; fi</command>
+      </leafNode>
+    </children>
+  </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/show-secure-boot.xml.in b/op-mode-definitions/show-secure-boot.xml.in
new file mode 100644
index 000000000..ff731bac9
--- /dev/null
+++ b/op-mode-definitions/show-secure-boot.xml.in
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+  <node name="show">
+    <children>
+      <node name="secure-boot">
+        <properties>
+          <help>Show Secure Boot state</help>
+        </properties>
+        <command>${vyos_op_scripts_dir}/secure_boot.py show</command>
+        <children>
+          <leafNode name="keys">
+            <properties>
+              <help>Show enrolled certificates</help>
+            </properties>
+            <command>mokutil --list-enrolled</command>
+          </leafNode>
+        </children>
+      </node>
+    </children>
+  </node>
+</interfaceDefinition>
diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py
index daddb799a..de8303ee2 100644
--- a/python/vyos/system/grub.py
+++ b/python/vyos/system/grub.py
@@ -1,464 +1,464 @@
 # Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import platform
 
 from pathlib import Path
 from re import MULTILINE, compile as re_compile
 from shutil import copy2
 from uuid import uuid5, NAMESPACE_URL, UUID
 
 from vyos.template import render
 from vyos.utils.process import cmd, rc_cmd
 from vyos.system import disk
 
 # Define variables
 GRUB_DIR_MAIN: str = '/boot/grub'
 GRUB_CFG_MAIN: str = f'{GRUB_DIR_MAIN}/grub.cfg'
 GRUB_DIR_VYOS: str = f'{GRUB_DIR_MAIN}/grub.cfg.d'
 CFG_VYOS_HEADER: str = f'{GRUB_DIR_VYOS}/00-vyos-header.cfg'
 CFG_VYOS_MODULES: str = f'{GRUB_DIR_VYOS}/10-vyos-modules-autoload.cfg'
 CFG_VYOS_VARS: str = f'{GRUB_DIR_VYOS}/20-vyos-defaults-autoload.cfg'
 CFG_VYOS_COMMON: str = f'{GRUB_DIR_VYOS}/25-vyos-common-autoload.cfg'
 CFG_VYOS_PLATFORM: str = f'{GRUB_DIR_VYOS}/30-vyos-platform-autoload.cfg'
 CFG_VYOS_MENU: str = f'{GRUB_DIR_VYOS}/40-vyos-menu-autoload.cfg'
 CFG_VYOS_OPTIONS: str = f'{GRUB_DIR_VYOS}/50-vyos-options.cfg'
 GRUB_DIR_VYOS_VERS: str = f'{GRUB_DIR_VYOS}/vyos-versions'
 
 TMPL_VYOS_VERSION: str = 'grub/grub_vyos_version.j2'
 TMPL_GRUB_VARS: str = 'grub/grub_vars.j2'
 TMPL_GRUB_MAIN: str = 'grub/grub_main.j2'
 TMPL_GRUB_MENU: str = 'grub/grub_menu.j2'
 TMPL_GRUB_MODULES: str = 'grub/grub_modules.j2'
 TMPL_GRUB_OPTS: str = 'grub/grub_options.j2'
 TMPL_GRUB_COMMON: str = 'grub/grub_common.j2'
 
 # default boot options
 BOOT_OPTS_STEM: str = 'boot=live rootdelay=5 noautologin net.ifnames=0 biosdevname=0 vyos-union=/boot/'
 
 # prepare regexes
 REGEX_GRUB_VARS: str = r'^set (?P<variable_name>\w+)=[\'"]?(?P<variable_value>.*)(?<![\'"])[\'"]?$'
 REGEX_GRUB_MODULES: str = r'^insmod (?P<module_name>.+)$'
 REGEX_KERNEL_CMDLINE: str = r'^BOOT_IMAGE=/(?P<boot_type>boot|live)/((?P<image_version>.+)/)?vmlinuz.*$'
 REGEX_GRUB_BOOT_OPTS: str = r'^\s*set boot_opts="(?P<boot_opts>[^$]+)"$'
 
 
 def install(drive_path: str, boot_dir: str, efi_dir: str, id: str = 'VyOS', chroot : str = "") -> None:
     """Install GRUB for both BIOS and EFI modes (hybrid boot)
 
     Args:
         drive_path (str): path to a drive where GRUB must be installed
         boot_dir (str): a path to '/boot' directory
         efi_dir (str): a path to '/boot/efi' directory
     """
 
     if chroot:
         chroot_cmd = f"chroot {chroot}"
     else:
         chroot_cmd = ""
 
     efi_installation_arch = "x86_64"
     if platform.machine() == "aarch64":
         efi_installation_arch = "arm64"
     elif platform.machine() == "x86_64":
         cmd(
             f'{chroot_cmd} grub-install --no-floppy --target=i386-pc \
             --boot-directory={boot_dir}  {drive_path} --force'
         )
 
     cmd(
         f'{chroot_cmd} grub-install --no-floppy --recheck --target={efi_installation_arch}-efi \
             --force-extra-removable --boot-directory={boot_dir} \
             --efi-directory={efi_dir} --bootloader-id="{id}" \
-            --no-uefi-secure-boot'
+            --uefi-secure-boot'
     )
 
 
 def gen_version_uuid(version_name: str) -> str:
     """Generate unique ID from version name
 
     Use UUID5 / NAMESPACE_URL with prefix `uuid5-`
 
     Args:
         version_name (str): version name
 
     Returns:
         str: generated unique ID
     """
     ver_uuid: UUID = uuid5(NAMESPACE_URL, version_name)
     ver_id: str = f'uuid5-{ver_uuid}'
     return ver_id
 
 
 def version_add(version_name: str,
                 root_dir: str = '',
                 boot_opts: str = '',
                 boot_opts_config = None) -> None:
     """Add a new VyOS version to GRUB loader configuration
 
     Args:
         vyos_version (str): VyOS version name
         root_dir (str): an optional path to the root directory.
         Defaults to empty.
         boot_opts (str): an optional boot options for Linux kernel.
         Defaults to empty.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
     version_config: str = f'{root_dir}/{GRUB_DIR_VYOS_VERS}/{version_name}.cfg'
     render(
         version_config, TMPL_VYOS_VERSION, {
             'version_name': version_name,
             'version_uuid': gen_version_uuid(version_name),
             'boot_opts_default': BOOT_OPTS_STEM + version_name,
             'boot_opts': boot_opts,
             'boot_opts_config': boot_opts_config
         })
 
 
 def version_del(vyos_version: str, root_dir: str = '') -> None:
     """Delete a VyOS version from GRUB loader configuration
 
     Args:
         vyos_version (str): VyOS version name
         root_dir (str): an optional path to the root directory.
         Defaults to empty.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
     version_config: str = f'{root_dir}/{GRUB_DIR_VYOS_VERS}/{vyos_version}.cfg'
     Path(version_config).unlink(missing_ok=True)
 
 
 def version_list(root_dir: str = '') -> list[str]:
     """Generate a list with installed VyOS versions
 
     Args:
         root_dir (str): an optional path to the root directory.
         Defaults to empty.
 
     Returns:
         list: A list with versions names
 
     N.B. coreutils stat reports st_birthtime, but not available in
     Path.stat()/os.stat()
     """
     if not root_dir:
         root_dir = disk.find_persistence()
     versions_files = Path(f'{root_dir}/{GRUB_DIR_VYOS_VERS}').glob('*.cfg')
     versions_order: dict[str, int] = {}
     for file in versions_files:
         p = Path(root_dir).joinpath('boot').joinpath(file.stem)
         command = f'stat -c %W {p.as_posix()}'
         rc, out = rc_cmd(command)
         if rc == 0:
             versions_order[file.stem] = int(out)
     versions_order = sorted(versions_order, key=versions_order.get, reverse=True)
     versions_list: list[str] = list(versions_order)
 
     return versions_list
 
 
 def read_env(env_file: str = '') -> dict[str, str]:
     """Read GRUB environment
 
     Args:
         env_file (str, optional): a path to grub environment file.
         Defaults to empty.
 
     Returns:
         dict: dictionary with GRUB environment
     """
     if not env_file:
         root_dir: str = disk.find_persistence()
         env_file = f'{root_dir}/{GRUB_DIR_MAIN}/grubenv'
 
     env_content: str = cmd(f'grub-editenv {env_file} list').splitlines()
     regex_filter = re_compile(r'^(?P<variable_name>.*)=(?P<variable_value>.*)$')
     env_dict: dict[str, str] = {}
     for env_item in env_content:
         search_result = regex_filter.fullmatch(env_item)
         if search_result:
             search_result_dict: dict[str, str] = search_result.groupdict()
             variable_name: str = search_result_dict.get('variable_name', '')
             variable_value: str = search_result_dict.get('variable_value', '')
             if variable_name and variable_value:
                 env_dict.update({variable_name: variable_value})
     return env_dict
 
 
 def get_cfg_ver(root_dir: str = '') -> int:
     """Get current version of GRUB configuration
 
     Args:
         root_dir (str, optional): an optional path to the root directory.
         Defaults to empty.
 
     Returns:
         int: a configuration version
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     cfg_ver: str = vars_read(f'{root_dir}/{CFG_VYOS_HEADER}').get(
             'VYOS_CFG_VER')
     if cfg_ver:
         cfg_ver_int: int = int(cfg_ver)
     else:
         cfg_ver_int: int = 0
     return cfg_ver_int
 
 
 def write_cfg_ver(cfg_ver: int, root_dir: str = '') -> None:
     """Write version number of GRUB configuration
 
     Args:
         cfg_ver (int): a version number to write
         root_dir (str, optional): an optional path to the root directory.
         Defaults to empty.
 
     Returns:
         int: a configuration version
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     vars_file: str = f'{root_dir}/{CFG_VYOS_HEADER}'
     vars_current: dict[str, str] = vars_read(vars_file)
     vars_current['VYOS_CFG_VER'] = str(cfg_ver)
     vars_write(vars_file, vars_current)
 
 
 def vars_read(grub_cfg: str) -> dict[str, str]:
     """Read variables from a GRUB configuration file
 
     Args:
         grub_cfg (str): a path to the GRUB config file
 
     Returns:
         dict: a dictionary with variables and values
     """
     vars_dict: dict[str, str] = {}
     regex_filter = re_compile(REGEX_GRUB_VARS)
     try:
         config_text: list[str] = Path(grub_cfg).read_text().splitlines()
     except FileNotFoundError:
         return vars_dict
     for line in config_text:
         search_result = regex_filter.fullmatch(line)
         if search_result:
             search_dict = search_result.groupdict()
             variable_name: str = search_dict.get('variable_name', '')
             variable_value: str = search_dict.get('variable_value', '')
             if variable_name and variable_value:
                 vars_dict.update({variable_name: variable_value})
     return vars_dict
 
 
 def modules_read(grub_cfg: str) -> list[str]:
     """Read modules list from a GRUB configuration file
 
     Args:
         grub_cfg (str): a path to the GRUB config file
 
     Returns:
         list: a list with modules to load
     """
     mods_list: list[str] = []
     regex_filter = re_compile(REGEX_GRUB_MODULES, MULTILINE)
     try:
         config_text = Path(grub_cfg).read_text()
     except FileNotFoundError:
         return mods_list
     mods_list = regex_filter.findall(config_text)
 
     return mods_list
 
 
 def modules_write(grub_cfg: str, mods_list: list[str]) -> None:
     """Write modules list to a GRUB configuration file (overwrite everything)
 
     Args:
         grub_cfg (str): a path to GRUB configuration file
         mods_list (list): a list with modules to load
     """
     render(grub_cfg, TMPL_GRUB_MODULES, {'mods_list': mods_list})
 
 
 def vars_write(grub_cfg: str, grub_vars: dict[str, str]) -> None:
     """Write variables to a GRUB configuration file (overwrite everything)
 
     Args:
         grub_cfg (str): a path to GRUB configuration file
         grub_vars (dict): a dictionary with new variables
     """
     render(grub_cfg, TMPL_GRUB_VARS, {'vars': grub_vars})
 
 def get_boot_opts(version_name: str, root_dir: str = '') -> str:
     """Read boot_opts setting from version file; return default setting on
     any failure.
 
     Args:
         version_name (str): version name
         root_dir (str, optional): an optional path to the root directory.
         Defaults to empty.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     boot_opts_default: str = BOOT_OPTS_STEM + version_name
     boot_opts: str = ''
     regex_filter = re_compile(REGEX_GRUB_BOOT_OPTS)
     version_config: str = f'{root_dir}/{GRUB_DIR_VYOS_VERS}/{version_name}.cfg'
     try:
         config_text: list[str] = Path(version_config).read_text().splitlines()
     except FileNotFoundError:
         return boot_opts_default
     for line in config_text:
         search_result = regex_filter.fullmatch(line)
         if search_result:
             search_dict = search_result.groupdict()
             boot_opts = search_dict.get('boot_opts', '')
             break
 
     if not boot_opts:
         boot_opts = boot_opts_default
 
     return boot_opts
 
 def set_default(version_name: str, root_dir: str = '') -> None:
     """Set version as default boot entry
 
     Args:
         version_name (str): version name
         root_dir (str, optional): an optional path to the root directory.
         Defaults to empty.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     vars_file = f'{root_dir}/{CFG_VYOS_VARS}'
     vars_current = vars_read(vars_file)
     vars_current['default'] = gen_version_uuid(version_name)
     vars_write(vars_file, vars_current)
 
 
 def common_write(root_dir: str = '', grub_common: dict[str, str] = {}) -> None:
     """Write common GRUB configuration file (overwrite everything)
 
     Args:
         root_dir (str, optional): an optional path to the root directory.
         Defaults to empty.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
     common_config = f'{root_dir}/{CFG_VYOS_COMMON}'
     render(common_config, TMPL_GRUB_COMMON, grub_common)
 
 
 def create_structure(root_dir: str = '') -> None:
     """Create GRUB directories structure
 
     Args:
         root_dir (str, optional): an optional path to the root directory.
         Defaults to ''.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     Path(f'{root_dir}/{GRUB_DIR_VYOS_VERS}').mkdir(parents=True, exist_ok=True)
 
 
 def set_console_type(console_type: str, root_dir: str = '') -> None:
     """Write default console type to GRUB configuration
 
     Args:
         console_type (str): a default console type
         root_dir (str, optional): an optional path to the root directory.
         Defaults to empty.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     vars_file: str = f'{root_dir}/{CFG_VYOS_VARS}'
     vars_current: dict[str, str] = vars_read(vars_file)
     vars_current['console_type'] = str(console_type)
     vars_write(vars_file, vars_current)
 
 def set_console_speed(console_speed: str, root_dir: str = '') -> None:
     """Write default console speed to GRUB configuration
 
     Args:
         console_speed (str): default console speed
         root_dir (str, optional): an optional path to the root directory.
         Defaults to empty.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     vars_file: str = f'{root_dir}/{CFG_VYOS_VARS}'
     vars_current: dict[str, str] = vars_read(vars_file)
     vars_current['console_speed'] = str(console_speed)
     vars_write(vars_file, vars_current)
 
 def set_kernel_cmdline_options(cmdline_options: str, version_name: str,
                                root_dir: str = '') -> None:
     """Write additional cmdline options to GRUB configuration
 
     Args:
         cmdline_options (str): cmdline options to add to default boot line
         version_name (str): image version name
         root_dir (str, optional): an optional path to the root directory.
     """
     if not root_dir:
         root_dir = disk.find_persistence()
 
     version_add(version_name=version_name, root_dir=root_dir,
                 boot_opts_config=cmdline_options)
 
 
 def sort_inodes(dir_path: str) -> None:
     """Sort inodes for files inside a folder
     Regenerate inodes for each file to get the same order for both inodes
     and file names
 
     GRUB iterates files by inodes, not alphabetically. Therefore, if we
     want to read them in proper order, we need to sort inodes for all
     config files in a folder.
 
     Args:
         dir_path (str): a path to directory
     """
     dir_content: list[Path] = sorted(Path(dir_path).iterdir())
     temp_list_old: list[Path] = []
     temp_list_new: list[Path] = []
 
     # create a copy of all files, to get new inodes
     for item in dir_content:
         # skip directories
         if item.is_dir():
             continue
         # create a new copy of file with a temporary name
         copy_path = Path(f'{item.as_posix()}_tmp')
         copy2(item, Path(copy_path))
         temp_list_old.append(item)
         temp_list_new.append(copy_path)
 
     # delete old files and rename new ones
     for item in temp_list_old:
         item.unlink()
     for item in temp_list_new:
         new_name = Path(f'{item.as_posix()[0:-4]}')
         item.rename(new_name)
diff --git a/python/vyos/utils/boot.py b/python/vyos/utils/boot.py
index 3aecbec64..708bef14d 100644
--- a/python/vyos/utils/boot.py
+++ b/python/vyos/utils/boot.py
@@ -1,35 +1,39 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 
 def boot_configuration_complete() -> bool:
     """ Check if the boot config loader has completed
     """
     from vyos.defaults import config_status
     if os.path.isfile(config_status):
         return True
     return False
 
 def boot_configuration_success() -> bool:
     from vyos.defaults import config_status
     try:
         with open(config_status) as f:
             res = f.read().strip()
     except FileNotFoundError:
         return False
     if int(res) == 0:
         return True
     return False
+
+def is_uefi_system() -> bool:
+    efi_fw_dir = '/sys/firmware/efi'
+    return os.path.exists(efi_fw_dir) and os.path.isdir(efi_fw_dir)
diff --git a/python/vyos/utils/system.py b/python/vyos/utils/system.py
index fca93d118..7b12efb14 100644
--- a/python/vyos/utils/system.py
+++ b/python/vyos/utils/system.py
@@ -1,141 +1,149 @@
 # Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 from subprocess import run
 
 def sysctl_read(name: str) -> str:
     """Read and return current value of sysctl() option
 
     Args:
         name (str): sysctl key name
 
     Returns:
         str: sysctl key value
     """
     tmp = run(['sysctl', '-nb', name], capture_output=True)
     return tmp.stdout.decode()
 
 def sysctl_write(name: str, value: str | int) -> bool:
     """Change value via sysctl()
 
     Args:
         name (str): sysctl key name
         value (str | int): sysctl key value
 
     Returns:
         bool: True if changed, False otherwise
     """
     # convert other types to string before comparison
     if not isinstance(value, str):
         value = str(value)
     # do not change anything if a value is already configured
     if sysctl_read(name) == value:
         return True
     # return False if sysctl call failed
     if run(['sysctl', '-wq', f'{name}={value}']).returncode != 0:
         return False
     # compare old and new values
     # sysctl may apply value, but its actual value will be
     # different from requested
     if sysctl_read(name) == value:
         return True
     # False in other cases
     return False
 
 def sysctl_apply(sysctl_dict: dict[str, str], revert: bool = True) -> bool:
     """Apply sysctl values.
 
     Args:
         sysctl_dict (dict[str, str]): dictionary with sysctl keys with values
         revert (bool, optional): Revert to original values if new were not
         applied. Defaults to True.
 
     Returns:
         bool: True if all params configured properly, False in other cases
     """
     # get current values
     sysctl_original: dict[str, str] = {}
     for key_name in sysctl_dict.keys():
         sysctl_original[key_name] = sysctl_read(key_name)
     # apply new values and revert in case one of them was not applied
     for key_name, value in sysctl_dict.items():
         if not sysctl_write(key_name, value):
             if revert:
                 sysctl_apply(sysctl_original, revert=False)
             return False
     # everything applied
     return True
 
 def find_device_file(device):
     """ Recurively search /dev for the given device file and return its full path.
         If no device file was found 'None' is returned """
     from fnmatch import fnmatch
 
     for root, dirs, files in os.walk('/dev'):
         for basename in files:
             if fnmatch(basename, device):
                 return os.path.join(root, basename)
 
     return None
 
 def load_as_module(name: str, path: str):
     import importlib.util
 
     spec = importlib.util.spec_from_file_location(name, path)
     mod = importlib.util.module_from_spec(spec)
     spec.loader.exec_module(mod)
     return mod
 
 def load_as_module_source(name: str, path: str):
     """ Necessary modification of load_as_module for files without *.py
     extension """
     import importlib.util
     from importlib.machinery import SourceFileLoader
 
     loader = SourceFileLoader(name, path)
     spec = importlib.util.spec_from_loader(name, loader)
     mod = importlib.util.module_from_spec(spec)
     spec.loader.exec_module(mod)
     return mod
 
 def get_uptime_seconds():
     """ Returns system uptime in seconds """
     from re import search
     from vyos.utils.file import read_file
 
     data = read_file("/proc/uptime")
     seconds = search(r"([0-9\.]+)\s", data).group(1)
     res  = int(float(seconds))
 
     return res
 
 def get_load_averages():
     """ Returns load averages for 1, 5, and 15 minutes as a dict """
     from re import search
     from vyos.utils.file import read_file
     from vyos.utils.cpu import get_core_count
 
     data = read_file("/proc/loadavg")
     matches = search(r"\s*(?P<one>[0-9\.]+)\s+(?P<five>[0-9\.]+)\s+(?P<fifteen>[0-9\.]+)\s*", data)
 
     core_count = get_core_count()
 
     res = {}
     res[1]  = float(matches["one"]) / core_count
     res[5]  = float(matches["five"]) / core_count
     res[15] = float(matches["fifteen"]) / core_count
 
     return res
+
+def get_secure_boot_state() -> bool:
+    from vyos.utils.process import cmd
+    from vyos.utils.boot import is_uefi_system
+    if not is_uefi_system():
+        return False
+    tmp = cmd('mokutil --sb-state')
+    return bool('enabled' in tmp)
diff --git a/src/etc/sudoers.d/vyos b/src/etc/sudoers.d/vyos
index 63a944f41..67d7babc4 100644
--- a/src/etc/sudoers.d/vyos
+++ b/src/etc/sudoers.d/vyos
@@ -1,60 +1,63 @@
 #
 # VyOS modifications to sudo configuration
 #
 Defaults syslog_goodpri=info
 Defaults env_keep+=VYATTA_*
 
 #
 # Command groups allowed for operator users
 #
 Cmnd_Alias IPTABLES = /sbin/iptables --list -n,\
 		      /sbin/iptables -L -vn,\
                       /sbin/iptables -L * -vn,\
 		      /sbin/iptables -t * -L *, \
                       /sbin/iptables -Z *,\
 		      /sbin/iptables -Z -t nat, \
                       /sbin/iptables -t * -Z *
 Cmnd_Alias IP6TABLES = /sbin/ip6tables -t * -Z *, \
                        /sbin/ip6tables -t * -L *
 Cmnd_Alias CONNTRACK = /usr/sbin/conntrack -L *, \
                        /usr/sbin/conntrack -G *, \
 		       /usr/sbin/conntrack -E *
 Cmnd_Alias IPFLUSH = /sbin/ip route flush cache, \
 		     /sbin/ip route flush cache *,\
 		     /sbin/ip neigh flush to *, \
 		     /sbin/ip neigh flush dev *, \
                      /sbin/ip -f inet6 route flush cache, \
 		     /sbin/ip -f inet6 route flush cache *,\
 		     /sbin/ip -f inet6 neigh flush to *, \
 		     /sbin/ip -f inet6 neigh flush dev *
 Cmnd_Alias ETHTOOL = /sbin/ethtool -p *, \
                      /sbin/ethtool -S *, \
                      /sbin/ethtool -a *, \
                      /sbin/ethtool -c *, \
                      /sbin/ethtool -i *
 Cmnd_Alias DMIDECODE = /usr/sbin/dmidecode
 Cmnd_Alias DISK    = /usr/bin/lsof, /sbin/fdisk -l *, /sbin/sfdisk -d *
 Cmnd_Alias DATE    = /bin/date, /usr/sbin/ntpdate
 Cmnd_Alias PPPOE_CMDS = /sbin/pppd, /sbin/poff, /usr/sbin/pppstats
 Cmnd_Alias PCAPTURE = /usr/bin/tcpdump
 Cmnd_Alias HWINFO   = /usr/bin/lspci
 Cmnd_Alias FORCE_CLUSTER = /usr/share/heartbeat/hb_takeover, \
                            /usr/share/heartbeat/hb_standby
 Cmnd_Alias DIAGNOSTICS = /bin/ip vrf exec * /bin/ping *,       \
                          /bin/ip vrf exec * /bin/traceroute *, \
                          /bin/ip vrf exec * /usr/bin/mtr *, \
                          /usr/libexec/vyos/op_mode/*
 Cmnd_Alias KEA_IP6_ROUTES = /sbin/ip -6 route replace *,\
                            /sbin/ip -6 route del *
 %operator ALL=NOPASSWD: DATE, IPTABLES, ETHTOOL, IPFLUSH, HWINFO, \
 			PPPOE_CMDS, PCAPTURE, /usr/sbin/wanpipemon, \
                         DMIDECODE, DISK, CONNTRACK, IP6TABLES,  \
                         FORCE_CLUSTER, DIAGNOSTICS
 
 # Allow any user to run files in sudo-users
 %users ALL=NOPASSWD: /opt/vyatta/bin/sudo-users/
 
 # Allow members of group sudo to execute any command
 %sudo ALL=NOPASSWD: ALL
 
+# Allow any user to query Machine Owner Key status
+%sudo ALL=NOPASSWD: /usr/bin/mokutil
+
 _kea ALL=NOPASSWD: KEA_IP6_ROUTES
diff --git a/src/op_mode/secure_boot.py b/src/op_mode/secure_boot.py
new file mode 100755
index 000000000..5f6390a15
--- /dev/null
+++ b/src/op_mode/secure_boot.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import vyos.opmode
+
+from vyos.utils.boot import is_uefi_system
+from vyos.utils.system import get_secure_boot_state
+
+def _get_raw_data(name=None):
+    sb_data = {
+        'state' : get_secure_boot_state(),
+        'uefi' : is_uefi_system()
+    }
+    return sb_data
+
+def _get_formatted_output(raw_data):
+    if not raw_data['uefi']:
+        print('System run in legacy BIOS mode!')
+    state = 'enabled' if raw_data['state'] else 'disabled'
+    return f'SecureBoot {state}'
+
+def show(raw: bool):
+    sb_data = _get_raw_data()
+    if raw:
+        return sb_data
+    else:
+        return _get_formatted_output(sb_data)
+
+if __name__ == "__main__":
+    try:
+        res = vyos.opmode.run(sys.modules[__name__])
+        if res:
+            print(res)
+    except (ValueError, vyos.opmode.Error) as e:
+        print(e)
+        sys.exit(1)
diff --git a/src/op_mode/version.py b/src/op_mode/version.py
index 09d69ad1d..71a40dd50 100755
--- a/src/op_mode/version.py
+++ b/src/op_mode/version.py
@@ -1,88 +1,97 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2016-2024 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 # Purpose:
 #    Displays image version and system information.
 #    Used by the "run show version" command.
 
 import sys
 import typing
 
 import vyos.opmode
 import vyos.version
 import vyos.limericks
 
+from vyos.utils.boot import is_uefi_system
+from vyos.utils.system import get_secure_boot_state
+
 from jinja2 import Template
 
 version_output_tmpl = """
 Version:          VyOS {{version}}
 Release train:    {{release_train}}
 Release flavor:   {{flavor}}
 
 Built by:         {{built_by}}
 Built on:         {{built_on}}
 Build UUID:       {{build_uuid}}
 Build commit ID:  {{build_git}}
 {%- if build_comment %}
 Build comment:    {{build_comment}}
 {% endif %}
 
 Architecture:     {{system_arch}}
 Boot via:         {{boot_via}}
 System type:      {{system_type}}
+Secure Boot:      {{secure_boot}}
 
 Hardware vendor:  {{hardware_vendor}}
 Hardware model:   {{hardware_model}}
 Hardware S/N:     {{hardware_serial}}
 Hardware UUID:    {{hardware_uuid}}
 
 Copyright:        VyOS maintainers and contributors
 {%- if limerick %}
 {{limerick}}
 {% endif -%}
 """
 
 def _get_raw_data(funny=False):
     version_data = vyos.version.get_full_version_data()
+    version_data["secure_boot"] = "n/a (BIOS)"
+    if is_uefi_system():
+        version_data["secure_boot"] = "disabled"
+        if get_secure_boot_state():
+            version_data["secure_boot"] = "enabled"
 
     if funny:
         version_data["limerick"] = vyos.limericks.get_random()
 
     return version_data
 
 def _get_formatted_output(version_data):
     tmpl = Template(version_output_tmpl)
     return tmpl.render(version_data).strip()
 
 def show(raw: bool, funny: typing.Optional[bool]):
     """ Display neighbor table contents """
     version_data = _get_raw_data(funny=funny)
 
     if raw:
         return version_data
     else:
         return _get_formatted_output(version_data)
 
 
 if __name__ == '__main__':
     try:
         res = vyos.opmode.run(sys.modules[__name__])
         if res:
             print(res)
     except (ValueError, vyos.opmode.Error) as e:
         print(e)
         sys.exit(1)