2

A colleague recommended I repost this from StackOverflow to ServerFault:

This has been driving me crazy. I have a web application that's being served via Apache Web Server. The database server that backs the application is Apache CouchDB, which exposes an HTTP API to retrieve documents and stream attachments.

I've secured the CouchDB database by providing a security object, which only allows certain users to access data within the database, and returns 401 for anonymous requests to HTTP endpoints.

I want to be able to map public URLs to document attachments stored within this database. So, I've attempted to create a rewrite rule inside my .htaccess file that proxies requests from certain URLs directly to CouchDB, while hardcoding the user credentials, like so:

## DOWNLOAD STREAM:
RewriteCond %{HTTP_HOST} ^example.com$
RewriteRule download/(.*) http://user:pass@127.0.0.1:5984/database/$1 [P]

In an ideal world, the above example would take the following URL:

http://example.com/download/UUID/attachment.ext

And proxy it to:

http://user:pass@127.0.0.1:5984/database/UUID/attachment.ext

This method does indeed proxy the request to CouchDB, but omits the userinfo component of the URI scheme. So, the request is treated as anonymous and I get a 401 error. The attachment is only streamed if I remove security from the database.

I've spent a couple of hours reading up on Apache configuration and experimenting to no avail. Web searches are fruitless because of all the related queries with similar keywords.

How can I ensure that mod_rewrite includes the username and password provided in the rewrite rule when it proxies to CouchDB?

Andrew Schulman
  • 8,561
  • 21
  • 31
  • 47
StickByAtlas
  • 141
  • 3

2 Answers2

2

I figured it out! Rather than including the username:password as part of the URI scheme, the Authorization header needs to be set independently. The following solution works completely within a .htaccess file, which is important since OS X periodically blows away settings inside VirtualHost sites:

SetEnvIf Request_URI ^/download/* ADD_COUCH_BASIC_AUTH
RequestHeader set Authorization "Basic XXXXXXXXXXXX" env=ADD_COUCH_BASIC_AUTH

## DOWNLOAD STREAM
RewriteCond %{HTTP_HOST} ^example.com$
RewriteRule download/(.*) http://127.0.0.1:5984/database/$1 [P]

The way this works: we use SetEnvIf to check whether the request path matches the path we want to proxy, and if so, set an arbitrary environment variable ADD_COUCH_BASIC_AUTH

On the subsequent line, we add a Basic Auth header to the outgoing request, only if the environment variable we set exists. So, the basic auth header will only be added when requesting a resource via /download/, thus sending authentication credentials to CouchDB.

Note: you'll have to Base64-encode your username:password credentials, and replace XXXXXXXXXXX with the encoded value. An easy way to do this, on a Mac:

echo -n 'user:pass' | openssl base64

Hope this helps somebody besides me!

StickByAtlas
  • 141
  • 3
0

Alternative to @StickByAtlas's answer you can set the Environment variable through the RewriteRule itself. Here is an example how I use it to load files from an external storage if they do not exist locally (.htaccess is inside the /download folder):

RewriteEngine on
# rewrite to external source if local file does not exist
RequestHeader set Authorization "Basic XXXXXXXXXXXX" env=EXTERNAL_SOURCE
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ https://external.example.com/download/$1 [P,E=EXTERNAL_SOURCE]
mgutt
  • 459
  • 6
  • 22