问题
Given the following docker containers:
- an
nginx
service that runs an unmodified official nginx:latest image- container name:
proxy
- container name:
- two applications running in separate containers based on modified official php:7.4.1-apache images
- container names:
app1
andapp2
- container names:
- all containers
proxy
,app1
, andapp2
are in the same Docker-created network with automatic DNS resolution
With the following example domain names:
local.web.test
local1.web.test
local2.web.test
I want to achieve the following behavior:
- serve
local.web.test
fromnginx
as the default server block - configure
nginx
to proxy requests fromlocal1.web.test
andlocal2.web.test
toapp1
andapp2
, respectively, both listening on port80
- configure
nginx
to serve all three domain names using a wildcard SSL certificate
I experience two problems:
- I notice the following error in the
nginx
logs: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
- the
mod_rpaf
seems not to work properly (i.e., theip
address in theapache
access logs is of thenginx
server [e.g.,172.19.0.2
] instead of theip
of the client that issues the request172.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
- the output of
phpinfo()
for Apache Environment shows that:HTTP_X_REAL_IP
lists the clientip
SERVER_ADDR
lists theapp1
containerip
(e.g.,172.19.0.4
)REMOTE_ADDR
shows theproxy
containerip
(e.g.,172.19.0.2
) instead of the clientip
To make this reproducible, this is how everything is set up. I tried this on my Windows machine so there are two preliminary steps.
Preliminary steps
a. in my
C:\Windows\System32\drivers\etc\hosts
file I added the following:127.0.0.1 local.web.test 127.0.0.1 local1.web.test 127.0.0.1 local2.web.test
b. I generated a self-signed SSL certificate with the Common Name set to
*.local.test
viaopenssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 365 -keyout localhost.key -out localhost.crt
The
proxy
service setupa. the
nginx.yml
for thedocker-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. within
./nginx
that is bind mounted at/etc/nginx/conf.d
there is a filedefault.conf
that contains: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; }
c. the
./certs:/etc/ssl/nginx
bind mounts the folder containing the self-signed certificate and keyd. the
./static/local.web.test:/usr/share/nginx/html
makes available a fileindex.html
that contains<h1>local.web.test</h1>
The
app1
andapp2
services setupa. the
apache.yml
for thedocker-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. the
apache.dockerfile
image looks like this:# 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. the file
./apache/mods/rpaf.conf
copied contains:<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. the
./static/local1.web.test:/var/www/html
bind mounts anindex.php
file containing:<h1>local1.web.test</h1> <?php phpinfo(); ?>
the same goes for
./static/local2.web.test:/var/www/html
e. the
000-default.conf
virtual hosts inapp1
andapp2
are not modified:<VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>
Starting the setup
a. start the
proxy
serverdocker-compose -f nginx.yml up -d --build
b. start the
app1
andapp2
servicesdocker-compose -f apache.yml up -d --build
c. check containers to see if
mod_rpaf
is enableddocker-compose -f apache.yml exec app1 apachectl -t -D DUMP_MODULES
d. add two files in
./nginx
that will be available on theproxy
container at/etc/nginx/conf.d
local1.web.test.conf
containing
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; }
- the second file is
local2.web.test.conf
with a similar setup (i.e., number1
is replaced with2
)
e. check the config and restart the
proxy
container (or reload thenginx
server)docker-compose -f nginx.yml exec proxy nginx -t docker-compose -f nginx.yml exec proxy service nginx reload
The issues:
- when I run
docker logs proxy -f
I notice the SSL internal error mentioned above:SSL_read() failed
- someone faced a similar error (http2: SSL read failed while sending req in nginx) but in that case, the message more specifically points to the certificate authority
- if I run
docker logs app1 -f
and visithttps://local1.web.test
, theip
in theGET
request matches theip
of theproxy
container (i.e.,172.19.0.2
) and not that of the remote client- I suspect the cuprit is this
RPAF_ProxyIPs 127.0.0.1
, but I can't manually fix theip
because I don't know whatip
the container will get in theexp_proxy
network - also I can't use the hostname because
RPAF_ProxyIPs
expects anip
docker inspect proxy
shows"IPAddress": "172.19.0.2"
docker inspect app1
shows"IPAddress": "172.19.0.4"
- I suspect the cuprit is this
I can't seem to understand what goes wrong and would appreciate your help.
来源:https://stackoverflow.com/questions/62647604/how-to-correctly-use-nginx-as-reverse-proxy-for-multiple-apache-docker-container