Skip to content

Commit

Permalink
add support for multiple domains in cors config (close #1436) (#1536)
Browse files Browse the repository at this point in the history
Support for multiple domains (as CSV) in the `--cors-domain` flag and `HASURA_GRAPHQL_CORS_DOMAIN` env var.

Following are all valid configurations (must include scheme and optional port):
```shell
HASURA_GRAPHQL_CORS_DOMAIN="https://*.foo.bar.com:8080"
HASURA_GRAPHQL_CORS_DOMAIN="https://*.foo.bar.com, http://*.localhost, https://example.com"
HASURA_GRAPHQL_CORS_DOMAIN="*"
HASURA_GRAPHQL_CORS_DOMAIN="http://example.com, http://*.localhost, http://localhost:3000, https://*.foo.bar.com, https://foo.bar.com"
```

**Note**: top-level domains are not considered as part of wildcard domains. You have to add them separately. E.g - `https://*.foo.com` doesn't include `https://foo.com`.

The default (if the flag or env var is not specified) is `*`. Which means CORS headers are sent for all domains.
  • Loading branch information
ecthiender authored and shahidhk committed Feb 14, 2019
1 parent 68bb898 commit 199a24d
Show file tree
Hide file tree
Showing 13 changed files with 447 additions and 191 deletions.
31 changes: 28 additions & 3 deletions .circleci/test-server.sh
Expand Up @@ -119,6 +119,8 @@ WH_PID=""
trap stop_services ERR
trap stop_services INT

# test without access key

echo -e "\n<########## TEST GRAPHQL-ENGINE WITHOUT ACCESS KEYS ###########################################>\n"

"$GRAPHQL_ENGINE" serve > "$OUTPUT_FOLDER/graphql-engine.log" & PID=$!
Expand All @@ -131,7 +133,9 @@ kill -INT $PID
sleep 4
mv graphql-engine.tix graphql-engine-combined.tix || true

##########

# test with access key

echo -e "\n<########## TEST GRAPHQL-ENGINE WITH ACCESS KEY #####################################>\n"

export HASURA_GRAPHQL_ACCESS_KEY="HGE$RANDOM$RANDOM"
Expand All @@ -146,7 +150,9 @@ kill -INT $PID
sleep 4
combine_hpc_reports

##########

# test with jwt

echo -e "\n<########## TEST GRAPHQL-ENGINE WITH ACCESS KEY AND JWT #####################################>\n"

init_jwt
Expand Down Expand Up @@ -177,7 +183,24 @@ combine_hpc_reports

unset HASURA_GRAPHQL_JWT_SECRET

##########
# test with CORS modes

echo -e "\n<########## TEST GRAPHQL-ENGINE WITH CORS DOMAINS ########>\n"
export HASURA_GRAPHQL_CORS_DOMAIN="http://*.localhost, http://localhost:3000, https://*.foo.bar.com"

"$GRAPHQL_ENGINE" serve >> "$OUTPUT_FOLDER/graphql-engine.log" 2>&1 & PID=$!

wait_for_port 8080

pytest -vv --hge-url="$HGE_URL" --pg-url="$HASURA_GRAPHQL_DATABASE_URL" --hge-key="$HASURA_GRAPHQL_ACCESS_KEY" --test-cors test_cors.py

kill -INT $PID
sleep 4
combine_hpc_reports
unset HASURA_GRAPHQL_CORS_DOMAIN


# webhook tests

if [ $EUID != 0 ] ; then
echo -e "SKIPPING webhook based tests, as \nroot permission is required for running webhook tests (inorder to trust certificate authority)."
Expand Down Expand Up @@ -248,6 +271,8 @@ if [ "$RUN_WEBHOOK_TESTS" == "true" ] ; then
combine_hpc_reports

kill $WH_PID


fi

mv graphql-engine-combined.tix "$OUTPUT_FOLDER/graphql-engine.tix" || true
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -5,3 +5,4 @@ npm-debug.log
test-server-output
test-server-flags-output
.vscode
.idea
Expand Up @@ -94,14 +94,33 @@ You can also set the access key using a flag to the command:
Configure CORS
--------------

By default, all CORS requests are allowed. To run Hasura with more restrictive CORS settings, use the ``--cors-domain`` flag.
By default, all CORS requests to Hasura GraphQL engine are allowed. To run with more restrictive CORS settings,
use the ``--cors-domain`` flag or the ``HASURA_GRAPHQL_CORS_DOMAIN`` ENV variable. The default value is ``*``,
which means CORS headers are sent for all domains.

For example:
Scheme + host with optional wildcard + optional port has to be mentioned.

Examples:

.. code-block:: bash
docker run -P -d hasura/graphql-engine:latest graphql-engine \
--database-url postgres://username:password@host:5432/dbname \
serve \
--access-key XXXXXXXXXXXXXXXX
--cors-domain https://mywebsite.com:8090
# Accepts from https://app.foo.bar.com , https://api.foo.bar.com etc.
HASURA_GRAPHQL_CORS_DOMAIN="https://*.foo.bar.com"
# Accepts from https://app.foo.bar.com:8080 , http://api.foo.bar.com:8080,
# http://app.localhost, http://api.localhost, http://localhost:3000,
# http://example.com etc.
HASURA_GRAPHQL_CORS_DOMAIN="https://*.foo.bar.com:8080, http://*.localhost, http://localhost:3000, http://example.com"
# Accepts from all domain
HASURA_GRAPHQL_CORS_DOMAIN="*"
# Accepts only from http://example.com
HASURA_GRAPHQL_CORS_DOMAIN="http://example.com"
.. note::

Top-level domains are not considered as part of wildcard domains. You
have to add them separately. E.g - ``https://*.foo.com`` doesn't include
``https://foo.com``.
191 changes: 90 additions & 101 deletions docs/graphql/manual/deployment/graphql-engine-flags/reference.rst
Expand Up @@ -12,18 +12,30 @@ Every GraphQL engine command is structured as:
$ graphql-engine <server-flags> serve <command-flags>
The flags can be passed as ENV variables as well.

Server flags
^^^^^^^^^^^^

For ``graphql-engine`` command these are the flags available
For ``graphql-engine`` command these are the flags and ENV variables available:

.. code-block:: none

--database-url Postgres database URL
<postgres/postgresql>://<user>:<password>@<host>:<port>/<db-name>
Example: postgres://admin:mypass@mydomain.com:5432/mydb
.. list-table::
:header-rows: 1

* - Flag
- ENV variable
- Description

* - ``--database-url <DB_URL>``
- ``HASURA_GRAPHQL_DATABASE_URL``
- Postgres database URL:

Or either you can specify following options
``postgres://<user>:<password>@<host>:<port>/<db-name>``

Example: ``postgres://admin:mypass@mydomain.com:5432/mydb``

Or you can specify following options *(only via flags)*

.. code-block:: none
Expand All @@ -33,123 +45,100 @@ Or either you can specify following options
-p, --password Password of the user
-d, --dbname Database name to connect to
Command flags
^^^^^^^^^^^^^

For ``serve`` subcommand these are the flags available

.. code-block:: none
--server-host IP address of network interface that graphql-engine will listen on (default: '*', all interfaces)
--server-port Port on which graphql-engine should be served (default: 8080)
--access-key Secret access key, required to access this instance.
If specified client needs to send 'X-Hasura-Access-Key'
header
--cors-domain The domain, including sheme and port, to allow CORS for
--disable-cors Disable CORS handling
--auth-hook The authentication webhook, required to authenticate
incoming request
--auth-hook-mode The authentication webhook mode. GET|POST (default: GET)
For ``serve`` sub-command these are the flags and ENV variables available:

--jwt-secret The JSON containing type and the JWK used for
verifying. e.g: `{"type": "HS256", "key":
"<your-hmac-shared-secret>"}`,`{"type": "RS256",
"key": "<your-PEM-RSA-public-key>"}
.. list-table::
:header-rows: 1

--unauthorized-role Unauthorized role, used when access-key is not sent in
access-key only mode or "Authorization" header is absent
in JWT mode
* - Flag
- ENV variable
- Description

-s, --stripes Number of stripes (default: 1)
* - ``--server-port <PORT>``
- ``HASURA_GRAPHQL_SERVER_PORT``
- Port on which graphql-engine should be served (default: 8080)

-c, --connections Number of connections that need to be opened to Postgres
(default: 50)
* - ``--server-host <HOST>``
- ``HASURA_GRAPHQL_SERVER_HOST``
- Host on which graphql-engine will listen (default: ``*``)

--timeout Each connection's idle time before it is closed
(default: 180 sec)
* - ``--enable-console <true|false>``
- ``HASURA_GRAPHQL_ENABLE_CONSOLE``
- Enable the Hasura Console (served by the server on ``/`` and ``/console``)

-i, --tx-iso Transaction isolation. read-commited / repeatable-read /
serializable
* - ``--access-key <SECRET_ACCESS_KEY>``
- ``HASURA_GRAPHQL_ACCESS_KEY``
- Secret access key, for admin access to this instance. This is mandatory
when you use webhook or JWT.

--enable-console Enable API console. It is served at '/' and '/console'
* - ``--auth-hook <WEBHOOK_URL>``
- ``HASURA_GRAPHQL_AUTH_HOOK``
- URL of the authorization webhook required to authorize requests.
See auth webhooks docs for more details.

--use-prepared-statements Use prepared statements for SQL queries (default: true)
* - ``--auth-hook-mode <GET|POST>``
- ``HASURA_GRAPHQL_AUTH_HOOK_MODE``
- HTTP method to use for the authorization webhook (default: GET)

--enable-telemetry Enable anonymous telemetry (default: true)
* - ``--jwt-secret <JSON_CONFIG>``
- ``HASURA_GRAPHQL_JWT_SECRET``
- A JSON string containing type and the JWK used for verifying (and other
optional details).
Example: ``{"type": "HS256", "key": "3bd561c37d214b4496d09049fadc542c"}``.
See the JWT docs for more details.

* - ``--unauthorized-role <ROLE>``
- ``HASURA_GRAPHQL_UNAUTHORIZED_ROLE``
- Unauthorized role, used when access-key is not sent in access-key only
mode or "Authorization" header is absent in JWT mode.
Example: ``anonymous``. Now whenever "Authorization" header is
absent, request's role will default to "anonymous".

Default environment variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* - ``--cors-domain <DOMAINS>``
- ``HASURA_GRAPHQL_CORS_DOMAIN``
- CSV of list of domains, excluding scheme (http/https) and including port,
to allow CORS for. Wildcard domains are allowed.

You can use environment variables to configure defaults instead of using flags:
* - ``--disable-cors``
- N/A
- Disable CORS. Do not send any CORS headers on any request.

.. note::
When the equivalent flags for environment variables are used, the flags will take precedence.

For example:

.. code-block:: bash
$ HASURA_GRAPHQL_DATABASE_URL=postgres://user:pass@host:5432/dbname graphql-engine serve
These are the environment variables which are available:

.. code-block:: none
HASURA_GRAPHQL_DATABASE_URL Postgres database URL
<postgres/postgresql>://<user>:<password>@<host>:
<port>/<db-name> Example:
postgres://admin:mypass@mydomain.com:5432/mydb
HASURA_GRAPHQL_PG_STRIPES Number of stripes (default: 1)
HASURA_GRAPHQL_PG_CONNECTIONS Number of connections that need to be opened to
Postgres (default: 50)
* - ``--enable-telemetry <true|false>``
- ``HASURA_GRAPHQL_ENABLE_TELEMETRY``
- Enable anonymous telemetry (default: true)

HASURA_GRAPHQL_PG_TIMEOUT Each connection's idle time before it is closed
(default: 180 sec)
* - N/A
- ``HASURA_GRAPHQL_EVENTS_HTTP_POOL_SIZE``
- Max event threads

HASURA_GRAPHQL_TX_ISOLATION transaction isolation. read-committed /
repeatable-read / serializable
(default: read-commited)
HASURA_GRAPHQL_SERVER_HOST IP address of network interface that graphql-engine will listen on
* - N/A
- ``HASURA_GRAPHQL_EVENTS_FETCH_INTERVAL``
- Postgres events polling interval

HASURA_GRAPHQL_SERVER_PORT Port on which graphql-engine should be served
* - ``-s, --stripes <NO_OF_STRIPES>``
- ``HASURA_GRAPHQL_PG_STRIPES``
- Number of conns that need to be opened to Postgres (default: 1)

HASURA_GRAPHQL_ACCESS_KEY Secret access key, required to access this
instance. If specified client needs to send
'X-Hasura-Access-Key' header
* - ``-c, --connections <NO_OF_CONNS>``
- ``HASURA_GRAPHQL_PG_CONNECTIONS``
- Number of conns that need to be opened to Postgres (default: 50)

HASURA_GRAPHQL_AUTH_HOOK The authentication webhook, required to
authenticate incoming request
* - ``--timeout <SECONDS>``
- ``HASURA_GRAPHQL_PG_TIMEOUT``
- Each connection's idle time before it is closed (default: 180 sec)

HASURA_GRAPHQL_AUTH_HOOK_MODE The authentication webhook mode, GET|POST
(default: GET)
* - ``--use-prepared-statements <true|false>``
- ``HASURA_GRAPHQL_USE_PREPARED_STATEMENTS``
- Use prepared statements for queries (default: true)

HASURA_GRAPHQL_CORS_DOMAIN The domain, including sheme and port,
to allow CORS for
* - ``-i, --tx-iso <TXISO>``
- ``HASURA_GRAPHQL_TX_ISOLATION``
- transaction isolation. read-committed / repeatable-read / serializable (default: read-commited)

HASURA_GRAPHQL_JWT_SECRET The JSON containing type and the JWK used for
verifying. e.g: `{"type": "HS256", "key":
"<your-hmac-shared-secret>"}`,`{"type": "RS256",
"key": "<your-PEM-RSA-public-key>"}
Enable JWT mode, the value of which is a JSON

HASURA_GRAPHQL_UNAUTHORIZED_ROLE Unauthorized role, used when access-key is not sent
in access-key only mode or "Authorization" header
is absent in JWT mode
HASURA_GRAPHQL_ENABLE_CONSOLE Enable API console. It is served at
'/' and '/console'
HASURA_GRAPHQL_ENABLE_TELEMETRY Enable anonymous telemetry (default: true)
HASURA_GRAPHQL_USE_PREPARED_STATEMENTS Use prepared statements for SQL queries
(default: true)
.. note::
When the equivalent flags for environment variables are used, the flags will take precedence.
1 change: 1 addition & 0 deletions server/graphql-engine.cabal
Expand Up @@ -146,6 +146,7 @@ library
, Hasura.Server.Logging
, Hasura.Server.Query
, Hasura.Server.Utils
, Hasura.Server.Cors
, Hasura.Server.Version
, Hasura.Server.CheckUpdates
, Hasura.Server.Telemetry
Expand Down
8 changes: 4 additions & 4 deletions server/src-lib/Hasura/Server/App.hs
Expand Up @@ -43,10 +43,10 @@ import Hasura.RQL.DML.QueryTemplate
import Hasura.RQL.Types
import Hasura.Server.Auth (AuthMode (..),
getUserInfo)
import Hasura.Server.Cors
import Hasura.Server.Init
import Hasura.Server.Logging
import Hasura.Server.Middleware (corsMiddleware,
mkDefaultCorsPolicy)
import Hasura.Server.Middleware (corsMiddleware)
import Hasura.Server.Query
import Hasura.Server.Utils
import Hasura.Server.Version
Expand Down Expand Up @@ -314,8 +314,8 @@ mkWaiApp isoLevel loggerCtx pool httpManager mode corsCfg enableConsole enableTe
httpApp :: CorsConfig -> ServerCtx -> Bool -> Bool -> SpockT IO ()
httpApp corsCfg serverCtx enableConsole enableTelemetry = do
-- cors middleware
unless (ccDisabled corsCfg) $
middleware $ corsMiddleware (mkDefaultCorsPolicy $ ccDomain corsCfg)
unless (isCorsDisabled corsCfg) $
middleware $ corsMiddleware (mkDefaultCorsPolicy corsCfg)

-- API Console and Root Dir
when enableConsole serveApiConsole
Expand Down
5 changes: 1 addition & 4 deletions server/src-lib/Hasura/Server/Auth/JWT/Internal.hs
Expand Up @@ -12,6 +12,7 @@ import Data.ASN1.Types (ASN1 (End, IntVal, Start),
import Data.Int (Int64)

import Hasura.Prelude
import Hasura.Server.Utils (fmapL)

import qualified Data.ByteString.Lazy as BL
import qualified Data.PEM as PEM
Expand Down Expand Up @@ -97,10 +98,6 @@ pubKeyToJwk pubKey = do
RSAKeyParameters (Base64Integer n) (Base64Integer e) Nothing


fmapL :: (a -> a') -> Either a b -> Either a' b
fmapL fn (Left e) = Left (fn e)
fmapL _ (Right x) = pure x

getAtleastOne :: Text -> [a] -> Either Text a
getAtleastOne err [] = Left err
getAtleastOne _ (x:_) = Right x
Expand Down

0 comments on commit 199a24d

Please sign in to comment.