From 11b007a31deac072d932ab40b8d62776a17c1f7e Mon Sep 17 00:00:00 2001 From: Lucy C <12953208+elvece@users.noreply.github.com> Date: Fri, 19 Mar 2021 16:50:52 -0600 Subject: [PATCH] Fix/integration/0.2.11 (#265) * backports tor security fix to 0.2.10, adds functionality to allow for ssh key management during an update (#263) * actually upgrade to 0.3.5.14-1 * update lan services on backup restore * reload nginx, update welcome message, move reset lan to handler * moves lan refresh after backup restore to asynchronous part of restore * fix certificate generation * match guards Co-authored-by: Keagan McClelland --- agent/src/Foundation.hs | 2 ++ agent/src/Handler/Apps.hs | 10 +++++- agent/src/Handler/Backups.hs | 35 ++++++++++++------- agent/src/Handler/Network.hs | 12 ++++--- agent/src/Lib/Synchronizers.hs | 10 +++--- appmgr/src/backup.rs | 24 +++++++++++++ appmgr/src/util.rs | 8 +++++ .../modals/os-welcome/os-welcome.page.html | 1 + 8 files changed, 80 insertions(+), 22 deletions(-) diff --git a/agent/src/Foundation.hs b/agent/src/Foundation.hs index c494469f3..c5c04f78f 100644 --- a/agent/src/Foundation.hs +++ b/agent/src/Foundation.hs @@ -186,6 +186,8 @@ cutoffDuringUpdate m = do path <- asks $ pathInfo . reqWaiRequest . handlerRequest case path of [v] | v == "v" <> (show . major $ agentVersion) -> m + [auth] | auth == "auth" -> m + (_:ssh:_) | ssh == "sshKeys" -> m _ -> handleS9ErrT $ throwE UpdateInProgressE Nothing -> m diff --git a/agent/src/Handler/Apps.hs b/agent/src/Handler/Apps.hs index eb2f40a15..cd616d88c 100644 --- a/agent/src/Handler/Apps.hs +++ b/agent/src/Handler/Apps.hs @@ -109,7 +109,11 @@ type AllEffects m ( Labelled "databaseConnection" (ReaderT ConnectionPool) - (ReaderT AgentCtx (ErrorC S9Error (LiftC m))) + ( Labelled + "lanThread" + (ReaderT (MVar ThreadId)) + (ReaderT AgentCtx (ErrorC S9Error (LiftC m))) + ) ) ) ) @@ -122,6 +126,8 @@ intoHandler m = do runM . handleS9ErrC . flip runReaderT ctx + . flip runReaderT (appLanThread ctx) + . runLabelled @"lanThread" . flip runReaderT (appConnPool ctx) . runLabelled @"databaseConnection" . flip runReaderT fsbase @@ -376,6 +382,7 @@ postUninstallAppLogic :: ( HasFilesystemBase sig m , MonadIO m , HasLabelled "databaseConnection" (Reader ConnectionPool) sig m , HasLabelled "iconTagCache" (Reader (TVar (HM.HashMap AppId (Digest MD5)))) sig m + , HasLabelled "lanThread" (Reader (MVar ThreadId)) sig m ) => AppId -> AppMgr2.DryRun @@ -413,6 +420,7 @@ postInstallNewAppR appId = do postInstallNewAppLogic :: forall sig m a . ( Has (Reader AgentCtx) sig m + , HasLabelled "lanThread" (Reader (MVar ThreadId)) sig m , HasLabelled "databaseConnection" (Reader ConnectionPool) sig m , HasLabelled "iconTagCache" (Reader (TVar (HM.HashMap AppId (Digest MD5)))) sig m , Has (Error S9Error) sig m diff --git a/agent/src/Handler/Backups.hs b/agent/src/Handler/Backups.hs index d1b5f8c6b..a8994c84c 100644 --- a/agent/src/Handler/Backups.hs +++ b/agent/src/Handler/Backups.hs @@ -7,11 +7,11 @@ import Startlude hiding ( Reader , runReader ) -import Control.Effect.Labelled hiding ( Handler ) -import Control.Effect.Reader.Labelled import Control.Carrier.Error.Church import Control.Carrier.Lift import Control.Carrier.Reader ( runReader ) +import Control.Effect.Labelled hiding ( Handler ) +import Control.Effect.Reader.Labelled import Data.Aeson import qualified Data.HashMap.Strict as HM import Data.UUID.V4 @@ -20,8 +20,13 @@ import Yesod.Auth import Yesod.Core import Yesod.Core.Types +import Control.Concurrent.STM +import Exinst import Foundation +import Handler.Network import Handler.Util +import qualified Lib.Algebra.Domain.AppMgr as AppMgr2 +import Lib.Background import Lib.Error import qualified Lib.External.AppMgr as AppMgr import qualified Lib.Notifications as Notifications @@ -29,10 +34,6 @@ import Lib.Password import Lib.Types.Core import Lib.Types.Emver import Model -import qualified Lib.Algebra.Domain.AppMgr as AppMgr2 -import Lib.Background -import Control.Concurrent.STM -import Exinst data CreateBackupReq = CreateBackupReq @@ -58,8 +59,9 @@ instance FromJSON RestoreBackupReq where pure RestoreBackupReq { .. } data EjectDiskReq = EjectDiskReq - { ejectDiskLogicalName :: Text - } deriving (Eq, Show) + { ejectDiskLogicalName :: Text + } + deriving (Eq, Show) instance FromJSON EjectDiskReq where parseJSON = withObject "Eject Disk Req" $ \o -> do ejectDiskLogicalName <- o .: "logicalName" @@ -100,6 +102,8 @@ postRestoreBackupR appId = disableEndpointOnFailedUpdate $ do & runReader appConnPool & runLabelled @"backgroundJobCache" & runReader appBackgroundJobs + & runLabelled @"lanThread" + & runReader appLanThread & handleS9ErrC & runM @@ -173,6 +177,7 @@ stopBackupLogic appId = do restoreBackupLogic :: ( HasLabelled "backgroundJobCache" (Reader (TVar JobCache)) sig m , HasLabelled "databaseConnection" (Reader ConnectionPool) sig m + , HasLabelled "lanThread" (Reader (MVar ThreadId)) sig m , Has (Error S9Error) sig m , Has AppMgr2.AppMgr sig m , MonadIO m @@ -181,10 +186,11 @@ restoreBackupLogic :: ( HasLabelled "backgroundJobCache" (Reader (TVar JobCache) -> RestoreBackupReq -> m () restoreBackupLogic appId RestoreBackupReq {..} = do - jobCache <- ask @"backgroundJobCache" - db <- ask @"databaseConnection" - version <- fmap AppMgr2.infoResVersion $ AppMgr2.info [AppMgr2.flags| |] appId `orThrowM` NotFoundE "appId" - (show appId) + lanThread <- ask @"lanThread" + jobCache <- ask @"backgroundJobCache" + db <- ask @"databaseConnection" + version <- fmap AppMgr2.infoResVersion $ AppMgr2.info [AppMgr2.flags| |] appId `orThrowM` NotFoundE "appId" + (show appId) res <- liftIO . atomically $ do (JobCache jobs) <- readTVar jobCache case HM.lookup appId jobs of @@ -206,10 +212,13 @@ restoreBackupLogic appId RestoreBackupReq {..} = do let notif = case appmgrRes of Left e -> Notifications.RestoreFailed e Right _ -> Notifications.RestoreSucceeded + resetRes <- runExceptT @S9Error $ runReader lanThread . runLabelled @"lanThread" $ postResetLanLogic + case resetRes of + Left _ -> pure () -- temporarily forbidden is the only possible thing here so ignore it + Right () -> pure () flip runSqlPool db $ void $ Notifications.emit appId version notif liftIO . atomically $ modifyTVar jobCache (insertJob appId Restore tid) - listDisksLogic :: (Has (Error S9Error) sig m, MonadIO m) => m [AppMgr.DiskInfo] listDisksLogic = runExceptT AppMgr.diskShow >>= liftEither diff --git a/agent/src/Handler/Network.hs b/agent/src/Handler/Network.hs index 1ea42a97a..f34a99b29 100644 --- a/agent/src/Handler/Network.hs +++ b/agent/src/Handler/Network.hs @@ -1,16 +1,19 @@ module Handler.Network where import Startlude hiding ( Reader + , ask , asks , runReader ) import Control.Carrier.Lift ( runM ) import Control.Effect.Error -import Control.Carrier.Reader import Lib.Error import Yesod.Core ( getYesod ) +import Control.Carrier.Reader ( runReader ) +import Control.Effect.Labelled ( runLabelled ) +import Control.Effect.Reader.Labelled import Foundation import qualified Lib.Algebra.Domain.AppMgr as AppMgr2 import Lib.Types.Core @@ -18,11 +21,12 @@ import Lib.Types.Core postResetLanR :: Handler () postResetLanR = do ctx <- getYesod - runM . handleS9ErrC . runReader ctx $ postResetLanLogic + runM . handleS9ErrC . runReader (appLanThread ctx) . runLabelled @"lanThread" $ postResetLanLogic -postResetLanLogic :: (MonadIO m, Has (Reader AgentCtx) sig m, Has (Error S9Error) sig m) => m () +postResetLanLogic :: (MonadIO m, HasLabelled "lanThread" (Reader (MVar ThreadId)) sig m, Has (Error S9Error) sig m) + => m () postResetLanLogic = do - threadVar <- asks appLanThread + threadVar <- ask @"lanThread" mtid <- liftIO . tryTakeMVar $ threadVar case mtid of Nothing -> throwError $ TemporarilyForbiddenE (AppId "LAN") "reset" "being reset" diff --git a/agent/src/Lib/Synchronizers.hs b/agent/src/Lib/Synchronizers.hs index b394479df..6f4b21263 100644 --- a/agent/src/Lib/Synchronizers.hs +++ b/agent/src/Lib/Synchronizers.hs @@ -48,6 +48,7 @@ import System.Process ( callCommand ) import Constants import Control.Effect.Error hiding ( run ) +import Control.Effect.Labelled ( runLabelled ) import Daemon.ZeroConf ( getStart9AgentHostname ) import qualified Data.Text as T import Foundation @@ -438,10 +439,11 @@ syncInstallAppMgr = SyncOp "Install AppMgr" check migrate False Left _ -> pure True Right v -> not . (v <||) <$> asks (appMgrVersionSpec . appSettings) migrate = fmap (either absurd id) . runExceptT . flip catchE failUpdate $ do + lan <- asks appLanThread avs <- asks $ appMgrVersionSpec . appSettings av <- AppMgr.installNewAppMgr avs unless (av <|| avs) $ throwE $ AppMgrVersionE av avs - postResetLanLogic -- to accommodate 0.2.x -> 0.2.9 where previous appmgr didn't correctly set up lan + flip runReaderT lan $ runLabelled @"lanThread" $ postResetLanLogic -- to accommodate 0.2.x -> 0.2.9 where previous appmgr didn't correctly set up lan syncUpgradeLifeline :: SyncOp syncUpgradeLifeline = SyncOp "Upgrade Lifeline" check migrate False @@ -583,11 +585,11 @@ syncRestarterService = SyncOp "Install Restarter Service" check migrate True liftIO $ callCommand "systemctl enable restarter.timer" syncUpgradeTor :: SyncOp -syncUpgradeTor = SyncOp "Install Tor 0.3.5.12-1" check migrate False +syncUpgradeTor = SyncOp "Install Tor 0.3.5.14-1" check migrate False where check = liftIO - $ ( run (shell [i|dpkg -l|] $| shell [i|grep tor|] $| shell [i|grep 0.3.5.12-1|] $| conduit await) + $ ( run (shell [i|dpkg -l|] $| shell [i|grep tor|] $| shell [i|grep 0.3.5.14-1|] $| conduit await) $> False ) `catch` \(e :: ProcessException) -> case e of @@ -595,7 +597,7 @@ syncUpgradeTor = SyncOp "Install Tor 0.3.5.12-1" check migrate False _ -> throwIO e migrate = liftIO . run $ do shell "apt-get update" - shell "apt-get install -y tor=0.3.5.12-1" + shell "apt-get install -y tor=0.3.5.14-1" syncDropCertificateUniqueness :: SyncOp syncDropCertificateUniqueness = SyncOp "Eliminate OpenSSL unique_subject=yes" check migrate False diff --git a/appmgr/src/backup.rs b/appmgr/src/backup.rs index baa48dd8a..4031c8226 100644 --- a/appmgr/src/backup.rs +++ b/appmgr/src/backup.rs @@ -1,3 +1,4 @@ +use std::os::unix::process::ExitStatusExt; use std::path::Path; use argon2::Config; @@ -10,6 +11,7 @@ use serde::Serialize; use crate::util::from_yaml_async_reader; use crate::util::to_yaml_async_writer; use crate::util::Invoke; +use crate::util::PersistencePath; use crate::version::VersionT; use crate::Error; use crate::ResultExt; @@ -224,6 +226,28 @@ pub async fn restore_backup>( } crate::tor::restart().await?; + // Delete the fullchain certificate, so it can be regenerated with the restored tor pubkey address + PersistencePath::from_ref("apps") + .join(&app_id) + .join("cert-local.fullchain.crt.pem") + .delete() + .await?; + crate::tor::write_lan_services( + &crate::tor::services_map(&PersistencePath::from_ref(crate::SERVICES_YAML)).await?, + ) + .await?; + let svc_exit = std::process::Command::new("service") + .args(&["nginx", "reload"]) + .status()?; + crate::ensure_code!( + svc_exit.success(), + crate::error::GENERAL_ERROR, + "Failed to Reload Nginx: {}", + svc_exit + .code() + .or_else(|| { svc_exit.signal().map(|a| 128 + a) }) + .unwrap_or(0) + ); Ok(()) } diff --git a/appmgr/src/util.rs b/appmgr/src/util.rs index 5d6ab00da..0390797be 100644 --- a/appmgr/src/util.rs +++ b/appmgr/src/util.rs @@ -110,6 +110,14 @@ impl PersistencePath { pub async fn for_update(self) -> Result, Error> { UpdateHandle::new(self).await } + + pub async fn delete(&self) -> Result<(), Error> { + match tokio::fs::remove_file(self.path()).await { + Ok(()) => Ok(()), + Err(k) if k.kind() == std::io::ErrorKind::NotFound => Ok(()), + e => e.with_code(crate::error::FILESYSTEM_ERROR), + } + } } #[derive(Debug)] diff --git a/ui/src/app/modals/os-welcome/os-welcome.page.html b/ui/src/app/modals/os-welcome/os-welcome.page.html index d15d553e9..85340b1d8 100644 --- a/ui/src/app/modals/os-welcome/os-welcome.page.html +++ b/ui/src/app/modals/os-welcome/os-welcome.page.html @@ -18,6 +18,7 @@
  • Redirecting to HTTPS when navigating to LAN address
  • Displaying warning messages during concurrent upgrades of dependent services
  • Allowing larger file uploads
  • +
  • Patching a security fix for Tor