mirror of
https://code.sup39.dev/repos/Wqawg
synced 2024-12-28 07:54:50 +09:00
Periodically rotated AP actor key for signing ActivityPub requests
The actor key will be used for all actors on the server. It's held in a `TVar` so that it can always be safely updated and safely retrieved (technically there is a single writer so IORef and MVar could work, but they require extra care while TVar is by design suited for this sort of thing).
This commit is contained in:
parent
adaa920aa4
commit
499e26db48
8 changed files with 115 additions and 4 deletions
|
@ -33,6 +33,17 @@ client-session-timeout:
|
||||||
amount: 2
|
amount: 2
|
||||||
unit: hours
|
unit: hours
|
||||||
|
|
||||||
|
# Maximal accepted time difference between request date and current time, when
|
||||||
|
# performing this check during HTTP signature verification
|
||||||
|
request-time-limit:
|
||||||
|
amount: 5
|
||||||
|
unit: seconds
|
||||||
|
|
||||||
|
# How often to generate a new actor key for HTTP-signing requests
|
||||||
|
actor-key-rotation:
|
||||||
|
amount: 1
|
||||||
|
unit: hours
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Development
|
# Development
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
73
src/Vervis/ActorKey.hs
Normal file
73
src/Vervis/ActorKey.hs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
-
|
||||||
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
-
|
||||||
|
- The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
- rights to this software to the public domain worldwide. This software is
|
||||||
|
- distributed without any warranty.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
- with this software. If not, see
|
||||||
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
module Vervis.ActorKey
|
||||||
|
( ActorKey ()
|
||||||
|
, generateActorKey
|
||||||
|
, actorKeyRotator
|
||||||
|
, actorPublicKey
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
|
import Control.Concurrent.STM (TVar, writeTVar)
|
||||||
|
import Control.Monad (forever)
|
||||||
|
import Control.Monad.STM (atomically)
|
||||||
|
import Crypto.PubKey.Ed25519
|
||||||
|
import Data.ByteArray (convert)
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.Time.Interval (TimeInterval, microseconds)
|
||||||
|
import Data.PEM
|
||||||
|
|
||||||
|
-- | Ed25519 signing key.
|
||||||
|
data ActorKey = ActorKey
|
||||||
|
{ actorKeySecret :: SecretKey
|
||||||
|
, actorKeyPublic :: PublicKey
|
||||||
|
, actorKeyPublicPem :: ByteString
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Generate a new random key.
|
||||||
|
generateActorKey :: IO ActorKey
|
||||||
|
generateActorKey = mk <$> generateSecretKey
|
||||||
|
where
|
||||||
|
mk secret =
|
||||||
|
let public = toPublic secret
|
||||||
|
in ActorKey
|
||||||
|
{ actorKeySecret = secret
|
||||||
|
, actorKeyPublic = public
|
||||||
|
, actorKeyPublicPem =
|
||||||
|
pemWriteBS $ PEM "PUBLIC KEY" [] $ convert public
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | A loop that runs forever and periodically generates a new actor key,
|
||||||
|
-- storing it in a 'TVar'.
|
||||||
|
actorKeyRotator :: TimeInterval -> TVar ActorKey -> IO ()
|
||||||
|
actorKeyRotator interval key =
|
||||||
|
let micros = microseconds interval
|
||||||
|
in if 0 < micros && micros <= toInteger (maxBound :: Int)
|
||||||
|
then
|
||||||
|
let micros' = fromInteger micros
|
||||||
|
in forever $ do
|
||||||
|
threadDelay micros'
|
||||||
|
generateActorKey >>= atomically . writeTVar key
|
||||||
|
else
|
||||||
|
error $
|
||||||
|
"actorKeyRotator: interval out of range: " ++ show micros
|
||||||
|
|
||||||
|
-- | The public key in PEM format, can be directly placed in responses.
|
||||||
|
actorPublicKey :: ActorKey -> ByteString
|
||||||
|
actorPublicKey = actorKeyPublicPem
|
|
@ -108,6 +108,8 @@ makeFoundation appSettings = do
|
||||||
then lin2
|
then lin2
|
||||||
else loadFont "data/LinLibertineCut.svg"
|
else loadFont "data/LinLibertineCut.svg"
|
||||||
|
|
||||||
|
appActorKey <- newTVarIO =<< generateActorKey
|
||||||
|
|
||||||
-- We need a log function to create a connection pool. We need a connection
|
-- We need a log function to create a connection pool. We need a connection
|
||||||
-- pool to create our foundation. And we need our foundation to get a
|
-- pool to create our foundation. And we need our foundation to get a
|
||||||
-- logging function. To get out of this loop, we initially create a
|
-- logging function. To get out of this loop, we initially create a
|
||||||
|
@ -193,6 +195,10 @@ getAppSettings = loadAppSettings [configSettingsYml] [] useEnv
|
||||||
develMain :: IO ()
|
develMain :: IO ()
|
||||||
develMain = develMainHelper getApplicationDev
|
develMain = develMainHelper getApplicationDev
|
||||||
|
|
||||||
|
actorKeyPeriodicRotator :: App -> IO ()
|
||||||
|
actorKeyPeriodicRotator app =
|
||||||
|
actorKeyRotator (appActorKeyRotation $ appSettings app) (appActorKey app)
|
||||||
|
|
||||||
sshServer :: App -> IO ()
|
sshServer :: App -> IO ()
|
||||||
sshServer foundation =
|
sshServer foundation =
|
||||||
runSsh
|
runSsh
|
||||||
|
@ -231,6 +237,9 @@ appMain = do
|
||||||
-- Generate a WAI Application from the foundation
|
-- Generate a WAI Application from the foundation
|
||||||
app <- makeApplication foundation
|
app <- makeApplication foundation
|
||||||
|
|
||||||
|
-- Run actor signature key periodic generation thread
|
||||||
|
forkCheck $ actorKeyPeriodicRotator foundation
|
||||||
|
|
||||||
-- Run SSH server
|
-- Run SSH server
|
||||||
forkCheck $ sshServer foundation
|
forkCheck $ sshServer foundation
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -44,6 +44,7 @@ import Data.Text as T (pack, intercalate, concat)
|
||||||
--import qualified Data.Text.Encoding as TE
|
--import qualified Data.Text.Encoding as TE
|
||||||
|
|
||||||
import Text.Jasmine.Local (discardm)
|
import Text.Jasmine.Local (discardm)
|
||||||
|
import Vervis.ActorKey (ActorKey)
|
||||||
import Vervis.Import.NoFoundation hiding (Handler, Day, last, init)
|
import Vervis.Import.NoFoundation hiding (Handler, Day, last, init)
|
||||||
import Vervis.Model.Group
|
import Vervis.Model.Group
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
@ -63,6 +64,7 @@ data App = App
|
||||||
, appLogger :: Logger
|
, appLogger :: Logger
|
||||||
, appMailQueue :: Maybe (Chan (MailRecipe App))
|
, appMailQueue :: Maybe (Chan (MailRecipe App))
|
||||||
, appSvgFont :: PreparedFont Double
|
, appSvgFont :: PreparedFont Double
|
||||||
|
, appActorKey :: TVar ActorKey
|
||||||
}
|
}
|
||||||
|
|
||||||
-- This is where we define all of the routes in our application. For a full
|
-- This is where we define all of the routes in our application. For a full
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -66,6 +66,12 @@ data AppSettings = AppSettings
|
||||||
-- | Idle timeout for session cookie expiration
|
-- | Idle timeout for session cookie expiration
|
||||||
, appClientSessionTimeout :: TimeInterval
|
, appClientSessionTimeout :: TimeInterval
|
||||||
|
|
||||||
|
-- Maximal accepted difference between current time and Date header
|
||||||
|
, appHttpSigTimeLimit :: TimeInterval
|
||||||
|
|
||||||
|
-- How often to generate a new actor key for making HTTP signatures
|
||||||
|
, appActorKeyRotation :: TimeInterval
|
||||||
|
|
||||||
-- | Use detailed request logging system
|
-- | Use detailed request logging system
|
||||||
, appDetailedRequestLogging :: Bool
|
, appDetailedRequestLogging :: Bool
|
||||||
-- | Should all log messages be displayed?
|
-- | Should all log messages be displayed?
|
||||||
|
@ -116,6 +122,9 @@ instance FromJSON AppSettings where
|
||||||
appClientSessionKeyFile <- o .: "client-session-key"
|
appClientSessionKeyFile <- o .: "client-session-key"
|
||||||
appClientSessionTimeout <- interval <$> o .: "client-session-timeout"
|
appClientSessionTimeout <- interval <$> o .: "client-session-timeout"
|
||||||
|
|
||||||
|
appHttpSigTimeLimit <- interval <$> o .: "request-time-limit"
|
||||||
|
appActorKeyRotation <- interval <$> o .: "actor-key-rotation"
|
||||||
|
|
||||||
appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev
|
appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev
|
||||||
appShouldLogAll <- o .:? "should-log-all" .!= defaultDev
|
appShouldLogAll <- o .:? "should-log-all" .!= defaultDev
|
||||||
appReloadTemplates <- o .:? "reload-templates" .!= defaultDev
|
appReloadTemplates <- o .:? "reload-templates" .!= defaultDev
|
||||||
|
|
|
@ -18,6 +18,7 @@ packages:
|
||||||
- lib/hit-graph
|
- lib/hit-graph
|
||||||
- lib/hit-harder
|
- lib/hit-harder
|
||||||
- lib/hit-network
|
- lib/hit-network
|
||||||
|
- lib/http-signature
|
||||||
- lib/persistent-migration
|
- lib/persistent-migration
|
||||||
- lib/persistent-email-address
|
- lib/persistent-email-address
|
||||||
- lib/time-interval-aeson
|
- lib/time-interval-aeson
|
||||||
|
@ -26,6 +27,7 @@ packages:
|
||||||
git: https://dev.angeley.es/s/fr33domlover/r/yesod-auth-account
|
git: https://dev.angeley.es/s/fr33domlover/r/yesod-auth-account
|
||||||
commit: c14795264c3d63b2126e91e98107a631405cea74
|
commit: c14795264c3d63b2126e91e98107a631405cea74
|
||||||
extra-dep: true
|
extra-dep: true
|
||||||
|
- lib/yesod-http-signature
|
||||||
- lib/yesod-mail-send
|
- lib/yesod-mail-send
|
||||||
|
|
||||||
# Packages to be pulled from upstream that are not in the resolver (e.g.,
|
# Packages to be pulled from upstream that are not in the resolver (e.g.,
|
||||||
|
|
|
@ -7,10 +7,12 @@ DEPS='hit-graph
|
||||||
hit-network
|
hit-network
|
||||||
darcs-lights
|
darcs-lights
|
||||||
darcs-rev
|
darcs-rev
|
||||||
|
http-signature
|
||||||
ssh
|
ssh
|
||||||
persistent-migration
|
persistent-migration
|
||||||
persistent-email-address
|
persistent-email-address
|
||||||
time-interval-aeson
|
time-interval-aeson
|
||||||
|
yesod-http-signature
|
||||||
yesod-mail-send'
|
yesod-mail-send'
|
||||||
|
|
||||||
mkdir -p lib
|
mkdir -p lib
|
||||||
|
|
|
@ -97,6 +97,7 @@ library
|
||||||
Yesod.SessionEntity
|
Yesod.SessionEntity
|
||||||
|
|
||||||
Vervis.ActivityStreams
|
Vervis.ActivityStreams
|
||||||
|
Vervis.ActorKey
|
||||||
Vervis.Application
|
Vervis.Application
|
||||||
Vervis.Avatar
|
Vervis.Avatar
|
||||||
Vervis.BinaryBody
|
Vervis.BinaryBody
|
||||||
|
@ -224,7 +225,6 @@ library
|
||||||
, colour
|
, colour
|
||||||
, conduit
|
, conduit
|
||||||
, containers
|
, containers
|
||||||
-- for SHA1 hashing when parsing Darcs patch metadata
|
|
||||||
, cryptonite
|
, cryptonite
|
||||||
-- for Storage.Hashed because hashed-storage seems
|
-- for Storage.Hashed because hashed-storage seems
|
||||||
-- unmaintained and darcs has its own copy
|
-- unmaintained and darcs has its own copy
|
||||||
|
@ -264,9 +264,9 @@ library
|
||||||
--, hjsmin
|
--, hjsmin
|
||||||
-- 'git' uses it for 'GitTime'
|
-- 'git' uses it for 'GitTime'
|
||||||
, hourglass
|
, hourglass
|
||||||
|
, yesod-http-signature
|
||||||
, http-types
|
, http-types
|
||||||
, libravatar
|
, libravatar
|
||||||
-- for converting Darcs patch hash Digest to ByteString
|
|
||||||
, memory
|
, memory
|
||||||
, mime-mail
|
, mime-mail
|
||||||
, monad-control
|
, monad-control
|
||||||
|
@ -279,6 +279,7 @@ library
|
||||||
-- for PathPiece instance for CI, Web.PathPieces.Local
|
-- for PathPiece instance for CI, Web.PathPieces.Local
|
||||||
, path-pieces
|
, path-pieces
|
||||||
, patience
|
, patience
|
||||||
|
, pem
|
||||||
, persistent
|
, persistent
|
||||||
, persistent-email-address
|
, persistent-email-address
|
||||||
, persistent-migration
|
, persistent-migration
|
||||||
|
@ -293,6 +294,8 @@ library
|
||||||
, skylighting
|
, skylighting
|
||||||
, smtp-mail
|
, smtp-mail
|
||||||
, ssh
|
, ssh
|
||||||
|
-- for holding actor key in a TVar
|
||||||
|
, stm
|
||||||
-- for rendering diagrams
|
-- for rendering diagrams
|
||||||
, svg-builder
|
, svg-builder
|
||||||
-- for text drawing in 'diagrams'
|
-- for text drawing in 'diagrams'
|
||||||
|
|
Loading…
Reference in a new issue