Skip to content

Commit d6ca440

Browse files
authored
Merge pull request #83 from mdujava/enable_fips
Fix FIPS enabled cluster operations
2 parents 442a6c1 + 4562d61 commit d6ca440

File tree

4 files changed

+99
-24
lines changed

4 files changed

+99
-24
lines changed

Diff for: osia/cli.py

+38-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,46 @@ def _read_list(in_str: str) -> List[str]:
8487
a['proc'] = _identity
8588

8689

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

110+
rhel_version = None
111+
if distro.id() == "rhel":
112+
rhel_version = distro.major_version()
113+
114+
if from_args.enable_fips:
115+
supported, msg = _check_fips_compatible(rhel_version)
116+
if not supported:
117+
raise Exception(msg)
118+
119+
if Version.coerce(from_args.installer_version.split("-")[-1]) in SimpleSpec("<4.16"):
120+
# ocp <4.16 does not have dedicated openshift-install-fips, it is
121+
# fine to run normal installer on FIPS enabled RHEL
122+
from_args.enable_fips = False
123+
93124
return download_installer(from_args.installer_version,
94125
from_args.installer_arch,
95126
from_args.installers_dir,
96-
from_args.installer_source)
127+
from_args.installer_source,
128+
rhel_version=rhel_version,
129+
fips=from_args.enable_fips)
97130

98131

99132
def _merge_dictionaries(from_args):
@@ -122,6 +155,9 @@ def _exec_install_cluster(args):
122155

123156

124157
def _exec_delete_cluster(args):
158+
# cleanup of fips cluster can be done from anywhere
159+
args.enable_fips = None
160+
125161
conf = _merge_dictionaries(args)
126162

127163
if not args.skip_git:

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

+48-21
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@
3535
BUILD_ROOT = "https://openshift-release-artifacts.svc.ci.openshift.org/"
3636
PREVIEW_ROOT = "http://mirror.openshift.com/pub/openshift-v4/{}/clients/ocp-dev-preview/"
3737

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

4242

43-
def _current_platform():
43+
def _current_platform() -> Tuple[str, str]:
4444
if platform.system() == "Linux" and platform.machine() == "x86_64":
4545
return "linux", "amd64"
4646
if platform.system() == "Linux" and (
@@ -54,10 +54,14 @@ def _current_platform():
5454
raise Exception(f"Unrecognized platform {platform.system()} {platform.machine()}")
5555

5656

57-
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]]:
5859
"""Searches the http directory and returns both url to installer
5960
and version.
6061
"""
62+
if fips and not rhel_version:
63+
raise Exception("Rhel version was not detected. Please download installer separatly.")
64+
6165
logging.debug('Url for installers look-up %s', directory)
6266
lst = requests.get(directory, allow_redirects=True)
6367
tree = BeautifulSoup(lst.content, 'html.parser')
@@ -67,16 +71,29 @@ def get_url(directory: str, arch: str) -> Tuple[Optional[str], Optional[str]]:
6771
for k in links:
6872
logging.debug('Parsing link: %s', k.get('href'))
6973
match = VERSION_RE.match(k.get('href'))
70-
if match and match.group('platform') == os_name:
71-
if (local_arch == match.group('architecture')) \
72-
or (local_arch == arch and not match.group('architecture')):
73-
installer = lst.url + k.get('href')
74+
75+
if match:
76+
if match.group("version"):
7477
version = match.group('version')
78+
79+
if fips and match.group("rhel") == rhel_version:
80+
installer = lst.url + k.get('href')
7581
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")
7692
return installer, version
7793

7894

79-
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]]:
8097
"""
8198
Searches developement sources and returns url to installer
8299
"""
@@ -89,17 +106,19 @@ def get_devel_url(version: str, arch: str) -> Tuple[Optional[str], Optional[str]
89106
req = requests.get(BUILD_ROOT + version, allow_redirects=True)
90107
ast = BeautifulSoup(req.content, 'html.parser')
91108
logging.debug('Installer found on page, continuing')
92-
return get_url(req.url, arch)
109+
return get_url(req.url, arch, fips, rhel_version)
93110

94111

95-
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]]:
96114
"""Returns installer url from dev-preview sources"""
97-
return get_url(PREVIEW_ROOT.format(arch) + version + "/", arch)
115+
return get_url(PREVIEW_ROOT.format(arch) + version + "/", arch, fips, rhel_version)
98116

99117

100-
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]]:
101120
"""Returns installer url from production sources"""
102-
return get_url(PROD_ROOT.format(arch) + version + "/", arch)
121+
return get_url(PROD_ROOT.format(arch) + version + "/", arch, fips, rhel_version)
103122

104123

105124
def _get_storage_path(version: str, install_base: str) -> str:
@@ -115,12 +134,12 @@ def _extract_tar(buffer: NamedTemporaryFile, target: str) -> Path:
115134
with tarfile.open(buffer.name) as tar:
116135
inst_info = None
117136
for i in tar.getmembers():
118-
if i.name == 'openshift-install':
137+
if i.name in ['openshift-install', 'openshift-install-fips']:
119138
inst_info = i
120139
if inst_info is None:
121140
raise Exception("error")
122141
stream = tar.extractfile(inst_info)
123-
result = Path(target).joinpath('openshift-install')
142+
result = Path(target).joinpath(inst_info.name)
124143
with result.open('wb') as output:
125144
copyfileobj(stream, output)
126145
result.chmod(result.stat().st_mode | stat.S_IXUSR)
@@ -132,10 +151,13 @@ def get_installer(tar_url: str, target: str):
132151
return get_data(tar_url, target, _extract_tar)
133152

134153

154+
# pylint: disable=too-many-arguments
135155
def download_installer(installer_version: str,
136156
installer_arch: str,
137157
dest_directory: str,
138-
source: str) -> str:
158+
source: str,
159+
fips: bool = False,
160+
rhel_version: str = None) -> str:
139161
"""Starts search and extraction of installer"""
140162
logging.debug("Getting version %s of %s, storing to directory %s and devel is %r",
141163
installer_version, installer_arch, dest_directory, source)
@@ -150,12 +172,17 @@ def download_installer(installer_version: str,
150172
else:
151173
raise Exception("Error for source profile " + source)
152174

153-
url, version = downloader(installer_version, installer_arch)
175+
url, version = downloader(installer_version, installer_arch, fips, rhel_version)
154176
logging.debug('Installer\'s URL is %s and full version is %s', url, version)
155177
root = Path(dest_directory).joinpath(version)
156178

157-
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():
158185
logging.info('Found installer at %s', root.as_posix())
159-
return root.joinpath('openshift-install').as_posix()
160-
root.mkdir(parents=True)
186+
return root.joinpath(installer_exe_name).as_posix()
187+
root.mkdir(parents=True, exist_ok=True)
161188
return get_installer(url, root.as_posix())

Diff for: poetry.lock

+12-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ beautifulsoup4 = "*"
2727
boto3 = "*"
2828
coloredlogs = "*"
2929
dynaconf = {extras = ["yaml"], version = "*"}
30+
distro = "*"
3031
gitpython = "*"
3132
jinja2 = "*"
3233
openstacksdk = "*"

0 commit comments

Comments
 (0)