geonetwork / core-geonetwork

GeoNetwork is a catalog application to manage spatially referenced resources. It provides powerful metadata editing and search functions as well as an interactive web map viewer. It is currently used in numerous Spatial Data Infrastructure initiatives across the world.
http://geonetwork-opensource.org/
GNU General Public License v2.0
430 stars 489 forks source link

Enabling HTTPS for Geonetwork 4.2.2 does not work with OAUTH2 OpenID Connect #6947

Open JoseSanchezMoralesIDOX opened 1 year ago

JoseSanchezMoralesIDOX commented 1 year ago

After successfully implemented the install of SSL certificates in the server, Geonetwork can now be accessed through https://domain/geonetwork

However, when clicking on the 'Sign in' button, an error is displayed on the screen showing the following URL:

http://domain/geonetwork/login/oauth2/code/geonetwork-oicd

So, the above URL is using http instead, not https, not sure why, or if this misconfiguration is causing the OpenID Connect to fail.

I have changed in the database the values for system/server/port and system/server/protocol but these ones do not seem to have any effect in the above error.

Besides, the parameter: OPENIDCONNECT_IDTOKENROLELOCATION not sure how to configure it, where could I find an example of this value?

Thanks,

davidblasby commented 1 year ago

Hi,

I've been unable to reproduce your issue.

I created a jetty HTTPS:

a. follow this answer https://stackoverflow.com/a/31241196 b. You will need to change the (line 993) in web/pom.xml to this;

${basedir}/jetty.xml,${basedir}/jetty-ssl.xml,${basedir}/jetty-https.xml

Then, mvn jetty:run ... (as normal) and go to https://localhost:8443/geonetwork/

when I click on the "signin" link, it gets (correctly) redirected to https://localhost:8443/geonetwork/signin This will then redirect you to your OIDC provider with the correct redirect_uri=https://localhost:8443/geonetwork/login/oauth2/code/geonetwork-oicd.

The http/https detection is provided by your application container (i.e. jetty), and is handled inside spring.

  1. What application server are you using (ie. jetty or tomcat ...)
  2. How are you configuring your OIDC system and what is your configuration?
  3. Open up devtools in chrome and go to the "network" tag (click on "preserve log"). Then press the "login" button in geonetwork. You should see the first two requests as; a. request to GN's signin url (i.e. https://localhost:8443/geonetwork/signin). The response will be a redirect to your OIDC provider. b. request to your OIDC provider. You should see a redirect_uri=https://localhost:8443/geonetwork/login/oauth2/code/geonetwork-oicd in the URL.

Also, make sure that you access your GN with "https" and not "http" (if you have both configured).

davidblasby commented 1 year ago

RE: OPENIDCONNECT_IDTOKENROLELOCATION

From the documentation (https://geonetwork-opensource.org/manuals/4.0.x/en/administrator-guide/managing-users-and-groups/authentication-mode.html#authentication-openid):

Where, in the ID Token, are the users roles/groups stored (i.e. “groups”, “roles”, or “resource_access.gn-key.roles”)

In MS Azure (groups-based), it is "groups" In MS Azure (roles-based), it is "roles" In KeyCloak, it is "resource_access.gn-key.roles" (where gn-key is your client name).

Basically, look at your ID token (provided by your OIDC) system and find the list of roles in it. OPENIDCONNECT_IDTOKENROLELOCATION will be the path to that list.

Here is an example ID Token (from azure);

{
  "aud": "b9e8d05a-08b6-48a5-81c8-9590a0f550f3",
  "iss": "https://login.microsoftonline.com/87f91494-c0dc-493e-83c3-9226c111850a/v2.0",
  ...
  "email": "david.blasby@geocat.net",
  "groups": [
    "d93c6444-feee-4b67-8c0f-15d6796370cb",
    "3a94275f-7d53-4205-8d78-11f39e9ffa5a",
    "a9c82c77-dc4d-48a0-8286-22dcb992f18d",
    "fced5395-6be6-436c-a7f9-5c638cbdeb20"
  ],
  "name": "david blasby",
 ...
  "preferred_username": "david.blasby at geocat.net",

  "roles": [
    "Administrator"
  ],
  ...
}

This ID token is setup with roles and groups. If your roles are in the roles section, use OPENIDCONNECT_IDTOKENROLELOCATION=roles, if its in the groups section, use OPENIDCONNECT_IDTOKENROLELOCATION=groups.

NOTE: for azure (with groups), you have to use something like OPENIDCONNECT_ROLECONVERTER=3a94275f-7d53-4205-8d78-11f39e9ffa5a=Administrator because azure stores groups as UUIDS instead of group names.

If your ID token has the roles/groups nested inside other objects, you can navigate into sections of the token with ".". This is needed for keycloak because it put the roles 3 levels deep (resource_access.gn-key.roles) in the ID token.

JoseSanchezMoralesIDOX commented 1 year ago

Thanks,

1.- Accessing GN using 'https' in the browser works, it is the sign in (authentication) 2.- We are using Jetty, no Maven, no Tomcat, and a docker-compose file to launch GN, Kibana, NGNIX and Elastic Search all together. 3.- I have found the pom.xml into the /var/lib/jetty/webapps/geonetwork/META-INF/maven/org.geonetwork-opensource/gn-web-app folder, but inside the pom.xml file the piece of code to replace is missing. 4.- Version is 4.2.2 5.- The variables: GEONETWORK_SECURITY_TYPE (openidconnect), OPENIDCONNECT_CLIENTSECRET, OPENIDCONNECT_CLIENTID, OPENIDCONNECT_SERVERMETADATA_CONFIG_URL and OPENIDCONNECT_IDTOKENROLELOCATION are configured inside the docker-compose file 6.- Again, the redirect param displays:

redirect_uri=http://<domain>/geonetwork/login/oauth2/code/geonetwork-oicd&nonce=xxx......

However, if I copy the requested url in a new browser tab, and manually replace http by https, I got this error: image

7.- We are using a linux ec2 machine, where the https port (443) is open for inbound connections

davidblasby commented 1 year ago

are you using NGIX as a reverse proxy?

If you try to re-use the code (in the url), the login will fail - its a one-time-use code. So, your error is expected.

I've tested this with jetty 9.4 and it was working - how are you using NGIX in your configuration?

My instructions were for running GN4 from the command line (i.e. from source code).

JoseSanchezMoralesIDOX commented 1 year ago

are you using NGIX as a reverse proxy?

If you try to re-use the code (in the url), the login will fail - its a one-time-use code. So, your error is expected.

I've tested this with jetty 9.4 and it was working - how are you using NGIX in your configuration?

My instructions were for running GN4 from the command line (i.e. from source code).

My ngnix.conf:

events { }

http {

  map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
    }

  server {
    listen 80;
    server_name <domain.com>;
    return 301 https://<domain.com>$request_uri;
         }

   server {
    listen 443 ssl;
    server_name <domain.com>;

    ssl_certificate /ssl/<cert_name>.crt;
    ssl_certificate_key /ssl/<cert_name>.key;

    access_log /var/log/nginx/data-access.log combined;

    location / {
       proxy_pass http://geonetwork:8080/;
       proxy_set_header X-Real-IP  $remote_addr;
       proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_redirect http://geonetwork:8080/ $scheme://$http_host/;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $connection_upgrade;
       proxy_read_timeout 20d;
       proxy_buffering off;
       }
   }
}

Then, the ngnix part within the docker-compose:

  www:
    image: nginx:latest
    ports:
      - 80:80
      - 443:443  
#    environment:
#      # Only used for / redirect to default webapp
#      - NGINX_PUBLIC_HOST=195.201.225.214
#      #- NGINX_PUBLIC_HOST=apps.titellus.net
#      - NGINX_HOST=195.201.225.214
#      - NGINX_PORT=80
    volumes:
      - /opt/nginx/conf.d:/etc/nginx/conf.d
      - /opt/nginx/nginx.conf:/etc/nginx/nginx.conf  
      - /usr/local/share/ca-certificates:/ssl/
    depends_on:
      - geonetwork
    networks:
      - gn-network

The idea of using NGNIX was simply because of a viable way of using the SSL certificates. And actually it is in the official examples for launching GN as part of a docker-compose with other services.

I hope the above helps, really appreciate your input, thanks

davidblasby commented 1 year ago

If you are using NGNIX to handle the SSL certificate, you are likely using it as a reverse proxy. That means that GN only sees http connections - thats how its getting http as the url schema.

Currently, GN uses the request (which will be a http - NOT https - in your case) to form the redirect URL. I think a redirect-override configuration to GN would be required.

At least, that's my interpretation....

JoseSanchezMoralesIDOX commented 1 year ago

I am leaving out the openid issue for now, and concentrating in the https/ssl one. So, I am back to the default authentication method in Geonetwork.

At the moment, the site loads under https in the browser, but the internal URLs can fail (i.e. signing out)

Interestingly, if I use this in the nginx.conf: proxy_redirect http://geonetwork:8080/ $scheme://$http_host/;

The associated URL to the Sign out button becomes: https://domain.com/geonetwork/signout?redirectUrl=https://domain.com/geonetwork/srv

which doesn't work, as gives time-out in the browser while re-converting itself into: http://domain.com/geonetwork/?node=srv

On the other hand, if I do: proxy_redirect off;

the Sign out button encapsulates the same URL, so it doesn't make sense, does it?: https://domain.com/geonetwork/signout?redirectUrl=https://domain.com/geonetwork/srv

Perhaps, the above redirectUrl is internal to GN configuration, not related to NGinx?

I am wondering what is failing in my NGINX SET UP.

This is my nginx.conf:

events { }

http {

  map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
    }

  server {
    listen 80;
    server_name domain.com;
    return 301 https://domain$request_uri;
         }

   server {
    listen 443 ssl;
    server_name domain.com;

    ssl_certificate /ssl/domain.crt;
    ssl_certificate_key /ssl/domain.key;

    access_log /var/log/nginx/data-access.log combined;

    location / {
       proxy_pass http://geonetwork:8080/;
       proxy_set_header X-Real-IP  $remote_addr;
       proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_redirect off; #http://geonetwork:8080/ $scheme://$http_host/;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $connection_upgrade;
       proxy_read_timeout 20d;
       proxy_buffering off;
       }
   }
}
davebulaval commented 1 year ago

@davidblasby I'm trying to deploy a GeoNetwork server with OAuth (Azure AD) using the modified default Geonetwork docker-compose.yml. I have added SSL handling in NGINX, but GN still redirects to an HTTP URL.

Do you have an idea of how to deploy GN properly? NGINX seems to be a pain in the ass with the HTTP handling for GN.

My current hypothesis is that since we use the default GN image container, it comes in a Jetty image that does not support the HTTPS request nor properly handle the HTTP into HTTPS redirect (as you explained); thus, URLs are not properly handled.

Is there a way to inject configuration to Jetty in the docker image?

SamuelBradley commented 1 year ago

Is there anything that can be configured in the Docker container for Jetty to use https callback url for OIDC?

davebulaval commented 1 year ago

Not that I know of right now. My guess would be to inject a new configuration at the Jetty using the volumes in the docker entrypoint. But not sure how to do this.

roccoelleu commented 10 months ago

I have the same issue: GN redirects under HTTP instead of HTTPS. Is there any update on this thread?

jingking commented 9 months ago
source code

The redirect URI is formed from the last request (not the original request to your proxy) received by the Jetty server. As @davidblasby mentioned, GN uses this request, either HTTP or HTTPS, to form the redirect URI.

The GN official docker image (v4.4.x or v4.2.x) relies on Jetty with HTTP at default port 8080. To enable HTTPS for Jetty 9.4, you can, 1) Enable modules by adding ssl.ini (--module=ssl) and https.ini (--module=https) files to /var/lib/jetty/start.d/ 2) Import your SSL certificate into your keystore. 3) Update ports, path, password, etc., in your ssl.ini.

I updated official image with a self-signed certificate, jingking/geonetwork-hnap:4.2.8 Then set up nginx as reverse proxy,

proxy_pass         "https://localhost:8443";

The redirect URI works as expected.

duncanw commented 6 months ago

Is there anything that can be configured in the Docker container for Jetty to use https callback url for OIDC?

I managed to get this working by enabling Jetty's http-forwarded module. I have geonetwork behind an Apache reverse proxy which sets X-Forwarded HTTP headers:

RemoteIPHeader X-Forwarded-For
RequestHeader set "X-Forwarded-Proto" "https"
ProxyPass /geonetwork/             http://geonetwork:8080/geonetwork/
ProxyPassReverse /geonetwork/      http://geonetwork:8080/geonetwork/

For my Docker container build I created an http-forwarded.ini file (copied content from ini-template section of container file /usr/local/jetty/modules/http-forwarded.mod, then added the --module line):

# --------------------------------------- 
# Module: http-forwarded
# Adds a forwarded request customizer to the HTTP Connector
# to process forwarded-for style headers from a proxy.
# --------------------------------------- 
--module=http-forwarded

### ForwardedRequestCustomizer Configuration

## If true, only the RFC7239 Forwarded header is accepted
# jetty.httpConfig.forwardedOnly=false

## if true, the proxy address obtained from X-Forwarded-Server or RFC7239 is used as the request authority.
# jetty.httpConfig.forwardedProxyAsAuthority=false

## if true, the X-Forwarded-Port header applies to the authority, else it applies to the remote client address
# jetty.httpConfig.forwardedPortAsAuthority=true

# jetty.httpConfig.forwardedHeader=Forwarded
# jetty.httpConfig.forwardedHostHeader=X-Forwarded-Host
# jetty.httpConfig.forwardedServerHeader=X-Forwarded-Server
# jetty.httpConfig.forwardedProtoHeader=X-Forwarded-Proto
# jetty.httpConfig.forwardedForHeader=X-Forwarded-For
# jetty.httpConfig.forwardedPortHeader=X-Forwarded-Port
# jetty.httpConfig.forwardedHttpsHeader=X-Proxied-Https
# jetty.httpConfig.forwardedSslSessionIdHeader=Proxy-ssl-id
# jetty.httpConfig.forwardedCipherSuiteHeader=Proxy-auth-cert

...then added http-forwarded.ini in my Dockerfile:

FROM geonetwork:4.4.2

COPY http-forwarded.ini /var/lib/jetty/start.d/http-forwarded.ini

OAuth2 OpenID Connect now works using https redirect URL.