ansible / awx

AWX provides a web-based user interface, REST API, and task engine built on top of Ansible. It is one of the upstream projects for Red Hat Ansible Automation Platform.
Other
14.02k stars 3.42k forks source link

SAML integration behind reverse proxy : wrong ACS URL in SAMLrequest #6666

Open ggambini opened 4 years ago

ggambini commented 4 years ago
ISSUE TYPE
SUMMARY

Hello, I run an AWX behind a httpd reverse proxy. We have non ssl http between reverse proxy and awx_web and clients access are https. I setup the built-in SAML authentication and there is a difference in assertions consummer url between admin panel and SAMLrequest generated by AWX.

ENVIRONMENT
STEPS TO REPRODUCE

Need a reverse proxy in front of AWX. Clients use https on 443 with proxy and proxy use http on 80 with AWX. In SAML admin panel, ACS URL is auto generated and cant be modify. This URL seems good and have this format : https:///sso/complete/saml/. Setup one or more identity provider, exchange metadata with service provider and identity provider. Then test (plugin samltracer maybe usefull).

EXPECTED RESULTS

Identity provider look into metadata in request assertion consommer url exist, do the authentication and send SAMLresponse to service provider.

ACTUAL RESULTS

When AWX send SAMLrequest, assertion consummer url mismatch the one setup in admin panel, use the wrong protocol (http instead of https).

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                    ID="ONELOGIN_d49724618111881c963be5d17a07d242995d7042"
                    Version="2.0"
                    ProviderName="<my_app>"
                    IssueInstant="2020-04-09T12:43:53Z"
                    Destination="https://<my_identity_proivder>/idp/profile/SAML2/Redirect/SSO"
                    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                    AssertionConsumerServiceURL="http://<my_app>/sso/complete/saml/"
                    >
    <saml:Issuer>https://<my_app>/sp</saml:Issuer>
    <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
                        AllowCreate="true"
                        />
</samlp:AuthnRequest>
ADDITIONAL INFORMATION

I try to overwrite them in field "SAML SERVICE PROVIDER EXTRA CONFIGURATION DATA" with a JSON like this :

{
 "sp": {
  "assertionConsumerService": {
   "url": "https://<my_app>/sso/complete/saml/",
   "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
  }
 }
}

I try with the http version of ACS declared in identity provider metadata, SAMLrequest and authN work but i have a new error rise by AWX :

Authentication failed: SAML login failed: ['invalid_response'] (The response was received at http://<my_app>:8052/sso/complete/saml/ instead of http://<my_app>/sso/complete/saml/).

In awx_web, we found this :

[pid: 126|app: 0|req: 121/413] <reverse_proxy_ip_address> () {50 vars in 1748 bytes} [Thu Apr  9 13:06:10 2020] GET /sso/login/saml/?idp=<my_idp> => generated 0 bytes in 49 msecs (HTTP/1.1 302) 10 headers in 1088 bytes (1 switches on core 0)
<reverse_proxy_ip_address> - - [09/Apr/2020:13:06:10 +0000] "GET /sso/login/saml/?idp=<my_idp> HTTP/1.1" 302 0 "https://<my_app>/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0" "10.251.146.2"
func=xmlSecOpenSSLX509StoreVerify:file=x509vfy.c:line=341:obj=x509-store:subj=unknown:error=71:certificate verification failed:X509_verify_cert: subject=/C=FR/L=LLL/O=OOOO/CN=<my_app>; issuer=/C=FR/L=LLL/O=OOO/CN=<my_app>; err=18; msg=self signed certificate
func=xmlSecOpenSSLX509StoreVerify:file=x509vfy.c:line=380:obj=x509-store:subj=unknown:error=71:certificate verification failed:subject=/C=FR/L=LLL/O=OOOO/CN=<my_app>; issuer=/C=FR/L=LLL/O=OOO/CN=<my_app>; err=18; msg=self signed certificate
The response was received at http://<my_app>:8052/sso/complete/saml/ instead of http://<my_app>/sso/complete/saml/
2020-04-09 13:06:13,866 ERROR    social Authentication failed: SAML login failed: ['invalid_response'] (The response was received at http://<my_app>:8052/sso/complete/saml/ instead of http://<my_app>/sso/complete/saml/).

For information, self signed x509 is used for SAML, not for HTTPS.

My reverse proxy configuration is very simple :

        # Reverse proxy
        ProxyPreserveHost On
        ProxyPass / http://<awx_pivate_address>:80/
        ProxyPassReverse / http://<awx_pivate_address>:80/

I hope the information i provide is complete and clear. Have a nice day, Gilian.

planeturban commented 4 years ago

+1 I'm having the exact same problems with protocol mismatch: Authentication failed: SAML login failed: ['invalid_response'] (The response was received at https://<awx_fqdn>/sso/complete/saml/ instead of http://<awx_fqdn>/sso/complete/saml/).

@ggambini to fix the port problem you need to add uwsgi_param HTTP_X_FORWARDED_PORT 443; to location / in your awx_web containers nginx config, there is a bug regarding that filed. I did it by copying the nginx.conf file from the container, edit it and then add it as a volume (-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf) (Edit: seems to be updated in 10.0.0)

unreality commented 3 years ago

Setting the X-Forwarded-Proto header on your proxy may fix this (it looks like it has for me), eg in nginx:

proxy_set_header X-Forwarded-Proto https;

The social_core lib AWX uses respects the SECURE_PROXY_SSL_HEADER setting in Django for determining if the request is secure (https://docs.djangoproject.com/en/3.1/ref/settings/#secure-proxy-ssl-header), so thats another avenue to explore if setting that header doesnt work

TigerC10 commented 2 years ago

+1 for my cloud load balancer (not configurable like nginx):

Authentication failed: SAML login failed: ['invalid_response'] (The response was received at https://<my_app>:80/sso/complete/saml/ instead of https://<my_app>/sso/complete/saml/).

The cloud load balancer is setting X-Forwarded-Proto and is preserving the host name, but because it's hitting port 80 instead of 443 it appears that when this log line is output there's an attached port 80 to the URI.

EDIT: Found this document (I made sure that X-Forwarded-Port is set correctly, but it doesn't seem to be helping)... https://access.redhat.com/solutions/4004231

Resolution

Set the header X-Forwarded-Port in your load balancer.

Root Cause

Tower expects to find the following in your load balancer headers:

X-Forwarded-For X-Forwarded-Port X-Forwarded-Proto

EDIT 2: Finally understood what @planeturban was talking about. I have tried that suggestion (and I tried it a couple of different ways). I think my favorite involves adding the param directly to /etc/nginx/uwsgi_params like so:

echo "uwsgi_param  HTTP_X_FORWARDED_PORT  $http_x_forwarded_port;" | sudo tee -a /etc/nginx/uwsgi_params

This is easier to script post-install. However, this doesn't seem to be helping. For the record, I have also tried hardcoding the port number.

Also tried creating a /etc/tower/conf.d/custom.py file with the following contents:

USE_X_FORWARDED_HOST = True
USE_X_FORWARDED_PORT = True

But that's even worse - it crashed the entire automation-controller service on startup.