Skip to content

Commit

Permalink
Optionally read secret from a file
Browse files Browse the repository at this point in the history
When the secret value starts with '@', the rest of the value is
treated like the path to a file which contains the secret.

This change allows to hide the secret from the command line,
which solves issue PostgREST#474.

Based on:
commit 449480b
Author: Joe Nelson <[email protected]>
Date:   Sat Sep 3 23:47:56 2016 -0700

    Require JWT secret, remove default, optionally read from file

    Fixes PostgREST#474, fixes PostgREST#495 when file is used
  • Loading branch information
Eric Bréchemier committed May 18, 2017
1 parent e842667 commit 77269ab
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 11 deletions.
15 changes: 12 additions & 3 deletions main/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import PostgREST.DbStructure
import Control.Monad
import Data.Monoid ((<>))
import Data.String.Conversions (cs)
import Data.Text (stripPrefix,
pack, unpack)
import qualified Hasql.Query as H
import qualified Hasql.Session as H
import qualified Hasql.Decoders as HD
Expand All @@ -22,7 +24,6 @@ import Network.Wai.Handler.Warp
import System.IO (BufferMode (..),
hSetBuffering, stderr,
stdin, stdout)
import Web.JWT (secret)
import Data.IORef
#ifndef mingw32_HOST_OS
import Control.Monad.IO.Class (liftIO)
Expand All @@ -46,14 +47,14 @@ main = do
hSetBuffering stdin LineBuffering
hSetBuffering stderr NoBuffering

conf <- readOptions
conf <- loadSecretFile =<< readOptions
let port = configPort conf
pgSettings = cs (configDatabase conf)
appSettings = setPort port
. setServerName (cs $ "postgrest/" <> prettyVersion)
$ defaultSettings

unless (secret "secret" /= configJwtSecret conf) $
unless ("secret" /= configJwtSecret conf) $
putStrLn "WARNING, running in insecure mode, JWT secret is the default value"
Prelude.putStrLn $ "Listening on port " ++
(show $ configPort conf :: String)
Expand Down Expand Up @@ -85,3 +86,11 @@ main = do
#endif

runSettings appSettings $ postgrest conf refDbStructure pool

loadSecretFile :: AppConfig -> IO AppConfig
loadSecretFile conf = do
let s = configJwtSecret conf
real <- case stripPrefix "@" s of
Nothing -> return s -- the string is the secret, not a filename
Just filename -> pack <$> readFile ( unpack filename )
return conf { configJwtSecret = real }
7 changes: 4 additions & 3 deletions src/PostgREST/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import Text.ParserCombinators.Parsec (parse)

import Network.HTTP.Types.Header
import Network.HTTP.Types.Status
import Network.HTTP.Types.URI (renderSimpleQuery)
import Network.HTTP.Types.URI (renderSimpleQuery)
import Network.Wai
import Network.Wai.Middleware.RequestLogger (logStdout)
import Web.JWT (secret)

import Data.Aeson
import Data.Aeson.Types (emptyArray)
Expand Down Expand Up @@ -75,7 +76,7 @@ postgrest conf refDbStructure pool =

let schema = cs $ configSchema conf
apiRequest = userApiRequest schema req body
eClaims = jwtClaims (configJwtSecret conf) (iJWT apiRequest) time
eClaims = jwtClaims (secret $ configJwtSecret conf) (iJWT apiRequest) time
authed = containsRole eClaims
handleReq = runWithClaims conf eClaims (app dbStructure conf) apiRequest
txMode = transactionMode $ iAction apiRequest
Expand Down Expand Up @@ -192,7 +193,7 @@ app dbStructure conf apiRequest =
if exists
then do
let p = V.head payload
jwtSecret = configJwtSecret conf
jwtSecret = secret $ configJwtSecret conf
respondToRange $ do
row <- H.query () (callProc qi p topLevelRange shouldCount)
returnJWT <- H.query qi doesProcReturnJWT
Expand Down
8 changes: 3 additions & 5 deletions src/PostgREST/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,22 @@ import qualified Data.ByteString.Char8 as BS
import qualified Data.CaseInsensitive as CI
import Data.List (intercalate)
import Data.String.Conversions (cs)
import Data.Text (strip)
import Data.Text (Text, pack, strip)
import Data.Version (versionBranch)
import Network.Wai
import Network.Wai.Middleware.Cors (CorsResourcePolicy (..))
import Options.Applicative
import Paths_postgrest (version)
import Prelude
import Safe (readMay)
import Web.JWT (Secret, secret)

-- | Data type to store all command line options
data AppConfig = AppConfig {
configDatabase :: String
, configAnonRole :: String
, configSchema :: String
, configPort :: Int
, configJwtSecret :: Secret
, configJwtSecret :: Text
, configPool :: Int
, configMaxRows :: Maybe Integer
, configQuiet :: Bool
Expand All @@ -52,8 +51,7 @@ argParser = AppConfig
<*> strOption (long "anonymous" <> short 'a' <> help "(REQUIRED) postgres role to use for non-authenticated requests" <> metavar "ROLE")
<*> strOption (long "schema" <> short 's' <> help "schema to use for API routes" <> metavar "NAME" <> value "public" <> showDefault)
<*> option auto (long "port" <> short 'p' <> help "port number on which to run HTTP server" <> metavar "PORT" <> value 3000 <> showDefault)
<*> (secret . cs <$>
strOption (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET" <> value "secret" <> showDefault))
<*> (pack <$> strOption (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET" <> value "secret" <> showDefault))
<*> option auto (long "pool" <> short 'o' <> help "max connections in database pool" <> metavar "COUNT" <> value 10 <> showDefault)
<*> (readMay <$> strOption (long "max-rows" <> short 'm' <> help "max rows in response" <> metavar "COUNT" <> value "infinity" <> showDefault))
<*> pure False
Expand Down

0 comments on commit 77269ab

Please sign in to comment.