Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: How to restrict an API to a particular virtual host (value of the request "Host:" header)? #1705

Open
vdukhovni opened this issue Sep 10, 2023 · 1 comment

Comments

@vdukhovni
Copy link

vdukhovni commented Sep 10, 2023

My server hosts:

  1. A redirect from the "/" path to a different host that describes the project
  2. The project's backend API under "/prefix"
  3. Some static content (paths other than "/" or the API prefix.

I am adding a variant of "3" which is a virtual host for another name that will serve static content under a different directory based on the incoming "Host:" header. This works.

However, I'd prefer to not serve either the redirect or the API to requests for the alternative name. How can this be done?

type Blank  = Headers '[Header "Location" String] NoContent
type Redirect = Verb 'GET 301 '[OctetStream] Blank  -- Redirect GET /
type Backend = "prefix" :> Capture "input" Text :> Get '[JSON] Result
type Files       = Header' '[Required, Strict] "Host" String :> Raw

type API  = Redirect
       :<|> Backend
       :<|> Files

This is currently served via the below, which is only host-specific for the static content. How would I restrict the redirect and the API to just the default vhost, with all other hosts seeing just the Files API (or perhaps some day a different host-specific router)?

router = redirect :<|> backend :<|> serveStatic rootPath
  where
    redirect              = pure $ addHeader redirURL NoContent

    backend input   = ...

    -- Choose a host-specific root if applicable, else use default.
    serveStatic :: forall m. FilePath -> String -> S.ServerT Raw m
    serveStatic root hostport
        | Just root' <- rootFor $ break (== ':') $ map toLower hostport =
            S.serveDirectoryWith
                $ W.webAppSettingsWithLookup root' $ getEtag maxAge
        | otherwise =
            S.serveDirectoryWith
                $ (W.webAppSettingsWithLookup root $ getEtag maxAge)
                    { W.ssMaxAge = maybe W.NoMaxAge W.MaxAgeSeconds maxAge }

P.S. I also didn't know how to reject malformed Host: headers with a 401 or suitable error code. So settled to just ignore them. Perhaps something I could do with RawM, but it is not immediately obvious how to use serveDirectoryWith as a handler for RawM.

@vdukhovni vdukhovni changed the title Question: How restrict an API to a particular virtual host (value of the request "Host:" header)? Question: How to restrict an API to a particular virtual host (value of the request "Host:" header)? Sep 11, 2023
@tchoutri
Copy link
Contributor

Hmmm, it's an interesting problematic. I think my first reflex would be to do routing at the reverse proxy level (nginx, Apache, caddy, etc), and expose endpoints with specific audiences in mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants