How to log the real IP address when behind a proxy using Scotty / wai
Asked Answered
T

1

5

This is my scotty app, notice how I am logging requests to the console:

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.RequestLogger

import Data.Monoid (mconcat)

main = scotty 3000 $ do
    --log requests to console
    middleware logStdoutDev

    get "/:word" $ do
        beam <- param "word"
        html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]

My scotty app runs behind nginx using the proxy mechanism. This causes the scotty app to log like this:

127.0.0.1 - - [27/Aug/2014:15:12:00 +0000] "GET / HTTP/1.0" 200 - ...

I want the REAL IP ADDRESS to be logged.

I had the same issue in my Node.js/Express apps, where I solved it like this:

Express.js: how to get remote client address

How do I solve this problem in Scotty?

Tempera answered 27/8, 2014 at 21:47 Comment(0)
H
10

There's an IPAddrSource data type in wai-extra which originates in the wai-logger package. So, if you want the IP address to come from a header, it looks like you can do something like:

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.RequestLogger

import Control.Monad.IO.Class
import Data.Monoid (mconcat)
import Data.Default

main = scotty 3000 $ do
    --log requests to console
    logger <- liftIO $ mkRequestLogger def { outputFormat = Apache FromHeader }
    middleware logger

    get "/:word" $ do
        beam <- param "word"
        html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]

From the description, it also looks like Apache FromFallback will check the headers first, and use the socket IP address if no header is found.

Update

You can also just create the logger outside the scotty function:

main = do
    logger <- mkRequestLogger def { outputFormat = Apache FromHeader }
    scotty 3000 $ do
        middleware logger

        get "/:word" $ do
            beam <- param "word"
            html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
Holzman answered 28/8, 2014 at 0:0 Comment(5)
Hi Luke. Your code worked perfectly back in 2014. But now, with the latest Stack snapshot, it no longer compiles. (It's complaining now that there is no MonadIO instance for the stuff that mkRequestLogger returns.) Do you have time to look at what changed in the meantime and how to get around it? I'd be happy to give you +50 bounty award for it. Thank you.Tempera
Thanks, it works now! I've set the bounty and can award it to you in 24 hours. Another question in the meantime: Can you see why things stopped working? Did they just remove a MonadIO instance at some point? As an experienced Haskell programmer, can you imagine why they would do that in the first place?Tempera
If you look at the changelog, you can see that the MonadIO instance was removed from ActionT. The commit log doesn't explain the motivation though, but I guess it allows for situations where you don't want all IO exceptions to be trapped within Scotty, which is what happened before.Holzman
Looks like it might be related to this issue.Holzman
Thanks! You'll get the bounty in 6 hours :)Tempera

© 2022 - 2024 — McMap. All rights reserved.