Skip to content

Commit 679d471

Browse files
authored
Merge pull request #79 from diogob/tls
Add native TLS server support
2 parents aabc803 + 1950862 commit 679d471

File tree

8 files changed

+67
-28
lines changed

8 files changed

+67
-28
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ site
1212
rails.conf
1313
cabal-helper*
1414
.dir-locals.el
15+
server.key
16+
server.crt

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## 0.11.1.0
4+
5+
- Add TLS native connection so we can have `wss://` connections without using any sort of proxy `PGWS_CERTIFICATE_FILE` and `PGWS_KEY_FILE` should be used to set the the TLS certificate and enable secure connections.
6+
37
## 0.11.0.0
48

59
- Add `PGWS_CHECK_LISTENER_INTERVAL` to configure interval to check database listener connection and respawn listener in case the connection is not found.

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ cd postgres-websockets
2626
docker-compose up
2727
```
2828

29+
### Pre-compiled binaries
30+
31+
You can download binaries from the [releases page](./releases). Currently only linux binaries complied against Ubuntu on amd64 are provided.
32+
2933
### Building from source
3034
To build the project I recommend the use of [Stack](http://docs.haskellstack.org/en/stable/README/).
3135
You also need to have [git](https://git-scm.com) installed to download the source code.
@@ -86,7 +90,13 @@ The address will look like:
8690
ws://eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtb2RlIjoicnciLCJjaGFubmVsIjoiY2hhdCJ9.fEm6P7GHeJWZG8OtZhv3H0JdqPljE5dainvsoupM9pA
8791
```
8892

89-
To use a secure socket (`wss://`) you will need a proxy server like nginx to handle the TLS layer. Some services (e.g. Heroku) will handle this automatially.
93+
To use a secure socket (`wss://`) you can set the configuration variables `PGWS_CERTIFICATE_FILE` and `PGWS_KEY_FILE`. Once these two variables point to a valid X.509 certificate the server will enable TLS connections. Bellow a quick example of how to generate a self-signed certificate using [OpenSSL](https://www.openssl.org/) command line tool:
94+
95+
```
96+
openssl genrsa -out key.pem 2048
97+
openssl req -new -key key.pem -out certificate.csr
98+
openssl x509 -req -in certificate.csr -signkey key.pem -out certificate.pem
99+
```
90100

91101
## Receiving messages from the browser
92102

postgres-websockets.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: postgres-websockets
2-
version: 0.11.0.0
2+
version: 0.11.1.0
33
synopsis: Middleware to map LISTEN/NOTIFY messages to Websockets
44
description: WAI middleware that adds websockets capabilites on top of PostgreSQL's asynchronous notifications using LISTEN and NOTIFY commands. Fully functioning server included.
55
homepage: https://github.com/diogob/postgres-websockets#readme
@@ -52,6 +52,7 @@ library
5252
, base64-bytestring >= 1.0.0.3 && < 1.2
5353
, bytestring >= 0.10
5454
, warp >= 3.2 && < 4
55+
, warp-tls >= 3.2 && < 4
5556
, wai-extra >= 3.0.29 && < 3.2
5657
, wai-app-static >= 3.1.7.1 && < 3.2
5758
, auto-update >= 0.1.6 && < 0.2

sample-env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ export PGWS_JWT_SECRET_BASE64=False
2525
## Check database listener every 10 seconds
2626
## comment it out to disable and shutdown the server on listener errors (can be useful when using external process supervisors)
2727
export PGWS_CHECK_LISTENER_INTERVAL=10000
28+
29+
## Place server.crt and server.key in the working directory and uncomment the following lines for TLS connections (wss)
30+
# export PGWS_CERTIFICATE_FILE="./certificate.pem"
31+
# export PGWS_KEY_FILE="./key.pem"

src/PostgresWebsockets/Config.hs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,23 @@ data AppConfig = AppConfig
3737
configJwtSecretIsBase64 :: Bool,
3838
configPool :: Int,
3939
configRetries :: Int,
40-
configReconnectInterval :: Maybe Int
40+
configReconnectInterval :: Maybe Int,
41+
configCertificateFile :: Maybe Text,
42+
configKeyFile :: Maybe Text
4143
}
44+
deriving (Show)
4245

4346
-- | User friendly version number
4447
prettyVersion :: Text
4548
prettyVersion = intercalate "." $ map show $ versionBranch version
4649

4750
-- | Load all postgres-websockets config from Environment variables. This can be used to use just the middleware or to feed into warpSettings
4851
loadConfig :: IO AppConfig
49-
loadConfig = readOptions >>= loadSecretFile >>= loadDatabaseURIFile
52+
loadConfig =
53+
readOptions
54+
>>= verifyTLSConfig
55+
>>= loadSecretFile
56+
>>= loadDatabaseURIFile
5057

5158
-- | Given a shutdown handler and an AppConfig builds a Warp Settings to start a stand-alone server
5259
warpSettings :: (IO () -> IO ()) -> AppConfig -> Settings
@@ -76,6 +83,14 @@ readOptions =
7683
<*> var auto "PGWS_POOL_SIZE" (def 10 <> helpDef show <> help "How many connection to the database should be used by the connection pool")
7784
<*> var auto "PGWS_RETRIES" (def 5 <> helpDef show <> help "How many times it should try to connect to the database on startup before exiting with an error")
7885
<*> optional (var auto "PGWS_CHECK_LISTENER_INTERVAL" (helpDef show <> help "Interval for supervisor thread to check if listener connection is alive. 0 to disable it."))
86+
<*> optional (var str "PGWS_CERTIFICATE_FILE" (helpDef show <> help "Certificate file to serve secure websockets connection (wss)."))
87+
<*> optional (var str "PGWS_KEY_FILE" (helpDef show <> help "Key file to serve secure websockets connection (wss)."))
88+
89+
verifyTLSConfig :: AppConfig -> IO AppConfig
90+
verifyTLSConfig conf@AppConfig {..} = do
91+
when (isJust configCertificateFile /= isJust configKeyFile) $
92+
panic "PGWS_TLS_CERTIFICATE and PGWS_TLS_KEY must be set in tandem"
93+
pure conf
7994

8095
loadDatabaseURIFile :: AppConfig -> IO AppConfig
8196
loadDatabaseURIFile conf@AppConfig {..} =

src/PostgresWebsockets/Server.hs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,43 @@
1-
{-|
2-
Module : PostgresWebsockets.Server
3-
Description : Functions to start a full stand-alone PostgresWebsockets server.
4-
-}
1+
-- |
2+
-- Module : PostgresWebsockets.Server
3+
-- Description : Functions to start a full stand-alone PostgresWebsockets server.
54
module PostgresWebsockets.Server
6-
( serve
7-
) where
5+
( serve,
6+
)
7+
where
88

9-
import Protolude
10-
import Network.Wai.Application.Static ( staticApp, defaultFileServerSettings )
11-
import Network.Wai (Application, responseLBS)
129
import Network.HTTP.Types (status200)
13-
import Network.Wai.Handler.Warp ( runSettings )
10+
import Network.Wai (Application, responseLBS)
11+
import Network.Wai.Application.Static (defaultFileServerSettings, staticApp)
12+
import Network.Wai.Handler.Warp (runSettings)
13+
import Network.Wai.Handler.WarpTLS (runTLS, tlsSettings)
1414
import Network.Wai.Middleware.RequestLogger (logStdout)
15-
16-
import PostgresWebsockets.Middleware ( postgresWsMiddleware )
17-
import PostgresWebsockets.Config ( AppConfig(..), warpSettings )
18-
import PostgresWebsockets.Context ( mkContext )
15+
import PostgresWebsockets.Config (AppConfig (..), warpSettings)
16+
import PostgresWebsockets.Context (mkContext)
17+
import PostgresWebsockets.Middleware (postgresWsMiddleware)
18+
import Protolude
1919

2020
-- | Start a stand-alone warp server using the parameters from AppConfig and a opening a database connection pool.
2121
serve :: AppConfig -> IO ()
22-
serve conf@AppConfig{..} = do
22+
serve conf@AppConfig {..} = do
2323
shutdownSignal <- newEmptyMVar
24-
let waitForShutdown cl = void $ forkIO (takeMVar shutdownSignal >> cl)
25-
appSettings = warpSettings waitForShutdown conf
26-
2724
putStrLn $ ("Listening on port " :: Text) <> show configPort
2825

2926
let shutdown = putErrLn ("Broadcaster connection is dead" :: Text) >> putMVar shutdownSignal ()
3027
ctx <- mkContext conf shutdown
3128

32-
runSettings appSettings $
33-
postgresWsMiddleware ctx $
34-
logStdout $ maybe dummyApp staticApp' configPath
35-
die "Shutting down server..."
29+
let waitForShutdown cl = void $ forkIO (takeMVar shutdownSignal >> cl)
30+
appSettings = warpSettings waitForShutdown conf
31+
app = postgresWsMiddleware ctx $ logStdout $ maybe dummyApp staticApp' configPath
32+
33+
case (configCertificateFile, configKeyFile) of
34+
(Just certificate, Just key) -> runTLS (tlsSettings (toS certificate) (toS key)) appSettings app
35+
_ -> runSettings appSettings app
3636

37+
die "Shutting down server..."
3738
where
3839
staticApp' :: Text -> Application
3940
staticApp' = staticApp . defaultFileServerSettings . toS
4041
dummyApp :: Application
4142
dummyApp _ respond =
42-
respond $ responseLBS status200 [("Content-Type", "text/plain")] "Hello, Web!"
43+
respond $ responseLBS status200 [("Content-Type", "text/plain")] "Hello, Web!"

test/ServerSpec.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ testServerConfig =
2222
configJwtSecretIsBase64 = False,
2323
configPool = 10,
2424
configRetries = 5,
25-
configReconnectInterval = Nothing
25+
configReconnectInterval = Nothing,
26+
configCertificateFile = Nothing,
27+
configKeyFile = Nothing
2628
}
2729

2830
startTestServer :: IO ThreadId

0 commit comments

Comments
 (0)