Merge pull request #71 from Start9Labs/release/0.2.6

Release/0.2.6
This commit is contained in:
Keagan McClelland
2020-12-01 13:46:50 -07:00
committed by GitHub
24 changed files with 763 additions and 846 deletions

View File

@@ -34,6 +34,6 @@ database:
database: "start9_agent.sqlite3"
poolsize: "_env:YESOD_SQLITE_POOLSIZE:10"
app-mgr-version-spec: "=0.2.5"
app-mgr-version-spec: "=0.2.6"
#analytics: UA-YOURCODE

View File

@@ -0,0 +1 @@
SELECT TRUE;

View File

@@ -1,5 +1,5 @@
name: ambassador-agent
version: 0.2.5
version: 0.2.6
default-extensions:
- NoImplicitPrelude

View File

@@ -19,6 +19,7 @@ module Application
, handler
, runDb
, getAgentCtx
, sleep
)
where
@@ -65,6 +66,7 @@ import Lib.WebServer
import Model
import Settings
import Lib.Background
import qualified Daemon.SslRenew as SSLRenew
appMain :: IO ()
appMain = do
@@ -81,10 +83,10 @@ appMain = do
die . toS $ "Invalid Port: " <> n
["--git-hash"] -> do
putStrLn @Text $embedGitRevision
exitWith ExitSuccess
exitSuccess
["--version"] -> do
putStrLn @Text (show agentVersion)
exitWith ExitSuccess
exitSuccess
_ -> pure settings
createDirectoryIfMissing False (toS $ agentDataDirectory `relativeTo` appFilesystemBase settings')
@@ -187,6 +189,10 @@ startupSequence foundation = do
void . forkIO . forever $ forkIO (runReaderT AppNotifications.fetchAndSave foundation) >> threadDelay 5_000_000
withAgentVersionLog_ "App notifications refreshing"
withAgentVersionLog_ "Initializing SSL certificate renewal loop"
void . forkIO . forever $ forkIO (SSLRenew.renewSslLeafCert foundation) *> sleep 86_400
withAgentVersionLog_ "SSL Renewal daemon started"
-- reloading avahi daemon
-- DRAGONS! make sure this step happens AFTER system synchronization
withAgentVersionLog_ "Publishing Agent to Avahi Daemon"
@@ -199,6 +205,10 @@ startupSequence foundation = do
withAgentVersionLog_ "Listening for Self-Update Signal"
waitForUpdateSignal foundation
sleep :: Integer -> IO ()
sleep n = let (full, r) = (n * 1_000_000) `divMod` fromIntegral (maxBound :: Int) in
replicateM_ (fromIntegral full) (threadDelay maxBound) *> threadDelay (fromIntegral r)
--------------------------------------------------------------
-- Functions for DevelMain.hs (a way to run the AgentCtx from GHCi)
--------------------------------------------------------------

View File

@@ -0,0 +1,78 @@
{-# LANGUAGE QuasiQuotes #-}
module Daemon.SslRenew where
import Startlude hiding ( err )
import Data.String.Interpolate ( i )
import System.Process ( system )
import Foundation
import Lib.SystemPaths
import Settings
import Lib.Ssl
import Daemon.ZeroConf ( getStart9AgentHostname )
import Lib.Tor
import Control.Carrier.Lift
import System.Directory ( removePathForcibly
, renameDirectory
)
import Lib.SystemCtl
import qualified Lib.Notifications as Notifications
import Database.Persist.Sql ( runSqlPool )
import Lib.Types.Core
import Constants
renewSslLeafCert :: AgentCtx -> IO ()
renewSslLeafCert ctx = do
let base = appFilesystemBase . appSettings $ ctx
sid <- injectFilesystemBase base getStart9AgentHostname
let hostname = sid <> ".local"
tor <- injectFilesystemBase base getAgentHiddenServiceUrl
putStr @Text "SSL Renewal Required? "
needsRenew <- doesSslNeedRenew (toS $ entityCertPath sid `relativeTo` base)
print needsRenew
when needsRenew $ runM . injectFilesystemBase base $ do
intCaKeyPath <- toS <$> getAbsoluteLocationFor intermediateCaKeyPath
intCaConfPath <- toS <$> getAbsoluteLocationFor intermediateCaOpenSslConfPath
intCaCertPath <- toS <$> getAbsoluteLocationFor intermediateCaCertPath
sslDirTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> sslDirectory)
entKeyPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityKeyPath sid)
entConfPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityConfPath sid)
entCertPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityCertPath sid)
(ec, out, err) <- writeLeafCert
DeriveCertificate { applicantConfPath = entConfPathTmp
, applicantKeyPath = entKeyPathTmp
, applicantCertPath = entCertPathTmp
, signingConfPath = intCaConfPath
, signingKeyPath = intCaKeyPath
, signingCertPath = intCaCertPath
, duration = 365
}
hostname
tor
liftIO $ do
putStrLn @Text "openssl logs"
putStrLn @Text "exit code: "
print ec
putStrLn @String $ "stdout: " <> out
putStrLn @String $ "stderr: " <> err
case ec of
ExitSuccess -> pure ()
ExitFailure n ->
liftIO
. void
$ flip runSqlPool (appConnPool ctx)
$ Notifications.emit (AppId "EmbassyOS") agentVersion
$ Notifications.CertRenewFailed (ExitFailure n) out err
let sslDir = toS $ sslDirectory `relativeTo` base
liftIO $ removePathForcibly sslDir
liftIO $ renameDirectory sslDirTmp sslDir
liftIO $ systemCtl RestartService "nginx" $> ()
doesSslNeedRenew :: FilePath -> IO Bool
doesSslNeedRenew cert = do
ec <- liftIO $ system [i|openssl x509 -checkend 2592000 -noout -in #{cert}|]
pure $ ec /= ExitSuccess

View File

@@ -1,4 +1,5 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
module Handler.Register where
@@ -55,7 +56,7 @@ postRegisterR = handleS9ErrT $ do
-- install new ssl CA cert + nginx conf and restart nginx
registerResCert <-
runM . handleS9ErrC . (>>= liftEither) . liftIO . runM . injectFilesystemBaseFromContext settings $ do
runM . handleS9ErrC . liftEither <=< liftIO . runM . injectFilesystemBaseFromContext settings $ do
bootupHttpNginx
runError @S9Error $ bootupSslNginx rsaKeyFileContents

View File

@@ -5,6 +5,7 @@ module Handler.Register.Nginx where
import Startlude hiding ( ask
, catchError
, err
)
import Control.Carrier.Error.Church
@@ -54,13 +55,13 @@ resetSslState = do
traverse_
(liftIO . removePathForcibly . toS . flip relativeTo base)
[ rootCaKeyPath
, relBase $ (rootCaCertPath `relativeTo` "/") <> ".csr"
, relBase $ (rootCaCertPath `relativeTo` base) <> ".csr"
, rootCaCertPath
, intermediateCaKeyPath
, relBase $ (intermediateCaCertPath `relativeTo` "/") <> ".csr"
, relBase $ (intermediateCaCertPath `relativeTo` base) <> ".csr"
, intermediateCaCertPath
, entityKeyPath host
, relBase $ (entityCertPath host `relativeTo` "/") <> ".csr"
, relBase $ (entityCertPath host `relativeTo` base) <> ".csr"
, entityCertPath host
, entityConfPath host
, nginxSitesAvailable nginxSslConf
@@ -74,10 +75,9 @@ resetSslState = do
>>= traverse_ removePathForcibly
writeFile (toS $ flip relativeTo base $ rootCaDirectory <> "/index.txt") ""
writeFile (toS $ flip relativeTo base $ intermediateCaDirectory <> "/index.txt") ""
_ <- liftIO $ try @SomeException . removeLink . toS $ (nginxSitesEnabled nginxSslConf) `relativeTo` base
_ <- liftIO $ try @SomeException . removeLink . toS $ nginxSitesEnabled nginxSslConf `relativeTo` base
pure ()
bootupHttpNginx :: (HasFilesystemBase sig m, MonadIO m) => m ()
bootupHttpNginx = installAmbassadorUiNginxHTTP "start9-ambassador.conf"

View File

@@ -198,7 +198,7 @@ toStatus = \case
NoCompliantAgentE _ -> status404
PersistentE _ -> status500
WifiConnectionE -> status500
AppMgrParseE _ _ _ -> status500
AppMgrParseE{} -> status500
AppMgrInvalidConfigE _ -> status400
AppMgrE _ _ -> status500
AppMgrVersionE _ _ -> status500
@@ -220,28 +220,28 @@ toStatus = \case
(AppStatusTmp NeedsConfig, Start) -> status403
(AppStatusTmp NeedsConfig, Stop ) -> status200
(AppStatusTmp _ , _ ) -> status403
UpdateSelfE _ _ -> status500
InvalidSshKeyE _ -> status400
InvalidSsidE -> status400
InvalidPskE -> status400
InvalidRequestE _ _ -> status400
NotFoundE _ _ -> status404
UpdateInProgressE -> status403
TemporarilyForbiddenE _ _ _ -> status403
TorServiceTimeoutE -> status500
NginxSslE _ -> status500
WifiOrphaningE -> status403
ManifestParseE _ _ -> status500
NoPasswordExistsE -> status401
MissingFileE _ -> status500
ClientCryptographyE _ -> status401
TTLExpirationE _ -> status403
EnvironmentValE _ -> status500
HostsParamsE _ -> status400
BackupE _ _ -> status500
BackupPassInvalidE -> status403
InternalE _ -> status500
OpenSslE _ _ _ _ -> status500
UpdateSelfE _ _ -> status500
InvalidSshKeyE _ -> status400
InvalidSsidE -> status400
InvalidPskE -> status400
InvalidRequestE _ _ -> status400
NotFoundE _ _ -> status404
UpdateInProgressE -> status403
TemporarilyForbiddenE{} -> status403
TorServiceTimeoutE -> status500
NginxSslE _ -> status500
WifiOrphaningE -> status403
ManifestParseE _ _ -> status500
NoPasswordExistsE -> status401
MissingFileE _ -> status500
ClientCryptographyE _ -> status401
TTLExpirationE _ -> status403
EnvironmentValE _ -> status500
HostsParamsE _ -> status400
BackupE _ _ -> status500
BackupPassInvalidE -> status403
InternalE _ -> status500
OpenSslE{} -> status500
handleS9ErrC :: (MonadHandler m, MonadLogger m) => ErrorC S9Error m a -> m a
handleS9ErrC action =
@@ -251,12 +251,11 @@ handleS9ErrC action =
in runErrorC action handleIt pure
handleS9ErrT :: (MonadHandler m, MonadLogger m) => S9ErrT m a -> m a
handleS9ErrT action = do
runExceptT action >>= \case
Left e -> do
$logError $ show e
toStatus >>= sendResponseStatus $ e
Right a -> pure a
handleS9ErrT action = runExceptT action >>= \case
Left e -> do
$logError $ show e
toStatus >>= sendResponseStatus $ e
Right a -> pure a
runS9ErrT :: MonadIO m => S9ErrT m a -> m (Either S9Error a)
runS9ErrT = runExceptT

View File

@@ -19,8 +19,8 @@ emit :: MonadIO m => AppId -> Version -> AgentNotification -> SqlPersistT m (Ent
emit appId version ty = do
uuid <- liftIO nextRandom
now <- liftIO getCurrentTime
let k = (NotificationKey uuid)
let v = (Notification now Nothing appId version (toCode ty) (toTitle ty) (toMessage appId version ty))
let k = NotificationKey uuid
let v = Notification now Nothing appId version (toCode ty) (toTitle ty) (toMessage appId version ty)
insertKey k v
putStrLn $ toMessage appId version ty
pure $ Entity k v
@@ -42,6 +42,7 @@ data AgentNotification =
| RestoreFailed S9Error
| RestartFailed S9Error
| DockerFuckening
| CertRenewFailed ExitCode String String
-- CODES
-- RULES:
@@ -54,6 +55,7 @@ data AgentNotification =
-- The second digit indicates where the error was originated from as follows
-- 0: Originates from Agent
-- 1: Originates from App (Not presently used)
-- 2: Originates from Agent ABOUT THE AGENT
--
-- The remaining section of the code may be as long as you want but must be at least one digit
-- EXAMPLES:
@@ -78,6 +80,7 @@ toCode (InstallFailedS9Error _) = "303"
toCode (BackupFailed _) = "304"
toCode (RestoreFailed _) = "305"
toCode (RestartFailed _) = "306"
toCode CertRenewFailed{} = "320"
toTitle :: AgentNotification -> Text
toTitle InstallSuccess = "Install succeeded"
@@ -90,6 +93,7 @@ toTitle (BackupFailed _) = "Backup failed"
toTitle (RestoreFailed _) = "Restore failed"
toTitle (RestartFailed _) = "Restart failed"
toTitle DockerFuckening = "App unstoppable"
toTitle CertRenewFailed{} = "Embassy Certificate Renewal Failed"
toMessage :: AppId -> Version -> AgentNotification -> Text
toMessage appId version InstallSuccess = [i|Successfully installed #{appId} at version #{version}|]
@@ -107,3 +111,10 @@ toMessage appId _version (BackupFailed reason) = [i|Failed to back up #{appId}:
toMessage appId _version (RestoreFailed reason) = [i|Failed to restore #{appId}: #{errorMessage $ toError reason}|]
toMessage appId _version (RestartFailed reason) =
[i|Failed to restart #{appId}: #{errorMessage $ toError reason}. Please manually restart|]
toMessage _ version (CertRenewFailed ec o e) = [i|Failed to renew SSL Certificates for EmbassyOS (#{version})
ExitCode: #{ec}
Stdout:
#{o}
Stderr:
#{e}
|]

View File

@@ -1,6 +1,17 @@
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE QuasiQuotes #-}
module Lib.Ssl where
module Lib.Ssl
( DeriveCertificate(..)
, root_CA_CERT_NAME
, writeRootCaCert
, writeIntermediateCert
, domain_CSR_CONF
, writeLeafCert
, root_CA_OPENSSL_CONF
, intermediate_CA_OPENSSL_CONF
, segment
)
where
import Startlude
@@ -295,7 +306,7 @@ data DeriveCertificate = DeriveCertificate
writeIntermediateCert :: MonadIO m => DeriveCertificate -> m (ExitCode, String, String)
writeIntermediateCert DeriveCertificate {..} = liftIO $ interpret $ do
-- openssl genrsa -out dump/int.key 4096
segment $ openssl [i|genrsa -out #{applicantKeyPath} 4096|]
segment $ openssl [i|ecparam -genkey -name prime256v1 -noout -out #{applicantKeyPath}|]
-- openssl req -new -config dump/int-csr.conf -key dump/int.key -nodes -out dump/int.csr
segment $ openssl [i|req -new
-config #{applicantConfPath}
@@ -317,7 +328,7 @@ writeIntermediateCert DeriveCertificate {..} = liftIO $ interpret $ do
writeLeafCert :: MonadIO m => DeriveCertificate -> Text -> Text -> m (ExitCode, String, String)
writeLeafCert DeriveCertificate {..} hostname torAddress = liftIO $ interpret $ do
segment $ openssl [i|genrsa -out #{applicantKeyPath} 4096|]
segment $ openssl [i|ecparam -genkey -name prime256v1 -noout -out #{applicantKeyPath}|]
segment $ openssl [i|req -config #{applicantConfPath}
-key #{applicantKeyPath}
-new
@@ -336,11 +347,11 @@ writeLeafCert DeriveCertificate {..} hostname torAddress = liftIO $ interpret $
|]
liftIO $ readFile signingCertPath >>= appendFile applicantCertPath
openssl :: Text -> IO (ExitCode, String, String)
openssl = ($ "") . readProcessWithExitCode "openssl" . fmap toS . words
openssl :: MonadIO m => Text -> m (ExitCode, String, String)
openssl = liftIO . ($ "") . readProcessWithExitCode "openssl" . fmap toS . words
{-# INLINE openssl #-}
interpret :: ExceptT ExitCode (StateT (String, String) IO) () -> IO (ExitCode, String, String)
interpret :: MonadIO m => ExceptT ExitCode (StateT (String, String) m) () -> m (ExitCode, String, String)
interpret = fmap (over _1 (either id (const ExitSuccess)) . regroup) . flip runStateT ("", "") . runExceptT
{-# INLINE interpret #-}
@@ -348,8 +359,8 @@ regroup :: (a, (b, c)) -> (a, b, c)
regroup (a, (b, c)) = (a, b, c)
{-# INLINE regroup #-}
segment :: IO (ExitCode, String, String) -> ExceptT ExitCode (StateT (String, String) IO) ()
segment action = liftIO action >>= \case
segment :: MonadIO m => m (ExitCode, String, String) -> ExceptT ExitCode (StateT (String, String) m) ()
segment action = (lift . lift) action >>= \case
(ExitSuccess, o, e) -> modify (bimap (<> o) (<> e))
(ec , o, e) -> modify (bimap (<> o) (<> e)) *> throwE ec
{-# INLINE segment #-}

View File

@@ -5,7 +5,9 @@
{-# LANGUAGE TemplateHaskell #-}
module Lib.Synchronizers where
import Startlude hiding ( check )
import Startlude hiding ( check
, err
)
import qualified Startlude.ByteStream as ByteStream
import qualified Startlude.ByteStream.Char8 as ByteStream
@@ -61,6 +63,8 @@ import Settings
import Util.File
import qualified Lib.Algebra.Domain.AppMgr as AppMgr2
import Daemon.ZeroConf ( getStart9AgentHostname )
import qualified Data.Text as T
import Control.Effect.Error hiding ( run )
data Synchronizer = Synchronizer
@@ -87,16 +91,16 @@ parseKernelVersion = do
major' <- decimal
minor' <- char '.' *> decimal
patch' <- char '.' *> decimal
arch <- string "-v7l+" *> pure ArmV7 <|> string "-v8+" *> pure ArmV8
arch <- string "-v7l+" $> ArmV7 <|> string "-v8+" $> ArmV8
pure $ KernelVersion (Version (major', minor', patch', 0)) arch
synchronizer :: Synchronizer
synchronizer = sync_0_2_5
synchronizer = sync_0_2_6
{-# INLINE synchronizer #-}
sync_0_2_5 :: Synchronizer
sync_0_2_5 = Synchronizer
"0.2.5"
sync_0_2_6 :: Synchronizer
sync_0_2_6 = Synchronizer
"0.2.6"
[ syncCreateAgentTmp
, syncCreateSshDir
, syncRemoveAvahiSystemdDependency
@@ -114,6 +118,7 @@ sync_0_2_5 = Synchronizer
, syncPrepSslRootCaDir
, syncPrepSslIntermediateCaDir
, syncPersistLogs
, syncConvertEcdsaCerts
]
syncCreateAgentTmp :: SyncOp
@@ -141,7 +146,7 @@ syncCreateSshDir = SyncOp "Create SSH directory" check migrate False
syncRemoveAvahiSystemdDependency :: SyncOp
syncRemoveAvahiSystemdDependency = SyncOp "Remove Avahi Systemd Dependency" check migrate False
where
wanted = decodeUtf8 $ $(embedFile "config/agent.service")
wanted = decodeUtf8 $(embedFile "config/agent.service")
check = do
base <- asks $ appFilesystemBase . appSettings
content <- liftIO $ readFile (toS $ agentServicePath `relativeTo` base)
@@ -172,7 +177,7 @@ sync32BitKernel = SyncOp "32 Bit Kernel Switch" check migrate True
check = do
settings <- asks appSettings
cfg <- injectFilesystemBaseFromContext settings getBootCfgPath
liftIO . run $ fmap isNothing $ (shell [i|grep "arm_64bit=0" #{cfg} || true|] $| conduit await)
liftIO . run $ isNothing <$> (shell [i|grep "arm_64bit=0" #{cfg} || true|] $| conduit await)
migrate = do
base <- asks $ appFilesystemBase . appSettings
let tmpFile = bootConfigTempPath `relativeTo` base
@@ -234,9 +239,9 @@ syncWriteConf name contents' confLocation = SyncOp [i|Write #{name} Conf|] check
liftIO
$ (Just <$> readFile (toS $ confLocation `relativeTo` base))
`catch` (\(e :: IOException) -> if isDoesNotExistError e then pure Nothing else throwIO e)
case conf of
Nothing -> pure True
Just co -> pure $ if co == contents then False else True
pure $ case conf of
Nothing -> True
Just co -> co /= contents
migrate = do
base <- asks $ appFilesystemBase . appSettings
void . liftIO $ createDirectoryIfMissing True (takeDirectory (toS $ confLocation `relativeTo` base))
@@ -330,7 +335,7 @@ syncInstallAmbassadorUI = SyncOp "Install Ambassador UI" check migrate False
streamUntar root stream = Conduit.runConduit $ Conduit.fromBStream stream .| Conduit.untar \f -> do
let path = toS . (toS root </>) . joinPath . drop 1 . splitPath . B8.unpack . Conduit.filePath $ f
print path
if (Conduit.fileType f == Conduit.FTDirectory)
if Conduit.fileType f == Conduit.FTDirectory
then liftIO $ createDirectoryIfMissing True path
else Conduit.sinkFile path
@@ -372,8 +377,8 @@ installAmbassadorUiNginx mSslOverrides fileName = do
void . liftIO $ systemCtl RestartService "nginx"
where
ambassadorUiClientManifiest b = toS $ (ambassadorUiPath <> "/client-manifest.yaml") `relativeTo` b
nginxAvailableConf b = toS $ (nginxSitesAvailable fileName) `relativeTo` b
nginxEnabledConf b = toS $ (nginxSitesEnabled fileName) `relativeTo` b
nginxAvailableConf b = toS $ nginxSitesAvailable fileName `relativeTo` b
nginxEnabledConf b = toS $ nginxSitesEnabled fileName `relativeTo` b
syncOpenHttpPorts :: SyncOp
syncOpenHttpPorts = SyncOp "Open Hidden Service Port 80" check migrate False
@@ -426,6 +431,111 @@ syncPersistLogs :: SyncOp
syncPersistLogs =
(syncWriteConf "Journald" $(embedFile "config/journald.conf") journaldConfig) { syncOpRequiresReboot = True }
syncRepairSsl :: SyncOp
syncRepairSsl = SyncOp "Repair SSL Certs" check migrate False
where
check = do
base <- asks $ appFilesystemBase . appSettings
let p = toS $ sslDirectory `relativeTo` base
liftIO $ not <$> doesDirectoryExist p
migrate = do
base <- asks $ appFilesystemBase . appSettings
let newCerts = toS $ (agentTmpDirectory <> sslDirectory) `relativeTo` base
liftIO $ renameDirectory newCerts (toS $ sslDirectory `relativeTo` base)
liftIO $ systemCtl RestartService "nginx" $> ()
syncConvertEcdsaCerts :: SyncOp
syncConvertEcdsaCerts = SyncOp "Convert Intermediate Cert to ECDSA P256" check migrate False
where
check = do
fs <- asks $ appFilesystemBase . appSettings
let intCertKey = toS $ intermediateCaKeyPath `relativeTo` fs
exists <- liftIO $ doesPathExist intCertKey
if exists
then do
header <- liftIO $ headMay . lines <$> readFile intCertKey
pure $ case header of
Nothing -> False
Just y -> "BEGIN RSA PRIVATE KEY" `T.isInfixOf` y
else pure False
migrate = cantFail $ do
base <- asks $ appFilesystemBase . appSettings
(runM . runExceptT) (injectFilesystemBase base replaceDerivativeCerts) >>= \case
Left e -> failUpdate e
Right () -> pure ()
replaceDerivativeCerts :: (HasFilesystemBase sig m, Fused.Has (Error S9Error) sig m, MonadIO m) => m ()
replaceDerivativeCerts = do
sid <- getStart9AgentHostname
let hostname = sid <> ".local"
tor <- getAgentHiddenServiceUrl
caKeyPath <- toS <$> getAbsoluteLocationFor rootCaKeyPath
caConfPath <- toS <$> getAbsoluteLocationFor rootCaOpenSslConfPath
caCertPath <- toS <$> getAbsoluteLocationFor rootCaCertPath
intCaKeyPath <- toS <$> getAbsoluteLocationFor intermediateCaKeyPath
intCaConfPath <- toS <$> getAbsoluteLocationFor intermediateCaOpenSslConfPath
intCaCertPath <- toS <$> getAbsoluteLocationFor intermediateCaCertPath
sslDirTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> sslDirectory)
entKeyPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityKeyPath sid)
entConfPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityConfPath sid)
entCertPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityCertPath sid)
liftIO $ createDirectoryIfMissing True sslDirTmp
liftIO $ BS.writeFile entConfPathTmp (domain_CSR_CONF hostname)
-- ensure duplicate certificates are acceptable
base <- Fused.ask @"filesystemBase"
liftIO $ BS.writeFile (toS $ (rootCaDirectory <> "index.txt.attr") `relativeTo` base) "unique_subject = no\n"
liftIO $ BS.writeFile (toS $ (intermediateCaDirectory <> "index.txt.attr") `relativeTo` base)
"unique_subject = no\n"
(ec, out, err) <- writeIntermediateCert DeriveCertificate { applicantConfPath = intCaConfPath
, applicantKeyPath = intCaKeyPath
, applicantCertPath = intCaCertPath
, signingConfPath = caConfPath
, signingKeyPath = caKeyPath
, signingCertPath = caCertPath
, duration = 3650
}
liftIO $ do
putStrLn @Text "openssl logs"
putStrLn @Text "exit code: "
print ec
putStrLn @String $ "stdout: " <> out
putStrLn @String $ "stderr: " <> err
case ec of
ExitSuccess -> pure ()
ExitFailure n -> throwError $ OpenSslE "leaf" n out err
(ec', out', err') <- writeLeafCert
DeriveCertificate { applicantConfPath = entConfPathTmp
, applicantKeyPath = entKeyPathTmp
, applicantCertPath = entCertPathTmp
, signingConfPath = intCaConfPath
, signingKeyPath = intCaKeyPath
, signingCertPath = intCaCertPath
, duration = 365
}
hostname
tor
liftIO $ do
putStrLn @Text "openssl logs"
putStrLn @Text "exit code: "
print ec
putStrLn @String $ "stdout: " <> out'
putStrLn @String $ "stderr: " <> err'
case ec' of
ExitSuccess -> pure ()
ExitFailure n -> throwError $ OpenSslE "leaf" n out' err'
sslDir <- toS <$> getAbsoluteLocationFor sslDirectory
liftIO $ removePathForcibly sslDir
liftIO $ renameDirectory sslDirTmp sslDir
liftIO $ systemCtl RestartService "nginx" $> ()
failUpdate :: S9Error -> ExceptT Void (ReaderT AgentCtx IO) ()
failUpdate e = do
ref <- asks appIsUpdateFailed

View File

@@ -76,18 +76,15 @@ getAbsoluteLocationFor path = do
readSystemPath :: (HasFilesystemBase sig m, MonadIO m) => SystemPath -> m (Maybe Text)
readSystemPath path = do
loadPath <- getAbsoluteLocationFor path
contents <-
liftIO
liftIO
$ (Just <$> readFile (toS loadPath))
`catch` (\(e :: IOException) -> if isDoesNotExistError e then pure Nothing else throwIO e)
pure contents
-- like the above, but throws IO error if file not found
readSystemPath' :: (HasFilesystemBase sig m, MonadIO m) => SystemPath -> m Text
readSystemPath' path = do
loadPath <- getAbsoluteLocationFor path
contents <- liftIO . readFile . toS $ loadPath
pure contents
liftIO . readFile . toS $ loadPath
writeSystemPath :: (HasFilesystemBase sig m, MonadIO m) => SystemPath -> Text -> m ()
writeSystemPath path contents = do

2
appmgr/Cargo.lock generated
View File

@@ -35,7 +35,7 @@ dependencies = [
[[package]]
name = "appmgr"
version = "0.2.5"
version = "0.2.6"
dependencies = [
"argonautica",
"async-trait",

View File

@@ -1,6 +1,6 @@
[package]
name = "appmgr"
version = "0.2.5"
version = "0.2.6"
authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"

View File

@@ -5,4 +5,5 @@ shopt -s expand_aliases
alias 'rust-arm-builder'='docker run --rm -it -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src start9/rust-arm-cross:latest'
rust-arm-builder cargo build --release
cd ..
rust-arm-builder sh -c "(cd appmgr && cargo build --release)"

View File

@@ -5,5 +5,7 @@ shopt -s expand_aliases
alias 'rust-arm-builder'='docker run --rm -it -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src start9/rust-arm-cross:latest'
rust-arm-builder cargo build --release --features=production
cd ..
rust-arm-builder sh -c "(cd appmgr && cargo build --release --features=production)"
cd appmgr
rust-arm-builder arm-linux-gnueabi-strip target/armv7-unknown-linux-gnueabihf/release/appmgr

View File

@@ -20,6 +20,7 @@ mod v0_2_2;
mod v0_2_3;
mod v0_2_4;
mod v0_2_5;
mod v0_2_6;
pub use v0_2_5::Version as Current;
@@ -39,6 +40,7 @@ enum Version {
V0_2_3(Wrapper<v0_2_3::Version>),
V0_2_4(Wrapper<v0_2_4::Version>),
V0_2_5(Wrapper<v0_2_5::Version>),
V0_2_6(Wrapper<v0_2_6::Version>),
Other(emver::Version),
}
@@ -146,6 +148,7 @@ pub async fn init() -> Result<(), failure::Error> {
Version::V0_2_3(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_4(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_5(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_6(v) => v.0.migrate_to(&Current::new()).await?,
Version::Other(_) => (),
// TODO find some way to automate this?
}
@@ -231,6 +234,7 @@ pub async fn self_update(requirement: emver::VersionRange) -> Result<(), Error>
Version::V0_2_3(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_4(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_5(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_6(v) => Current::new().migrate_to(&v.0).await?,
Version::Other(_) => (),
// TODO find some way to automate this?
};

View File

@@ -0,0 +1,21 @@
use super::*;
const V0_2_6: emver::Version = emver::Version::new(0, 2, 6, 0);
pub struct Version;
#[async_trait]
impl VersionT for Version {
type Previous = v0_2_5::Version;
fn new() -> Self {
Version
}
fn semver(&self) -> &'static emver::Version {
&V0_2_6
}
async fn up(&self) -> Result<(), Error> {
Ok(())
}
async fn down(&self) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -1,6 +1,6 @@
manifest-version: 0
app-id: start9-ambassador
app-version: 0.2.5
app-version: 0.2.6
uri-rewrites:
- =/api -> http://{{start9-ambassador}}:5959/authenticate
- /api/ -> http://{{start9-ambassador}}:5959/

1116
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "embassy-ui",
"version": "0.2.5",
"version": "0.2.6",
"description": "GUI for EmbassyOS",
"author": "Start9 Labs",
"homepage": "https://github.com/Start9Labs/embassy-ui",
@@ -23,7 +23,6 @@
"@angular/router": "^10.1.6",
"@ionic/angular": "^5.4.0",
"@ionic/storage": "2.2.0",
"@start9labs/ambassador-sdk": "file:../ambassador-sdk",
"@start9labs/emver": "^0.1.1",
"ajv": "^6.12.6",
"angularx-qrcode": "^10.0.11",
@@ -57,6 +56,6 @@
"node-html-parser": "^1.3.1",
"ts-node": "^9.0.0",
"tslint": "^6.1.0",
"typescript": "^4.0.3"
"typescript": "4.0.5"
}
}

View File

@@ -63,46 +63,46 @@
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
<!-- Ionicons -->
<ion-icon name="add"></ion-icon> <!--0.2.5-->
<ion-icon name="alert-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="alert-circle-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="arrow-back"></ion-icon> <!--0.2.5-->
<ion-icon name="arrow-forward"></ion-icon> <!--0.2.5-->
<ion-icon name="arrow-up"></ion-icon> <!--0.2.5-->
<ion-icon name="add"></ion-icon>
<ion-icon name="alert-outline"></ion-icon>
<ion-icon name="alert-circle-outline"></ion-icon>
<ion-icon name="arrow-back"></ion-icon>
<ion-icon name="arrow-forward"></ion-icon>
<ion-icon name="arrow-up"></ion-icon>
<ion-icon name="bookmark-outline"></ion-icon>
<ion-icon name="cart-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="chevron-down"></ion-icon> <!--0.2.5-->
<ion-icon name="chevron-up"></ion-icon> <!--0.2.5-->
<ion-icon name="close"></ion-icon> <!--0.2.5-->
<ion-icon name="close-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="code-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="cog-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="color-wand-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="construct-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="copy-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="cube-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="download-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="ellipse"></ion-icon> <!--0.2.5-->
<ion-icon name="eye-off-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="eye-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="cart-outline"></ion-icon>
<ion-icon name="chevron-down"></ion-icon>
<ion-icon name="chevron-up"></ion-icon>
<ion-icon name="close"></ion-icon>
<ion-icon name="close-outline"></ion-icon>
<ion-icon name="code-outline"></ion-icon>
<ion-icon name="cog-outline"></ion-icon>
<ion-icon name="color-wand-outline"></ion-icon>
<ion-icon name="construct-outline"></ion-icon>
<ion-icon name="copy-outline"></ion-icon>
<ion-icon name="cube-outline"></ion-icon>
<ion-icon name="download-outline"></ion-icon>
<ion-icon name="ellipse"></ion-icon>
<ion-icon name="eye-off-outline"></ion-icon>
<ion-icon name="eye-outline"></ion-icon>
<ion-icon name="file-tray-stacked-outline"></ion-icon>
<ion-icon name="grid-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="help-circle-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="home-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="information-circle-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="list-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="newspaper-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="notifications-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="power"></ion-icon> <!--0.2.5-->
<ion-icon name="pulse"></ion-icon> <!--0.2.5-->
<ion-icon name="qr-code-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="reload-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="refresh-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="save-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="terminal-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="trash-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="warning-outline"></ion-icon> <!--0.2.5-->
<ion-icon name="wifi"></ion-icon> <!--0.2.5-->
<ion-icon name="grid-outline"></ion-icon>
<ion-icon name="help-circle-outline"></ion-icon>
<ion-icon name="home-outline"></ion-icon>
<ion-icon name="information-circle-outline"></ion-icon>
<ion-icon name="list-outline"></ion-icon>
<ion-icon name="newspaper-outline"></ion-icon>
<ion-icon name="notifications-outline"></ion-icon>
<ion-icon name="power"></ion-icon>
<ion-icon name="pulse"></ion-icon>
<ion-icon name="qr-code-outline"></ion-icon>
<ion-icon name="reload-outline"></ion-icon>
<ion-icon name="refresh-outline"></ion-icon>
<ion-icon name="save-outline"></ion-icon>
<ion-icon name="terminal-outline"></ion-icon>
<ion-icon name="trash-outline"></ion-icon>
<ion-icon name="warning-outline"></ion-icon>
<ion-icon name="wifi"></ion-icon>
<!-- Ionic components -->
<ion-action-sheet></ion-action-sheet>
<ion-alert></ion-alert>
@@ -139,15 +139,15 @@
<ion-loading></ion-loading>
<ion-modal></ion-modal>
<ion-note></ion-note>
<ion-radio></ion-radio> <!--0.2.5-->
<ion-radio></ion-radio>
<ion-row></ion-row>
<ion-segment></ion-segment>
<ion-segment-button></ion-segment-button>
<ion-select></ion-select>
<ion-select-option></ion-select-option>
<ion-slides></ion-slides> <!--0.2.5-->
<ion-spinner name="dots"></ion-spinner> <!--0.2.5-->
<ion-spinner name="lines"></ion-spinner> <!--0.2.5-->
<ion-slides></ion-slides>
<ion-spinner name="dots"></ion-spinner>
<ion-spinner name="lines"></ion-spinner>
<ion-text></ion-text>
<ion-textarea></ion-textarea>
<ion-title></ion-title>

View File

@@ -389,7 +389,7 @@ const mockApiNotifications: ReqRes.GetNotificationsRes = [
const mockApiServer: () => ReqRes.GetServerRes = () => ({
serverId: 'start9-mockxyzab',
name: 'Embassy:12345678',
versionInstalled: '0.2.5',
versionInstalled: '0.2.6',
status: ServerStatus.RUNNING,
alternativeRegistryUrl: 'beta-registry.start9labs.com',
specs: {
@@ -420,7 +420,7 @@ const mockApiServer: () => ReqRes.GetServerRes = () => ({
})
const mockVersionLatest: ReqRes.GetVersionLatestRes = {
versionLatest: '0.2.5',
versionLatest: '0.2.6',
canUpdate: true,
}

View File

@@ -1,8 +0,0 @@
import WebviewContext from '@start9labs/ambassador-sdk/dist/webview-context'
export const webviewContext = new WebviewContext(async (method: string, data: any) => {
throw new Error (`${method} UNIMPLEMENTED`)
// switch(method){
// case 'getConfigValue': throw new Error ('getConfigValue UNIMPLEMENTED')
// }
})