I have a chef server running on a machine (listening on port 4000), and I'd like to access it over SSL. I've set up an Apache reverse proxy to do so (listening on port 4001). The Apache setup looks like (with my machine name replaced with www.example.com)
Listen 4001
<VirtualHost *:4001>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
ProxyRequests Off
ProxyPreserveHost On
<Proxy http://localhost:4000*>
Order deny,allow
Allow from all
</Proxy>
ProxyPass / http://localhost:4000/
ProxyPassReverse / http://localhost:4000/
ProxyPassReverse / http://www.example.com:4000/
ProxyPassReverse / http://www.example.com:4001/
CustomLog ${APACHE_LOG_DIR}/chef.access.log common
ErrorLog ${APACHE_LOG_DIR}/chef.error.log
</VirtualHost>
The problem is that the URLs in the JSON returned by chef API calls have http instead of https. For example, they look like:
http://www.example.com:4001/sandboxes/a25fa2615d3d4dbd91e91a5e3a76c0ea
Instead of:
https://www.example.com:4001/sandboxes/a25fa2615d3d4dbd91e91a5e3a76c0ea
One example is when doing a POST to /sandboxes
to create a new sandbox, which created the link above. This breaks things like trying to use knife to upload cookbooks.
From what I can tell, the chef server create method which is invoked by the /sandboxes
POST calls the Merb absolute_url helper method to generate the URL.
At this point, I'm stumped about how to fix this problem. Do I need to make a change my Apache reverse proxy configuration somehow? Is it a configuration option in the chef server? Or something else?
I think you just need to add:
RequestHeader set X-Forwarded-Proto "https"
to your Apache configuration.
This makes Apache add the X-Forwarder-Proto: https
header to all requests to the Chef/Merb server. Merb checks for this header when deciding whether the request should be treated as secure (you can follow the requests from options[:protocol] ||= request.protocol
in absolute_url
to request#protocol
to request#ssl?
).
The ProxyPassReverse
directive only changes headers from the backend, it doesn't alter any content (that would require parsing and recognising urls in the content). Passing this header allows Merb to know that it should act as if the request is using SSL, and create links as appropriate.