diff --git a/changelog.d/5-internal/sts-expiry-metrics b/changelog.d/5-internal/sts-expiry-metrics new file mode 100644 index 0000000000..9416e009f3 --- /dev/null +++ b/changelog.d/5-internal/sts-expiry-metrics @@ -0,0 +1 @@ +Add AWS security token metrics to brig diff --git a/services/brig/src/Brig/AWS.hs b/services/brig/src/Brig/AWS.hs index 85c081a514..529d3d88bb 100644 --- a/services/brig/src/Brig/AWS.hs +++ b/services/brig/src/Brig/AWS.hs @@ -42,6 +42,8 @@ module Brig.AWS -- * AWS exec, execCatch, + readAuthExpiration, + isAuthARef, ) where @@ -62,6 +64,7 @@ import Data.ByteString.Builder (toLazyByteString) import qualified Data.ByteString.Lazy as BL import qualified Data.Text as Text import qualified Data.Text.Encoding as Text +import Data.Time import Data.UUID hiding (null) import Imports hiding (group) import Network.HTTP.Client (HttpException (..), HttpExceptionContent (..), Manager) @@ -270,3 +273,19 @@ canRetry (Left e) = case e of retry5x :: (Monad m) => RetryPolicyM m retry5x = limitRetries 5 <> exponentialBackoff 100000 + +readAuthExpiration :: AWS.Env -> IO (Maybe NominalDiffTime) +readAuthExpiration env = do + authEnv <- + case runIdentity (AWS.envAuth env) of + AWS.Auth authEnv -> pure authEnv + AWS.Ref _ ref -> do + readIORef ref + now <- getCurrentTime + pure $ ((`diffUTCTime` now) . AWS.fromTime) <$> (AWS._authExpiration authEnv) + +isAuthARef :: AWS.Env -> Bool +isAuthARef env = + case runIdentity (AWS.envAuth env) of + AWS.Auth _ -> False + AWS.Ref _ _ -> True diff --git a/services/brig/src/Brig/Run.hs b/services/brig/src/Brig/Run.hs index a2b87e71a1..606fe74210 100644 --- a/services/brig/src/Brig/Run.hs +++ b/services/brig/src/Brig/Run.hs @@ -29,7 +29,7 @@ import Brig.API.Handler import qualified Brig.API.Internal as IAPI import Brig.API.Public (SwaggerDocsAPI, servantSitemap, swaggerDocsAPI) import qualified Brig.API.User as API -import Brig.AWS (sesQueue) +import Brig.AWS (amazonkaEnv, sesQueue) import qualified Brig.AWS as AWS import qualified Brig.AWS.SesNotification as SesNotification import Brig.App @@ -49,10 +49,12 @@ import Control.Monad.Random (randomRIO) import qualified Data.Aeson as Aeson import Data.Default (Default (def)) import Data.Id (RequestId (..)) +import Data.Metrics (gaugeSet, path) import qualified Data.Metrics.Servant as Metrics import Data.Proxy (Proxy (Proxy)) import Data.String.Conversions (cs) import Data.Text (unpack) +import Data.Time (NominalDiffTime) import Imports hiding (head) import qualified Network.HTTP.Media as HTTPMedia import qualified Network.HTTP.Types as HTTP @@ -94,6 +96,7 @@ run o = do AWS.listen throttleMillis q (runAppT e . SesNotification.onEvent) sftDiscovery <- forM (e ^. sftEnv) $ Async.async . Calling.startSFTServiceDiscovery (e ^. applog) turnDiscovery <- Calling.startTurnDiscovery (e ^. applog) (e ^. fsWatcher) (e ^. turnEnv) + authMetrics <- Async.async (runAppT e collectAuthMetrics) pendingActivationCleanupAsync <- Async.async (runAppT e pendingActivationCleanup) runSettingsWithShutdown s app 5 `finally` do @@ -102,6 +105,7 @@ run o = do mapM_ Async.cancel sftDiscovery Async.cancel pendingActivationCleanupAsync mapM_ Async.cancel turnDiscovery + Async.cancel authMetrics closeEnv e where endpoint = brig o @@ -230,3 +234,17 @@ pendingActivationCleanup = do hours :: Double -> Timeout hours n = realToFrac (n * 60 * 60) + +collectAuthMetrics :: forall r. AppT r () +collectAuthMetrics = do + m <- view metrics + env <- view (awsEnv . amazonkaEnv) + + forever $ do + t <- toSeconds . fromMaybe 0 <$$> liftIO $ AWS.readAuthExpiration env + gaugeSet t (path "aws_auth.token_secs_remaining") m + gaugeSet (if AWS.isAuthARef env then 1.0 else 0.0) (path "aws_auth.token_is_reference") m + liftIO $ threadDelay 1_000_000 + where + toSeconds :: NominalDiffTime -> Double + toSeconds = fromRational . toRational