Apache redirects based on symlinks

帅比萌擦擦* 提交于 2019-12-22 17:38:39

问题


In one app, some public image resources are symlinked to alternative names for historic reasons. For example, we might have

a.png
b.png -> a.png

Google's PageSpeed Insight that identical content should be served from a consistent URL applies to assets like this as well as content. Rather than reorganising the assets we have in place, I would like to have Apache perform an external redirect from b.png to a.png.

With mod_rewrite I can make a RewriteCond to narrow in on symbolic links:

'-l' (is symbolic link) Treats the TestString as a pathname and tests whether or not it exists, and is a symbolic link.

But how can I get the expanded path of the symlink? I need it partly to ensure that the target is in web scope, and partly to perform the redirect.


回答1:


(first post here)

I've been looking for a solution to a similar problem. It's possible, but there is no easy answer. I had to mix info from multiple Google searches to make it work.

First of all, a note about symlinks (wiki):

Symbolic links are automatically resolved by the file system. Any software program, upon accessing a symbolic link, will see the target instead, whether the program is aware of symbolic links or not.

So, unless the apache devs add specific options to deal with symlinks (like the -l you mentionned), we have to rely on another mean to properly resolve them.

mod_rewrite has the RewriteMap configuration option. With it, you can use different types of external resources to perform the rewriting, such as text files or DB queries. It can also call external programs, such as ... a symlink resolver for example :)

So, here we go. Run all the following commands as root and adapt to your system settings (this is for Debian).


First, if we use the RewriteMap option, we also need the RewriteLock option to prevent race conditions. The RewriteLock option can't be in a <VirtualHost> or <Directory> context. It has to be in the global context, so I added it in a new rewrite.conf and reloaded the module. Run the following:
lock=/var/lock/apache2/rewrite_lock
touch $lock
chown www-data:www-data $lock
echo "RewriteLock $lock" >> /etc/apache2/mods-available/rewrite.conf
a2dismod rewrite
a2enmod rewrite

Next, create /usr/local/bin/resolve-symlink and add the following code:

#!/bin/sh
while read line
do
    echo `readlink "$line"`
done

Make it executable and test it as apache:

chmod +x /usr/local/bin/resolve-symlink
su - www-data
/usr/local/bin/resolve-symlink

The script should wait for your input, then either return a blank line or the target of the given symlink. Use CTRL+C to exit. Example (> is STDIN, < is STDOUT):

> test
<
> /bin/sh
< bash
> /bin/bash
<

test doesn't exist, so a blank line is returned. /bin/sh is a symlink and the script resolved it to bash. Finally, /bin/bash is a file and not a link, thus another blank line.

Now, in your <VirtualHost> configuration, add the following lines:

RewriteEngine On
RewriteMap symlink prg:/usr/local/bin/resolve-symlink
RewriteLog /var/log/apache2/rewrite.log
RewriteLogLevel 9

RewriteMap can't be in a <Directory> context, and won't be active unless RewriteEngine is also set to on in the <VirtualHost> context, even if you later set it to on in a <Directory> contest!!! Pay attention to all those peculiarities and read your log files carefully or you may lose a few hours banging your head on the wall wondering why it says "map lookup FAILED".

Finally, the rewriting, in whatever context you prefer:

RewriteCond %{REQUEST_FILENAME} -l
RewriteCond ${symlink:%{REQUEST_FILENAME}} ^(.+)$
RewriteRule $ ${REQUEST_SCHEME}://${HTTP_HOST}/%1 [R,L]

First line detects symlinks, second one performs the resolution and checks that the results is not empty, third line rewrites the URL (%1 is the result of the mapping) and sends a 302 redirection to the browser. You may need the END flag instead of the L flag.

Now restart apache...

service apache2 restart

... test, check your log files, tweak, rinse and repeat...

When your are done, don't forget to remove the RewriteLog and RewriteLogLevel directives from your configuration because they slow apache down a lot.


Important note. In my case, the symlinks all points to files within the same folder, so the URL rewriting is a little easier. If your symlinks point to sub-directories, or even somewhere else on the filesystem, you will have to modify the script and the configuration to account for it, but even then it might not work as expected, as apache won't always be able to figure out the correct URL to send to the browser. For example, if you have a symlink to, let say, /usr/share/apache2/icons/apache_pb.png, apache might redirect to http://example.com/usr/share/apache2/icons/apache_pb.png which, of course, does not exists...

Also, I would have added a few informative links but I'm limited to 2... Anyway, happy debugging!




回答2:


I've got more information about this topic. It's been running for quite some time on my side and I made the following amendments to the file /usr/local/bin/resolve-symlinks:

#!/bin/bash

BASE=_ALL_YOUR_BASE_ARE_BELONG_TO_US_
BASE_LEN=${#BASE}

while read FILE
do
    # $FILE must be in $BASE
    if [[ $BASE != ${FILE:0:$BASE_LEN} ]]
    then
            echo
            continue
    fi
    REL_FILE=${FILE:$BASE_LEN}
#       echo $REL_FILE

    # $LINK must also be in $BASE
    LINK=$(readlink -f $FILE)
    if [[ $BASE != ${LINK:0:$BASE_LEN} ]]
    then
            echo
            continue
    fi
    REL_LINK=${LINK:$BASE_LEN}
#       echo $REL_LINK

    # $REL_FILE and $REL_LINK must be different
    if [[ $(basename $REL_FILE) == $(basename $REL_LINK) ]]
    then
            echo
            continue;
    fi

    # success!
    echo $REL_LINK
done

Of course, replace _ALL_YOUR_BASE_ARE_BELONG_TO_US_ with your favorite path. Now it should make sure both the requested file and the link target are in the same directory.



来源:https://stackoverflow.com/questions/16351271/apache-redirects-based-on-symlinks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!