如何正确使用Nginx作为多个Apache Docker容器的SSL反向代理?

bxjv4tth  于 2023-05-28  发布在  Nginx
关注(0)|答案(1)|浏览(210)

给定如下docker容器:

  • 运行未修改的官方nginx:latest映像的nginx服务
  • 容器名称:proxy
  • 两个应用程序基于修改后的官方php:7.4.1-apache镜像在单独的容器中运行
  • 容器名称:app1app2
  • 所有容器proxyapp1app2都在同一个Docker创建的网络中,并具有自动DNS解析功能
    示例域名如下:
  • local.web.test
  • local1.web.test
  • local2.web.test
    我想实现以下行为:
  • nginx中的local.web.test用作默认服务器块
  • nginx配置为将来自local1.web.testlocal2.web.test的请求分别代理到app1app2,这两个请求都侦听端口80
  • nginx配置为使用wildcard SSL certificate服务所有三个域名
    我遇到两个问题:
  • 我注意到nginx日志中的以下错误:
  • 2020/06/28 20:00:59 [crit] 27#27: *36 SSL_read() failed (SSL: error:14191044:SSL routines:tls1_enc:internal error) while waiting for request, client: 172.19.0.1, server: 0.0.0.0:443
  • mod_rpaf似乎无法正常工作(即,apache访问日志中的ip地址属于nginx服务器[例如,172.19.0.2 ],而不是发出请求的客户端的ip
  • 172.19.0.2 - - [28/Jun/2020:20:05:05 +0000] "GET /favicon.ico HTTP/1.0" 404 457 "http://local1.web.test/" "Mozilla/5.0 (Windows NTndows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
  • phpinfo() forApache Environment的输出显示:
  • HTTP_X_REAL_IP列出客户端ip
  • SERVER_ADDR列出app1容器ip(例如,172.19.0.4
  • REMOTE_ADDR显示proxy容器ip(例如172.19.0.2),而不是客户端ip

为了使其可重现,这就是一切的设置方式。我在我的Windows机器上尝试了这个,所以有两个初步步骤。
1.初步步骤
a.在我的C:\Windows\System32\drivers\etc\hosts文件中,我添加了以下内容:

127.0.0.1 local.web.test
127.0.0.1 local1.web.test
127.0.0.1 local2.web.test

B.我通过以下方式生成了一个自签名的SSL证书,通用名设置为***.local.test**

openssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 365 -keyout localhost.key -out localhost.crt

1.proxy服务设置
a. nginx.yml用于docker-compose

version: "3.8"

services:
    nginx:
        image: nginx:latest
        container_name: proxy
        ports:
            - "80:80"
            - "443:443"
        volumes:
            - ./nginx:/etc/nginx/conf.d
            - ./certs:/etc/ssl/nginx
            - ./static/local.web.test:/usr/share/nginx/html
        networks:
            - proxy
networks:
    proxy:
        driver: bridge

B.在绑定挂载到/etc/nginx/conf.d./nginx中,有一个文件default.conf,其中包含:

server {
    listen 80 default_server;
    server_name local.web.test;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name local.web.test;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    ssl_certificate /etc/ssl/nginx/localhost.crt;
    ssl_certificate_key /etc/ssl/nginx/localhost.key;
}

./certs:/etc/ssl/nginx绑定挂载包含自签名证书和密钥的文件夹
d. ./static/local.web.test:/usr/share/nginx/html使包含以下内容的文件index.html可用

<h1>local.web.test</h1>

1.app1app2业务设置
a. apache.yml用于docker-compose

version: "3.8"

services:
    app1:
        build:
            context: .
            dockerfile: apache.dockerfile
        image: app1
        container_name: app1
        volumes:
            - ./static/local1.web.test:/var/www/html
        networks:
            - exp_proxy

    app2:
        build:
            context: .
            dockerfile: apache.dockerfile
        image: app2
        container_name: app2
        volumes:
            - ./static/local2.web.test:/var/www/html
        networks:
            - exp_proxy

networks:
    # Note: the network is named `exp_proxy` because the root directory is `exp`.
    exp_proxy:
        external: true

B. apache.dockerfile图像如下所示:

# Base image.
FROM php:7.4.1-apache

# Install dependencies.
RUN apt-get update && apt-get install -y curl nano wget unzip build-essential apache2-dev

# Clear cache.
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Change working directory,
WORKDIR /root

# Fetch mod_rpaf.
RUN wget https://github.com/gnif/mod_rpaf/archive/stable.zip

# Unzip.
RUN unzip stable.zip

# Change working directory,
WORKDIR /root/mod_rpaf-stable

# Compile and install.
RUN make && make install

# Register the module for load.
RUN echo "LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.so" > /etc/apache2/mods-available/rpaf.load

# Copy the configuration for mod_rpaf.
COPY ./apache/mods/rpaf.conf /etc/apache2/mods-available/rpaf.conf

# Enable the module.
RUN a2enmod rpaf

# Set working directory.
WORKDIR /var/www/html

c.复制的文件./apache/mods/rpaf.conf包含:

<IfModule mod_rpaf.c>
    RPAF_Enable             On
    RPAF_Header             X-Real-Ip
    RPAF_ProxyIPs           127.0.0.1
    RPAF_SetHostName        On
    RPAF_SetHTTPS           On
    RPAF_SetPort            On
</IfModule>

d. ./static/local1.web.test:/var/www/html绑定挂载一个index.php文件,其中包含:

<h1>local1.web.test</h1>
<?php phpinfo(); ?>
  • ./static/local2.web.test:/var/www/html也是如此 *

e.不修改app1app2中的000-default.conf虚拟主机:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

*开始设置

启动proxy服务器

docker-compose -f nginx.yml up -d --build

B.启动app1app2服务

docker-compose -f apache.yml up -d --build

c.检查容器是否启用了mod_rpaf

docker-compose -f apache.yml exec app1 apachectl -t -D DUMP_MODULES

d.在./nginx中添加两个文件,它们将在proxy容器中的/etc/nginx/conf.d上可用

  • local1.web.test.conf包含
upstream app-1 {
    server app1;
}

server {
    listen 80;
    server_name local1.web.test;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name local1.web.test;

    location / {
        proxy_pass http://app-1;
        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_set_header X-Forwarded-Proto $scheme;
    }

    ssl_certificate /etc/ssl/nginx/localhost.crt;
    ssl_certificate_key /etc/ssl/nginx/localhost.key;
}
  • 第二个文件是具有类似设置的local2.web.test.conf(即,编号12替换)

e.检查配置并重新启动proxy容器(或重新加载nginx服务器)

docker-compose -f nginx.yml exec proxy nginx -t 
docker-compose -f nginx.yml exec proxy service nginx reload

问题:

  • 当我运行docker logs proxy -f时,我注意到上面提到的SSL内部错误:SSL_read() failed
  • 有人遇到了类似的错误(http2: SSL read failed while sending req in nginx),但在这种情况下,消息更具体地指向证书颁发机构
  • 如果我运行docker logs app1 -f并访问https://local1.web.test,则GET请求中的ipproxy容器(即172.19.0.2)的ip匹配,而与远程客户端的ip不匹配
  • 我怀疑是这个RPAF_ProxyIPs 127.0.0.1,但我不能手动修复ip,因为我不知道容器将在exp_proxy网络中获得什么ip
  • 我也不能使用主机名,因为RPAF_ProxyIPs需要一个ip
  • docker inspect proxy显示"IPAddress": "172.19.0.2"
  • docker inspect app1显示"IPAddress": "172.19.0.4"

我不知道出了什么问题,希望你能帮忙。

iugsix8n

iugsix8n1#

根据所提供的信息,您似乎遇到了两个问题。以下是解决这些问题的一些建议:

  • SSL_read()failed在nginx日志中出现错误:
  • 验证SSL证书和密钥文件是否正确挂载到nginx容器中。仔细检查文件路径并确保证书有效。
  • 确保证书和密钥文件对容器内的nginx进程具有适当的读取权限。
  • 如果问题仍然存在,请考虑重新生成SSL证书和密钥文件,并确保它们在nginx容器中正确配置。
  • mod_rpaf无法正常工作:
  • RPAF_ProxyIPs配置需要IP地址。您可以尝试使用网络网关IP,而不是硬编码IP地址。将rpaf.conf中的RPAF_ProxyIPs指令更新为RPAF_ProxyIPs <network_gateway_ip>
  • 要查找网络网关IP,您可以检查与容器关联的网络。运行docker network inspect <network_name>获取exp_proxy网络的网关IP。
  • 使用获取的网关IP更新rpaf.conf中的RPAF_ProxyIPs指令,并重新启动容器。

相关问题