module Propellor.Property.HostingProvider.DigitalOcean ( distroKernel ) where import Propellor.Base import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.File as File import qualified Propellor.Property.Reboot as Reboot import Data.List -- | Digital Ocean does not provide any way to boot -- the kernel provided by the distribution, except using kexec. -- Without this, some old, and perhaps insecure kernel will be used. -- -- This property causes the distro kernel to be loaded on reboot, using kexec. -- -- If the power is cycled, the non-distro kernel still boots up. -- So, this property also checks if the running kernel is present in /boot, -- and if not, reboots immediately into a distro kernel. distroKernel :: Property NoInfo distroKernel = propertyList "digital ocean distro kernel hack" [ Apt.installed ["grub-pc", "kexec-tools", "file"] , "/etc/default/kexec" `File.containsLines` [ "LOAD_KEXEC=true" , "USE_GRUB_CONFIG=true" ] `describe` "kexec configured" , check (not <$> runningInstalledKernel) Reboot.now `describe` "running installed kernel" ] runningInstalledKernel :: IO Bool runningInstalledKernel = do kernelver <- takeWhile (/= '\n') <$> readProcess "uname" ["-r"] when (null kernelver) $ error "failed to read uname -r" kernelimages <- concat <$> mapM kernelsIn ["/", "/boot/"] when (null kernelimages) $ error "failed to find any installed kernel images" findVersion kernelver <$> readProcess "file" ("-L" : kernelimages) -- | File output looks something like this, we want to unambiguously -- match the running kernel version: -- Linux kernel x86 boot executable bzImage, version 3.16-3-amd64 (debian-kernel@lists.debian.org) #1 SMP Debian 3.1, RO-rootFS, swap_dev 0x2, Normal VGA findVersion :: String -> String -> Bool findVersion ver s = (" version " ++ ver ++ " ") `isInfixOf` s kernelsIn :: FilePath -> IO [FilePath] kernelsIn d = filter ("vmlinu" `isInfixOf`) <$> dirContents d