diff -Nru ubuntu-advantage-tools-9/debian/changelog ubuntu-advantage-tools-10/debian/changelog --- ubuntu-advantage-tools-9/debian/changelog 2017-09-04 15:15:58.000000000 -0300 +++ ubuntu-advantage-tools-10/debian/changelog 2017-09-19 18:58:30.000000000 -0300 @@ -1,3 +1,9 @@ +ubuntu-advantage-tools (10) artful; urgency=medium + + * New upstream release with FIPS support (LP: #1718291) + + -- Andreas Hasenack Tue, 19 Sep 2017 18:33:03 -0300 + ubuntu-advantage-tools (9) artful; urgency=medium * New upstream release: diff -Nru ubuntu-advantage-tools-9/debian/install ubuntu-advantage-tools-10/debian/install --- ubuntu-advantage-tools-9/debian/install 2017-09-04 15:15:58.000000000 -0300 +++ ubuntu-advantage-tools-10/debian/install 2017-09-19 18:58:30.000000000 -0300 @@ -1,3 +1,3 @@ ubuntu-advantage usr/bin/ -ubuntu-esm-keyring.gpg usr/share/keyrings/ +keyrings/*.gpg usr/share/keyrings/ update-motd.d etc/ Binary files /tmp/Wk9KZaxsG_/ubuntu-advantage-tools-9/keyrings/ubuntu-esm-keyring.gpg and /tmp/wb2bY62bR1/ubuntu-advantage-tools-10/keyrings/ubuntu-esm-keyring.gpg differ Binary files /tmp/Wk9KZaxsG_/ubuntu-advantage-tools-9/keyrings/ubuntu-fips-keyring.gpg and /tmp/wb2bY62bR1/ubuntu-advantage-tools-10/keyrings/ubuntu-fips-keyring.gpg differ diff -Nru ubuntu-advantage-tools-9/README.md ubuntu-advantage-tools-10/README.md --- ubuntu-advantage-tools-9/README.md 2017-09-04 15:15:58.000000000 -0300 +++ ubuntu-advantage-tools-10/README.md 2017-09-19 18:58:30.000000000 -0300 @@ -8,7 +8,7 @@ - [Ubuntu Extended Security Maintenance](https://ubuntu.com/esm) archive. - [Canonical Livepatch](https://www.ubuntu.com/server/livepatch) service for managed live kernel patching. - +- Canonical FIPS 140-2 Certified Modules. Install Configure and Enable FIPS modules. Run diff -Nru ubuntu-advantage-tools-9/tests/test_fips.py ubuntu-advantage-tools-10/tests/test_fips.py --- ubuntu-advantage-tools-9/tests/test_fips.py 1969-12-31 21:00:00.000000000 -0300 +++ ubuntu-advantage-tools-10/tests/test_fips.py 2017-09-19 18:58:30.000000000 -0300 @@ -0,0 +1,143 @@ +# Tests for FIPS-related commands. + +from testing import UbuntuAdvantageTest + + +class FIPSTest(UbuntuAdvantageTest): + + SERIES = 'xenial' + + def test_enable_fips(self): + """The enable-fips option enables the FIPS repository.""" + process = self.script('enable-fips', 'user:pass') + self.assertEqual(0, process.returncode) + self.assertIn('Ubuntu FIPS PPA repository enabled.', process.stdout) + expected = ( + 'deb https://user:pass@private-ppa.launchpad.net/ubuntu-advantage/' + 'fips/ubuntu xenial main\n' + '# deb-src https://user:pass@private-ppa.launchpad.net/' + 'ubuntu-advantage/fips/ubuntu xenial main\n') + self.assertEqual(expected, self.repo_list.read_text()) + keyring_file = self.trusted_gpg_dir / 'ubuntu-fips-keyring.gpg' + self.assertEqual('GPG key', keyring_file.read_text()) + self.assertIn('Successfully configured FIPS. PLEASE REBOOT ' + 'to complete FIPS enablement.', process.stdout) + # the apt-transport-https dependency is already installed + self.assertNotIn( + 'Installing missing dependency apt-transport-https', + process.stdout) + + def test_enable_fips_already_enabled(self): + """If fips is already enabled, an error is returned.""" + self.make_fake_binary('dpkg-query') + p = self.fips_enabled_file + p.write_text('1') + process = self.script('enable-fips', 'user:pass') + self.assertEqual(6, process.returncode) + self.assertEqual( + 'FIPS is already enabled.', process.stdout.strip()) + + def test_enable_fips_installed_not_enabled(self): + """If fips is installed but not enabled an error is returned.""" + self.make_fake_binary('dpkg-query') + process = self.script('enable-fips', 'user:pass') + self.assertEqual(6, process.returncode) + self.assertEqual( + 'FIPS is already installed. ' + 'Please reboot into the FIPS kernel to enable it.', + process.stdout.strip()) + + def test_enable_fips_writes_config(self): + """The enable-fips option writes fips configuration.""" + self.script('enable-fips', 'user:pass') + self.assertEqual( + 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT fips=1"', + self.boot_cfg.read_text().strip()) + + def test_enable_fips_writes_config_with_boot_partition(self): + """The fips configuration includes the /boot partition.""" + self.fstab.write_text('/dev/sda1 /boot ext2 defaults 0 1\n') + self.script('enable-fips', 'user:pass') + self.assertIn('bootdev=/dev/sda1', self.boot_cfg.read_text()) + + def test_enable_fips_writes_config_s390x_parameters(self): + """On S390x, FIPS parameters are appended to the config file.""" + self.ARCH = 's390x' + self.boot_cfg.write_text('parameters=foo\n') + self.script('enable-fips', 'user:pass') + self.assertEqual('parameters=foo fips=1\n', self.boot_cfg.read_text()) + + def test_enable_fips_install_apt_transport_https(self): + """enable-fips installs apt-transport-https if needed.""" + self.apt_method_https.unlink() + process = self.script('enable-fips', 'user:pass') + self.assertEqual(0, process.returncode) + self.assertIn( + 'Installing missing dependency apt-transport-https', + process.stdout) + + def test_enable_fips_install_apt_transport_https_fails(self): + """Stderr is printed if apt-transport-https install fails.""" + self.apt_method_https.unlink() + self.make_fake_binary('apt-get', command='echo failed >&2; false') + process = self.script('enable-fips', 'user:pass') + self.assertEqual(1, process.returncode) + self.assertIn('failed', process.stderr) + + def test_enable_fips_install_ca_certificates(self): + """enable-fips installs ca-certificates if needed.""" + self.ca_certificates.unlink() + process = self.script('enable-fips', 'user:pass') + self.assertEqual(0, process.returncode) + self.assertIn( + 'Installing missing dependency ca-certificates', + process.stdout) + + def test_enable_fips_install_ca_certificates_fails(self): + """Stderr is printed if ca-certificates install fails.""" + self.ca_certificates.unlink() + self.make_fake_binary('apt-get', command='echo failed >&2; false') + process = self.script('enable-fips', 'user:pass') + self.assertEqual(1, process.returncode) + self.assertIn('failed', process.stderr) + + def test_enable_fips_missing_token(self): + """The token must be specified when using enable-fips.""" + process = self.script('enable-fips') + self.assertEqual(3, process.returncode) + self.assertIn( + 'Invalid token, it must be in the form "user:password"', + process.stderr) + + def test_enable_fips_invalid_token(self): + """The FIPS token must be specified as "user:password".""" + process = self.script('enable-fips', 'foo-bar') + self.assertEqual(3, process.returncode) + self.assertIn( + 'Invalid token, it must be in the form "user:password"', + process.stderr) + + def test_enable_fips_only_supported_on_xenial(self): + """The enable-fips option fails if not on Xenial.""" + self.SERIES = 'zesty' + process = self.script('enable-fips', 'user:pass') + self.assertEqual(4, process.returncode) + self.assertIn( + 'Canonical FIPS 140-2 Modules is not supported on zesty', + process.stderr) + + def test_is_fips_enabled_true(self): + """is-fips-enabled returns 0 if fips is enabled.""" + self.make_fake_binary('dpkg-query') + p = self.fips_enabled_file + p.write_text('1') + process = self.script('is-fips-enabled') + self.assertEqual(0, process.returncode) + + def test_is_fips_enabled_false(self): + """is-fips-enabled returns 1 if fips is not enabled.""" + self.make_fake_binary('dpkg-query') + p = self.fips_enabled_file + p.write_text('0') + process = self.script('is-fips-enabled') + self.assertEqual(1, process.returncode) diff -Nru ubuntu-advantage-tools-9/tests/testing.py ubuntu-advantage-tools-10/tests/testing.py --- ubuntu-advantage-tools-9/tests/testing.py 2017-09-04 15:15:58.000000000 -0300 +++ ubuntu-advantage-tools-10/tests/testing.py 2017-09-19 18:58:30.000000000 -0300 @@ -22,12 +22,17 @@ SERIES = None KERNEL_VERSION = None + ARCH = None def setUp(self): super(UbuntuAdvantageTest, self).setUp() self.tempdir = self.useFixture(TempDir()) self.repo_list = Path(self.tempdir.join('repo.list')) + self.boot_cfg = Path(self.tempdir.join('boot.cfg')) + self.fstab = Path(self.tempdir.join('fstab')) + self.fips_enabled_file = Path(self.tempdir.join('fips_enabled_file')) self.bin_dir = Path(self.tempdir.join('bin')) + self.etc_dir = Path(self.tempdir.join('etc')) self.keyrings_dir = Path(self.tempdir.join('keyrings')) self.trusted_gpg_dir = Path(self.tempdir.join('trusted.gpg.d')) self.apt_method_https = self.bin_dir / 'apt-method-https' @@ -36,13 +41,18 @@ # setup directories and files self.bin_dir.mkdir() self.keyrings_dir.mkdir() + self.etc_dir.mkdir() + self.fstab.write_text('') self.trusted_gpg_dir.mkdir() (self.keyrings_dir / 'ubuntu-esm-keyring.gpg').write_text('GPG key') + (self.keyrings_dir / 'ubuntu-fips-keyring.gpg').write_text('GPG key') self.make_fake_binary('apt-get') self.make_fake_binary('apt-method-https') self.make_fake_binary('update-ca-certificates') self.make_fake_binary('id', command='echo 0') self.make_fake_binary('snapd') + self.make_fake_binary('update-grub') + self.make_fake_binary('zipl') def make_fake_binary(self, binary, command='true'): """Create a script to fake a binary in path.""" @@ -62,7 +72,12 @@ path = os.pathsep.join([str(self.bin_dir), os.environ['PATH']]) env = { 'PATH': path, + 'FSTAB': str(self.fstab), 'REPO_LIST': str(self.repo_list), + 'FIPS_REPO_LIST': str(self.repo_list), + 'FIPS_BOOT_CFG': str(self.boot_cfg), + 'FIPS_BOOT_CFG_DIR': str(self.etc_dir), + 'FIPS_ENABLED_FILE': str(self.fips_enabled_file), 'KEYRINGS_DIR': str(self.keyrings_dir), 'APT_KEYS_DIR': str(self.trusted_gpg_dir), 'APT_METHOD_HTTPS': str(self.apt_method_https), @@ -72,6 +87,8 @@ env['SERIES'] = self.SERIES if self.KERNEL_VERSION: env['KERNEL_VERSION'] = self.KERNEL_VERSION + if self.ARCH: + env['ARCH'] = self.ARCH process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) process.wait() diff -Nru ubuntu-advantage-tools-9/ubuntu-advantage ubuntu-advantage-tools-10/ubuntu-advantage --- ubuntu-advantage-tools-9/ubuntu-advantage 2017-09-04 15:15:58.000000000 -0300 +++ ubuntu-advantage-tools-10/ubuntu-advantage 2017-09-19 18:58:30.000000000 -0300 @@ -5,10 +5,15 @@ ESM_SUPPORTED_SERIES="precise" LIVEPATCH_SUPPORTED_SERIES="trusty xenial" +FIPS_SUPPORTED_SERIES="xenial" SERIES=${SERIES:-$(lsb_release -cs)} KERNEL_VERSION=${KERNEL_VERSION:-$(uname -r)} +ARCH=${ARCH:-$(uname -m)} +FIPS_REPO_URL="private-ppa.launchpad.net/ubuntu-advantage/fips" +FIPS_REPO_KEY_FILE="ubuntu-fips-keyring.gpg" +FIPS_REPO_LIST=${FIPS_REPO_LIST:-"/etc/apt/sources.list.d/ubuntu-fips-${SERIES}.list"} REPO_URL="esm.ubuntu.com" REPO_KEY_FILE="ubuntu-esm-keyring.gpg" REPO_LIST=${REPO_LIST:-"/etc/apt/sources.list.d/ubuntu-esm-${SERIES}.list"} @@ -19,6 +24,14 @@ SNAPD=${SNAPD:-"/usr/lib/snapd/snapd"} +FIPS_ENABLED_FILE=${FIPS_ENABLED_FILE:-"/proc/sys/crypto/fips_enabled"} +FSTAB=${FSTAB:-"/etc/fstab"} +if [ "$ARCH" = "s390x" ]; then + FIPS_BOOT_CFG=${FIPS_BOOT_CFG:-"/etc/zipl.conf"} +else + FIPS_BOOT_CFG_DIR=${FIPS_BOOT_CFG_DIR:-"/etc/default/grub.d"} + FIPS_BOOT_CFG=${FIPS_BOOT_CFG:-"${FIPS_BOOT_CFG_DIR}/99-fips.cfg"} +fi check_result() { local result output @@ -37,9 +50,27 @@ apt_get() { DEBIAN_FRONTEND=noninteractive \ - apt-get -y -o Dpkg::Options::='--force-confold' "$@" + apt-get -y -o Dpkg::Options::='--force-confold' "$@" } + +is_package_installed() { + dpkg-query -s "$1" > /dev/null 2>&1 +} + + +# Install a package if the specified file doesn't exist +install_package_if_missing_file() { + local file="$1" + local package="$2" + + if [ ! -f "$file" ]; then + echo -n "Installing missing dependency $package... " + check_result apt_get install "$package" + fi +} + + # Whether the current series is among supported ones. is_supported_series() { local s @@ -53,10 +84,7 @@ install_livepatch_prereqs() { - if [ ! -f "$SNAPD" ]; then - echo -n 'Installing missing dependency snapd... ' - check_result apt_get install snapd - fi + install_package_if_missing_file "$SNAPD" snapd if ! snap list canonical-livepatch >/dev/null 2>&1; then echo 'Installing the canonical-livepatch snap.' echo 'This may take a few minutes depending on your bandwidth.' @@ -116,14 +144,8 @@ enable_esm() { cp "${KEYRINGS_DIR}/${REPO_KEY_FILE}" "$APT_KEYS_DIR" write_esm_list_file "$1" - if [ ! -f "$APT_METHOD_HTTPS" ]; then - echo -n 'Installing missing dependency apt-transport-https... ' - check_result apt_get install apt-transport-https - fi - if [ ! -f "$CA_CERTIFICATES" ]; then - echo -n 'Installing missing dependency ca-certificates... ' - check_result apt_get install ca-certificates - fi + install_package_if_missing_file "$APT_METHOD_HTTPS" apt-transport-https + install_package_if_missing_file "$CA_CERTIFICATES" ca-certificates echo -n 'Running apt-get update... ' check_result apt_get update echo 'Ubuntu ESM repository enabled.' @@ -141,6 +163,108 @@ fi } +fips_prep_check() { + local fips_hmacs pkg + + # sanity check + case "$ARCH" in + s390x|x86_64|ppc64le) ;; + *) + echo "ERROR: $ARCH is not fips supported." + return 1 + ;; + esac + + # check to see if fips pkgs already installed. + fips_hmacs="openssh-client-hmac openssh-server-hmac libssl1.0.0-hmac \ + linux-fips strongswan-hmac" + for pkg in $fips_hmacs; do + if is_package_installed "$pkg"; then + if is_fips_enabled; then + echo 'FIPS is already enabled.' + else + echo 'FIPS is already installed. Please reboot into the FIPS kernel to enable it.' + fi + return 1 + fi + done + + return 0 +} + +configure_fips() { + local bootdev fips_params result + + # if /boot has its own partition, then get the bootdevice + # Note: /boot/efi does not count + bootdev=$(awk '!/^\s*#/ && $2 ~ /^\/boot\/?$/ { print $1 }' "$FSTAB") + fips_params="fips=1" + if [ -n "$bootdev" ]; then + fips_params="$fips_params bootdev=$bootdev" + fi + + if [ "$ARCH" = "s390x" ]; then + sed -i -e 's,^parameters\s*=.*,& '"$fips_params"',' "$FIPS_BOOT_CFG" + echo -n 'Updating zipl to enable fips... ' + check_result zipl + else + result=0 + if [ ! -d "$FIPS_BOOT_CFG_DIR" ]; then + mkdir "$FIPS_BOOT_CFG_DIR" > /dev/null 2>&1 || result=$? + if [ $result -ne 0 ]; then + echo "Failed to make directory, $FIPS_BOOT_CFG_DIR." + return 1 + fi + fi + echo "GRUB_CMDLINE_LINUX_DEFAULT=\"\$GRUB_CMDLINE_LINUX_DEFAULT $fips_params\"" > "$FIPS_BOOT_CFG" + echo -n 'Updating grub to enable fips... ' + check_result update-grub + fi +} + +write_fips_list_file() { + cat > "$FIPS_REPO_LIST" </dev/null 2>&1 } -validate_esm_token(){ +validate_user_pass_token(){ echo "$1" | grep -q '^[^:]\+:[^:]\+$' } @@ -176,6 +300,10 @@ check_service_support "Canonical Livepatch" "$LIVEPATCH_SUPPORTED_SERIES" } +check_fips_support() { + check_service_support "Canonical FIPS 140-2 Modules" "$FIPS_SUPPORTED_SERIES" +} + check_service_support() { local title="$1" local supported_series="$2" @@ -218,6 +346,17 @@ is_supported_series "$ESM_SUPPORTED_SERIES" || esm_status="$esm_status (not available)" fi echo "esm: $esm_status" + + echo + + local fips_status + if is_fips_enabled; then + fips_status="enabled" + else + fips_status="disabled" + is_supported_series "$FIPS_SUPPORTED_SERIES" || fips_status="$fips_status (not available)" + fi + echo "fips: $fips_status" } @@ -231,6 +370,7 @@ Currently available are: - Ubuntu Extended Security Maintenance archive (https://ubuntu.com/esm) - Canonical Livepatch Service (https://www.ubuntu.com/server/livepatch) +- Canonical FIPS 140-2 Certified Modules Commands: status show current status of Ubuntu Advantage offerings @@ -239,7 +379,8 @@ enable-livepatch enable the Livepatch service disable-livepatch [-r] disable the Livepatch service. With "-r", the canonical-livepatch snap will also be removed - + enable-fips enable the FIPS PPA repository and install, + configure and enable FIPS certified modules EOF } @@ -282,7 +423,7 @@ check_user check_esm_support token="$2" - if ! validate_esm_token "$token"; then + if ! validate_user_pass_token "$token"; then echo 'Invalid token, it must be in the form "user:password"' >&2 exit 3 fi @@ -300,6 +441,23 @@ is_esm_enabled ;; + enable-fips) + check_user + check_fips_support + token="$2" + if ! validate_user_pass_token "$token"; then + echo 'Invalid token, it must be in the form "user:password"' >&2 + exit 3 + fi + + enable_fips "$token" + ;; + + is-fips-enabled) + # no root needed + is_fips_enabled + ;; + status) print_status ;; diff -Nru ubuntu-advantage-tools-9/ubuntu-advantage.1 ubuntu-advantage-tools-10/ubuntu-advantage.1 --- ubuntu-advantage-tools-9/ubuntu-advantage.1 2017-09-04 15:15:58.000000000 -0300 +++ ubuntu-advantage-tools-10/ubuntu-advantage.1 2017-09-19 18:58:30.000000000 -0300 @@ -39,6 +39,22 @@ disable-livepatch \fR[\fB\-r\fR] Disable the Livepatch service. If the \fB\-r\fR option is given, the canonical-livepatch snap will be removed after the sevice is disabled. + +.SH FIPS (Canonical FIPS 140-2 certified modules) +Install, Configure, and Enable FIPS 140-2 certified modules. +.TP +.B +enable-fips \fItoken\fR +Enable the FIPS PPA repository, install the FIPS modules, configure +the bootloader and enable fips on the system. After successfully executing the +ubuntu-advantage script to enable fips, the system MUST be rebooted to +complete the enablement process. Failing to reboot will result in the system +not being fips enabled. +The \fItoken\fR argument must be in the form "user:password". + +The following FIPS certified modules will be installed and put in fips mode; +openssh-server, openssh-client, strongswan, openssl, and the kernel +cryptoapi. .SH EXIT STATUS .TP .B @@ -65,4 +81,8 @@ 5 Current kernel is too old to support Snaps (required for the Livepatch service) .TP +.B +6 +It was determined that FIPS has already been installed. +.TP If apt commands run by the tool fail, the exit status from apt is returned. Binary files /tmp/Wk9KZaxsG_/ubuntu-advantage-tools-9/ubuntu-esm-keyring.gpg and /tmp/wb2bY62bR1/ubuntu-advantage-tools-10/ubuntu-esm-keyring.gpg differ