Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,54 @@
[Unreleased](https://github.com/bird-house/birdhouse-deploy/tree/master) (latest)
------------------------------------------------------------------------------------------------------------------

[//]: # (list changes here, using '-' for each new entry, remove this when items are added)
## Changes

- Allow each service to specify values for `Access-Control-Allow-Origin`

Previously, if a `location` block in the `nginx` configuration for a given service included the cors helper
configuration (with `include /etc/nginx/conf.d/cors.include;`) then all origins were allowed by default.

This was done by setting the header `Access-Control-Allow-Origin: *` which works well but is a bit too permissive
since it allowed __all__ origins.

This change introduces a mechanism to specify specific additional allowed origins by setting the
`$access_control_allow_origin` nginx variable in the `location` block before including the `cors.include` file.

For example:

```shell
set $access_control_allow_origin http://example.com;
include /etc/nginx/conf.d/cors.include;
```

will set the value of the `Access-Control-Allow-Origin` response header to `http://example.com`.

By default, the header value will be `*` if `$access_control_allow_origin` is not set (to maintain backwards
compatibility).

To specify multiple allowed origins, use a `map` directive (see the implementation for `components/stac` for an
example).

- Set allowed CORS origins for `stac` through an environment variable

This change implements this flexibility for the `components/stac` component. By setting the `STAC_CORS_ORIGINS`
variable a user can specify allowed origins for responses from the `components/stac` component.

For example, setting the following:

```shell
export STAC_CORS_ORIGINS='https://example.com ~^https?://(www\.)?other\.example\.com$'
```

then requests from https://example.com and http://other.example.com will get a response with the
`Access-Control-Allow-Origin header` set to their origin, but http://example.ca will not.

Note that this breaks backwards compatibility slightly since previously all origins were allowed for `/stac` by
default. To match all origins and keep the backwards compatible behaviour you can set:

```shell
export STAC_CORS_ORIGINS='~.*'
```

[2.18.8](https://github.com/bird-house/birdhouse-deploy/tree/2.18.8) (2025-10-30)
------------------------------------------------------------------------------------------------------------------
Expand Down
51 changes: 40 additions & 11 deletions birdhouse/components/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -698,31 +698,60 @@ information.
Usage
-----

The STAC API can be browsed via the ``stac-browser`` component. By default, the browser will point to the STAC API
exposed by the current stack instance. Once this component is enabled, STAC API will be accessible at
``https://<BIRDHOUSE_FQDN_PUBLIC>/stac`` endpoint and the STAC browser will be available at
``https://<BIRDHOUSE_FQDN_PUBLIC>/stac-browser`` endpoint. In order to make the STAC browser the default entrypoint,
define the following in the ``env.local`` file::

export BIRDHOUSE_PROXY_ROOT_LOCATION='return 302 ${BIRDHOUSE_PROXY_SCHEME}://\$host/stac-browser;'
The STAC API can be browsed via the ``stac-browser`` component. Once this component is enabled, STAC API
will be accessible at the ``https://<BIRDHOUSE_FQDN_PUBLIC>/stac``.

Here is a sample search query using a CLI::
Here is a sample search query using a the ``pystac-client`` python CLI:

.. code-block:: shell

pip install pystac-client
stac-client search $PAVIS_FQDN/stac -q "variable_id=txgt_32" "scenario=ssp585"
stac-client search $BIRDHOUSE_FQDN_PUBLIC/stac -q "variable_id=txgt_32" "scenario=ssp585"

Calls to the STAC API pass through Twitcher in order to validate authorization. Unauthenticated users will have
read-only access by default to STAC API resources while members of the `stac-admin` group can create and modify
resources. STAC Browser is not protected by any authorization mechanism.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"STAC Browser is not protected by any authorization mechanism", this part is not true anymore, that's why it is removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's technically true but it's misleading. Access to the stac-browser endpoint is not checked by twitcher but if the user doesn't have permissions to read the stac data (because the stac endpoint is protected by twitcher) then the stac-browser page shows a 403 error anyway.

So from the user's perspective it is protected by an authorization mechanism, it's the same one that protects the stac endpoint.

read-only access to STAC API resources while members of the `stac-admin` group can create and modify
resources if the ``optional-components/stac-public-access`` component is enabled.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording makes it seem like the optional component is needed for stac-admin to create/modify resources, but they always have access to do it. The component mention should be placed after the "Unauthenticated users will have read-only access" part.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is needed. The magpie permissions set by components/stac give write permissions to the administrators group. The stac-admin group is created by the optional-components/stac-public-access component.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh true! It should be moved to components/stac. The stac-admin group could be relevant even when used for fully-protected API, while restricting the member's permission scope only to editing the STAC API resources.

As the comment mentions, administrators is used just because a user/group must be provided (for the config to be valid and work), but the intent is just to make sure the resource exists. The admins do not need the explicit permission to access, they always can anyway.

permissions:
# create a default 'stac' resource under 'stac' service
# because of the '/stac/stac' path prefix required to resolve the API links properly,
# all permissions must be nested under this 'stac' resource for requests and permissions to be resolved accordingly
- service: stac
resource: /stac
permission: read
group: administrator # they already have access, just using admins to create the resource by default
action: create


How to Enable the Component
---------------------------

- Edit ``env.local`` (a copy of `env.local.example`_)
- Add ``./components/stac`` to ``BIRDHOUSE_EXTRA_CONF_DIRS``.

STAC Browser
============

STAC Browser is a web UI used to interact with the STAC API.

Usage
-----

The STAC API can be browsed via the ``stac-browser`` component. By default, the browser will point to the STAC API
exposed by the current ``components/stac`` service.
Once this component is enabled, the STAC browser will be available at the ``https://<BIRDHOUSE_FQDN_PUBLIC>/stac-browser``
endpoint

If your STAC API contains geojson data, it is recommended to set the ``STAC_CORS_ORIGINS`` value to accept the origin
``https://geojson.io`` since the STAC Browser offers a link to open geojson data at this URL.
Note that you do not need to change the ``STAC_CORS_ORIGINS`` value from the default (which accepts all origins), but
if you have changed it please update it to include this origin as well.

For example:

.. code::shell

# If the STAC_CORS_ORIGINS is currently
export STAC_CORS_ORIGINS='http://example.com ~http:(www|other)\.api\.example\.com'

# you can update it to
export STAC_CORS_ORIGINS='http://example.com ~http:(www|other)\.api\.example\.com https://geojson.io'

How to Enable the Component
---------------------------

- Edit ``env.local`` (a copy of `env.local.example`_)
- Add ``./components/stac-browser`` to ``BIRDHOUSE_EXTRA_CONF_DIRS``.

Canarie-API
===========

Expand Down
18 changes: 15 additions & 3 deletions birdhouse/components/proxy/conf.d/cors.include
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
proxy_hide_header 'Access-Control-Allow-Origin';

if ( $access_control_allow_origin ~ "^$" ) {
set $access_control_allow_origin '*';
}

set $vary_origin "";
if ( $access_control_allow_origin != '*' ) {
set $vary_origin Origin;
}

if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Origin' $access_control_allow_origin;
add_header Vary $vary_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
Expand All @@ -16,13 +26,15 @@
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Origin' $access_control_allow_origin;
add_header Vary $vary_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Origin' $access_control_allow_origin;
add_header Vary $vary_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
Expand Down
2 changes: 2 additions & 0 deletions birdhouse/components/proxy/conf.d/frontend.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ map $http_x_forwarded_proto $real_scheme {
'' $scheme;
}

include /etc/nginx/conf.extra-directives.d/*/*.conf;

server {
listen 80 ${PROXY_LISTEN_80_PARAMS};
server_name localhost;
Expand Down
1 change: 1 addition & 0 deletions birdhouse/components/stac/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
config/magpie/config.yml
config/proxy/conf.extra-service.d/stac.conf
config/proxy/conf.extra-directives.d/stac.conf
config/canarie-api/canarie_api_monitoring.py
service-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
map $http_origin $stac_origin_allowed {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remind me what the Nginx map directive do again?

This block means if the http_origin variable exists, create stac_origin_allowed variable with value ${BIRDHOUSE_PROXY_SCHEME}://${BIRDHOUSE_FQDN_PUBLIC}; ${STAC_ADDITIONAL_CORS_ORIGINS} ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's mostly right...

This block creates a new variable stac_origin_allowed whose value is based on the value of the http_origin variable.
If the value of http_origin matches any of the patterns defined in ${STAC_ADDITIONAL_CORS_ORIGINS} then the stac_origin_allowed value will be set to http_origin.
Otherwise stac_origin_allowed will be set to ${BIRDHOUSE_PROXY_SCHEME}://${BIRDHOUSE_FQDN_PUBLIC} (the default).

# default should not be set to the empty string because the cors.include file will interpret
# that as "unset" and will change it to * by default. To get around this, set this to birdhouse's
# own origin (which has the same effect as being unset since same-origin requests are already allowed).
default ${BIRDHOUSE_PROXY_SCHEME}://${BIRDHOUSE_FQDN_PUBLIC};
${STAC_ADDITIONAL_CORS_ORIGINS}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_buffering off;
set $access_control_allow_origin $stac_origin_allowed;
include /etc/nginx/conf.d/cors.include;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ services:
volumes:
- ./components/stac/service-config.json:/static/services/stac.json:ro
- ./components/stac/config/proxy/conf.extra-service.d:/etc/nginx/conf.extra-service.d/stac:ro
- ./components/stac/config/proxy/conf.extra-directives.d:/etc/nginx/conf.extra-directives.d/stac:ro
13 changes: 13 additions & 0 deletions birdhouse/components/stac/default.env
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ export STAC_POPULATOR_BACKUP_IMAGE='${STAC_POPULATOR_BACKUP_DOCKER}:${STAC_POPUL
# This must match the "stac_version" value in the current STAC catalog.
export PYSTAC_STAC_VERSION_OVERRIDE=1.0.0

# Add additional origins that are allowed to access the /stac endpoint (other than the same origin) according to CORS rules.
# The values in the space delimited list set by STAC_CORS_ORIGINS are origins that will be allowed to access responses
# from /stac. These values can either be strings which will be matched directly or regular expressions prefixed by ~.
#
# For example, if STAC_CORS_ORIGINS='https://example.com ~^https?://(www\.)?other\.example\.com$' then requests from
# https://example.com and http://other.example.com will get a response with the Access-Control-Allow-Origin header set
# to their origin, but http://example.ca will not.
# By default all origins are allowed. Set STAC_CORS_ORIGINS in the local environment file to limit the allowed origins.
export STAC_CORS_ORIGINS="~.*"
export STAC_ADDITIONAL_CORS_ORIGINS='$(for origin in $STAC_CORS_ORIGINS; do echo "$origin \$http_origin;"; done;)'

# add any new variables not already in 'VARS' or 'OPTIONAL_VARS' that must be replaced in templates here
# single quotes are important in below list to keep variable names intact until 'birdhouse-compose' parses them
EXTRA_VARS='
Expand All @@ -71,6 +82,7 @@ export DELAYED_EVAL="
STAC_MIGRATION_DOCKER
STAC_MIGRATION_IMAGE
STAC_POPULATOR_BACKUP_IMAGE
STAC_ADDITIONAL_CORS_ORIGINS
"

OPTIONAL_VARS="
Expand All @@ -86,4 +98,5 @@ OPTIONAL_VARS="
\$STAC_MIGRATION_TAGGED
\$STAC_MIGRATION_DOCKER
\$STAC_MIGRATION_IMAGE
\$STAC_ADDITIONAL_CORS_ORIGINS
"
Loading