Skip to content

Commit a7c9558

Browse files
committed
Fix FIPS enabled cluster operations
Make sure host system is FIPS enabled and proper installer is downloaded
1 parent 5452052 commit a7c9558

File tree

4 files changed

+507
-478
lines changed

4 files changed

+507
-478
lines changed

Diff for: osia/cli.py

+42-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
openshift"""
1818
import argparse
1919
import logging
20-
from typing import List
20+
from typing import List, Tuple, Optional
21+
from subprocess import Popen
22+
from semantic_version import Version, SimpleSpec
2123
import coloredlogs
24+
import distro
2225

2326
from .config.config import ARCH_AMD, ARCH_ARM, ARCH_X86_64, ARCH_AARCH64, ARCH_S390X, ARCH_PPC
2427
from .installer import install_cluster, delete_cluster, storage, download_installer
@@ -84,16 +87,44 @@ def _read_list(in_str: str) -> List[str]:
8487
a['proc'] = _identity
8588

8689

90+
def _check_fips_compatible() -> Tuple[bool, Optional[str]]:
91+
rhel_version = None
92+
if distro.id() == "rhel":
93+
rhel_version = distro.major_version()
94+
95+
if not rhel_version:
96+
return False, "FIPS installs are supported only from RHEL systems"
97+
try:
98+
with Popen(["fips-mode-setup", "--check"]) as proc:
99+
proc.wait()
100+
if proc.returncode != 0:
101+
return False, "FIPS is not enabled on the system"
102+
except FileNotFoundError:
103+
return False, "fips-mode-setup must be installed on the system"
104+
105+
return True, None
106+
107+
87108
def _resolve_installer(from_args):
88109
if from_args.installer is None and from_args.installer_version is None:
89110
raise Exception('Either installer or installer-version must be passed')
90111
if from_args.installer:
91112
return from_args.installer
92113

114+
rhel_version = None
115+
if distro.id() == "rhel":
116+
rhel_version = distro.major_version()
117+
118+
if from_args.enable_fips and Version.coerce(
119+
from_args.installer_version.split("-")[-1]) in SimpleSpec("<4.16"):
120+
from_args.enable_fips = False
121+
93122
return download_installer(from_args.installer_version,
94123
from_args.installer_arch,
95124
from_args.installers_dir,
96-
from_args.installer_source)
125+
from_args.installer_source,
126+
rhel_version=rhel_version,
127+
fips=from_args.enable_fips)
97128

98129

99130
def _merge_dictionaries(from_args):
@@ -106,7 +137,13 @@ def _merge_dictionaries(from_args):
106137

107138

108139
def _exec_install_cluster(args):
140+
if args.enable_fips:
141+
supported, msg = _check_fips_compatible()
142+
if not supported:
143+
raise Exception(msg)
144+
109145
conf = _merge_dictionaries(args)
146+
110147
if not args.skip_git:
111148
storage.check_repository()
112149
logging.info('Starting the installer with cloud name %s', conf['cloud_name'])
@@ -122,6 +159,9 @@ def _exec_install_cluster(args):
122159

123160

124161
def _exec_delete_cluster(args):
162+
# cleanup of fips cluster can be done from anywhere
163+
args.enable_fips = None
164+
125165
conf = _merge_dictionaries(args)
126166

127167
if not args.skip_git:

Diff for: osia/installer/downloader/install.py

+49-21
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,20 @@
2828
import requests
2929

3030
from bs4 import BeautifulSoup
31+
from semantic_version import Version, SimpleSpec
3132
from .utils import get_data
3233

3334

3435
PROD_ROOT = "http://mirror.openshift.com/pub/openshift-v4/{}/clients/ocp/"
3536
BUILD_ROOT = "https://openshift-release-artifacts.svc.ci.openshift.org/"
3637
PREVIEW_ROOT = "http://mirror.openshift.com/pub/openshift-v4/{}/clients/ocp-dev-preview/"
3738

38-
VERSION_RE = re.compile(r"^openshift-install-(?P<platform>\w+)"
39-
r"(-(?P<architecture>\w+))?-(?P<version>\d+.*)\.tar\.gz")
39+
VERSION_RE = re.compile(r"^openshift-install(-rhel(?P<rhel>\d+))?(-(?P<platform>(linux|mac)))?"
40+
r"(-(?P<architecture>\w+))?(-(?P<version>\d+.*))?\.tar\.gz")
4041
EXTRACTION_RE = re.compile(r'.*Extracting tools for .*, may take up to a minute.*')
4142

4243

43-
def _current_platform():
44+
def _current_platform() -> Tuple[str, str]:
4445
if platform.system() == "Linux" and platform.machine() == "x86_64":
4546
return "linux", "amd64"
4647
if platform.system() == "Linux" and platform.machine() == "arm64":
@@ -53,10 +54,14 @@ def _current_platform():
5354
raise Exception(f"Unrecognized platform {platform.system()} {platform.machine()}")
5455

5556

56-
def get_url(directory: str, arch: str) -> Tuple[Optional[str], Optional[str]]:
57+
def get_url(directory: str, arch: str, fips: bool = False,
58+
rhel_version: str = None) -> Tuple[Optional[str], Optional[str]]:
5759
"""Searches the http directory and returns both url to installer
5860
and version.
5961
"""
62+
if fips and not rhel_version:
63+
raise Exception("Rhel version was not detected. Please download installer separatly.")
64+
6065
logging.debug('Url for installers look-up %s', directory)
6166
lst = requests.get(directory, allow_redirects=True)
6267
tree = BeautifulSoup(lst.content, 'html.parser')
@@ -66,16 +71,29 @@ def get_url(directory: str, arch: str) -> Tuple[Optional[str], Optional[str]]:
6671
for k in links:
6772
logging.debug('Parsing link: %s', k.get('href'))
6873
match = VERSION_RE.match(k.get('href'))
69-
if match and match.group('platform') == os_name:
70-
if (local_arch == match.group('architecture')) \
71-
or (local_arch == arch and not match.group('architecture')):
72-
installer = lst.url + k.get('href')
74+
75+
if match:
76+
if match.group("version"):
7377
version = match.group('version')
78+
79+
if fips and match.group("rhel") == rhel_version:
80+
installer = lst.url + k.get('href')
7481
break
82+
83+
if not fips and match.group('platform') == os_name:
84+
if (local_arch == match.group('architecture')) \
85+
or (local_arch == arch and not match.group('architecture')):
86+
installer = lst.url + k.get('href')
87+
break
88+
else:
89+
if fips:
90+
raise Exception(f"FIPS Installer not found for {rhel_version=}")
91+
raise Exception("Installer not found")
7592
return installer, version
7693

7794

78-
def get_devel_url(version: str, arch: str) -> Tuple[Optional[str], Optional[str]]:
95+
def get_devel_url(version: str, arch: str, fips: bool = False,
96+
rhel_version: str = None) -> Tuple[Optional[str], Optional[str]]:
7997
"""
8098
Searches developement sources and returns url to installer
8199
"""
@@ -88,17 +106,19 @@ def get_devel_url(version: str, arch: str) -> Tuple[Optional[str], Optional[str]
88106
req = requests.get(BUILD_ROOT + version, allow_redirects=True)
89107
ast = BeautifulSoup(req.content, 'html.parser')
90108
logging.debug('Installer found on page, continuing')
91-
return get_url(req.url, arch)
109+
return get_url(req.url, arch, fips, rhel_version)
92110

93111

94-
def get_prev_url(version: str, arch: str) -> Tuple[Optional[str], Optional[str]]:
112+
def get_prev_url(version: str, arch: str, fips: bool = False,
113+
rhel_version: str = None) -> Tuple[Optional[str], Optional[str]]:
95114
"""Returns installer url from dev-preview sources"""
96-
return get_url(PREVIEW_ROOT.format(arch) + version + "/", arch)
115+
return get_url(PREVIEW_ROOT.format(arch) + version + "/", arch, fips, rhel_version)
97116

98117

99-
def get_prod_url(version: str, arch: str) -> Tuple[Optional[str], Optional[str]]:
118+
def get_prod_url(version: str, arch: str, fips: bool = False,
119+
rhel_version: str = None) -> Tuple[Optional[str], Optional[str]]:
100120
"""Returns installer url from production sources"""
101-
return get_url(PROD_ROOT.format(arch) + version + "/", arch)
121+
return get_url(PROD_ROOT.format(arch) + version + "/", arch, fips, rhel_version)
102122

103123

104124
def _get_storage_path(version: str, install_base: str) -> str:
@@ -114,12 +134,12 @@ def _extract_tar(buffer: NamedTemporaryFile, target: str) -> Path:
114134
with tarfile.open(buffer.name) as tar:
115135
inst_info = None
116136
for i in tar.getmembers():
117-
if i.name == 'openshift-install':
137+
if i.name in ['openshift-install', 'openshift-install-fips']:
118138
inst_info = i
119139
if inst_info is None:
120140
raise Exception("error")
121141
stream = tar.extractfile(inst_info)
122-
result = Path(target).joinpath('openshift-install')
142+
result = Path(target).joinpath(inst_info.name)
123143
with result.open('wb') as output:
124144
copyfileobj(stream, output)
125145
result.chmod(result.stat().st_mode | stat.S_IXUSR)
@@ -131,10 +151,13 @@ def get_installer(tar_url: str, target: str):
131151
return get_data(tar_url, target, _extract_tar)
132152

133153

154+
# pylint: disable=too-many-arguments
134155
def download_installer(installer_version: str,
135156
installer_arch: str,
136157
dest_directory: str,
137-
source: str) -> str:
158+
source: str,
159+
fips: bool = False,
160+
rhel_version: str = None) -> str:
138161
"""Starts search and extraction of installer"""
139162
logging.debug("Getting version %s of %s, storing to directory %s and devel is %r",
140163
installer_version, installer_arch, dest_directory, source)
@@ -149,12 +172,17 @@ def download_installer(installer_version: str,
149172
else:
150173
raise Exception("Error for source profile " + source)
151174

152-
url, version = downloader(installer_version, installer_arch)
175+
url, version = downloader(installer_version, installer_arch, fips, rhel_version)
153176
logging.debug('Installer\'s URL is %s and full version is %s', url, version)
154177
root = Path(dest_directory).joinpath(version)
155178

156-
if root.exists() and root.joinpath('openshift-install').exists():
179+
installer_exe_name = 'openshift-install'
180+
181+
if fips:
182+
installer_exe_name = 'openshift-install-fips'
183+
184+
if root.exists() and root.joinpath(installer_exe_name).exists():
157185
logging.info('Found installer at %s', root.as_posix())
158-
return root.joinpath('openshift-install').as_posix()
159-
root.mkdir(parents=True)
186+
return root.joinpath(installer_exe_name).as_posix()
187+
root.mkdir(parents=True, exist_ok=True)
160188
return get_installer(url, root.as_posix())

0 commit comments

Comments
 (0)