756 lines
26 KiB
Python
756 lines
26 KiB
Python
import logging
|
|
import os
|
|
import re
|
|
from itertools import groupby
|
|
from typing import List, Optional, Tuple
|
|
|
|
from uaclient import api, apt, event_logger, exceptions, messages, system, util
|
|
from uaclient.clouds.identity import NoCloudTypeReason, get_cloud_type
|
|
from uaclient.entitlements import repo
|
|
from uaclient.entitlements.base import EntitlementWithMessage
|
|
from uaclient.entitlements.entitlement_status import ApplicationStatus
|
|
from uaclient.files import notices
|
|
from uaclient.files.notices import Notice
|
|
from uaclient.files.state_files import (
|
|
ServicesOnceEnabledData,
|
|
services_once_enabled_file,
|
|
)
|
|
from uaclient.types import ( # noqa: F401
|
|
MessagingOperations,
|
|
MessagingOperationsDict,
|
|
StaticAffordance,
|
|
)
|
|
|
|
event = event_logger.get_event_logger()
|
|
LOG = logging.getLogger(util.replace_top_level_logger_name(__name__))
|
|
|
|
CONDITIONAL_PACKAGES_EVERYWHERE = [
|
|
"strongswan",
|
|
"strongswan-hmac",
|
|
"openssh-client",
|
|
"openssh-server",
|
|
"shim-signed",
|
|
]
|
|
CONDITIONAL_PACKAGES_OPENSSH_HMAC = [
|
|
"openssh-client-hmac",
|
|
"openssh-server-hmac",
|
|
]
|
|
FIPS_CONDITIONAL_PACKAGES = {
|
|
"xenial": CONDITIONAL_PACKAGES_EVERYWHERE
|
|
+ CONDITIONAL_PACKAGES_OPENSSH_HMAC,
|
|
"bionic": CONDITIONAL_PACKAGES_EVERYWHERE
|
|
+ CONDITIONAL_PACKAGES_OPENSSH_HMAC,
|
|
"focal": CONDITIONAL_PACKAGES_EVERYWHERE,
|
|
}
|
|
|
|
|
|
# In containers, we don't install the ubuntu-fips
|
|
# metapackage, but we do want to auto-upgrade any
|
|
# fips related packages that are already installed.
|
|
# These lists need to be kept up to date with the
|
|
# Depends of ubuntu-fips.
|
|
# Note that these lists only include those packages
|
|
# that are relevant for a container to upgrade
|
|
# after enabling fips or fips-updates.
|
|
UBUNTU_FIPS_METAPACKAGE_DEPENDS_XENIAL = [
|
|
"openssl",
|
|
"libssl1.0.0",
|
|
"libssl1.0.0-hmac",
|
|
]
|
|
UBUNTU_FIPS_METAPACKAGE_DEPENDS_BIONIC = [
|
|
"openssl",
|
|
"libssl1.1",
|
|
"libssl1.1-hmac",
|
|
"libgcrypt20",
|
|
"libgcrypt20-hmac",
|
|
]
|
|
UBUNTU_FIPS_METAPACKAGE_DEPENDS_FOCAL = [
|
|
"openssl",
|
|
"libssl1.1",
|
|
"libssl1.1-hmac",
|
|
"libgcrypt20",
|
|
"libgcrypt20-hmac",
|
|
]
|
|
FIPS_CONTAINER_CONDITIONAL_PACKAGES = {
|
|
"xenial": CONDITIONAL_PACKAGES_EVERYWHERE
|
|
+ CONDITIONAL_PACKAGES_OPENSSH_HMAC
|
|
+ UBUNTU_FIPS_METAPACKAGE_DEPENDS_XENIAL,
|
|
"bionic": CONDITIONAL_PACKAGES_EVERYWHERE
|
|
+ CONDITIONAL_PACKAGES_OPENSSH_HMAC
|
|
+ UBUNTU_FIPS_METAPACKAGE_DEPENDS_BIONIC,
|
|
"focal": CONDITIONAL_PACKAGES_EVERYWHERE
|
|
+ UBUNTU_FIPS_METAPACKAGE_DEPENDS_FOCAL,
|
|
}
|
|
|
|
|
|
class FIPSCommonEntitlement(repo.RepoEntitlement):
|
|
repo_pin_priority = 1001
|
|
repo_key_file = "ubuntu-pro-fips.gpg" # Same for fips & fips-updates
|
|
FIPS_PROC_FILE = "/proc/sys/crypto/fips_enabled"
|
|
pre_enable_msg = messages.PROMPT_FIPS_PRE_ENABLE
|
|
supports_access_only = True
|
|
|
|
# RELEASE_BLOCKER GH: #104, don't prompt for conf differences in FIPS
|
|
# Review this fix to see if we want more general functionality for all
|
|
# services. And security/CPC signoff on expected conf behavior.
|
|
apt_noninteractive = True
|
|
|
|
help_doc_url = messages.urls.FIPS_HOME_PAGE
|
|
|
|
fips_pro_package_holds = [
|
|
"fips-initramfs",
|
|
"fips-initramfs-generic",
|
|
"libgcrypt20",
|
|
"libgcrypt20-hmac",
|
|
"libgmp10",
|
|
"libgnutls30",
|
|
"libhogweed6",
|
|
"libnettle8",
|
|
"libssl1.0.0",
|
|
"libssl1.0.0-hmac",
|
|
"libssl1.0.0",
|
|
"libssl1.0.0-hmac",
|
|
"libssl1.1",
|
|
"libssl1.1-hmac",
|
|
"libssl3",
|
|
"linux-fips",
|
|
"openssh-client",
|
|
"openssh-client-hmac",
|
|
"openssh-server",
|
|
"openssh-server-hmac",
|
|
"openssl",
|
|
"openssl-fips-module-3",
|
|
"shim-signed",
|
|
"strongswan",
|
|
"strongswan-hmac",
|
|
"ubuntu-fips",
|
|
"ubuntu-aws-fips",
|
|
"ubuntu-azure-fips",
|
|
"ubuntu-gcp-fips",
|
|
]
|
|
|
|
@property
|
|
def messaging(self) -> MessagingOperationsDict:
|
|
pre_enable = None # type: Optional[MessagingOperations]
|
|
post_enable = None # type: Optional[MessagingOperations]
|
|
|
|
if not self.access_only:
|
|
if system.is_container():
|
|
pre_enable_prompt = (
|
|
messages.PROMPT_FIPS_CONTAINER_PRE_ENABLE.format(
|
|
title=self.title
|
|
)
|
|
)
|
|
if not self.auto_upgrade_all_on_enable():
|
|
post_enable = [messages.FIPS_RUN_APT_UPGRADE]
|
|
else:
|
|
pre_enable_prompt = self.pre_enable_msg
|
|
pre_enable = [
|
|
(
|
|
util.prompt_for_confirmation,
|
|
{"msg": pre_enable_prompt},
|
|
)
|
|
]
|
|
|
|
pre_disable = None # type: Optional[MessagingOperations]
|
|
if not self.purge and not self.access_only:
|
|
pre_disable = [
|
|
(
|
|
util.prompt_for_confirmation,
|
|
{
|
|
"msg": messages.PROMPT_FIPS_PRE_DISABLE.format(
|
|
title=self.title
|
|
),
|
|
},
|
|
)
|
|
]
|
|
|
|
pre_install = (
|
|
[
|
|
(
|
|
self.prompt_if_kernel_downgrade,
|
|
{},
|
|
)
|
|
]
|
|
if not self.access_only
|
|
else None
|
|
) # type: Optional[MessagingOperations]
|
|
|
|
messaging = {
|
|
"pre_enable": pre_enable,
|
|
"pre_install": pre_install,
|
|
"post_enable": post_enable,
|
|
"pre_disable": pre_disable,
|
|
} # type: MessagingOperationsDict
|
|
|
|
if len(self.packages) == 1:
|
|
# that is the kernel "ubuntu-fips" or "ubuntu-${cloud}-fips"
|
|
ubuntu_fips_package_name = self.packages[0]
|
|
ubuntu_fips_package_flavor_match = re.match(
|
|
"ubuntu-([a-z]+)-fips", ubuntu_fips_package_name
|
|
)
|
|
if ubuntu_fips_package_flavor_match:
|
|
ubuntu_fips_package_flavor = (
|
|
ubuntu_fips_package_flavor_match.group(1)
|
|
)
|
|
else:
|
|
ubuntu_fips_package_flavor = "generic"
|
|
current_flavor = system.get_kernel_info().flavor
|
|
if ubuntu_fips_package_flavor != current_flavor:
|
|
pre_enable = messaging.get("pre_enable") or []
|
|
msg = messages.KERNEL_FLAVOR_CHANGE_WARNING_PROMPT.format(
|
|
variant=ubuntu_fips_package_flavor,
|
|
service=self.name,
|
|
base_flavor=ubuntu_fips_package_flavor,
|
|
current_flavor=current_flavor or "unknown",
|
|
)
|
|
pre_enable.append(
|
|
(
|
|
util.prompt_for_confirmation,
|
|
{"msg": msg},
|
|
)
|
|
)
|
|
messaging["pre_enable"] = pre_enable
|
|
|
|
return messaging
|
|
|
|
@property
|
|
def conditional_packages(self):
|
|
"""
|
|
Dictionary of conditional packages to be installed when
|
|
enabling FIPS services. For example, if we are enabling
|
|
FIPS services in a machine that has openssh-client installed,
|
|
we will perform two actions:
|
|
|
|
1. Upgrade the package to the FIPS version
|
|
2. Install the corresponding hmac version of that package
|
|
when available.
|
|
"""
|
|
series = system.get_release_info().series
|
|
|
|
if system.is_container():
|
|
return FIPS_CONTAINER_CONDITIONAL_PACKAGES.get(series, [])
|
|
|
|
return FIPS_CONDITIONAL_PACKAGES.get(series, [])
|
|
|
|
def prompt_if_kernel_downgrade(self, *, assume_yes: bool) -> bool:
|
|
"""Check if installing a FIPS kernel will downgrade the kernel
|
|
and prompt for confirmation if it will.
|
|
"""
|
|
# Prior to installing packages, check if the kernel is being downgraded
|
|
# and if so verify that the user wants to continue
|
|
our_full_kernel_str = (
|
|
system.get_kernel_info().proc_version_signature_version
|
|
)
|
|
if our_full_kernel_str is None:
|
|
LOG.warning("Cannot gather kernel information")
|
|
return False
|
|
our_m = re.search(
|
|
r"(?P<kernel_version>\d+\.\d+\.\d+)", our_full_kernel_str
|
|
)
|
|
# This relies on the fact that fips has a single metapackage in
|
|
# additionalPackages. If more packages are added to additionalPackages
|
|
# for some reason, we fall back to "ubuntu-fips".
|
|
fips_package = "ubuntu-fips"
|
|
if len(self.packages) == 1:
|
|
fips_package = self.packages[0]
|
|
|
|
# We are interested in the actual kernel version
|
|
fips_kernel_version_str = apt.get_pkg_candidate_version(
|
|
fips_package.replace("ubuntu", "linux")
|
|
)
|
|
if our_m is not None and fips_kernel_version_str is not None:
|
|
our_kernel_version_str = our_m.group("kernel_version")
|
|
LOG.debug(
|
|
"Kernel information: cur='%s' and fips='%s'",
|
|
our_full_kernel_str,
|
|
fips_kernel_version_str,
|
|
)
|
|
if (
|
|
apt.version_compare(
|
|
fips_kernel_version_str, our_kernel_version_str
|
|
)
|
|
< 0
|
|
):
|
|
event.info(
|
|
messages.KERNEL_DOWNGRADE_WARNING.format(
|
|
current_version=our_kernel_version_str,
|
|
new_version=fips_kernel_version_str,
|
|
)
|
|
)
|
|
return util.prompt_for_confirmation(
|
|
msg=messages.PROMPT_YES_NO, assume_yes=assume_yes
|
|
)
|
|
else:
|
|
LOG.warning(
|
|
"Cannot gather kernel information for '%s' and '%s'",
|
|
our_full_kernel_str,
|
|
fips_kernel_version_str,
|
|
)
|
|
return True
|
|
|
|
def hardcoded_install_conditional_packages(
|
|
self, progress: api.ProgressWrapper
|
|
):
|
|
# Any conditional packages should still be installed, but if
|
|
# they fail to install we should not block the enable operation.
|
|
desired_packages = [] # type: List[str]
|
|
installed_packages = apt.get_installed_packages_names()
|
|
pkg_groups = groupby(
|
|
sorted(self.conditional_packages),
|
|
key=lambda pkg_name: pkg_name.replace("-hmac", ""),
|
|
)
|
|
|
|
for pkg_name, pkg_list in pkg_groups:
|
|
if pkg_name in installed_packages:
|
|
desired_packages += pkg_list
|
|
|
|
for pkg in desired_packages:
|
|
try:
|
|
apt.run_apt_install_command(
|
|
packages=[pkg],
|
|
override_env_vars={"DEBIAN_FRONTEND": "noninteractive"},
|
|
apt_options=[
|
|
"--allow-downgrades",
|
|
'-o Dpkg::Options::="--force-confdef"',
|
|
'-o Dpkg::Options::="--force-confold"',
|
|
],
|
|
)
|
|
except exceptions.UbuntuProError:
|
|
progress.emit(
|
|
"info",
|
|
messages.FIPS_PACKAGE_NOT_AVAILABLE.format(
|
|
service=self.title, pkg=pkg
|
|
),
|
|
)
|
|
|
|
def auto_upgrade_all_on_enable(self) -> bool:
|
|
install_all_updates_override = util.is_config_value_true(
|
|
config=self.cfg.cfg, path_to_value="features.fips_auto_upgrade_all"
|
|
)
|
|
# jammy onward automatically uses new auto-upgrade logic
|
|
hardcoded_release = system.get_release_info().series in {
|
|
"xenial",
|
|
"bionic",
|
|
"focal",
|
|
}
|
|
return install_all_updates_override or not hardcoded_release
|
|
|
|
def install_all_available_fips_upgrades(
|
|
self, progress: api.ProgressWrapper
|
|
):
|
|
to_upgrade = [
|
|
package.name
|
|
for package in apt.get_installed_packages_with_uninstalled_candidate_in_origin( # noqa: E501
|
|
self.origin
|
|
)
|
|
]
|
|
|
|
# See GH #3390
|
|
if system.get_release_info().series == "jammy":
|
|
to_upgrade.append("openssl-fips-module-3")
|
|
|
|
to_upgrade.sort()
|
|
if len(to_upgrade) > 0:
|
|
try:
|
|
progress.emit(
|
|
"info",
|
|
messages.INSTALLING_PACKAGES.format(
|
|
packages=" ".join(to_upgrade)
|
|
),
|
|
)
|
|
self.unhold_packages(to_upgrade)
|
|
apt.run_apt_install_command(
|
|
packages=to_upgrade,
|
|
override_env_vars={"DEBIAN_FRONTEND": "noninteractive"},
|
|
apt_options=[
|
|
"--allow-downgrades",
|
|
'-o Dpkg::Options::="--force-confdef"',
|
|
'-o Dpkg::Options::="--force-confold"',
|
|
],
|
|
)
|
|
except exceptions.UbuntuProError:
|
|
progress.emit("info", messages.FIPS_PACKAGES_UPGRADE_FAILURE)
|
|
|
|
def install_packages(
|
|
self,
|
|
progress: api.ProgressWrapper,
|
|
package_list: Optional[List[str]] = None,
|
|
cleanup_on_failure: bool = True,
|
|
) -> None:
|
|
"""Install contract recommended packages for the entitlement.
|
|
|
|
:param package_list: Optional package list to use instead of
|
|
self.packages.
|
|
:param cleanup_on_failure: Cleanup apt files if apt install fails.
|
|
"""
|
|
# We need to guarantee that the metapackage is installed.
|
|
# While the other packages should still be installed, if they
|
|
# fail, we should not block the enable operation.
|
|
mandatory_packages = self.packages
|
|
if mandatory_packages:
|
|
super().install_packages(
|
|
progress,
|
|
package_list=mandatory_packages,
|
|
)
|
|
else:
|
|
# then this won't get printed by install_packages, so do it here
|
|
# instead
|
|
progress.progress(
|
|
messages.INSTALLING_SERVICE_PACKAGES.format(title=self.title)
|
|
)
|
|
|
|
if self.auto_upgrade_all_on_enable():
|
|
self.install_all_available_fips_upgrades(progress)
|
|
else:
|
|
self.hardcoded_install_conditional_packages(progress)
|
|
|
|
if self._check_for_reboot():
|
|
notices.add(
|
|
Notice.FIPS_SYSTEM_REBOOT_REQUIRED,
|
|
)
|
|
|
|
def _check_for_reboot(self) -> bool:
|
|
"""Check if system needs to be rebooted because of this service."""
|
|
return system.should_reboot()
|
|
|
|
def _check_for_reboot_msg(
|
|
self, operation: str, silent: bool = False
|
|
) -> None:
|
|
"""Check if user should be alerted that a reboot must be performed.
|
|
|
|
@param operation: The operation being executed.
|
|
@param silent: Boolean set True to silence print/log of messages
|
|
"""
|
|
reboot_required = self._check_for_reboot()
|
|
event.needs_reboot(reboot_required)
|
|
if reboot_required:
|
|
if not silent:
|
|
event.info(
|
|
messages.ENABLE_REBOOT_REQUIRED_TMPL.format(
|
|
operation=operation
|
|
)
|
|
)
|
|
if operation == "disable operation":
|
|
notices.add(
|
|
Notice.FIPS_DISABLE_REBOOT_REQUIRED,
|
|
)
|
|
|
|
def _allow_fips_on_cloud_instance(
|
|
self, series: str, cloud_id: str
|
|
) -> bool:
|
|
"""Return False when FIPS is allowed on this cloud and series.
|
|
|
|
On Xenial GCP there will be no cloud-optimized kernel so
|
|
block default ubuntu-fips enable. This can be overridden in
|
|
config with features.allow_xenial_fips_on_cloud.
|
|
|
|
GCP doesn't yet have a cloud-optimized kernel or metapackage so
|
|
block enable of fips if the contract does not specify ubuntu-gcp-fips.
|
|
This also can be overridden in config with
|
|
features.allow_default_fips_metapackage_on_gcp.
|
|
|
|
:return: False when this cloud, series or config override allows FIPS.
|
|
"""
|
|
if cloud_id == "gce":
|
|
if util.is_config_value_true(
|
|
config=self.cfg.cfg,
|
|
path_to_value="features.allow_default_fips_metapackage_on_gcp",
|
|
):
|
|
return True
|
|
|
|
# GCE only has FIPS support for bionic and focal machines
|
|
if series in ("bionic", "focal"):
|
|
return True
|
|
|
|
return bool("ubuntu-gcp-fips" in super().packages)
|
|
|
|
return True
|
|
|
|
@property
|
|
def static_affordances(self) -> Tuple[StaticAffordance, ...]:
|
|
cloud_titles = {"aws": "an AWS", "azure": "an Azure", "gce": "a GCP"}
|
|
cloud_id, _ = get_cloud_type()
|
|
if cloud_id is None:
|
|
cloud_id = ""
|
|
|
|
series = system.get_release_info().series
|
|
blocked_message = messages.FIPS_BLOCK_ON_CLOUD.format(
|
|
series=series.title(), cloud=cloud_titles.get(cloud_id)
|
|
)
|
|
return (
|
|
(
|
|
blocked_message,
|
|
lambda: self._allow_fips_on_cloud_instance(series, cloud_id),
|
|
True,
|
|
),
|
|
)
|
|
|
|
@property
|
|
def packages(self) -> List[str]:
|
|
if system.is_container():
|
|
return []
|
|
return super().packages
|
|
|
|
def application_status(
|
|
self,
|
|
) -> Tuple[ApplicationStatus, Optional[messages.NamedMessage]]:
|
|
super_status, super_msg = super().application_status()
|
|
|
|
if system.is_container() and not system.should_reboot():
|
|
notices.remove(
|
|
Notice.FIPS_SYSTEM_REBOOT_REQUIRED,
|
|
)
|
|
return super_status, super_msg
|
|
|
|
if os.path.exists(self.FIPS_PROC_FILE):
|
|
# We are now only removing the notice if there is no reboot
|
|
# required information regarding the fips metapackage we install.
|
|
if not system.should_reboot(set(self.packages)):
|
|
notices.remove(
|
|
Notice.FIPS_SYSTEM_REBOOT_REQUIRED,
|
|
)
|
|
|
|
if system.load_file(self.FIPS_PROC_FILE).strip() == "1":
|
|
notices.remove(
|
|
Notice.FIPS_MANUAL_DISABLE_URL,
|
|
)
|
|
return super_status, super_msg
|
|
else:
|
|
notices.add(
|
|
Notice.FIPS_MANUAL_DISABLE_URL,
|
|
)
|
|
return (
|
|
ApplicationStatus.DISABLED,
|
|
messages.FIPS_PROC_FILE_ERROR.format(
|
|
file_name=self.FIPS_PROC_FILE
|
|
),
|
|
)
|
|
|
|
if super_status != ApplicationStatus.ENABLED:
|
|
return super_status, super_msg
|
|
|
|
installed_packages = apt.get_installed_packages_names()
|
|
missing = []
|
|
# Should be a single package (ubuntu-fips and its cloud variants)
|
|
# or no package at all for containers
|
|
for package in self.packages:
|
|
if package not in installed_packages:
|
|
missing.append(package)
|
|
|
|
if missing:
|
|
return (
|
|
ApplicationStatus.WARNING,
|
|
messages.FIPS_PACKAGES_NOT_INSTALLED.format(
|
|
packages=" ".join(missing), service=self.name
|
|
),
|
|
)
|
|
|
|
return (
|
|
ApplicationStatus.ENABLED,
|
|
messages.FIPS_REBOOT_REQUIRED,
|
|
)
|
|
|
|
def remove_packages(self) -> None:
|
|
"""Remove fips meta package to disable the service.
|
|
|
|
FIPS meta-package will unset grub config options which will deactivate
|
|
FIPS on any related packages.
|
|
"""
|
|
installed_packages = set(apt.get_installed_packages_names())
|
|
fips_metapackage = set(self.packages).difference(
|
|
set(self.conditional_packages)
|
|
)
|
|
remove_packages = fips_metapackage.intersection(installed_packages)
|
|
if remove_packages:
|
|
apt.remove_packages(
|
|
list(fips_metapackage),
|
|
messages.DISABLE_FAILED_TMPL.format(title=self.title),
|
|
)
|
|
|
|
def _perform_enable(self, progress: api.ProgressWrapper) -> bool:
|
|
if super()._perform_enable(progress):
|
|
notices.remove(
|
|
Notice.WRONG_FIPS_METAPACKAGE_ON_CLOUD,
|
|
)
|
|
notices.remove(Notice.FIPS_REBOOT_REQUIRED)
|
|
notices.remove(Notice.FIPS_DISABLE_REBOOT_REQUIRED)
|
|
return True
|
|
|
|
return False
|
|
|
|
def _perform_disable(self, progress: api.ProgressWrapper) -> bool:
|
|
if super()._perform_disable(progress):
|
|
if self._check_for_reboot():
|
|
notices.add(
|
|
Notice.FIPS_DISABLE_REBOOT_REQUIRED,
|
|
)
|
|
return True
|
|
|
|
return False
|
|
|
|
def unhold_packages(self, package_names):
|
|
cmd = ["apt-mark", "showholds"]
|
|
holds = apt.run_apt_command(
|
|
cmd,
|
|
messages.EXECUTING_COMMAND_FAILED.format(command=" ".join(cmd)),
|
|
)
|
|
unholds = []
|
|
for hold in holds.splitlines():
|
|
if hold in package_names:
|
|
unholds.append(hold)
|
|
if unholds:
|
|
unhold_cmd = ["apt-mark", "unhold"] + unholds
|
|
holds = apt.run_apt_command(
|
|
unhold_cmd,
|
|
messages.EXECUTING_COMMAND_FAILED.format(
|
|
command=" ".join(unhold_cmd)
|
|
),
|
|
)
|
|
|
|
def setup_apt_config(self, progress: api.ProgressWrapper) -> None:
|
|
"""Setup apt config based on the resourceToken and directives.
|
|
|
|
FIPS-specifically handle apt-mark unhold
|
|
|
|
:raise UbuntuProError: on failure to setup any aspect of this apt
|
|
configuration
|
|
"""
|
|
self.unhold_packages(self.fips_pro_package_holds)
|
|
super().setup_apt_config(progress)
|
|
|
|
|
|
class FIPSEntitlement(FIPSCommonEntitlement):
|
|
name = "fips"
|
|
title = messages.FIPS_TITLE
|
|
description = messages.FIPS_DESCRIPTION
|
|
help_text = messages.FIPS_HELP_TEXT
|
|
origin = "UbuntuFIPS"
|
|
pre_enable_msg = messages.PROMPT_FIPS_PRE_ENABLE
|
|
|
|
@property
|
|
def incompatible_services(self) -> Tuple[EntitlementWithMessage, ...]:
|
|
from uaclient.entitlements.livepatch import LivepatchEntitlement
|
|
from uaclient.entitlements.realtime import RealtimeKernelEntitlement
|
|
|
|
return (
|
|
EntitlementWithMessage(
|
|
LivepatchEntitlement, messages.LIVEPATCH_INVALIDATES_FIPS
|
|
),
|
|
EntitlementWithMessage(
|
|
FIPSUpdatesEntitlement, messages.FIPS_UPDATES_INVALIDATES_FIPS
|
|
),
|
|
EntitlementWithMessage(
|
|
RealtimeKernelEntitlement, messages.REALTIME_FIPS_INCOMPATIBLE
|
|
),
|
|
)
|
|
|
|
@property
|
|
def static_affordances(self) -> Tuple[StaticAffordance, ...]:
|
|
static_affordances = super().static_affordances
|
|
|
|
fips_updates = FIPSUpdatesEntitlement(cfg=self.cfg)
|
|
disabled_status = ApplicationStatus.DISABLED
|
|
is_fips_updates_enabled = bool(
|
|
fips_updates.application_status()[0] != disabled_status
|
|
)
|
|
|
|
services_once_enabled_obj = services_once_enabled_file.read()
|
|
fips_updates_once_enabled = (
|
|
services_once_enabled_obj.fips_updates
|
|
if services_once_enabled_obj
|
|
else False
|
|
)
|
|
|
|
return static_affordances + (
|
|
(
|
|
messages.FIPS_ERROR_WHEN_FIPS_UPDATES_ENABLED.format(
|
|
fips=self.title, fips_updates=fips_updates.title
|
|
),
|
|
lambda: is_fips_updates_enabled,
|
|
False,
|
|
),
|
|
(
|
|
messages.FIPS_ERROR_WHEN_FIPS_UPDATES_ONCE_ENABLED.format(
|
|
fips=self.title, fips_updates=fips_updates.title
|
|
),
|
|
lambda: fips_updates_once_enabled,
|
|
False,
|
|
),
|
|
)
|
|
|
|
def _perform_enable(self, progress: api.ProgressWrapper) -> bool:
|
|
cloud_type, error = get_cloud_type()
|
|
if cloud_type is None and error == NoCloudTypeReason.CLOUD_ID_ERROR:
|
|
LOG.warning(
|
|
"Could not determine cloud, "
|
|
"defaulting to generic FIPS package."
|
|
)
|
|
event.info(messages.FIPS_COULD_NOT_DETERMINE_CLOUD_DEFAULT_PACKAGE)
|
|
if super()._perform_enable(progress):
|
|
notices.remove(
|
|
Notice.FIPS_INSTALL_OUT_OF_DATE,
|
|
)
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
class FIPSUpdatesEntitlement(FIPSCommonEntitlement):
|
|
name = "fips-updates"
|
|
title = messages.FIPS_UPDATES_TITLE
|
|
origin = "UbuntuFIPSUpdates"
|
|
description = messages.FIPS_UPDATES_DESCRIPTION
|
|
help_text = messages.FIPS_UPDATES_HELP_TEXT
|
|
pre_enable_msg = messages.PROMPT_FIPS_UPDATES_PRE_ENABLE
|
|
|
|
@property
|
|
def incompatible_services(self) -> Tuple[EntitlementWithMessage, ...]:
|
|
from uaclient.entitlements.realtime import RealtimeKernelEntitlement
|
|
|
|
return (
|
|
EntitlementWithMessage(
|
|
FIPSEntitlement, messages.FIPS_INVALIDATES_FIPS_UPDATES
|
|
),
|
|
EntitlementWithMessage(
|
|
RealtimeKernelEntitlement,
|
|
messages.REALTIME_FIPS_UPDATES_INCOMPATIBLE,
|
|
),
|
|
)
|
|
|
|
def _perform_enable(self, progress: api.ProgressWrapper) -> bool:
|
|
if super()._perform_enable(progress=progress):
|
|
services_once_enabled_file.write(
|
|
ServicesOnceEnabledData(fips_updates=True)
|
|
)
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
class FIPSPreviewEntitlement(FIPSEntitlement):
|
|
name = "fips-preview"
|
|
title = messages.FIPS_PREVIEW_TITLE
|
|
description = messages.FIPS_PREVIEW_DESCRIPTION
|
|
help_text = messages.FIPS_PREVIEW_HELP_TEXT
|
|
origin = "UbuntuFIPSPreview"
|
|
pre_enable_msg = messages.PROMPT_FIPS_PREVIEW_PRE_ENABLE
|
|
repo_key_file = "ubuntu-pro-fips-preview.gpg"
|
|
|
|
@property
|
|
def incompatible_services(self) -> Tuple[EntitlementWithMessage, ...]:
|
|
return super().incompatible_services + (
|
|
EntitlementWithMessage(
|
|
FIPSEntitlement, messages.FIPS_INVALIDATES_FIPS_UPDATES
|
|
),
|
|
)
|
|
|
|
def _allow_fips_on_cloud_instance(
|
|
self, series: str, cloud_id: str
|
|
) -> bool:
|
|
# For fips-preview, we should not block the service
|
|
# if there is no FIPS cloud-optimized kernel. That is
|
|
# because this service is intended as an early access for
|
|
# FIPS service, so users should be aware of the risks
|
|
return True
|