Merge ~ahasenack/ubuntu/+source/ubuntu-advantage-tools:v17-fips-updates into ubuntu/+source/ubuntu-advantage-tools:ubuntu/devel

Proposed by Andreas Hasenack
Status: Merged
Merge reported by: Andreas Hasenack
Merged at revision: 25878f5d7e4c18e97cf7118be3440f030fc9adbe
Proposed branch: ~ahasenack/ubuntu/+source/ubuntu-advantage-tools:v17-fips-updates
Merge into: ubuntu/+source/ubuntu-advantage-tools:ubuntu/devel
Diff against target: 779 lines (+437/-49)
14 files modified
.gitignore (+4/-0)
debian/changelog (+12/-0)
modules/apt.sh (+14/-2)
modules/service-esm.sh (+2/-2)
modules/service-fips.sh (+110/-17)
modules/service-livepatch.sh (+1/-1)
modules/service.sh (+6/-6)
modules/utils.sh (+20/-0)
tests/test_esm.py (+2/-2)
tests/test_fips.py (+172/-1)
tests/test_script.py (+12/-0)
tests/testing.py (+16/-3)
ubuntu-advantage (+47/-15)
ubuntu-advantage.1 (+19/-0)
Reviewer Review Type Date Requested Status
Christian Ehrhardt  (community) Approve
Steve Langasek (community) Approve
Canonical Server Core Reviewers Pending
Canonical Server Pending
Review via email: mp+342208@code.launchpad.net

Description of the change

ubuntu-advantage-tools FIPS updates. These are not used in bionic, because fips is not supported in bionic yet, but are needed for an upcoming xenial SRU.

The bug this is fixing is a Feature Freeze Exception request, which was granted provided there are no new package relations introduced, which is correct.

PPA with bionic test packages: https://launchpad.net/~ahasenack/+archive/ubuntu/ua-tools-fips-updates-1759280

sudo add-apt-repository ppa:ahasenack/ua-tools-fips-updates-1759280

To post a comment you must log in.
Revision history for this message
Andreas Hasenack (ahasenack) wrote :

I'll create matching commits for the entries in d/changelog, but there will be remaining commits that won't be mentioned there. d/changelog is not a git log after alll, and this LP branch is not a mirror of the upstream git repo.

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

That is boiling down to a manual mirroring from github from the landed pull requests. The commits and individual pull requests can be seen in https://github.com/CanonicalLtd/ubuntu-advantage-script/commits/master and https://github.com/CanonicalLtd/ubuntu-advantage-script/pulls?q=is%3Apr+is%3Aclosed respectively. Please let me know if you need any other information or have other questions.

Revision history for this message
Steve Langasek (vorlon) wrote :

Reviewing with respect to archive and feature freeze only. Comments inline.

review: Approve
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Some potential improvements inline, but could as well be on the next version being uploaded.
I'll set need info and leave it to you if you want to address now or later.

review: Needs Information
Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Replied to commends from Steve and Christian inline. Thanks guys!

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Upstream issues opened.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

All that was asked was explained and I agree to those you opened new bugs for.
As I said none of these has to be in this upload.

Approve +1

review: Approve
Revision history for this message
Joy Latten (j-latten) wrote :

We were not able to anticipate which fips modules a customer wants, so the enable-fips installs all the certified fips modules. Thus naturally assumed the update-fips should update all of them. Manual install instructions are published and provided for customers that prefer to "customize" their fips installations. See, https://assets.ubuntu.com/v1/f35fe02e-Canonical+FIPS+Installation+Instructions.pdf?utm_source=ubunteu&utm_medium=url_shortner&utm_term=fips-install&utm_campaign=shortner

Given that after initial installation, whether done manually or via enable-fips, a customer may have removed fips modules not desired or only installed those desired, the update-fips perhaps should loop thru and see which fips modules are installed, and only update those. In the special case of an update-fips being called where no fips modules have been installed, the script can just install all the updated fips modules similar to the enable-fips.

If that sounds good, then I can open an issue in github to do that.

In regards to fips updates, both potential and existing customers have indicated they would like "updated" fips modules, that is, they want fips modules with bugfixes and CVEs applied and is ok if these "updated" fips modules are not re-certified. They want the fips code paths and fips approved algorithms. For example, not all the fips approved algorithms are in the corresponding, regular version of ssl. We also found this is what several other major Linux distros do as well. So we are giving customers choice of strict fips compliance with fips certified modules from fips ppa and|or the ability to run in "fips mode" with CVEs+bugfixes on top of the fips-certified modules from fips-update ppa. We decided to include that warning or info message in update-fips to do just that, provide info and warn, just in case. Thus absolving us of any potential liability or misunderstanding. Hopefully, that all seems ok. We are learning as we go.

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Thanks Christian.

I need sponsoring for this package, if you could please push the upload tag and dput the package, thanks.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Tag push and sponsoring done as requested

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.gitignore b/.gitignore
2new file mode 100644
3index 0000000..34972a2
4--- /dev/null
5+++ b/.gitignore
6@@ -0,0 +1,4 @@
7+*.pyc
8+*.pyo
9+__pycache__
10+.tox
11diff --git a/debian/changelog b/debian/changelog
12index bb95c3d..39ea084 100644
13--- a/debian/changelog
14+++ b/debian/changelog
15@@ -1,3 +1,15 @@
16+ubuntu-advantage-tools (17) bionic; urgency=medium
17+
18+ * New upstream release (LP: #1759280):
19+ - Added enable-fips-updates command. This command enables the fips-updates
20+ repository to install updates to FIPS modules. The updated modules from
21+ fips-updates repository are non-certified.
22+ - Add repository pinning for FIPS packages
23+ - Check that all prerequisite packages are installed when enabling FIPS
24+ - Support returning the status for a single service
25+
26+ -- Andreas Hasenack <andreas@canonical.com> Wed, 21 Mar 2018 14:20:04 -0300
27+
28 ubuntu-advantage-tools (16) bionic; urgency=medium
29
30 * d/t/update-motd-run: fix path to the esm motd (LP: #1757490)
31diff --git a/keyrings/ubuntu-fips-updates-keyring.gpg b/keyrings/ubuntu-fips-updates-keyring.gpg
32new file mode 100644
33index 0000000..d00f63f
34Binary files /dev/null and b/keyrings/ubuntu-fips-updates-keyring.gpg differ
35diff --git a/modules/apt.sh b/modules/apt.sh
36index 8a9a2ab..3d73d12 100644
37--- a/modules/apt.sh
38+++ b/modules/apt.sh
39@@ -35,19 +35,31 @@ apt_remove_repo() {
40 _apt_remove_auth "$repo_url"
41 }
42
43+apt_add_repo_pinning() {
44+ local repo_file="$1"
45+ local origin="$2"
46+ local priority="$3"
47+
48+ cat <<EOF >"$repo_file"
49+Package: *
50+Pin: release o=${origin}, n=${SERIES}
51+Pin-Priority: ${priority}
52+EOF
53+}
54+
55 apt_get() {
56 DEBIAN_FRONTEND=noninteractive \
57 apt-get -y -o Dpkg::Options::='--force-confold' "$@"
58 }
59
60-is_package_installed() {
61+apt_is_package_installed() {
62 local package="$1"
63
64 dpkg-query -s "$package" >/dev/null 2>&1
65 }
66
67 # Install a package if the specified file doesn't exist
68-install_package_if_missing_file() {
69+apt_install_package_if_missing_file() {
70 local file="$1"
71 local package="$2"
72
73diff --git a/modules/service-esm.sh b/modules/service-esm.sh
74index 0bbfaa2..de281f8 100644
75--- a/modules/service-esm.sh
76+++ b/modules/service-esm.sh
77@@ -14,8 +14,8 @@ esm_enable() {
78 check_token "$ESM_REPO_URL" "$token"
79 apt_add_repo "$ESM_REPO_LIST" "$ESM_REPO_URL" "$token" \
80 "${KEYRINGS_DIR}/${ESM_REPO_KEY_FILE}"
81- install_package_if_missing_file "$APT_METHOD_HTTPS" apt-transport-https
82- install_package_if_missing_file "$CA_CERTIFICATES" ca-certificates
83+ apt_install_package_if_missing_file "$APT_METHOD_HTTPS" apt-transport-https
84+ apt_install_package_if_missing_file "$CA_CERTIFICATES" ca-certificates
85 echo -n 'Running apt-get update... '
86 check_result apt_get update
87 echo 'Ubuntu ESM repository enabled.'
88diff --git a/modules/service-fips.sh b/modules/service-fips.sh
89index 38c2c6d..4df3872 100644
90--- a/modules/service-fips.sh
91+++ b/modules/service-fips.sh
92@@ -7,6 +7,11 @@ FIPS_SUPPORTED_ARCHS="x86_64 ppc64le s390x"
93 FIPS_REPO_URL="https://private-ppa.launchpad.net/ubuntu-advantage/fips"
94 FIPS_REPO_KEY_FILE="ubuntu-fips-keyring.gpg"
95 FIPS_REPO_LIST=${FIPS_REPO_LIST:-"/etc/apt/sources.list.d/ubuntu-fips-${SERIES}.list"}
96+FIPS_REPO_PREFERENCES=${FIPS_REPO_PREFERENCES:-"/etc/apt/preferences.d/ubuntu-fips-${SERIES}"}
97+FIPS_UPDATES_REPO_URL="https://private-ppa.launchpad.net/ubuntu-advantage/fips-updates"
98+FIPS_UPDATES_REPO_KEY_FILE="ubuntu-fips-updates-keyring.gpg"
99+FIPS_UPDATES_REPO_LIST=${FIPS_UPDATES_REPO_LIST:-"/etc/apt/sources.list.d/ubuntu-fips-updates-${SERIES}.list"}
100+FIPS_UPDATES_REPO_PREFERENCES=${FIPS_UPDATES_REPO_PREFERENCES:-"/etc/apt/preferences.d/ubuntu-fips-updates-${SERIES}"}
101 FIPS_ENABLED_FILE=${FIPS_ENABLED_FILE:-"/proc/sys/crypto/fips_enabled"}
102 if [ "$ARCH" = "s390x" ]; then
103 FIPS_BOOT_CFG=${FIPS_BOOT_CFG:-"/etc/zipl.conf"}
104@@ -15,28 +20,30 @@ else
105 FIPS_BOOT_CFG=${FIPS_BOOT_CFG:-"${FIPS_BOOT_CFG_DIR}/99-fips.cfg"}
106 fi
107 FIPS_HMAC_PACKAGES="openssh-client-hmac openssh-server-hmac libssl1.0.0-hmac \
108- linux-fips strongswan-hmac"
109+ linux-fips strongswan-hmac"
110+FIPS_OTHER_PACKAGES="openssh-client openssh-server openssl libssl1.0.0 \
111+ fips-initramfs strongswan"
112
113 fips_enable() {
114 local token="$1"
115
116- _fips_check_packages_installed || error_exit service_already_enabled
117+ _fips_check_installed || error_exit service_already_enabled
118
119 check_token "$FIPS_REPO_URL" "$token"
120 apt_add_repo "$FIPS_REPO_LIST" "$FIPS_REPO_URL" "$token" \
121 "${KEYRINGS_DIR}/${FIPS_REPO_KEY_FILE}"
122- install_package_if_missing_file "$APT_METHOD_HTTPS" apt-transport-https
123- install_package_if_missing_file "$CA_CERTIFICATES" ca-certificates
124+ apt_add_repo_pinning "$FIPS_REPO_PREFERENCES" \
125+ LP-PPA-ubuntu-advantage-fips 1001
126+ apt_install_package_if_missing_file "$APT_METHOD_HTTPS" apt-transport-https
127+ apt_install_package_if_missing_file "$CA_CERTIFICATES" ca-certificates
128 echo -n 'Running apt-get update... '
129 check_result apt_get update
130 echo 'Ubuntu FIPS PPA repository enabled.'
131
132 # install all the fips packages
133 echo -n 'Installing FIPS packages (this may take a while)... '
134- check_result apt_get install openssh-client openssh-client-hmac \
135- openssh-server openssh-server-hmac openssl libssl1.0.0 \
136- libssl1.0.0-hmac fips-initramfs linux-fips \
137- strongswan strongswan-hmac
138+ # shellcheck disable=SC2086
139+ check_result apt_get install $FIPS_HMAC_PACKAGES $FIPS_OTHER_PACKAGES
140
141 echo "Configuring FIPS... "
142 _fips_configure
143@@ -47,8 +54,73 @@ fips_disable() {
144 not_supported 'Disabling FIPS'
145 }
146
147+fips_updates_enable() {
148+ local token="$1"
149+ local bypass_prompt="$2"
150+
151+ local fips_configured=0
152+ local fips_kernel_version=0
153+ local result=0
154+
155+ _fips_updates_is_enabled || result=$?
156+ if [ $result -eq 0 ]; then
157+ error_msg "FIPS-UPDATES repository is already enabled."
158+ error_exit service_already_enabled
159+ fi
160+
161+ check_token "$FIPS_UPDATES_REPO_URL" "$token"
162+
163+ echo "Installing updates from FIPS-UPDATES repository will take the system out of FIPS compliance."
164+ if [ "$bypass_prompt" -ne 1 ]; then
165+ if ! prompt_user 'Do you want to proceed?'; then
166+ error_msg "Aborting updating FIPS packages..."
167+ return
168+ fi
169+ fi
170+
171+ # add the fips-updates repo if the system is undergoing updates the first time
172+ if [ ! -f "$FIPS_UPDATES_REPO_LIST" ]; then
173+ apt_add_repo "$FIPS_UPDATES_REPO_LIST" "$FIPS_UPDATES_REPO_URL" "$token" \
174+ "${KEYRINGS_DIR}/${FIPS_UPDATES_REPO_KEY_FILE}"
175+ apt_add_repo_pinning "$FIPS_UPDATES_REPO_PREFERENCES" \
176+ LP-PPA-ubuntu-advantage-fips-updates 1001
177+ apt_install_package_if_missing_file "$APT_METHOD_HTTPS" apt-transport-https
178+ apt_install_package_if_missing_file "$CA_CERTIFICATES" ca-certificates
179+ echo -n 'Running apt-get update... '
180+ check_result apt_get update
181+ echo 'Ubuntu FIPS-UPDATES PPA repository enabled.'
182+ fi
183+
184+ # if a fips package is found on the system, assume fips was configured before
185+ # users could be running with fips=0 or fips=1, so just checking package here
186+ if apt_is_package_installed fips-initramfs; then
187+ fips_configured=1
188+ # get the fips kernel version installed here
189+ fips_kernel_version=$(package_version linux-fips)
190+ fi
191+
192+ # update all the fips packages
193+ echo -n 'Updating FIPS packages (this may take a while)... '
194+ # shellcheck disable=SC2086
195+ check_result apt_get install $FIPS_HMAC_PACKAGES $FIPS_OTHER_PACKAGES
196+
197+ # if fips was never configured before and is enabled for the
198+ # first time, configure fips
199+ if [ "$fips_configured" -eq 0 ]; then
200+ echo "Configuring FIPS... "
201+ _fips_configure
202+ fi
203+ echo "Successfully updated FIPS packages."
204+
205+ # show the message to reboot only if fips kernel has undergone an upgrade or fips is
206+ # being configured the first time
207+ if [ "$fips_kernel_version" != "$(package_version linux-fips)" ] || [ "$fips_configured" -eq 0 ]; then
208+ echo "Please reboot into the new FIPS kernel."
209+ fi
210+}
211+
212 fips_is_enabled() {
213- is_package_installed fips-initramfs && [ "$(_fips_enabled_check)" -eq 1 ]
214+ apt_is_package_installed fips-initramfs && [ "$(_fips_enabled_check)" -eq 1 ]
215 }
216
217 fips_validate_token() {
218@@ -80,6 +152,21 @@ fips_check_support() {
219 esac
220 }
221
222+fips_print_status() {
223+ local result=0
224+
225+ _fips_updates_is_enabled || result=$?
226+ if [ $result -eq 0 ]; then
227+ echo "fips-updates (uncertified): enabled"
228+ else
229+ echo "fips-updates (uncertified): disabled"
230+ fi
231+}
232+
233+_fips_updates_is_enabled() {
234+ apt-cache policy | grep -Fq "$FIPS_UPDATES_REPO_URL"
235+}
236+
237 _fips_configure() {
238 local bootdev fips_params result
239
240@@ -118,16 +205,22 @@ _fips_enabled_check() {
241 echo 0
242 }
243
244+_fips_check_installed() {
245+ if ! _fips_check_packages_installed; then
246+ return
247+ fi
248+
249+ if fips_is_enabled; then
250+ error_msg "FIPS is already enabled."
251+ else
252+ error_msg "FIPS is already installed. Please reboot into the FIPS kernel to enable it."
253+ fi
254+ return 1
255+}
256+
257 _fips_check_packages_installed() {
258 local pkg
259 for pkg in $FIPS_HMAC_PACKAGES; do
260- if is_package_installed "$pkg"; then
261- if fips_is_enabled; then
262- error_msg "FIPS is already enabled."
263- else
264- error_msg "FIPS is already installed. Please reboot into the FIPS kernel to enable it."
265- fi
266- return 1
267- fi
268+ apt_is_package_installed "$pkg" || return 1
269 done
270 }
271diff --git a/modules/service-livepatch.sh b/modules/service-livepatch.sh
272index 7b3a5ad..0e35020 100644
273--- a/modules/service-livepatch.sh
274+++ b/modules/service-livepatch.sh
275@@ -81,7 +81,7 @@ _livepatch_validate_token() {
276 }
277
278 _livepatch_install_prereqs() {
279- install_package_if_missing_file "$SNAPD" snapd
280+ apt_install_package_if_missing_file "$SNAPD" snapd
281 if ! snap list canonical-livepatch >/dev/null 2>&1; then
282 echo 'Installing the canonical-livepatch snap.'
283 echo 'This may take a few minutes depending on your bandwidth.'
284diff --git a/modules/service.sh b/modules/service.sh
285index 1ac3243..26dc63e 100644
286--- a/modules/service.sh
287+++ b/modules/service.sh
288@@ -12,8 +12,8 @@ service_enable() {
289 local service="$1"
290 local token="$2"
291
292- _service_check_user
293- _service_check_support "$service"
294+ service_check_user
295+ service_check_support "$service"
296 _service_check_enabled "$service" || error_exit service_already_enabled
297 "${service}_validate_token" "$token" || error_exit invalid_token
298 "${service}_enable" "$token"
299@@ -22,8 +22,8 @@ service_enable() {
300 service_disable() {
301 local service="$1"
302
303- _service_check_user
304- _service_check_support "$service"
305+ service_check_user
306+ service_check_support "$service"
307 _service_check_disabled "$service" || error_exit service_already_disabled
308 shift 1
309 "${service}_disable" "$@"
310@@ -58,14 +58,14 @@ service_print_status() {
311 fi
312 }
313
314-_service_check_user() {
315+service_check_user() {
316 if [ "$(id -u)" -ne 0 ]; then
317 error_msg "This command must be run as root (try using sudo)"
318 error_exit not_root
319 fi
320 }
321
322-_service_check_support() {
323+service_check_support() {
324 local service="$1"
325
326 check_series_arch_supported "$service"
327diff --git a/modules/utils.sh b/modules/utils.sh
328index 1862fb4..5d13057 100644
329--- a/modules/utils.sh
330+++ b/modules/utils.sh
331@@ -20,6 +20,26 @@ error_exit() {
332 exit "${codes[$code]}"
333 }
334
335+# prompt the user to confirm
336+#
337+prompt_user() {
338+ local answer
339+
340+ while true; do
341+ read -r -p "$* [N/y] " answer 2>&1
342+ case "$answer" in
343+ y|Y)
344+ return 0
345+ ;;
346+ ''|n|N)
347+ return 1
348+ ;;
349+ *) echo "Please answer y or n."
350+ ;;
351+ esac
352+ done
353+}
354+
355 check_result() {
356 local result output
357 result=0
358diff --git a/tests/test_esm.py b/tests/test_esm.py
359index 04f73e9..acd0707 100644
360--- a/tests/test_esm.py
361+++ b/tests/test_esm.py
362@@ -20,7 +20,7 @@ class ESMTest(UbuntuAdvantageTest):
363 expected = (
364 'deb https://esm.ubuntu.com/ubuntu precise main\n'
365 '# deb-src https://esm.ubuntu.com/ubuntu precise main\n')
366- self.assertEqual(expected, self.repo_list.read_text())
367+ self.assertEqual(expected, self.esm_repo_list.read_text())
368 self.assertEqual(
369 self.apt_auth_file.read_text(),
370 'machine esm.ubuntu.com/ubuntu/ login user password pass\n')
371@@ -198,7 +198,7 @@ class ESMTest(UbuntuAdvantageTest):
372 process = self.script('disable-esm')
373 self.assertEqual(0, process.returncode)
374 self.assertIn('Ubuntu ESM repository disabled', process.stdout)
375- self.assertFalse(self.repo_list.exists())
376+ self.assertFalse(self.esm_repo_list.exists())
377 # the keyring file is removed
378 keyring_file = self.trusted_gpg_dir / 'ubuntu-esm-keyring.gpg'
379 self.assertFalse(keyring_file.exists())
380diff --git a/tests/test_fips.py b/tests/test_fips.py
381index 5efe325..433c0bf 100644
382--- a/tests/test_fips.py
383+++ b/tests/test_fips.py
384@@ -23,7 +23,12 @@ class FIPSTest(UbuntuAdvantageTest):
385 'fips/ubuntu xenial main\n'
386 '# deb-src https://private-ppa.launchpad.net/'
387 'ubuntu-advantage/fips/ubuntu xenial main\n')
388- self.assertEqual(expected, self.repo_list.read_text())
389+ self.assertEqual(expected, self.fips_repo_list.read_text())
390+ expected = (
391+ 'Package: *\n'
392+ 'Pin: release o=LP-PPA-ubuntu-advantage-fips, n=xenial\n'
393+ 'Pin-Priority: 1001\n')
394+ self.assertEqual(self.fips_repo_preferences.read_text(), expected)
395 self.assertEqual(
396 self.apt_auth_file.read_text(),
397 'machine private-ppa.launchpad.net/ubuntu-advantage/fips/ubuntu/'
398@@ -67,6 +72,15 @@ class FIPSTest(UbuntuAdvantageTest):
399 'Please reboot into the FIPS kernel to enable it.',
400 process.stderr.strip())
401
402+ def test_enable_fips_not_all_packages_installed(self):
403+ # one of the packages is not installed
404+ self.make_fake_binary(
405+ 'dpkg-query', command='[ $2 != openssh-client-hmac ]')
406+ process = self.script('enable-fips', 'user:pass')
407+ self.assertEqual(process.returncode, 0)
408+ self.assertIn('Installing FIPS packages', process.stdout)
409+ self.assertIn('Successfully configured FIPS', process.stdout)
410+
411 def test_enable_fips_writes_config(self):
412 """The enable-fips option writes fips configuration."""
413 self.script('enable-fips', 'user:pass')
414@@ -249,3 +263,160 @@ class FIPSTest(UbuntuAdvantageTest):
415 self.assertIn(
416 'Canonical FIPS 140-2 Modules is not enabled',
417 process.stderr)
418+
419+ def test_update_fips(self):
420+ """The enable-fips-updates option enables FIPS-UPDATES repository."""
421+ process = self.script('enable-fips-updates', 'user:pass', '-y')
422+ self.assertEqual(0, process.returncode)
423+ self.assertIn('Ubuntu FIPS-UPDATES PPA repository enabled.',
424+ process.stdout)
425+ expected = (
426+ 'deb https://private-ppa.launchpad.net/ubuntu-advantage/'
427+ 'fips-updates/ubuntu xenial main\n'
428+ '# deb-src https://private-ppa.launchpad.net/'
429+ 'ubuntu-advantage/fips-updates/ubuntu xenial main\n')
430+ self.assertEqual(expected, self.fips_updates_repo_list.read_text())
431+ expected = (
432+ 'Package: *\n'
433+ 'Pin: release o=LP-PPA-ubuntu-advantage-fips-updates, n=xenial\n'
434+ 'Pin-Priority: 1001\n')
435+ self.assertEqual(self.fips_updates_repo_preferences.read_text(),
436+ expected)
437+ self.assertEqual(
438+ self.apt_auth_file.read_text(),
439+ 'machine private-ppa.launchpad.net/ubuntu-advantage/'
440+ 'fips-updates/ubuntu/'
441+ ' login user password pass\n')
442+ self.assertEqual(self.apt_auth_file.stat().st_mode, 0o100600)
443+ keyring_file = self.trusted_gpg_dir / 'ubuntu-fips-updates-keyring.gpg'
444+ self.assertEqual('GPG key', keyring_file.read_text())
445+ self.assertIn(
446+ 'Configuring FIPS...',
447+ process.stdout)
448+ self.assertIn(
449+ 'Successfully updated FIPS packages.\n'
450+ 'Please reboot into the new FIPS kernel',
451+ process.stdout)
452+
453+ def test_update_fips_auth_if_other_entries(self):
454+ """Existing auth.conf entries are preserved."""
455+ auth = 'machine example.com login user password pass\n'
456+ self.apt_auth_file.write_text(auth)
457+ process = self.script('enable-fips-updates', 'user:pass', '-y')
458+ self.assertEqual(0, process.returncode)
459+ self.assertIn(auth, self.apt_auth_file.read_text())
460+
461+ def test_update_fips_writes_config(self):
462+ """The enable-fips-updates option writes fips configuration."""
463+ self.script('enable-fips-updates', 'user:pass', '-y')
464+ self.assertEqual(
465+ 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT fips=1"',
466+ self.boot_cfg.read_text().strip())
467+
468+ def test_update_fips_writes_config_with_boot_partition(self):
469+ """The fips configuration includes the /boot partition."""
470+ self.fstab.write_text('/dev/sda1 /boot ext2 defaults 0 1\n')
471+ self.script('enable-fips-updates', 'user:pass', '-y')
472+ self.assertIn('bootdev=/dev/sda1', self.boot_cfg.read_text())
473+
474+ def test_update_fips_writes_config_s390x_parameters(self):
475+ """On S390x, FIPS parameters are appended to the config file."""
476+ self.ARCH = 's390x'
477+ self.boot_cfg.write_text('parameters=foo\n')
478+ self.script('enable-fips-updates', 'user:pass', '-y')
479+ self.assertEqual('parameters=foo fips=1\n', self.boot_cfg.read_text())
480+
481+ def test_update_unsupported_on_i686(self):
482+ """FIPS is unsupported on i686 arch."""
483+ self.ARCH = 'i686'
484+ process = self.script('enable-fips-updates', 'user:pass', '-y')
485+ self.assertEqual(7, process.returncode)
486+ self.assertIn(
487+ 'Sorry, but Canonical FIPS 140-2 Modules is not supported on i686',
488+ process.stderr)
489+
490+ def test_update_fips_missing_token(self):
491+ """The token must be specified when using enable-fips-updates."""
492+ process = self.script('enable-fips-updates')
493+ self.assertEqual(3, process.returncode)
494+ self.assertIn(
495+ 'Invalid token, it must be in the form "user:password"',
496+ process.stderr)
497+
498+ def test_update_fips_invalid_token_format(self):
499+ """The FIPS token must be specified as "user:password"."""
500+ process = self.script('enable-fips-updates', 'foo-bar', '-y')
501+ self.assertEqual(3, process.returncode)
502+ self.assertIn(
503+ 'Invalid token, it must be in the form "user:password"',
504+ process.stderr)
505+
506+ def test_update_fips_invalid_token(self):
507+ """If token is invalid, an error is returned."""
508+ message = (
509+ 'E: Failed to fetch https://esm.ubuntu.com/'
510+ ' 401 Unauthorized [IP: 1.2.3.4]')
511+ self.make_fake_binary(
512+ 'apt-helper', command='echo "{}"; exit 1'.format(message))
513+ process = self.script('enable-fips-updates', 'user:pass', '-y')
514+ self.assertEqual(3, process.returncode)
515+ self.assertIn('Checking token... ERROR', process.stdout)
516+ self.assertIn('Invalid token', process.stderr)
517+
518+ def test_update_fips_invalid_token_trusty(self):
519+ """Invalid token error is caught with apt-helper in trusty."""
520+ message = 'E: Failed to fetch https://esm.ubuntu.com/ HttpError401'
521+ self.make_fake_binary(
522+ 'apt-helper', command='echo "{}"; exit 1'.format(message))
523+ process = self.script('enable-fips-updates', 'user:pass', '-y')
524+ self.assertEqual(3, process.returncode)
525+ self.assertIn('Checking token... ERROR', process.stdout)
526+ self.assertIn('Invalid token', process.stderr)
527+
528+ def test_update_fips_error_checking_token(self):
529+ """If token check fails, an error is returned."""
530+ message = (
531+ 'E: Failed to fetch https://esm.ubuntu.com/'
532+ ' 404 Not Found [IP: 1.2.3.4]')
533+ self.make_fake_binary(
534+ 'apt-helper', command='echo "{}"; exit 1'.format(message))
535+ process = self.script('enable-fips-updates', 'user:pass', '-y')
536+ self.assertEqual(3, process.returncode)
537+ self.assertIn('Checking token... ERROR', process.stdout)
538+ self.assertIn(
539+ 'Failed checking token (404 Not Found [IP: 1.2.3.4])',
540+ process.stderr)
541+
542+ def test_update_fips_only_supported_on_xenial(self):
543+ """The enable-fips-updates option fails if not on Xenial."""
544+ self.SERIES = 'zesty'
545+ process = self.script('enable-fips-updates', 'user:pass', '-y')
546+ self.assertEqual(4, process.returncode)
547+ self.assertIn(
548+ 'Canonical FIPS 140-2 Modules is not supported on zesty',
549+ process.stderr)
550+
551+ def test_update_fips_x86_64_aes_not_available(self):
552+ """The enable-fips-updates command fails if AESNI is not available."""
553+ self.cpuinfo.write_text('flags\t\t: fpu tsc')
554+ process = self.script('enable-fips-updates', 'user:pass', '-y')
555+ self.assertEqual(7, process.returncode)
556+ self.assertEqual(
557+ 'FIPS requires AES CPU extensions', process.stderr.strip())
558+
559+ def test_update_fips_ppc64le_power8(self):
560+ """POWER8 processors are supported by FIPS."""
561+ self.ARCH = 'ppc64le'
562+ self.cpuinfo.write_text('cpu\t\t: POWER8 (raw), altivec supported')
563+ process = self.script('enable-fips-updates', 'user:pass', '-y')
564+ self.assertEqual(0, process.returncode)
565+ self.assertIn('Successfully updated FIPS packages', process.stdout)
566+
567+ def test_update_fips_ppc64le_older_power(self):
568+ """processors older than POWER8 are not supported by FIPS."""
569+ self.ARCH = 'ppc64le'
570+ self.cpuinfo.write_text('cpu\t\t: POWER7')
571+ process = self.script('enable-fips-updates', 'user:pass', '-y')
572+ self.assertEqual(7, process.returncode)
573+ self.assertEqual(
574+ 'FIPS requires POWER8 or later', process.stderr.strip())
575diff --git a/tests/test_script.py b/tests/test_script.py
576index 1feb400..4e7da93 100644
577--- a/tests/test_script.py
578+++ b/tests/test_script.py
579@@ -79,6 +79,18 @@ class UbuntuAdvantageScriptTest(UbuntuAdvantageTest):
580 process = self.script('status')
581 self.assertIn("livepatch: disabled (not available)", process.stdout)
582
583+ def test_status_with_one_service(self):
584+ """The status for a single service can be returned."""
585+ self.SERIES = 'precise'
586+ process = self.script('status', 'fips')
587+ self.assertEqual(process.returncode, 0)
588+ self.assertEqual(process.stdout, 'fips: disabled (not available)\n')
589+
590+ def test_status_with_one_service_unknown(self):
591+ """The script exits with error on unknown service status name."""
592+ process = self.script('status', 'unknown')
593+ self.assertEqual(process.returncode, 1)
594+
595 def test_version(self):
596 """The version command shows the package version."""
597 self.make_fake_binary('dpkg-query', command='echo 123')
598diff --git a/tests/testing.py b/tests/testing.py
599index a26eb6c..c684f48 100644
600--- a/tests/testing.py
601+++ b/tests/testing.py
602@@ -50,10 +50,17 @@ class UbuntuAdvantageTest(TestWithFixtures):
603 def setUp(self):
604 super(UbuntuAdvantageTest, self).setUp()
605 self.tempdir = self.useFixture(TempDir())
606- self.repo_list = Path(self.tempdir.join('repo.list'))
607 self.boot_cfg = Path(self.tempdir.join('boot.cfg'))
608 self.fstab = Path(self.tempdir.join('fstab'))
609 self.cpuinfo = Path(self.tempdir.join('cpuinfo'))
610+ self.esm_repo_list = Path(self.tempdir.join('esm-repo.list'))
611+ self.fips_repo_list = Path(self.tempdir.join('fips-repo.list'))
612+ self.fips_updates_repo_list = Path(
613+ self.tempdir.join('fips-updates-repo.list'))
614+ self.fips_repo_preferences = Path(
615+ self.tempdir.join('preferences-fips'))
616+ self.fips_updates_repo_preferences = Path(
617+ self.tempdir.join('preferences-fips-updates'))
618 self.fips_enabled_file = Path(self.tempdir.join('fips_enabled_file'))
619 self.bin_dir = Path(self.tempdir.join('bin'))
620 self.etc_dir = Path(self.tempdir.join('etc'))
621@@ -73,6 +80,8 @@ class UbuntuAdvantageTest(TestWithFixtures):
622 self.trusted_gpg_dir.mkdir()
623 (self.keyrings_dir / 'ubuntu-esm-keyring.gpg').write_text('GPG key')
624 (self.keyrings_dir / 'ubuntu-fips-keyring.gpg').write_text('GPG key')
625+ (self.keyrings_dir / 'ubuntu-fips-updates-keyring.gpg').write_text(
626+ 'GPG key')
627 self.cpuinfo.write_text('flags\t\t: fpu apic')
628 self.make_fake_binary('apt-get')
629 self.make_fake_binary('apt-helper')
630@@ -105,11 +114,15 @@ class UbuntuAdvantageTest(TestWithFixtures):
631 'PATH': path,
632 'FSTAB': str(self.fstab),
633 'CPUINFO': str(self.cpuinfo),
634- 'ESM_REPO_LIST': str(self.repo_list),
635- 'FIPS_REPO_LIST': str(self.repo_list),
636+ 'ESM_REPO_LIST': str(self.esm_repo_list),
637+ 'FIPS_REPO_LIST': str(self.fips_repo_list),
638+ 'FIPS_UPDATES_REPO_LIST': str(self.fips_updates_repo_list),
639 'FIPS_BOOT_CFG': str(self.boot_cfg),
640 'FIPS_BOOT_CFG_DIR': str(self.etc_dir),
641 'FIPS_ENABLED_FILE': str(self.fips_enabled_file),
642+ 'FIPS_REPO_PREFERENCES': str(self.fips_repo_preferences),
643+ 'FIPS_UPDATES_REPO_PREFERENCES': str(
644+ self.fips_updates_repo_preferences),
645 'KEYRINGS_DIR': str(self.keyrings_dir),
646 'APT_HELPER': str(self.apt_helper),
647 'APT_AUTH_FILE': str(self.apt_auth_file),
648diff --git a/ubuntu-advantage b/ubuntu-advantage
649index 451e060..1ad03a5 100755
650--- a/ubuntu-advantage
651+++ b/ubuntu-advantage
652@@ -38,8 +38,15 @@ load_modules() {
653 }
654
655 print_status() {
656- local service
657- for service in $SERVICES; do
658+ local service="$1"
659+
660+ local services="$SERVICES"
661+ if [ "$service" ]; then
662+ name_in_list "$service" "$SERVICES" || error_exit invalid_command
663+ services="$service"
664+ fi
665+
666+ for service in $services; do
667 service_print_status "$service"
668 done
669 }
670@@ -54,20 +61,25 @@ Ubuntu Advantage offerings.
671 Currently available are:
672 - Ubuntu Extended Security Maintenance archive (https://ubuntu.com/esm)
673 - Canonical FIPS 140-2 Certified Modules
674+- Canonical FIPS 140-2 Non-Certified Module Updates
675 - Canonical Livepatch Service (https://www.ubuntu.com/server/livepatch)
676
677 Commands:
678- version show the tool version
679- status show current status of Ubuntu Advantage offerings
680- enable-esm <token> enable the ESM repository
681- disable-esm disable the ESM repository
682- enable-fips <token> enable the FIPS PPA repository and install,
683- configure and enable FIPS certified modules
684- disabe-fips currently not supported
685- enable-livepatch <token> enable the Livepatch service
686- disable-livepatch [-r] disable the Livepatch service. With "-r", the
687- canonical-livepatch snap will also be removed
688-
689+ version show the tool version
690+ status [NAME] show current status of Ubuntu Advantage
691+ offerings (or of a specific one if provided)
692+ enable-esm <TOKEN> enable the ESM repository
693+ disable-esm disable the ESM repository
694+ enable-fips <TOKEN> enable the FIPS PPA repository and install,
695+ configure and enable FIPS certified modules
696+ disable-fips currently not supported
697+ enable-fips-updates <TOKEN> [-y] enable non-certified FIPS-UPDATES PPA
698+ repository and install updates. With an
699+ optional "-y" the user prompt will be
700+ bypassed.
701+ enable-livepatch <TOKEN> enable the Livepatch service
702+ disable-livepatch [-r] disable the Livepatch service. With "-r", the
703+ canonical-livepatch snap will also be removed
704 EOF
705 error_exit invalid_command
706 }
707@@ -79,20 +91,40 @@ main() {
708 local service
709 service=$(service_from_command "$command")
710 # if the command contains a service name, check that it's valid
711- if [ "$service" ] && ! name_in_list "$service" "$SERVICES"; then
712+ if [ "$service" ] && ! name_in_list "$service" "$SERVICES" \
713+ && [ "$service" != "fips-updates" ]; then
714 error_msg "Invalid command: \"$command\""
715 usage
716 fi
717
718 case "$command" in
719 status)
720- print_status
721+ print_status "$@"
722 ;;
723
724 version)
725 package_version ubuntu-advantage-tools
726 ;;
727
728+ # special case, adding it above enable-*. There is no separate
729+ # fips-update service.
730+ enable-fips-updates)
731+ local token="$1"
732+ local bypass_prompt=0
733+ if [ -n "$2" ]; then
734+ if [ "$2" = "-y" ]; then
735+ bypass_prompt=1
736+ else
737+ error_msg "Unknown option \"$2\""
738+ usage
739+ fi
740+ fi
741+ service_check_user
742+ service_check_support "fips"
743+ fips_validate_token "$token" || error_exit invalid_token
744+ fips_updates_enable "$token" "$bypass_prompt"
745+ ;;
746+
747 enable-*)
748 service_enable "$service" "$@"
749 ;;
750diff --git a/ubuntu-advantage.1 b/ubuntu-advantage.1
751index 0fa3290..e86b85d 100644
752--- a/ubuntu-advantage.1
753+++ b/ubuntu-advantage.1
754@@ -55,6 +55,25 @@ cryptoapi.
755 disable-fips
756 It's currently not possible to disable FIPS after it has been enabled.
757
758+.TP
759+.B
760+enable-fips-updates \fItoken\fR [\fB\-y\fR]
761+Updating the FIPS modules will take the system out of FIPS compliance as the
762+updated modules are not FIPS certified. The option enables the FIPS-UPDATES
763+PPA repository and installs the updated FIPS modules. If the system is
764+installing FIPS modules for the first time, it configures FIPS on the
765+system. After successfully executing the ubuntu-advantage script to
766+update FIPS modules, the system MUST be rebooted if FIPS kernel was
767+upgraded in the upgrade process. Failing to reboot will result
768+in the system not running the updated FIPS kernel.
769+The \fItoken\fR argument must be in the form "user:password".
770+The \fB\-y\fR argument is optional to bypass the user prompt while
771+installing updates.
772+
773+The following FIPS modules will be updated and put in FIPS mode -
774+openssh-server, openssh-client, strongswan, openssl, and the kernel
775+cryptoapi.
776+
777 .SH Livepatch (Canonical Livepatch Service)
778 Managed live kernel patching. For more information, visit
779 https://www.ubuntu.com/server/livepatch

Subscribers

People subscribed via source and target branches

to status/vote changes: