From 680c79ed519e9a778403554730c051f2aa9cfe05 Mon Sep 17 00:00:00 2001 From: Francis Charette-Migneault Date: Mon, 12 Sep 2022 19:53:37 -0400 Subject: [PATCH 01/15] optional-component secure-data-proxy + related configs in cowbird/magpie/twitcher --- .../config/cowbird/config.yml.template | 97 ++++++++++++++++++- .../cowbird/docker-compose-extra.yml | 3 + .../weaver/config/magpie/config.yml.template | 7 +- .../weaver/config/magpie/weaver_hooks.py | 20 ++-- .../weaver/config/weaver/weaver.ini.template | 3 + .../conf.d/all-services.include.template | 16 +-- .../proxy/conf.d/frontend.conf.template | 9 +- .../secure-wps-outputs.conf.template | 10 ++ .../config/magpie/config.yml.template | 16 +++ .../docker-compose-extra.yml | 6 ++ 10 files changed, 160 insertions(+), 27 deletions(-) create mode 100644 birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf.template create mode 100644 birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template create mode 100644 birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml diff --git a/birdhouse/components/cowbird/config/cowbird/config.yml.template b/birdhouse/components/cowbird/config/cowbird/config.yml.template index da56039d0..e67cd1c3f 100644 --- a/birdhouse/components/cowbird/config/cowbird/config.yml.template +++ b/birdhouse/components/cowbird/config/cowbird/config.yml.template @@ -50,12 +50,16 @@ sync_permissions: # the `user` variable name would be matched with `user_xyz` and `synced_file`, with `file_abc`. # Also, this key would need to sync permissions with the `thredds_workspace` resource key, considering the # `permissions_mapping` defined below. The `thredds_workspace` would be deduced to the resource path - # `/catalog/workspaces/user_xyz/dir1/dir2/subdir/file_abc`. + # `/thredds/catalog/workspaces/user_xyz/dir1/dir2/subdir/file_abc`. # The types of each segment of this target resource path would be deduced # from the `thredds_workspace` config below. thredds_workspace: - - name: catalog + - name: thredds type: service + # not a resource in Magpie + # 'catalog' is the file/view format specifier for the rest of the path + # - name: catalog + # type: directory - name: workspaces type: directory - name: "{user}" @@ -102,13 +106,17 @@ sync_permissions: - "geoserver_workspace : createStoredQuery <-> thredds_workspace : write" weaver_outputs: services: - api: + weaver: process_description: + - name: weaver + type: service - name: processes type: route - name: "{processID}" type: route process_job_status: + - name: weaver + type: service - name: processes type: route - name: "{processID}" @@ -118,24 +126,102 @@ sync_permissions: - name: "{jobID}" type: route job_status: + - name: weaver + type: service - name: jobs type: route - name: "{jobID}" type: route job_outputs: + - name: weaver + type: service - name: jobs type: route - name: "{jobID}" type: route - name: outputs type: route + job_output_single: + - name: weaver + type: service + - name: jobs + type: route + - name: "{jobID}" + type: route + - name: outputs + type: route + - name: "{outputID}" + type: route + # see 'optional-components/secure-data-proxy' for more details on protected WPS-outputs + wps_outputs: + # /wpsoutputs/weaver/{public|}/{job-id} weaver_wps_outputs: + - name: secure-data-proxy + type: service - name: wpsoutputs type: route - name: weaver type: route + - name: "{user_context_dir}" + type: route - name: "{jobID}" type: route + # /wpsoutputs/weaver/{public|}/{job-id}/{output-file} + weaver_wps_output_single: + - name: secure-data-proxy + type: service + - name: wpsoutputs + type: route + - name: weaver + type: route + - name: "{user_context_dir}" + type: route + - name: "{jobID}" + type: route + - name: "{outputID}" + type: route + thredds: + # /twitcher/ows/proxy/thredds/catalog/birdhouse/wps_outputs/weaver/catalog.html + # /twitcher/ows/proxy/thredds/catalog/birdhouse/wps_outputs/weaver/{public|}/catalog.html + # /twitcher/ows/proxy/thredds/catalog/birdhouse/wps_outputs/weaver/{public|}/{job-id}/catalog.html + # /twitcher/ows/proxy/thredds/catalog/birdhouse/wps_outputs/weaver/{public|}/{job-id}/{output-file} + # note: paths start after ows-proxy portion extracted when Twitcher/Magpie resolve between each other + thredds_wps_outputs: + - name: thredds + type: service + # not a resource in Magpie + # 'catalog' is the file/view format specifier for the rest of the path + # - name: catalog + # type: directory + - name: birdhouse + type: directory + - name: wps_outputs + type: directory + - name: weaver + type: directory + - name: "{user_context_dir}" + type: directory + - name: "{jobID}" + type: directory + thredds_wps_output_single: + - name: thredds + type: service + # not a resource in Magpie + # 'catalog' is the file/view format specifier for the rest of the path + # - name: catalog + # type: directory + - name: birdhouse + type: directory + - name: wps_outputs + type: directory + - name: weaver + type: directory + - name: "{user_context_dir}" + type: directory + - name: "{jobID}" + type: directory + - name: "{outputID}" + type: file permissions_mapping: # When user is granted access to an output (either side), # output retrieval is allowed from both endpoints (wps-outputs/weaver). @@ -153,3 +239,8 @@ sync_permissions: - "process_job_status : read -> job_status : read" # different permission (match), otherwise all jobs/outputs become available. - "process_job_status : read -> process_description : read-match" + # corresponding outputs retrieved under wps-outputs or thredds share access + - "weaver_wps_outputs : read <-> thredds_wps_outputs : read" + # permissions if outputs are shared one-by-one in case of multiple files produced by the process + - "weaver_wps_output_single : read <-> thredds_wps_output_single : read" + - "weaver_wps_output_single : read <-> job_output_single : read" diff --git a/birdhouse/components/cowbird/docker-compose-extra.yml b/birdhouse/components/cowbird/docker-compose-extra.yml index df49c2b49..568dda99c 100644 --- a/birdhouse/components/cowbird/docker-compose-extra.yml +++ b/birdhouse/components/cowbird/docker-compose-extra.yml @@ -73,6 +73,9 @@ services: # extend Magpie permissions to grant access to Cowbird API via secured Twitcher proxy magpie: + links: + # must have link to send webhook requests directly though internal network + - cowbird volumes: # NOTE: # Although file uses the "config.yml" format, it is very important to pass it as independent/duplicate reference diff --git a/birdhouse/components/weaver/config/magpie/config.yml.template b/birdhouse/components/weaver/config/magpie/config.yml.template index 6298aca70..ae897589f 100644 --- a/birdhouse/components/weaver/config/magpie/config.yml.template +++ b/birdhouse/components/weaver/config/magpie/config.yml.template @@ -19,16 +19,17 @@ providers: # see also: # - https://pavics-weaver.readthedocs.io/en/latest/processes.html?highlight=x-wps-output-context#outputs-location # each path below are equivalents, but with more or less specific reference to the requested service/process + # for job execution, 2 endpoints exist for older and newer OGC API - Processes specification - type: request - path: "/providers/[\\w_-]+/processes/[\\w_-]+/jobs" + path: "/providers/[\\w_-]+/processes/[\\w_-]+/(jobs|execution)" method: POST target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:add_x_wps_output_context - type: request - path: "/processes/[\\w_-]+/jobs" + path: "/processes/[\\w_-]+/(jobs|execution)" method: POST target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:add_x_wps_output_context - type: request - path: "/jobs" + path: "/(jobs|execution)" method: POST target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:add_x_wps_output_context - type: response diff --git a/birdhouse/components/weaver/config/magpie/weaver_hooks.py b/birdhouse/components/weaver/config/magpie/weaver_hooks.py index 045cace3b..4d3842dcb 100644 --- a/birdhouse/components/weaver/config/magpie/weaver_hooks.py +++ b/birdhouse/components/weaver/config/magpie/weaver_hooks.py @@ -218,6 +218,7 @@ def allow_user_deployed_processes(response): # find the nested resource matching: "weaver/processes/" children = ru.get_resource_children(service, request.db, limit_depth=2) + p_res_create = False p_res = None for res in children.values(): if res["node"].resource_name == "processes": @@ -228,21 +229,24 @@ def allow_user_deployed_processes(response): break break else: - # resource 'processes' should already exist, but create it if somehow missing - # otherwise, it will be impossible to create '' under it - resp = ru.create_resource("processes", None, Route.resource_type_name, service.resource_id, request.db) - processes_res_id = resp.json["resource"]["resource_id"] + p_res_create = True # must create after in new transaction context # note: # since this is running within a *response* hook, the request transaction is already handled # define a new transaction to create new resources with transaction.manager: + session = request.db + if p_res_create: + # resource 'processes' should already exist, but create it if somehow missing + # otherwise, it will be impossible to create '' under it + resp = ru.create_resource("processes", None, Route.resource_type_name, service.resource_id, session) + processes_res_id = resp.json["resource"]["resource_id"] # if '' somehow already exists, use it if p_res is None: - resp = ru.create_resource(p_id, None, Route.resource_type_name, processes_res_id, request.db) + resp = ru.create_resource(p_id, None, Route.resource_type_name, processes_res_id, session) p_res_id = resp.json["resource"]["resource_id"] - p_res = ru.ResourceService.by_resource_id(p_res_id, request.db) + p_res = ru.ResourceService.by_resource_id(p_res_id, session) if not p_res: LOGGER.warning( "Failed creation of permissions for user [%s] to access deployed process [%s] in Weaver. " @@ -254,8 +258,8 @@ def allow_user_deployed_processes(response): # override permissions to undo what could have been previously applied (only if already existed) p_desc = PermissionSet(Permission.READ, Access.ALLOW, Scope.RECURSIVE) # describe proc + jobs statuses p_exec = PermissionSet(Permission.WRITE, Access.ALLOW, Scope.RECURSIVE) # edit process + execute jobs - r_desc = uu.create_user_resource_permission_response(user, p_res, p_desc, request.db, overwrite=True) - r_exec = uu.create_user_resource_permission_response(user, p_res, p_exec, request.db, overwrite=True) + r_desc = uu.create_user_resource_permission_response(user, p_res, p_desc, session, overwrite=True) + r_exec = uu.create_user_resource_permission_response(user, p_res, p_exec, session, overwrite=True) # summit transaction results (new resources and permissions) transaction.commit() diff --git a/birdhouse/components/weaver/config/weaver/weaver.ini.template b/birdhouse/components/weaver/config/weaver/weaver.ini.template index 28c2cb9b7..c867acf47 100644 --- a/birdhouse/components/weaver/config/weaver/weaver.ini.template +++ b/birdhouse/components/weaver/config/weaver/weaver.ini.template @@ -51,6 +51,9 @@ weaver.wps_output = true weaver.wps_output_dir = ${WEAVER_WPS_OUTPUTS_DIR} weaver.wps_output_url = https://${PAVICS_FQDN_PUBLIC}${WEAVER_WPS_OUTPUTS_PATH} weaver.wps_output_path = +# default output sub-dir if user not specified (just for good measure, otherwise hook should default to it also) +# see 'components/weaver/config/magpie/weaver_hooks.py:add_x_wps_output_context' that defines it when user is logged in +weaver.wps_output_context = public weaver.wps_workdir = ${WEAVER_WPS_WORKDIR} # --- Weaver WPS metadata --- diff --git a/birdhouse/config/proxy/conf.d/all-services.include.template b/birdhouse/config/proxy/conf.d/all-services.include.template index 0fb52085d..175d7aed6 100644 --- a/birdhouse/config/proxy/conf.d/all-services.include.template +++ b/birdhouse/config/proxy/conf.d/all-services.include.template @@ -38,14 +38,14 @@ } location /geoserver/ { - proxy_pass http://${PAVICS_FQDN}:8087; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $real_scheme; - proxy_set_header Accept-Encoding ""; - gzip_proxied any; - gzip on; - gzip_comp_level 1; - gzip_types application/json text/plain application/xml text/html; + proxy_pass http://${PAVICS_FQDN}:8087; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $real_scheme; + proxy_set_header Accept-Encoding ""; + gzip_proxied any; + gzip on; + gzip_comp_level 1; + gzip_types application/json text/plain application/xml text/html; } location /ncWMS2/ { diff --git a/birdhouse/config/proxy/conf.d/frontend.conf.template b/birdhouse/config/proxy/conf.d/frontend.conf.template index b7cae9be6..391f7902c 100644 --- a/birdhouse/config/proxy/conf.d/frontend.conf.template +++ b/birdhouse/config/proxy/conf.d/frontend.conf.template @@ -32,11 +32,11 @@ server { server_name localhost; proxy_buffering off; - resolver 127.0.0.11; + resolver 127.0.0.11; - ssl on; - ssl_certificate /etc/nginx/cert.pem; - ssl_certificate_key /etc/nginx/cert.pem; + ssl on; + ssl_certificate /etc/nginx/cert.pem; + ssl_certificate_key /etc/nginx/cert.pem; include /etc/nginx/conf.d/all-services.include; @@ -46,4 +46,3 @@ server { root /usr/share/nginx/html; } } - diff --git a/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf.template b/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf.template new file mode 100644 index 000000000..ddb0608d8 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf.template @@ -0,0 +1,10 @@ + + # NOTE: paths must be more explicit than '/wpsoutputs/' to take precedence on the default public location + + location ~ ^/wpsoutputs/weaver/(?!(public))/ { + proxy_pass https://${PAVICS_FQDN_PUBLIC}${TWITCHER_PROTECTED_PATH}/secure-data-proxy/; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $real_scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host:$server_port; + } diff --git a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template new file mode 100644 index 000000000..bb5392b61 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template @@ -0,0 +1,16 @@ +providers: + secure-data-proxy: + url: http://proxy:80/ + title: Secured Data Proxy + public: true + c4i: false + type: api + sync_type: api + +permissions: + - service: secure-data-proxy + resource: /wpsoutputs/weaver/public + type: route + permission: read + group: anonymous + action: create diff --git a/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml b/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml new file mode 100644 index 000000000..fe031bbb4 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml @@ -0,0 +1,6 @@ +version: "3.4" +services: + magpie: + volumes: + - ./optional-components/secure-data-proxy/config/magpie/permissions.yml:/opt/local/src/magpie/config/providers/secure-data-proxy.yml:ro + - ./optional-components/secure-data-proxy/config/magpie/permissions.yml:/opt/local/src/magpie/config/permissions/secure-data-proxy.yml:ro From 679d72a4f9dac431853a9d2b1cc42a153b8532bc Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Wed, 16 Nov 2022 23:46:57 -0500 Subject: [PATCH 02/15] patch cowbird perms sync config --- .../cowbird/config/cowbird/config.yml.template | 5 +++-- .../secure-wps-outputs.conf | 10 ++++++++++ .../secure-data-proxy/config/magpie/config.yml | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf create mode 100644 birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml diff --git a/birdhouse/components/cowbird/config/cowbird/config.yml.template b/birdhouse/components/cowbird/config/cowbird/config.yml.template index e67cd1c3f..55166dfd0 100644 --- a/birdhouse/components/cowbird/config/cowbird/config.yml.template +++ b/birdhouse/components/cowbird/config/cowbird/config.yml.template @@ -225,7 +225,7 @@ sync_permissions: permissions_mapping: # When user is granted access to an output (either side), # output retrieval is allowed from both endpoints (wps-outputs/weaver). - - "weaver_wps_outputs : read <-> job_outputs : read" + - "weaver_wps_outputs : read -> job_outputs : read" # When output can be retrieved, access to details about the process and # the job are also provided (to understand what each output represents), # but getting read access to a process description should not grant @@ -243,4 +243,5 @@ sync_permissions: - "weaver_wps_outputs : read <-> thredds_wps_outputs : read" # permissions if outputs are shared one-by-one in case of multiple files produced by the process - "weaver_wps_output_single : read <-> thredds_wps_output_single : read" - - "weaver_wps_output_single : read <-> job_output_single : read" + - "weaver_wps_output_single : read -> job_output_single : read" + - "thredds_wps_output_single : read -> job_output_single : read" diff --git a/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf b/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf new file mode 100644 index 000000000..5c67654b1 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf @@ -0,0 +1,10 @@ + + # NOTE: paths must be more explicit than '/wpsoutputs/' to take precedence on the default public location + + location ~ ^/wpsoutputs/weaver/(?!(public))/ { + proxy_pass https://localhost/twitcher/ows/proxy/secure-data-proxy/; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $real_scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host:$server_port; + } diff --git a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml new file mode 100644 index 000000000..bb5392b61 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml @@ -0,0 +1,16 @@ +providers: + secure-data-proxy: + url: http://proxy:80/ + title: Secured Data Proxy + public: true + c4i: false + type: api + sync_type: api + +permissions: + - service: secure-data-proxy + resource: /wpsoutputs/weaver/public + type: route + permission: read + group: anonymous + action: create From c3f1a1c32d7a2b770cd4514f7cc9b6b20a17025c Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Wed, 14 Dec 2022 12:08:06 -0500 Subject: [PATCH 03/15] remove/gitignore config instances from templates --- .../secure-data-proxy/.gitignore | 2 ++ .../secure-wps-outputs.conf | 10 ---------- .../secure-data-proxy/config/magpie/config.yml | 16 ---------------- 3 files changed, 2 insertions(+), 26 deletions(-) create mode 100644 birdhouse/optional-components/secure-data-proxy/.gitignore delete mode 100644 birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf delete mode 100644 birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml diff --git a/birdhouse/optional-components/secure-data-proxy/.gitignore b/birdhouse/optional-components/secure-data-proxy/.gitignore new file mode 100644 index 000000000..2c2168e83 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/.gitignore @@ -0,0 +1,2 @@ +config/magpie/config.yml +config.extra-service.d/secure-wps-outputs.conf diff --git a/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf b/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf deleted file mode 100644 index 5c67654b1..000000000 --- a/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf +++ /dev/null @@ -1,10 +0,0 @@ - - # NOTE: paths must be more explicit than '/wpsoutputs/' to take precedence on the default public location - - location ~ ^/wpsoutputs/weaver/(?!(public))/ { - proxy_pass https://localhost/twitcher/ows/proxy/secure-data-proxy/; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $real_scheme; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $host:$server_port; - } diff --git a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml deleted file mode 100644 index bb5392b61..000000000 --- a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml +++ /dev/null @@ -1,16 +0,0 @@ -providers: - secure-data-proxy: - url: http://proxy:80/ - title: Secured Data Proxy - public: true - c4i: false - type: api - sync_type: api - -permissions: - - service: secure-data-proxy - resource: /wpsoutputs/weaver/public - type: route - permission: read - group: anonymous - action: create From 5da6e3f7416d6a8cec315fef1fc571875d349792 Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Wed, 14 Dec 2022 12:33:53 -0500 Subject: [PATCH 04/15] add missing MAGPIE_WEBHOOKS_CONFIG_PATH to load webhooks defined by cowbird --- birdhouse/components/cowbird/docker-compose-extra.yml | 8 +++++--- birdhouse/docker-compose.yml | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/birdhouse/components/cowbird/docker-compose-extra.yml b/birdhouse/components/cowbird/docker-compose-extra.yml index 568dda99c..a68e0544d 100644 --- a/birdhouse/components/cowbird/docker-compose-extra.yml +++ b/birdhouse/components/cowbird/docker-compose-extra.yml @@ -78,8 +78,10 @@ services: - cowbird volumes: # NOTE: - # Although file uses the "config.yml" format, it is very important to pass it as independent/duplicate reference - # provider/permissions config files. This is because 'MAGPIE_CONFIG_PATH' is not used to allow parsing multiple - # config files for each extendable service, using loading of all configuration files found in mount directories. + # Although the file uses the combined "config.yml" format, it is very important to pass it as independent and + # duplicate references for providers/permissions/webhooks config files. This is because 'MAGPIE_CONFIG_PATH' is + # not used to allow parsing additive per-component config files for each extendable service, using loading of + # all configuration files found in mounted directories. - ./components/cowbird/config/magpie/config.yml:/opt/local/src/magpie/config/permissions/cowbird.yml:ro - ./components/cowbird/config/magpie/config.yml:/opt/local/src/magpie/config/providers/cowbird.yml:ro + - ./components/cowbird/config/magpie/config.yml:/opt/local/src/magpie/config/webhooks/cowbird.yml:ro diff --git a/birdhouse/docker-compose.yml b/birdhouse/docker-compose.yml index 4001b4b09..9daea9357 100644 --- a/birdhouse/docker-compose.yml +++ b/birdhouse/docker-compose.yml @@ -340,6 +340,7 @@ services: # (note: DO NOT use 'MAGPIE_CONFIG_PATH' that would disable multi-config loading capability) MAGPIE_PROVIDERS_CONFIG_PATH: "/opt/local/src/magpie/config/providers" MAGPIE_PERMISSIONS_CONFIG_PATH: "/opt/local/src/magpie/config/permissions" + MAGPIE_WEBHOOKS_CONFIG_PATH: "/opt/local/src/magpie/config/webhooks" MAGPIE_POSTGRES_HOST: postgres-magpie MAGPIE_PORT: 2001 FORWARDED_ALLOW_IPS: "*" From 3e8b918db64851ca938d7b63433110ede457594d Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Wed, 21 Dec 2022 17:17:59 -0500 Subject: [PATCH 05/15] allow colon character for process ID with tag refrence or namespace --- birdhouse/components/weaver/config/magpie/config.yml.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/birdhouse/components/weaver/config/magpie/config.yml.template b/birdhouse/components/weaver/config/magpie/config.yml.template index ae897589f..052574a8f 100644 --- a/birdhouse/components/weaver/config/magpie/config.yml.template +++ b/birdhouse/components/weaver/config/magpie/config.yml.template @@ -21,11 +21,11 @@ providers: # each path below are equivalents, but with more or less specific reference to the requested service/process # for job execution, 2 endpoints exist for older and newer OGC API - Processes specification - type: request - path: "/providers/[\\w_-]+/processes/[\\w_-]+/(jobs|execution)" + path: "/providers/[\\w_-]+/processes/[\\w_-:]+/(jobs|execution)" method: POST target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:add_x_wps_output_context - type: request - path: "/processes/[\\w_-]+/(jobs|execution)" + path: "/processes/[\\w_-:]+/(jobs|execution)" method: POST target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:add_x_wps_output_context - type: request From ccf39d29283a899939186f8f6ec9f523486739dc Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Wed, 25 Jan 2023 18:16:09 -0500 Subject: [PATCH 06/15] add components representation and JSON response endpoint --- .../conf.d/all-services.include.template | 5 +++ birdhouse/pavics-compose.sh | 2 + birdhouse/scripts/get-components-json.sh | 41 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 birdhouse/scripts/get-components-json.sh diff --git a/birdhouse/config/proxy/conf.d/all-services.include.template b/birdhouse/config/proxy/conf.d/all-services.include.template index 175d7aed6..3b6086332 100644 --- a/birdhouse/config/proxy/conf.d/all-services.include.template +++ b/birdhouse/config/proxy/conf.d/all-services.include.template @@ -8,6 +8,11 @@ proxy_set_header X-Forwarded-Proto $real_scheme; } + location /components/ { + default_type application/json; + return 200 '${BIRDHOUSE_DEPLOY_COMPONENTS_JSON}'; + } + location /magpie/ { proxy_pass http://${PAVICS_FQDN}:2001/; proxy_set_header Host $host; diff --git a/birdhouse/pavics-compose.sh b/birdhouse/pavics-compose.sh index e1ef874f5..273d28931 100755 --- a/birdhouse/pavics-compose.sh +++ b/birdhouse/pavics-compose.sh @@ -30,6 +30,7 @@ VARS=' $DATA_PERSIST_ROOT $GEOSERVER_ADMIN_USER $GEOSERVER_ADMIN_PASSWORD + $BIRDHOUSE_DEPLOY_COMPONENTS_JSON ' # list of vars to be substituted in template but they do not have to be set in env.local @@ -93,6 +94,7 @@ COMPOSE_DIR="`pwd`" # we source local configs, if present # we don't use usual .env filename, because docker-compose uses it [ -f env.local ] && . ./env.local +. ./scripts/get-components-json.sh for adir in ${EXTRA_CONF_DIRS}; do COMPONENT_DEFAULT_ENV="$adir/default.env" diff --git a/birdhouse/scripts/get-components-json.sh b/birdhouse/scripts/get-components-json.sh new file mode 100644 index 000000000..6a013c811 --- /dev/null +++ b/birdhouse/scripts/get-components-json.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Obtain a JSON representation of components enabled on this platform. +# +# Expected result should be similar to: +# { +# "components": [ +# "bird-house/birdhouse-deploy:components/monitoring", +# "bird-house/birdhouse-deploy:optional-components/canarie-api-full-monitoring", +# "bird-house/birdhouse-deploy:optional-components/all-public-access", +# "bird-house/birdhouse-deploy:optional-components/wps-healthchecks", +# "bird-house/birdhouse-deploy:optional-components/secure-thredds", +# "bird-house/birdhouse-deploy:optional-components/testthredds", +# "bird-house/birdhouse-deploy:optional-components/test-weaver", +# "bird-house/birdhouse-deploy:components/weaver", +# "bird-house/birdhouse-deploy:components/cowbird", +# "custom:daccs-env" +# ] +# } +# + +# default value in case of error or missing definitions +export BIRDHOUSE_DEPLOY_COMPONENTS_JSON='{"components": []}' +if [ -z "${EXTRA_CONF_DIRS}" ]; then + echo "No components in EXTRA_CONF_DIRS. Components JSON list will be empty!" + exit 0 +fi + +# create a JSON list using the specified components +# each component that starts by './' gets replaced with the below birdhouse prefix to provide contextual information +# other component locations are considered 'custom' and marked as such to provide contextual information +BIRDHOUSE_DEPLOY_COMPONENTS_BASE="bird-house/birdhouse-deploy:" +BIRDHOUSE_DEPLOY_COMPONENTS_LIST=$( \ + echo "${EXTRA_CONF_DIRS}" \ + | sed '/^[[:space:]]*$/d' \ + | sed -E 's/^\s*([A-Za-z0-0./_-]+)\s*$/"\1",/g' \ + | sed -E "s|^\"((\./)?(\.\./)+)+(.+)\"|\"custom:\\4\"|g" \ + | sed -E "s|^\"\./(.*)\"|\"${BIRDHOUSE_DEPLOY_COMPONENTS_BASE}\\1\"|g" \ + | sed '/^\n*$/d' \ +) +BIRDHOUSE_DEPLOY_COMPONENTS_LIST="${BIRDHOUSE_DEPLOY_COMPONENTS_LIST%?}" # remove last comma +export BIRDHOUSE_DEPLOY_COMPONENTS_JSON="{\"components\": [${BIRDHOUSE_DEPLOY_COMPONENTS_LIST}]}" From e7efc9473988e9541e3ef08fd9e8fe66f858900d Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Thu, 26 Jan 2023 18:26:30 -0500 Subject: [PATCH 07/15] fix partially duplicate nginx include of extra conf definitions in extra components --- CHANGES.md | 17 ++++++++++++++++- .../proxy/conf.d/all-services.include.template | 3 --- birdhouse/config/proxy/nginx.conf.template | 3 +-- .../secure-data-proxy/.gitignore | 2 +- .../secure-wps-outputs.conf.template | 3 ++- 5 files changed, 20 insertions(+), 8 deletions(-) rename birdhouse/optional-components/secure-data-proxy/{config.extra-service.d => conf.extra-service.d}/secure-wps-outputs.conf.template (84%) diff --git a/CHANGES.md b/CHANGES.md index 013b7780e..6e632b825 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,22 @@ [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: + + +## Fixes: + +- Remove `nginx` (partially) duplicate `include /etc/nginx/conf.extra[-service].d/*/*.conf;` definitions in the root + [nginx.conf.template][nginx.conf.template] and the nested + [all-services.include.template][all-services.include.template] configurations. + The `[-service]` portion was missing from one of the definitions, leading to invalid references according to the + extended configurations provided by components using this feature. The `include` is kept only inside + [nginx.conf.template][nginx.conf.template] to allow reuse of the generic server configuration in the new + [optional-components/secure-data-proxy][secure-data-proxy] component. + +[nginx.conf.template]: birdhouse/config/proxy/nginx.conf.template +[all-services.include.template]: birdhouse/config/proxy/conf.d/all-services.include.template +[secure-data-proxy]: birdhouse/optional-components/secure-data-proxy [1.22.9](https://github.com/bird-house/birdhouse-deploy/tree/1.22.9) (2023-01-25) ------------------------------------------------------------------------------------------------------------------ diff --git a/birdhouse/config/proxy/conf.d/all-services.include.template b/birdhouse/config/proxy/conf.d/all-services.include.template index 3b6086332..d8c10a111 100644 --- a/birdhouse/config/proxy/conf.d/all-services.include.template +++ b/birdhouse/config/proxy/conf.d/all-services.include.template @@ -95,6 +95,3 @@ location /doc { return 302 ${DOC_URL}; } - - # for other extra components to extend Nginx - include /etc/nginx/conf.extra-service.d/*/*.conf; diff --git a/birdhouse/config/proxy/nginx.conf.template b/birdhouse/config/proxy/nginx.conf.template index 95b98594e..64ab89830 100755 --- a/birdhouse/config/proxy/nginx.conf.template +++ b/birdhouse/config/proxy/nginx.conf.template @@ -36,7 +36,6 @@ http { include /etc/nginx/conf.d/*.conf; # for other extra components to extend Nginx - include /etc/nginx/conf.extra.d/*/*.conf; + include /etc/nginx/conf.extra-service.d/*/*.conf; } - diff --git a/birdhouse/optional-components/secure-data-proxy/.gitignore b/birdhouse/optional-components/secure-data-proxy/.gitignore index 2c2168e83..49a2c8d2f 100644 --- a/birdhouse/optional-components/secure-data-proxy/.gitignore +++ b/birdhouse/optional-components/secure-data-proxy/.gitignore @@ -1,2 +1,2 @@ +conf.extra-service.d/secure-wps-outputs.conf config/magpie/config.yml -config.extra-service.d/secure-wps-outputs.conf diff --git a/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf.template b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template similarity index 84% rename from birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf.template rename to birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template index ddb0608d8..120c2325e 100644 --- a/birdhouse/optional-components/secure-data-proxy/config.extra-service.d/secure-wps-outputs.conf.template +++ b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template @@ -1,7 +1,8 @@ # NOTE: paths must be more explicit than '/wpsoutputs/' to take precedence on the default public location - location ~ ^/wpsoutputs/weaver/(?!(public))/ { + #location ~ ^/wpsoutputs/weaver/(?!(public))/ { + location /wpsoutputs/ { proxy_pass https://${PAVICS_FQDN_PUBLIC}${TWITCHER_PROTECTED_PATH}/secure-data-proxy/; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $real_scheme; From e75d9235e5d0d2bb63f584872b694e4bcceabdbc Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Thu, 26 Jan 2023 18:36:34 -0500 Subject: [PATCH 08/15] revert nginx mix include/conf locations --- CHANGES.md | 10 ---------- .../config/proxy/conf.d/all-services.include.template | 3 +++ birdhouse/config/proxy/nginx.conf.template | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6e632b825..b71e857a6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,16 +17,6 @@ ## Changes: -## Fixes: - -- Remove `nginx` (partially) duplicate `include /etc/nginx/conf.extra[-service].d/*/*.conf;` definitions in the root - [nginx.conf.template][nginx.conf.template] and the nested - [all-services.include.template][all-services.include.template] configurations. - The `[-service]` portion was missing from one of the definitions, leading to invalid references according to the - extended configurations provided by components using this feature. The `include` is kept only inside - [nginx.conf.template][nginx.conf.template] to allow reuse of the generic server configuration in the new - [optional-components/secure-data-proxy][secure-data-proxy] component. - [nginx.conf.template]: birdhouse/config/proxy/nginx.conf.template [all-services.include.template]: birdhouse/config/proxy/conf.d/all-services.include.template [secure-data-proxy]: birdhouse/optional-components/secure-data-proxy diff --git a/birdhouse/config/proxy/conf.d/all-services.include.template b/birdhouse/config/proxy/conf.d/all-services.include.template index d8c10a111..3b6086332 100644 --- a/birdhouse/config/proxy/conf.d/all-services.include.template +++ b/birdhouse/config/proxy/conf.d/all-services.include.template @@ -95,3 +95,6 @@ location /doc { return 302 ${DOC_URL}; } + + # for other extra components to extend Nginx + include /etc/nginx/conf.extra-service.d/*/*.conf; diff --git a/birdhouse/config/proxy/nginx.conf.template b/birdhouse/config/proxy/nginx.conf.template index 64ab89830..a8673a620 100755 --- a/birdhouse/config/proxy/nginx.conf.template +++ b/birdhouse/config/proxy/nginx.conf.template @@ -36,6 +36,6 @@ http { include /etc/nginx/conf.d/*.conf; # for other extra components to extend Nginx - include /etc/nginx/conf.extra-service.d/*/*.conf; + include /etc/nginx/conf.extra.d/*/*.conf; } From 7061f6380629b42121275810e1977de5794ef7cb Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Thu, 26 Jan 2023 19:35:56 -0500 Subject: [PATCH 09/15] secure-data-proxy impl using alternate proxy-data service --- .../conf.d/all-services.include.template | 4 -- birdhouse/docker-compose.yml | 4 +- birdhouse/optional-components/README.rst | 8 +++- .../all-public-access-magpie-permission.cfg | 11 +++++ .../public-data-proxy/.gitignore | 1 + .../public-wps-outputs.conf.template | 8 ++++ .../docker-compose-extra.yml | 7 ++++ .../secure-data-proxy/.gitignore | 1 + .../conf.d/all-services.include.template | 3 ++ .../secure-wps-outputs.conf.template | 12 ++++-- .../config/magpie/config.yml.template | 2 +- .../docker-compose-extra.yml | 42 ++++++++++++++++++- 12 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 birdhouse/optional-components/public-data-proxy/.gitignore create mode 100644 birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template create mode 100644 birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml create mode 100644 birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template diff --git a/birdhouse/config/proxy/conf.d/all-services.include.template b/birdhouse/config/proxy/conf.d/all-services.include.template index 3b6086332..27d47e71c 100644 --- a/birdhouse/config/proxy/conf.d/all-services.include.template +++ b/birdhouse/config/proxy/conf.d/all-services.include.template @@ -38,10 +38,6 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } - location /wpsoutputs/ { - alias /pavics-data/wps_outputs/; - } - location /geoserver/ { proxy_pass http://${PAVICS_FQDN}:8087; proxy_set_header Host $host; diff --git a/birdhouse/docker-compose.yml b/birdhouse/docker-compose.yml index 9daea9357..76d060031 100644 --- a/birdhouse/docker-compose.yml +++ b/birdhouse/docker-compose.yml @@ -24,7 +24,9 @@ services: - ./config/proxy/nginx.conf:/etc/nginx/nginx.conf - ./config/canarie-api/docker_configuration.py:/config/docker_configuration.py - ${SSL_CERTIFICATE}:/etc/nginx/cert.pem - - wps_outputs:/pavics-data/wps_outputs + # WARNING: purposely omit outputs volume + # see 'optional-components/public-data-proxy' and 'optional-components/secure-data-proxy' + # - wps_outputs:/pavics-data/wps_outputs - ./config/canarie-api/entrypoint:/entrypoint:ro environment: CANARIE_API_CONFIG_FN: /config/docker_configuration.py diff --git a/birdhouse/optional-components/README.rst b/birdhouse/optional-components/README.rst index fb2539915..1a57e0a44 100644 --- a/birdhouse/optional-components/README.rst +++ b/birdhouse/optional-components/README.rst @@ -151,7 +151,7 @@ By enabling this component, all WPS services and data on THREDDS are completely Once enabled, if you need to revert the change, you have to do it manually by logging into Magpie. Just disabling this component will not revert the change. Alternatively, you can create a similar file to |magpie-public-perms|_ and replace all desired ``action: create`` -entries by ``action: remove`` to make sure the permissions are removed as startup if they exist. +entries by ``action: remove`` to make sure the permissions are removed at startup if they exist. This optional component is required for the test suite at https://github.com/Ouranosinc/PAVICS-e2e-workflow-tests. @@ -169,6 +169,10 @@ The anonymous user will now have all the permissions described in |magpie-public .. _env.local.example: ../env.local.example +Control secured access to WPS outputs +-------------------------------------------------------- + + Control secured access to resources example -------------------------------------------------------- @@ -263,4 +267,4 @@ interface. See |geoserver_secured_pr|_. for more details. .. _geoserver_secured_pr: https://github.com/bird-house/birdhouse-deploy/pull/242 -.. |geoserver_secured_pr| replace:: Pull Request \ No newline at end of file +.. |geoserver_secured_pr| replace:: Pull Request diff --git a/birdhouse/optional-components/all-public-access/all-public-access-magpie-permission.cfg b/birdhouse/optional-components/all-public-access/all-public-access-magpie-permission.cfg index aeb6d399c..8e75dda5d 100644 --- a/birdhouse/optional-components/all-public-access/all-public-access-magpie-permission.cfg +++ b/birdhouse/optional-components/all-public-access/all-public-access-magpie-permission.cfg @@ -104,3 +104,14 @@ permissions: permission: browse group: anonymous action: create + + + - service: secure-data-proxy + permission: read + group: anonymous + action: create + + - service: secure-data-proxy + permission: write + group: anonymous + action: create diff --git a/birdhouse/optional-components/public-data-proxy/.gitignore b/birdhouse/optional-components/public-data-proxy/.gitignore new file mode 100644 index 000000000..46defca82 --- /dev/null +++ b/birdhouse/optional-components/public-data-proxy/.gitignore @@ -0,0 +1 @@ +conf.extra-service.d/public-wps-outputs.conf diff --git a/birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template b/birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template new file mode 100644 index 000000000..95ac8cd8e --- /dev/null +++ b/birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template @@ -0,0 +1,8 @@ + + # NOTE: + # If more data volumes are specied here, their corresponding definitions for secured access should + # also be added to 'optinal-components/secure-data-proxy'. + + location /wpsoutputs/ { + alias /pavics-data/wps_outputs/; + } diff --git a/birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml b/birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml new file mode 100644 index 000000000..715b572c7 --- /dev/null +++ b/birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml @@ -0,0 +1,7 @@ +version: "3.4" +services: + + proxy: + volumes: + - ./optional-components/public-data-proxy/conf.extra-service.d:/etc/nginx/conf.extra-service.d/public-data-proxy:ro + - wps_outputs:/pavics-data/wps_outputs diff --git a/birdhouse/optional-components/secure-data-proxy/.gitignore b/birdhouse/optional-components/secure-data-proxy/.gitignore index 49a2c8d2f..335d8b7e7 100644 --- a/birdhouse/optional-components/secure-data-proxy/.gitignore +++ b/birdhouse/optional-components/secure-data-proxy/.gitignore @@ -1,2 +1,3 @@ +conf.d/all-services.include conf.extra-service.d/secure-wps-outputs.conf config/magpie/config.yml diff --git a/birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template b/birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template new file mode 100644 index 000000000..a37d280c8 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template @@ -0,0 +1,3 @@ + + # for other extra components to extend Nginx + include /etc/nginx/conf.extra-service.d/*/*.conf; diff --git a/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template index 120c2325e..221b98d5b 100644 --- a/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template +++ b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template @@ -1,9 +1,15 @@ - # NOTE: paths must be more explicit than '/wpsoutputs/' to take precedence on the default public location + # NOTE: + # The root of the service registered in magpie is 'secure-data-proxy'. + # The 'wpsoutputs' is only one sub-directory in the hierarchy. + # More data volumes, directories and rediction rules can be defined using the same service with similar mechanics. + + # NOTE: + # If more data volumes are specied here, their corresponding definitions of direct access publically should + # also be added to 'optinal-components/public-data-proxy'. - #location ~ ^/wpsoutputs/weaver/(?!(public))/ { location /wpsoutputs/ { - proxy_pass https://${PAVICS_FQDN_PUBLIC}${TWITCHER_PROTECTED_PATH}/secure-data-proxy/; + proxy_pass https://${PAVICS_FQDN_PUBLIC}${TWITCHER_PROTECTED_PATH}/secure-data-proxy/wpsoutputs/; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $real_scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template index bb5392b61..266928293 100644 --- a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template +++ b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template @@ -1,6 +1,6 @@ providers: secure-data-proxy: - url: http://proxy:80/ + url: http://proxy-data:80 title: Secured Data Proxy public: true c4i: false diff --git a/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml b/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml index fe031bbb4..00f657edc 100644 --- a/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml +++ b/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml @@ -1,6 +1,44 @@ +x-logging: + &default-logging + driver: "json-file" + options: + max-size: "200k" + max-file: "10" + version: "3.4" services: + + proxy: + volumes: + - ./optional-components/secure-data-proxy/conf.extra-service.d:/etc/nginx/conf.extra-service.d/secure-data-proxy:ro + # only to ensure it is (re)started along 'proxy' changes + depends_on: + - proxy-data + + proxy-data: + image: nginx + container_name: proxy-data + # NOTE: no ports mapping (so secured data is not exposed) + # rely on inter-docker networks to resolve 'proxy -> magpie -> proxy-data' after AuthN/AuthZ validation + volumes: + # same files as 'proxy' service except 'all-services.include' for which we provide + # the specific override definition to only map data access under this proxy + - ./config/proxy/conf.d/cors.include:/etc/nginx/conf.d/cors.include + - ./config/proxy/conf.d/frontend.conf:/etc/nginx/conf.d/frontend.conf + - ./config/proxy/conf.d/redirect-to-https.include:/etc/nginx/conf.d/redirect-to-https.include + - ./config/proxy/nginx.conf:/etc/nginx/nginx.conf + - ${SSL_CERTIFICATE}:/etc/nginx/cert.pem + # place override (without other services) were expected when included by 'frontend.conf' + - ./optional-components/secure-data-proxy/conf.d/all-services.include:/etc/nginx/conf.d/all-services.include:ro + # reuse definitions from 'public-data-proxy' to resolve the same way after AuthN/AuthZ validation and redirection + - ./optional-components/public-data-proxy/conf.extra-service.d:/etc/nginx/conf.extra-service.d/public-data-proxy:ro + - wps_outputs:/pavics-data/wps_outputs + restart: always + logging: *default-logging + magpie: + links: + - proxy-data volumes: - - ./optional-components/secure-data-proxy/config/magpie/permissions.yml:/opt/local/src/magpie/config/providers/secure-data-proxy.yml:ro - - ./optional-components/secure-data-proxy/config/magpie/permissions.yml:/opt/local/src/magpie/config/permissions/secure-data-proxy.yml:ro + - ./optional-components/secure-data-proxy/config/magpie/config.yml:/opt/local/src/magpie/config/providers/secure-data-proxy.yml:ro + - ./optional-components/secure-data-proxy/config/magpie/config.yml:/opt/local/src/magpie/config/permissions/secure-data-proxy.yml:ro From 7a84aba9caa7843605ca0d4c65d8a5ff117405ba Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Mon, 30 Jan 2023 22:42:59 -0500 Subject: [PATCH 10/15] setup working secure-data-proxy component --- CHANGES.md | 30 ++++++++++++++++-- birdhouse/components/cowbird/default.env | 6 ++-- birdhouse/components/monitoring/default.env | 6 ++-- birdhouse/components/weaver/default.env | 6 ++-- .../conf.d/all-services.include.template | 6 ++++ birdhouse/default.env | 21 +++++++++--- birdhouse/docker-compose.yml | 4 +-- birdhouse/optional-components/README.rst | 24 ++++++++++++++ birdhouse/optional-components/emu/default.env | 6 ++-- .../generic_bird/default.env | 6 ++-- .../public-data-proxy/.gitignore | 1 - .../public-wps-outputs.conf.template | 8 ----- .../docker-compose-extra.yml | 7 ---- .../secure-data-proxy/.gitignore | 3 +- .../conf.d/all-services.include.template | 3 -- .../secure-data-auth.conf.template | 15 +++++++++ .../secure-data-auth.include | 5 +++ .../secure-wps-outputs.conf.template | 17 ---------- .../config/magpie/config.yml.template | 15 ++++++++- .../secure-data-proxy/default.env | 19 +++++++++++ .../docker-compose-extra.yml | 26 --------------- .../images/magpie-service.png | Bin 0 -> 49803 bytes .../testthredds/default.env | 6 ++-- birdhouse/pavics-compose.sh | 1 + 24 files changed, 149 insertions(+), 92 deletions(-) delete mode 100644 birdhouse/optional-components/public-data-proxy/.gitignore delete mode 100644 birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template delete mode 100644 birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml delete mode 100644 birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template create mode 100644 birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.conf.template create mode 100644 birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.include delete mode 100644 birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template create mode 100644 birdhouse/optional-components/secure-data-proxy/default.env create mode 100644 birdhouse/optional-components/secure-data-proxy/images/magpie-service.png diff --git a/CHANGES.md b/CHANGES.md index b71e857a6..25d1ce637 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,10 +16,36 @@ ## Changes: +- secure-data-proxy: add new [`secure-data-proxy`][secure-data-proxy] optional component. + + When enabled, this component will enforce authentication and authorization to be resolved against the `/wpsoutputs` + endpoint prior to accessing the results produced by WPS executions. A Magpie service named `secure-data-proxy` is + created to define the resource and permission hierarchy of directories and files the users and groups can access. + When disabled, the original behavior to provide open access to `/wpsoutputs` is employed. + + A variable named ``SECURE_DATA_PROXY_AUTH_INCLUDE`` is dynamically assigned based on the activation or not of this + component. Corresponding validation of optional/mandatory/delayed-eval variables used by this component are also + applied dynamically, as well as mounting the necessary ``nginx`` and ``docker-compose`` extended configurations. + +## Fixes: + +- Magpie/Twitcher: update minimum version `magpie>=3.31.0` to employ `twitcher>=0.8.0` in `MapgieAdatepr`. + + - Resolve an issue where `response.request` references were not set in OWS proxy responses when handled by Twitcher. + This caused `MapgieAdatepr` response hooks to fail, which in turn caused failing requests for any non-WPS + service that defined any proxy request hook, such as in the case of [`weaver`][weaver-component] component. + + - Adds the Twitcher ``/ows/verify/{service_name}[/{extra_path}`` endpoint employed for validating authorized access + to Magpie service/resources, in the same fashion as the protected proxy endpoint, but without performing the proxied + request toward the target service. This is mandatory for using the new [`secure-data-proxy`][secure-data-proxy] + optional component, otherwise the proxy endpoint triggers data download twice, once for authorization and another + for actually accessing the data. + + See also [Ouranosinc/Magpie#571](https://github.com/Ouranosinc/Magpie/pull/571) + and [bird-house/twitcher#118](https://github.com/bird-house/twitcher/pull/118). -[nginx.conf.template]: birdhouse/config/proxy/nginx.conf.template -[all-services.include.template]: birdhouse/config/proxy/conf.d/all-services.include.template [secure-data-proxy]: birdhouse/optional-components/secure-data-proxy +[weaver-component]: birdhouse/components/weaver [1.22.9](https://github.com/bird-house/birdhouse-deploy/tree/1.22.9) (2023-01-25) ------------------------------------------------------------------------------------------------------------------ diff --git a/birdhouse/components/cowbird/default.env b/birdhouse/components/cowbird/default.env index 09eb8fda4..2afed28c7 100644 --- a/birdhouse/components/cowbird/default.env +++ b/birdhouse/components/cowbird/default.env @@ -4,9 +4,9 @@ # All env in this default.env can be overridden by env.local. -# All env in this default.env must NOT depend on any other env. If they do, -# must use single quote to avoid early expansion before overrides in local.env -# are applied and must add to the list of DELAYED_EVAL. +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. # 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 'pavics-compose' parses them diff --git a/birdhouse/components/monitoring/default.env b/birdhouse/components/monitoring/default.env index bcf798d09..026f15b3b 100644 --- a/birdhouse/components/monitoring/default.env +++ b/birdhouse/components/monitoring/default.env @@ -1,8 +1,8 @@ # All env in this default.env can be overridden by env.local. -# All env in this default.env must NOT depend on any other env. If they do, -# must use single quote to avoid early expansion before overrides in local.env -# are applied and must add to the list of DELAYED_EVAL. +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. export GRAFANA_ADMIN_PASSWORD="changeme!" export ALERTMANAGER_ADMIN_EMAIL_RECEIVER="" # "user1@example.com,user2@example.com" diff --git a/birdhouse/components/weaver/default.env b/birdhouse/components/weaver/default.env index e7ae4e39c..93eeaf861 100644 --- a/birdhouse/components/weaver/default.env +++ b/birdhouse/components/weaver/default.env @@ -4,9 +4,9 @@ # All env in this default.env can be overridden by env.local. -# All env in this default.env must NOT depend on any other env. If they do, -# must use single quote to avoid early expansion before overrides in local.env -# are applied and must add to the list of DELAYED_EVAL. +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. # 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 'pavics-compose' parses them diff --git a/birdhouse/config/proxy/conf.d/all-services.include.template b/birdhouse/config/proxy/conf.d/all-services.include.template index 27d47e71c..c6d9f5f14 100644 --- a/birdhouse/config/proxy/conf.d/all-services.include.template +++ b/birdhouse/config/proxy/conf.d/all-services.include.template @@ -38,6 +38,12 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } + location /wpsoutputs/ { + ${SECURE_DATA_PROXY_AUTH_INCLUDE} + + alias /pavics-data/wps_outputs/; + } + location /geoserver/ { proxy_pass http://${PAVICS_FQDN}:8087; proxy_set_header Host $host; diff --git a/birdhouse/default.env b/birdhouse/default.env index b52dda7f1..e560c4395 100644 --- a/birdhouse/default.env +++ b/birdhouse/default.env @@ -1,8 +1,10 @@ +#!/bin/sh + # All env in this default.env can be overridden by env.local. -# All env in this default.env must NOT depend on any other env. If they do, -# must use single quote to avoid early expansion before overrides in local.env -# are applied and must add to the list of DELAYED_EVAL. +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. # Jupyter single-user server images, can be overriden in env.local to have a space separated list of multiple images export DOCKER_NOTEBOOK_IMAGES="pavics/workflow-tests:221130" @@ -70,7 +72,7 @@ export CANARIE_MONITORING_EXTRA_CONF_DIR="/conf.d" export THREDDS_ORGANIZATION="Birdhouse" # Tag version that will be used to update Magpie API, Magpie CLI, and matching Twitcher with Magpie Adapter -export MAGPIE_VERSION=3.26.0 +export MAGPIE_VERSION=3.31.0 export MAGPIE_DB_NAME="magpiedb" @@ -89,6 +91,16 @@ export MAGPIE_USER_REGISTRATION_NOTIFY_ENABLED=false export MAGPIE_LOG_LEVEL=INFO export TWITCHER_LOG_LEVEL=INFO +# Endpoint to verify Magpie/Twitcher authorization to a service/resource by a user without proxy request +# Requires Twitcher>=0.8.0, Required for 'optional-compontents/secure-data-proxy' +# Uses single quotes for DELAYED_EVAL mechanic to infer the default location based on the expected proxy path. +export TWITCHER_VERIFY_PATH='$(echo "${TWITCHER_PROTECTED_PATH}" | sed "s/proxy/verify/")' + +# When 'optional-compontents/secure-data-proxy' is not enabled, this ensures the variable is defined for nginx. +# Otherwise, it raises that it is not defined. We want it to be empty when this component is not used. +# When the component is enabled, the value should be defined by its own 'default.env' file. +export SECURE_DATA_PROXY_AUTH_INCLUDE="" + export VERIFY_SSL="true" export GEOSERVER_ADMIN_USER="admin" @@ -132,4 +144,5 @@ export DELAYED_EVAL=' JUPYTERHUB_USER_DATA_DIR MAGPIE_PERSIST_DIR GEOSERVER_DATA_DIR + TWITCHER_VERIFY_PATH ' diff --git a/birdhouse/docker-compose.yml b/birdhouse/docker-compose.yml index 76d060031..9daea9357 100644 --- a/birdhouse/docker-compose.yml +++ b/birdhouse/docker-compose.yml @@ -24,9 +24,7 @@ services: - ./config/proxy/nginx.conf:/etc/nginx/nginx.conf - ./config/canarie-api/docker_configuration.py:/config/docker_configuration.py - ${SSL_CERTIFICATE}:/etc/nginx/cert.pem - # WARNING: purposely omit outputs volume - # see 'optional-components/public-data-proxy' and 'optional-components/secure-data-proxy' - # - wps_outputs:/pavics-data/wps_outputs + - wps_outputs:/pavics-data/wps_outputs - ./config/canarie-api/entrypoint:/entrypoint:ro environment: CANARIE_API_CONFIG_FN: /config/docker_configuration.py diff --git a/birdhouse/optional-components/README.rst b/birdhouse/optional-components/README.rst index 1a57e0a44..d87111176 100644 --- a/birdhouse/optional-components/README.rst +++ b/birdhouse/optional-components/README.rst @@ -172,6 +172,30 @@ The anonymous user will now have all the permissions described in |magpie-public Control secured access to WPS outputs -------------------------------------------------------- +By default, all outputs of WPS processes (i.e.: ``/wpsoutputs``) are publicly accessible. This is to preserve +backward compatibility with previous instances. However, enabling this optional component adds secured access to data +stored under ``/wpsoutputs``. + +To provide secured access, all requests sent to ``/wpsoutputs`` require a prior authorization from a new service added +to Magpie, called ``secure-data-proxy``. As shown below, this service should replicate the file system directory +hierarchy defined to store the data. A file located under ``/wpsoutputs/weaver/public`` for example would use the +corresponding resources and user/group permissions defined under this service to validate that the authenticated +request user can obtain access to it. + +.. image:: secure-data-proxy/images/magpie-service.png + +How to enable in ``env.local`` (a copy from `env.local.example`_ (:download:`download `)): + +* Add ``./optional-components/secure-data-proxy`` to ``EXTRA_CONF_DIRS``. + +Once enabled, users will *NOT* have public access to files under ``/wpsoutputs`` anymore, except for items defined +with authorized ``read`` permissions for the ``anonymous`` group under |secure-data-proxy-perms|_. As any other Magpie +configuration file, any combination of user/group/resource/permission could be defined for the ``secure-data-proxy`` +service to customize specific user access control to stored data files. + +.. _secure-data-proxy-perms: ./secure-data-proxy/config/magpie/config.yml.template +.. |secure-data-proxy-perms| replace:: optional-components/secure-data-proxy/config/magpie/config.yml.template + Control secured access to resources example -------------------------------------------------------- diff --git a/birdhouse/optional-components/emu/default.env b/birdhouse/optional-components/emu/default.env index 4f570f7f1..2c72182d9 100644 --- a/birdhouse/optional-components/emu/default.env +++ b/birdhouse/optional-components/emu/default.env @@ -1,8 +1,8 @@ # All env in this default.env can be overridden by env.local. -# All env in this default.env must NOT depend on any other env. If they do, -# must use single quote to avoid early expansion before overrides in local.env -# are applied and must add to the list of DELAYED_EVAL. +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. # Should have been "birdhouse/emu" but at the moment the current config only # works with the "watchdog/jobqueue" branch so have to default to an image that diff --git a/birdhouse/optional-components/generic_bird/default.env b/birdhouse/optional-components/generic_bird/default.env index 5aad4e39f..6c2535683 100644 --- a/birdhouse/optional-components/generic_bird/default.env +++ b/birdhouse/optional-components/generic_bird/default.env @@ -1,8 +1,8 @@ # All env in this default.env can be overridden by env.local. -# All env in this default.env must NOT depend on any other env. If they do, -# must use single quote to avoid early expansion before overrides in local.env -# are applied and must add to the list of DELAYED_EVAL. +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. export GENERIC_BIRD_IMAGE="$FINCH_IMAGE" export GENERIC_BIRD_PORT="8010" diff --git a/birdhouse/optional-components/public-data-proxy/.gitignore b/birdhouse/optional-components/public-data-proxy/.gitignore deleted file mode 100644 index 46defca82..000000000 --- a/birdhouse/optional-components/public-data-proxy/.gitignore +++ /dev/null @@ -1 +0,0 @@ -conf.extra-service.d/public-wps-outputs.conf diff --git a/birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template b/birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template deleted file mode 100644 index 95ac8cd8e..000000000 --- a/birdhouse/optional-components/public-data-proxy/conf.extra-service.d/public-wps-outputs.conf.template +++ /dev/null @@ -1,8 +0,0 @@ - - # NOTE: - # If more data volumes are specied here, their corresponding definitions for secured access should - # also be added to 'optinal-components/secure-data-proxy'. - - location /wpsoutputs/ { - alias /pavics-data/wps_outputs/; - } diff --git a/birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml b/birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml deleted file mode 100644 index 715b572c7..000000000 --- a/birdhouse/optional-components/public-data-proxy/docker-compose-extra.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: "3.4" -services: - - proxy: - volumes: - - ./optional-components/public-data-proxy/conf.extra-service.d:/etc/nginx/conf.extra-service.d/public-data-proxy:ro - - wps_outputs:/pavics-data/wps_outputs diff --git a/birdhouse/optional-components/secure-data-proxy/.gitignore b/birdhouse/optional-components/secure-data-proxy/.gitignore index 335d8b7e7..c71c7cea5 100644 --- a/birdhouse/optional-components/secure-data-proxy/.gitignore +++ b/birdhouse/optional-components/secure-data-proxy/.gitignore @@ -1,3 +1,2 @@ -conf.d/all-services.include -conf.extra-service.d/secure-wps-outputs.conf +conf.extra-service.d/secure-data-auth.conf config/magpie/config.yml diff --git a/birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template b/birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template deleted file mode 100644 index a37d280c8..000000000 --- a/birdhouse/optional-components/secure-data-proxy/conf.d/all-services.include.template +++ /dev/null @@ -1,3 +0,0 @@ - - # for other extra components to extend Nginx - include /etc/nginx/conf.extra-service.d/*/*.conf; diff --git a/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.conf.template b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.conf.template new file mode 100644 index 000000000..0c28a8593 --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.conf.template @@ -0,0 +1,15 @@ + + location = /secure-data-auth { + internal; + # note: using 'TWITCHER_VERIFY_PATH' path to avoid performing the request via 'proxy' endpoint + # This ensures that the data access is validated for the user, but does not trigger its access/download twice. + # Also, avoids getting an error as 'secure-data-proxy' private URL in Magpie doesn't resolve to a valid path. + proxy_pass https://${PAVICS_FQDN_PUBLIC}${TWITCHER_VERIFY_PATH}/secure-data-proxy$request_uri; + proxy_pass_request_body off; + proxy_set_header Host $host; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Forwarded-Proto $real_scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host:$server_port; + } diff --git a/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.include b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.include new file mode 100644 index 000000000..0b739788b --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-data-auth.include @@ -0,0 +1,5 @@ + + # this should be included within a 'location {}' block to verify 'secure-data-proxy' + # magpie service authorization prior to accessing the corresponding data + auth_request /secure-data-auth; + auth_request_set $auth_status $upstream_status; diff --git a/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template b/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template deleted file mode 100644 index 221b98d5b..000000000 --- a/birdhouse/optional-components/secure-data-proxy/conf.extra-service.d/secure-wps-outputs.conf.template +++ /dev/null @@ -1,17 +0,0 @@ - - # NOTE: - # The root of the service registered in magpie is 'secure-data-proxy'. - # The 'wpsoutputs' is only one sub-directory in the hierarchy. - # More data volumes, directories and rediction rules can be defined using the same service with similar mechanics. - - # NOTE: - # If more data volumes are specied here, their corresponding definitions of direct access publically should - # also be added to 'optinal-components/public-data-proxy'. - - location /wpsoutputs/ { - proxy_pass https://${PAVICS_FQDN_PUBLIC}${TWITCHER_PROTECTED_PATH}/secure-data-proxy/wpsoutputs/; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $real_scheme; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $host:$server_port; - } diff --git a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template index 266928293..8f0ca8720 100644 --- a/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template +++ b/birdhouse/optional-components/secure-data-proxy/config/magpie/config.yml.template @@ -1,6 +1,8 @@ providers: secure-data-proxy: - url: http://proxy-data:80 + # below URL is only used to fill in the required location in Magpie + # actual auth validation is performed with Twitcher 'verify' endpoint without accessing this proxied URL + url: http://proxy:80 title: Secured Data Proxy public: true c4i: false @@ -9,8 +11,19 @@ providers: permissions: - service: secure-data-proxy + # this location is where weaver saves job wps-outputs when no user + # was logged in when executing a publicly accessible process + # (see 'components/weaver/config/magpie/weaver_hooks.py') resource: /wpsoutputs/weaver/public type: route permission: read group: anonymous action: create + + - service: secure-data-proxy + # this location is a generic directory for demonstration purpose + resource: /wpsoutputs/public + type: route + permission: read + group: anonymous + action: create diff --git a/birdhouse/optional-components/secure-data-proxy/default.env b/birdhouse/optional-components/secure-data-proxy/default.env new file mode 100644 index 000000000..c0850de3e --- /dev/null +++ b/birdhouse/optional-components/secure-data-proxy/default.env @@ -0,0 +1,19 @@ +#!/bin/sh + +# All env in this default.env can be overridden by env.local. + +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. + +# 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 'pavics-compose' parses them +EXTRA_VARS=' + $TWITCHER_VERIFY_PATH + $SECURE_DATA_PROXY_AUTH_INCLUDE +' +# extend the original 'VARS' from 'birdhouse/pavics-compose.sh' to employ them for template substitution +# adding them to 'VARS', they will also be validated in case of override of 'default.env' using 'env.local' +VARS="$VARS $EXTRA_VARS" + +export SECURE_DATA_PROXY_AUTH_INCLUDE="include /etc/nginx/conf.extra-service.d/secure-data-proxy/secure-data-auth.include;" diff --git a/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml b/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml index 00f657edc..a3eae80eb 100644 --- a/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml +++ b/birdhouse/optional-components/secure-data-proxy/docker-compose-extra.yml @@ -11,34 +11,8 @@ services: proxy: volumes: - ./optional-components/secure-data-proxy/conf.extra-service.d:/etc/nginx/conf.extra-service.d/secure-data-proxy:ro - # only to ensure it is (re)started along 'proxy' changes - depends_on: - - proxy-data - - proxy-data: - image: nginx - container_name: proxy-data - # NOTE: no ports mapping (so secured data is not exposed) - # rely on inter-docker networks to resolve 'proxy -> magpie -> proxy-data' after AuthN/AuthZ validation - volumes: - # same files as 'proxy' service except 'all-services.include' for which we provide - # the specific override definition to only map data access under this proxy - - ./config/proxy/conf.d/cors.include:/etc/nginx/conf.d/cors.include - - ./config/proxy/conf.d/frontend.conf:/etc/nginx/conf.d/frontend.conf - - ./config/proxy/conf.d/redirect-to-https.include:/etc/nginx/conf.d/redirect-to-https.include - - ./config/proxy/nginx.conf:/etc/nginx/nginx.conf - - ${SSL_CERTIFICATE}:/etc/nginx/cert.pem - # place override (without other services) were expected when included by 'frontend.conf' - - ./optional-components/secure-data-proxy/conf.d/all-services.include:/etc/nginx/conf.d/all-services.include:ro - # reuse definitions from 'public-data-proxy' to resolve the same way after AuthN/AuthZ validation and redirection - - ./optional-components/public-data-proxy/conf.extra-service.d:/etc/nginx/conf.extra-service.d/public-data-proxy:ro - - wps_outputs:/pavics-data/wps_outputs - restart: always - logging: *default-logging magpie: - links: - - proxy-data volumes: - ./optional-components/secure-data-proxy/config/magpie/config.yml:/opt/local/src/magpie/config/providers/secure-data-proxy.yml:ro - ./optional-components/secure-data-proxy/config/magpie/config.yml:/opt/local/src/magpie/config/permissions/secure-data-proxy.yml:ro diff --git a/birdhouse/optional-components/secure-data-proxy/images/magpie-service.png b/birdhouse/optional-components/secure-data-proxy/images/magpie-service.png new file mode 100644 index 0000000000000000000000000000000000000000..2f8e3e35c42854acae85ca365e3519ced80c4ae0 GIT binary patch literal 49803 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%0DvqOVbcFcv# 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 B{2l-R literal 0 HcmV?d00001 diff --git a/birdhouse/optional-components/testthredds/default.env b/birdhouse/optional-components/testthredds/default.env index 5fb916f73..1e2060398 100644 --- a/birdhouse/optional-components/testthredds/default.env +++ b/birdhouse/optional-components/testthredds/default.env @@ -1,8 +1,8 @@ # All env in this default.env can be overridden by env.local. -# All env in this default.env must NOT depend on any other env. If they do, -# must use single quote to avoid early expansion before overrides in local.env -# are applied and must add to the list of DELAYED_EVAL. +# All env in this default.env must NOT depend on any other env. If they do, they +# must use single quotes to avoid early expansion before overrides in env.local +# are applied and must be added to the list of DELAYED_EVAL. export TESTTHREDDS_IMAGE="$THREDDS_IMAGE" export TESTTHREDDS_PORT="8084" diff --git a/birdhouse/pavics-compose.sh b/birdhouse/pavics-compose.sh index ad5a2a722..881eb6b8a 100755 --- a/birdhouse/pavics-compose.sh +++ b/birdhouse/pavics-compose.sh @@ -75,6 +75,7 @@ OPTIONAL_VARS=' $MAGPIE_SMTP_PASSWORD $MAGPIE_LOG_LEVEL $TWITCHER_LOG_LEVEL + $TWITCHER_VERIFY_PATH $VERIFY_SSL $JUPYTER_DEMO_USER $JUPYTER_LOGIN_BANNER_TOP_SECTION From 8ee5f463e97ec6a6f45a7efedb72acde202b73ab Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Mon, 30 Jan 2023 23:38:14 -0500 Subject: [PATCH 11/15] apply changes to support weaver wpsoutputs with protected access from secure-data-proxy component --- CHANGES.md | 11 ++++- .../config/cowbird/config.yml.template | 5 ++- .../weaver/config/magpie/config.yml.template | 14 ++++++ .../weaver/config/magpie/weaver_hooks.py | 44 +++++++++++++++++-- 4 files changed, 68 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 25d1ce637..37bb5db5a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,9 +23,16 @@ created to define the resource and permission hierarchy of directories and files the users and groups can access. When disabled, the original behavior to provide open access to `/wpsoutputs` is employed. - A variable named ``SECURE_DATA_PROXY_AUTH_INCLUDE`` is dynamically assigned based on the activation or not of this + A variable named `SECURE_DATA_PROXY_AUTH_INCLUDE` is dynamically assigned based on the activation or not of this component. Corresponding validation of optional/mandatory/delayed-eval variables used by this component are also - applied dynamically, as well as mounting the necessary ``nginx`` and ``docker-compose`` extended configurations. + applied dynamically, as well as mounting the necessary `nginx` and `docker-compose` extended configurations. + +- Weaver: adjust user-context output directory hooks and permissions for [`secure-data-proxy`][secure-data-proxy]. + + When a process defined in Weaver (either a WPS provider or a local definition) is executed by a user that was granted + authorization to run a job, the corresponding user-context directory under `/wpsoutputs/users/{user-id}` will be used + for storing the execution outputs and will have the appropriate permissions set for that user to grant them access to + those outputs. ## Fixes: diff --git a/birdhouse/components/cowbird/config/cowbird/config.yml.template b/birdhouse/components/cowbird/config/cowbird/config.yml.template index 55166dfd0..343da6f8a 100644 --- a/birdhouse/components/cowbird/config/cowbird/config.yml.template +++ b/birdhouse/components/cowbird/config/cowbird/config.yml.template @@ -235,8 +235,11 @@ sync_permissions: - "weaver_wps_outputs : read -> job_status : read" # process-prefixed items can be only one-way since wps-outputs does not # encode the 'processID' information ('jobID' directly the top-level dir) - - "process_job_status : read -> weaver_wps_outputs : read" - "process_job_status : read -> job_status : read" + # NOTE: + # missing 'user_context_dir' information not defined in path of process execution request + # this permission must be set using the magpie/twitcher pre/post request hook to extract the authorized user + ###- "process_job_status : read -> weaver_wps_outputs : read" # different permission (match), otherwise all jobs/outputs become available. - "process_job_status : read -> process_description : read-match" # corresponding outputs retrieved under wps-outputs or thredds share access diff --git a/birdhouse/components/weaver/config/magpie/config.yml.template b/birdhouse/components/weaver/config/magpie/config.yml.template index 052574a8f..f4449b097 100644 --- a/birdhouse/components/weaver/config/magpie/config.yml.template +++ b/birdhouse/components/weaver/config/magpie/config.yml.template @@ -32,6 +32,20 @@ providers: path: "/(jobs|execution)" method: POST target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:add_x_wps_output_context + # apply relevant permissions to allow the user executing a process to retrieve its outputs + - type: response + path: "/providers/[\\w_-]+/processes/[\\w_-:]+/(jobs|execution)" + method: POST + target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:allow_user_execute_outputs + - type: response + path: "/processes/[\\w_-:]+/(jobs|execution)" + method: POST + target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:allow_user_execute_outputs + - type: response + path: "/(jobs|execution)" + method: POST + target: /opt/birdhouse/src/magpie/hooks/weaver_hooks.py:allow_user_execute_outputs + # apply relevant permissions such that the user can access its deployed process - type: response path: "/processes" method: GET diff --git a/birdhouse/components/weaver/config/magpie/weaver_hooks.py b/birdhouse/components/weaver/config/magpie/weaver_hooks.py index 4d3842dcb..6513d3390 100644 --- a/birdhouse/components/weaver/config/magpie/weaver_hooks.py +++ b/birdhouse/components/weaver/config/magpie/weaver_hooks.py @@ -19,7 +19,8 @@ from magpie.api.management.user import user_utils as uu from magpie.api.requests import get_user, get_service_matchdict_checked from magpie.constants import get_constant -from magpie.models import Route +from magpie.models import Route, Service +from magpie.register import magpie_register_permissions_from_config from magpie.permissions import Access, Permission, PermissionSet, Scope from magpie.utils import get_header, get_logger @@ -54,12 +55,12 @@ def add_x_wps_output_context(request): if not is_admin(request): # override disallowed writing to other location # otherwise, up to admin to have written something sensible - header = "user-" + str(request.user.id) + header = "users/" + str(request.user.id) else: if request.user is None: header = "public" else: - header = "user-" + str(request.user.id) + header = "users/" + str(request.user.id) request.headers["X-WPS-Output-Context"] = header return request @@ -169,6 +170,43 @@ def filter_allowed_processes(response, context): return response +def allow_user_execute_outputs(response): + # type: (Response) -> Response + """ + Allow the authenticated user executing the process to access the expected output location. + + This ensures that, when ``optional-components/secure-data-proxy`` is enabled, the user will be able to retrieve + the result files stored under the ``/wpsoutputs/users/`` directory, by applying the corresponding + permission if missing. + """ + request = response.request + user = request.user + if user is None or is_admin(request): + return response + + session = request.db + service = Service.by_service_name("secure-data-proxy", db_session=session) + if not service: # optional component not enabled, nothing to be set (public access expected) + return response + + with transaction.manager: + config = { + "permissions": [ + { + "service": service.resource_name, + "resource": f"/wpsoutputs/users/{user.id}", + "type": "route", + "user": user.user_name, + "action": "create", + } + ] + } + magpie_register_permissions_from_config(config, db_session=session) + transaction.commit() + + return response + + def allow_user_deployed_processes(response): # type: (Response) -> Response """ From fc1c08e7bf5c7ac80233b9751e0403d4e97cccef Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Tue, 7 Feb 2023 15:38:37 -0500 Subject: [PATCH 12/15] revert permission change on pavics-compose script --- birdhouse/pavics-compose.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 birdhouse/pavics-compose.sh diff --git a/birdhouse/pavics-compose.sh b/birdhouse/pavics-compose.sh old mode 100644 new mode 100755 From ca829ecc05b7e43211e72d097dd4766fbe1bb779 Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Wed, 8 Feb 2023 20:03:49 -0500 Subject: [PATCH 13/15] add SECURE_DATA_PROXY_AUTH_INCLUDE to optional vars to allow empty resolution by default when secure-data-proxy component is not enabled --- birdhouse/pavics-compose.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/birdhouse/pavics-compose.sh b/birdhouse/pavics-compose.sh index ca24011ec..3f95d06fd 100755 --- a/birdhouse/pavics-compose.sh +++ b/birdhouse/pavics-compose.sh @@ -88,6 +88,7 @@ OPTIONAL_VARS=' $AUTODEPLOY_EXTRA_SCHEDULER_JOBS $PROXY_READ_TIMEOUT_VALUE $PROXY_ROOT_LOCATION + $SECURE_DATA_PROXY_AUTH_INCLUDE ' # we switch to the real directory of the script, so it still works when used from $PATH From e45c0b66700217d13af2f28a70adb3857c3fba5c Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Thu, 9 Feb 2023 23:12:28 -0500 Subject: [PATCH 14/15] remove obsolete script file included in #281 --- birdhouse/scripts/get-components-json.sh | 41 ------------------------ 1 file changed, 41 deletions(-) delete mode 100644 birdhouse/scripts/get-components-json.sh diff --git a/birdhouse/scripts/get-components-json.sh b/birdhouse/scripts/get-components-json.sh deleted file mode 100644 index 6a013c811..000000000 --- a/birdhouse/scripts/get-components-json.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# Obtain a JSON representation of components enabled on this platform. -# -# Expected result should be similar to: -# { -# "components": [ -# "bird-house/birdhouse-deploy:components/monitoring", -# "bird-house/birdhouse-deploy:optional-components/canarie-api-full-monitoring", -# "bird-house/birdhouse-deploy:optional-components/all-public-access", -# "bird-house/birdhouse-deploy:optional-components/wps-healthchecks", -# "bird-house/birdhouse-deploy:optional-components/secure-thredds", -# "bird-house/birdhouse-deploy:optional-components/testthredds", -# "bird-house/birdhouse-deploy:optional-components/test-weaver", -# "bird-house/birdhouse-deploy:components/weaver", -# "bird-house/birdhouse-deploy:components/cowbird", -# "custom:daccs-env" -# ] -# } -# - -# default value in case of error or missing definitions -export BIRDHOUSE_DEPLOY_COMPONENTS_JSON='{"components": []}' -if [ -z "${EXTRA_CONF_DIRS}" ]; then - echo "No components in EXTRA_CONF_DIRS. Components JSON list will be empty!" - exit 0 -fi - -# create a JSON list using the specified components -# each component that starts by './' gets replaced with the below birdhouse prefix to provide contextual information -# other component locations are considered 'custom' and marked as such to provide contextual information -BIRDHOUSE_DEPLOY_COMPONENTS_BASE="bird-house/birdhouse-deploy:" -BIRDHOUSE_DEPLOY_COMPONENTS_LIST=$( \ - echo "${EXTRA_CONF_DIRS}" \ - | sed '/^[[:space:]]*$/d' \ - | sed -E 's/^\s*([A-Za-z0-0./_-]+)\s*$/"\1",/g' \ - | sed -E "s|^\"((\./)?(\.\./)+)+(.+)\"|\"custom:\\4\"|g" \ - | sed -E "s|^\"\./(.*)\"|\"${BIRDHOUSE_DEPLOY_COMPONENTS_BASE}\\1\"|g" \ - | sed '/^\n*$/d' \ -) -BIRDHOUSE_DEPLOY_COMPONENTS_LIST="${BIRDHOUSE_DEPLOY_COMPONENTS_LIST%?}" # remove last comma -export BIRDHOUSE_DEPLOY_COMPONENTS_JSON="{\"components\": [${BIRDHOUSE_DEPLOY_COMPONENTS_LIST}]}" From 2817f97607e494f6023aa5cfd781e7ca18da9bd0 Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Fri, 10 Feb 2023 11:31:08 -0500 Subject: [PATCH 15/15] =?UTF-8?q?Bump=20version:=201.22.11=20=E2=86=92=201?= =?UTF-8?q?.23.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 6 +++--- CHANGES.md | 5 +++++ Makefile | 2 +- README.rst | 8 ++++---- RELEASE.txt | 2 +- .../config/canarie-api/docker_configuration.py.template | 8 ++++---- docs/source/conf.py | 4 ++-- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4cdd1ebe9..c69c061bb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.22.11 +current_version = 1.23.0 commit = True tag = False tag_name = {new_version} @@ -30,11 +30,11 @@ search = {current_version} replace = {new_version} [bumpversion:file:RELEASE.txt] -search = {current_version} 2023-02-03T04:58:52Z +search = {current_version} 2023-02-10T16:31:08Z replace = {new_version} {utcnow:%Y-%m-%dT%H:%M:%SZ} [bumpversion:part:releaseTime] -values = 2023-02-03T04:58:52Z +values = 2023-02-10T16:31:08Z [bumpversion:file(version):birdhouse/config/canarie-api/docker_configuration.py.template] search = 'version': '{current_version}' diff --git a/CHANGES.md b/CHANGES.md index 74f93ea01..e9f87b1f2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,11 @@ [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) + +[1.23.0](https://github.com/bird-house/birdhouse-deploy/tree/1.23.0) (2023-02-10) +------------------------------------------------------------------------------------------------------------------ + ## Changes: - secure-data-proxy: add new [`secure-data-proxy`][secure-data-proxy] optional component. diff --git a/Makefile b/Makefile index 3afe40e4a..21c6993d3 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Generic variables override SHELL := bash override APP_NAME := birdhouse-deploy -override APP_VERSION := 1.22.11 +override APP_VERSION := 1.23.0 # utility to remove comments after value of an option variable override clean_opt = $(shell echo "$(1)" | $(_SED) -r -e "s/[ '$'\t'']+$$//g") diff --git a/README.rst b/README.rst index 2d81b6e90..f825e6509 100644 --- a/README.rst +++ b/README.rst @@ -14,13 +14,13 @@ for a full-fledged production platform. * - releases - | |latest-version| |commits-since| -.. |commits-since| image:: https://img.shields.io/github/commits-since/bird-house/birdhouse-deploy/1.22.11.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/bird-house/birdhouse-deploy/1.23.0.svg :alt: Commits since latest release - :target: https://github.com/bird-house/birdhouse-deploy/compare/1.22.11...master + :target: https://github.com/bird-house/birdhouse-deploy/compare/1.23.0...master -.. |latest-version| image:: https://img.shields.io/badge/tag-1.22.11-blue.svg?style=flat +.. |latest-version| image:: https://img.shields.io/badge/tag-1.23.0-blue.svg?style=flat :alt: Latest Tag - :target: https://github.com/bird-house/birdhouse-deploy/tree/1.22.11 + :target: https://github.com/bird-house/birdhouse-deploy/tree/1.23.0 .. |readthedocs| image:: https://readthedocs.org/projects/birdhouse-deploy/badge/?version=latest :alt: ReadTheDocs Build Status (latest version) diff --git a/RELEASE.txt b/RELEASE.txt index eb5f63904..fad71e592 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1 +1 @@ -1.22.11 2023-02-03T04:58:52Z +1.23.0 2023-02-10T16:31:08Z diff --git a/birdhouse/config/canarie-api/docker_configuration.py.template b/birdhouse/config/canarie-api/docker_configuration.py.template index 38cd195cc..fb530631f 100644 --- a/birdhouse/config/canarie-api/docker_configuration.py.template +++ b/birdhouse/config/canarie-api/docker_configuration.py.template @@ -17,8 +17,8 @@ SERVICES = { 'info': { 'name': 'Node', 'synopsis': 'Nodes are data, compute and index endpoints accessed through the PAVICS platform or external clients. The Node service is the backend that allows: data storage, harvesting, indexation and discovery of local and federated data; authentication and authorization; server registration and management. Node service is therefore composed of several other services.', - 'version': '1.22.11', - 'releaseTime': '2023-02-03T04:58:52Z', + 'version': '1.23.0', + 'releaseTime': '2023-02-10T16:31:08Z', 'institution': 'Ouranos', 'researchSubject': 'Climatology', 'supportEmail': '${SUPPORT_EMAIL}', @@ -242,8 +242,8 @@ PLATFORMS = { 'info': { 'name': 'PAVICS', 'synopsis': 'The PAVICS (Power Analytics for Visualization of Climate Science) platform is a collection of climate analysis services served through Open Geospatial Consortium (OGC) protocols. These services include data access, processing and visualization. Both data and algorithms can be accessed either programmatically, through OGC-compliant clients such as QGIS or ArcGIS, or a custom web interface.', - 'version': '1.22.11', - 'releaseTime': '2023-02-03T04:58:52Z', + 'version': '1.23.0', + 'releaseTime': '2023-02-10T16:31:08Z', 'institution': 'Ouranos', 'researchSubject': 'Climatology', 'supportEmail': '${SUPPORT_EMAIL}', diff --git a/docs/source/conf.py b/docs/source/conf.py index 2ff4bcf3b..f5bb87dd1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -69,9 +69,9 @@ # built documents. # # The short X.Y version. -version = '1.22.11' +version = '1.23.0' # The full version, including alpha/beta/rc tags. -release = '1.22.11' +release = '1.23.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages.