Apache 落后于 Varnish;忽略 htaccess 拒绝规则

Apache 落后于 Varnish;忽略 htaccess 拒绝规则

因此,我有一个 wordpress 网站在 nginx -> varnish -> httpd 后面运行

wp-login.php 的 htaccess 规则设置为:

<Files wp-login.php>
allow from client ip
deny from all
</Files>

以前,不使用 varnish 时,这个功能也能正常工作,但是当我将 varnish 放在 nginx 和 httpd 中间时,这个缓存问题或 IP 转发问题就开始出现。

由于我可以轻松更改 Web 服务器配置,因此我可以轻松地为网站禁用/启用 Varnish。因此,作为测试,我将 htaccess 规则更改为:

<Files wp-login.php>
allow from Server Public IP
deny from all
</Files>

这样,每个人都可以访问 wp-login。然后我禁用了 varnish,并将服务器公共 IP 保留在 htaccess 中,现在没有人可以访问该页面(这是应该发生的)。

因此,罪魁祸首就是清漆。

我在 apache 上设置了 mod_cloudflare。我还测试过将其切换到 mod_remoteip,但无济于事。

这是我的 nginx:443、varnish:82 和 apache:8181 vhost 模板(此 IP 108.148.54.124 是服务器公共 IP 的一个示例):

server {
    listen 108.148.54.124:443 ssl http2;
    server_name %domain_idn% %alias_idn%;
    
    access_log /usr/local/apache/domlogs/%domain%.bytes bytes;
    access_log /usr/local/apache/domlogs/%domain%.log full;
    error_log /usr/local/apache/domlogs/%domain%.error.log error;

    ssl_certificate      %ssl_cert_path%/%domain%.bundle;
    ssl_certificate_key  %ssl_key_path%/%domain%.key;
    ssl_protocols TLSv1.3;
    ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA!RC4:EECDH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;
    ssl_prefer_server_ciphers   on;

    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 60m;

    location / {
        location ~.*\.(3gp|gif|jpg|jpeg|png|ico|wmv|avi|asf|asx|mpg|mpeg|mp4|pls|mp3|mid|wav|swf|flv|html|htm|txt|js|css|exe|zip|tar|rar|gz|tgz|bz2|uha|7z|doc|docx|xls|xlsx|pdf|iso|woff|ttf|svg|eot|sh|webp)$ {
            root %docroot%;
            expires max;
            try_files $uri $uri/ @backend;
        }
        
        error_page 405 = @backend;
        error_page 500 = @custom;
        add_header X-Cache "HIT from Backend";
        add_header Strict-Transport-Security "max-age=31536000";
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        proxy_pass %proxy_protocol%://108.148.54.124:82;
        include proxy.inc;
    }

    location @backend {
        internal;
        proxy_pass %proxy_protocol%://108.148.54.124:82;
        include proxy.inc;
    }

    location @custom {
        internal;
        proxy_pass %proxy_protocol%://108.148.54.124:82;
        include proxy.inc;
    }

    location ~ .*\.(php|jsp|cgi|pl|py)?$ {
        proxy_pass %proxy_protocol%://108.148.54.124:82;
        include proxy.inc;
    }

    location ~ /\.ht    {deny all;}
    location ~ /\.svn/  {deny all;}
    location ~ /\.git/  {deny all;}
    location ~ /\.hg/   {deny all;}
    location ~ /\.bzr/  {deny all;}
    location ~\.(ini|log|conf)$ {deny all;error_page 403 =404 / ;}

    disable_symlinks if_not_owner from=%docroot%;

    location /.well-known/acme-challenge {
        default_type "text/plain";
        alias /usr/local/apache/autossl_tmp/.well-known/acme-challenge;
    }

    location /.well-known/pki-validation {
        default_type "text/plain";
        alias /usr/local/apache/autossl_tmp/.well-known/acme-challenge;
    }
}
.....
backend %backend_domain% {
    .host = "108.148.54.124";
    .port = "8181";
}

sub vcl_recv {
    if (req.http.host ~ "%domain%") {
        set req.backend_hint = %backend_domain%;

        # Always cache the following file types for all users.
        if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$") {
            unset req.http.Cookie;
        }

        # Remove any Google Analytics based cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", "");

        # Do not cache AJAX requests.
        if (req.http.X-Requested-With == "XMLHttpRequest") {
            return(pass);
        }

        # Post requests will not be cached
        if (req.http.Authorization || req.method == "POST") {
            return (pass);
        }
        if (req.method != "GET" && req.method != "HEAD") {
            return (pass);
        }

        # Do not cache Authorized requests.
        if (req.http.Authorization) {
            return(pass);
        }

        # LetsEncrypt Certbot passthrough
        if (req.url ~ "^/\.well-known/acme-challenge/") {
            return (pass);
        }

        if (req.url ~ "^/\.well-known/pki-validation/") {
            return (pass);
        }

        # Forward client's IP to the backend
        if (req.restarts == 0) {
            if (req.http.X-Real-IP) {
                set req.http.X-Forwarded-For = req.http.X-Real-IP;
            } else if (req.http.X-Forwarded-For) {
                set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
            } else {
                set req.http.X-Forwarded-For = client.ip;
            }
        }

        ### Wordpress ###
        if (req.url ~ "(wp-admin|post\.php|edit\.php|wp-login)") {
            return(pass);
        }
        if (req.url ~ "/wp-cron.php" || req.url ~ "preview=true") {
            return (pass);
        }

        # WP-Affiliate
        if ( req.url ~ "\?ref=" ) {
            return (pass);
        }

        set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "PHPSESSID=[^;]+(; )?", "");

        return (hash);
    }
}
<VirtualHost 108.148.54.124:8443>
    ServerName %domain_idn%
    %domain_aliases%
    ServerAdmin webmaster@%domain%
    DocumentRoot %docroot%
    UseCanonicalName Off
    ScriptAlias /cgi-bin/ %docroot%/cgi-bin/

    CustomLog /usr/local/apache/domlogs/%domain%.bytes bytes
    CustomLog /usr/local/apache/domlogs/%domain%.log combined
    ErrorLog /usr/local/apache/domlogs/%domain%.error.log

    ## Custom settings are loaded below this line (if any exist)
    # IncludeOptional "/usr/local/apache/conf/userdata/%user%/%domain%/*.conf"

    SSLEngine on
    SSLCertificateFile %ssl_cert_path%/%domain%.cert
    SSLCertificateKeyFile %ssl_key_path%/%domain%.key
    SSLCertificateChainFile %ssl_cert_path%/%domain%.bundle
    SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown

    <IfModule mod_userdir.c>
        UserDir disabled
        UserDir enabled %user%
    </IfModule>

    <IfModule mod_suexec.c>
        SuexecUserGroup %user% %group%
    </IfModule>

    <IfModule mod_suphp.c>
        suPHP_UserGroup %user% %group%
        suPHP_ConfigPath %home%/%user%
    </IfModule>

    <IfModule mod_ruid2.c>
        RMode config
        RUidGid %user% %group%
    </IfModule>

    <IfModule itk.c>
        AssignUserID %user% %group%
    </IfModule>

    <Directory "%docroot%">
        AllowOverride All
        SSLRequireSSL
        Require all granted
    </Directory>

    <IfModule proxy_fcgi_module>
        <FilesMatch \.php$>
            SetHandler "proxy:%backend_fcgi%|fcgi://localhost"
        </FilesMatch>
    </IfModule>

</VirtualHost>

这是主要的 nginx conf 文件:

user nobody;
worker_processes auto;
#worker_rlimit_nofile    65535;
error_log               /var/log/nginx/error.log crit;
pid                     /var/run/nginx.pid;

events {
    worker_connections  1024;
    use                 epoll;
    multi_accept        on;

}
http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    client_header_timeout 3m;
    client_body_timeout 3m;
    client_max_body_size 256m;
    client_header_buffer_size 4k;
    client_body_buffer_size 256k;
    large_client_header_buffers 4 32k;
    send_timeout 3m;
    keepalive_timeout 60 60;
    reset_timedout_connection       on;
    server_names_hash_max_size 1024;
    server_names_hash_bucket_size 1024;
    ignore_invalid_headers on;
    connection_pool_size 256;
    request_pool_size 4k;
    output_buffers 4 32k;
    postpone_output 1460;

    include mime.types;
    default_type application/octet-stream;

    # Compression gzip
    gzip on;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";
    gzip_proxied any;
    gzip_min_length 512;
    gzip_comp_level 6;
    gzip_buffers 8 64k;
    gzip_types text/plain text/xml text/css text/js application/x-javascript application/xml image/png image/x-icon image/gif image/jpeg image/svg+xml application/xml+rss text/javascript application/atom+xml application/javascript application/json application/x-font-ttf font/opentype;

    # Proxy settings
    proxy_redirect      off;
    proxy_set_header    Host            $host;
    proxy_set_header    X-Real-IP       $remote_addr;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass_header   Set-Cookie;
    proxy_connect_timeout   300;
    proxy_send_timeout  300;
    proxy_read_timeout  300;
    proxy_buffers       32 4k;
    proxy_cache_path /var/cache/nginx levels=2 keys_zone=cache:10m inactive=60m max_size=512m;
    proxy_cache_key "$host$request_uri $cookie_user";
    proxy_temp_path  /var/cache/nginx/temp;
    proxy_ignore_headers Expires Cache-Control;
    proxy_cache_use_stale error timeout invalid_header http_502;
    proxy_cache_valid any 1d;

    open_file_cache_valid 120s;
    open_file_cache_min_uses 2;
    open_file_cache_errors off;
    open_file_cache max=5000 inactive=30s;
    open_log_file_cache max=1024 inactive=30s min_uses=2;

    # SSL Settings
    ssl_session_cache   shared:SSL:10m;
    ssl_protocols       TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers        "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA!RC4:EECDH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS";

    # Logs
    log_format  main    '$remote_addr - $remote_user [$time_local] $request '
                        '"$status" $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
    log_format  full '[$time_local] $remote_addr $remote_user - "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
    log_format  bytes   '$body_bytes_sent';
    #access_log          /var/log/nginx/access.log main;
    access_log off;

    # Cache bypass
    map $http_cookie $no_cache {
        default 0;
        ~SESS 1;
        ~wordpress_logged_in 1;
    }

    # Include additional configuration
    include /etc/nginx/cloudflare.inc;
    include /etc/nginx/conf.d/*.conf;
}

答案1

我刚刚找到了一个解决方案,就是用这个

<Files wp-login.php>
SetEnvIf X-Forwarded-For %Client_IP% allow_me
Allow from env=allow_me
deny from all
</Files>

我还可以将 SetEnvIf 放在外面,即 htaccess 文件的顶部,每当我的 IP 发生变化时,只需更改此行上的 ip。

SetEnvIf X-Forwarded-For %Client_IP% allow_me
<Files wp-login.php>
Allow from env=allow_me
deny from all
</Files>

答案2

X-Forward-For头应包含客户端 IP 地址。此标头将由 Nginx 设置,并且也将由 Varnish 使用。

标题可能X-Forward-For看起来像这样:

X-Forwarded-For: 1.2.3.4, 5.6.7.8

这是提取第一个值并在您的 中进行匹配的问题.htaccess file

本文提供了一种简单的allow from根据header的值进行调用的方法X-Forwarded-ForApache,使用 X-Forwarded-For 允许

这可以转化为以下配置:

<Files wp-login.php>
    SetEnvIf X-Forwarded-For ^1\.2\.3\.4 proxy_env
    Order allow,deny
    Satisfy Any
    Allow from env=proxy_env
    Deny from all
</Files>

wp-login.php您还可以在 Nginx 配置或 Varnish VCL 中限制访问。

相关内容