使用Nginx重定向到Docker容器。

17
在发布我的问题之前,我想知道是否有可能实现我想要的功能。
假设我有myserver.com运行着一个带有nginx和letsencrypt的docker容器。在同一台服务器上还有2个运行网站的docker容器。
目前所有的重定向都很好,所以www.myserver.com指向docker 1,site2.myserver.com指向docker 2。
我想让所有通信都通过HTTPS进行,但这里开始出现了问题。因此,我的问题是:nginx和letsencrypt的docker容器是否可以使用letsencrypt的证书连接到另一个docker容器?对我来说,这似乎是某种中间人攻击。更详细的示意图如下:
浏览到http:// site2.myserver.com -> nginx重定向到https:// site2.myserver.com -> 连接到容器2(192.168.0.10)的80端口。 或者另一种选择: 浏览到http:// site2.myserver.com -> nginx重定向到https:// site2.myserver.com -> 连接到具有site2.myserver.com证书的容器2(192.168.0.10)的443端口。
如果无法完成,那么解决方案是什么?将证书复制到docker容器中并使它们运行https,以便将http请求重定向到该容器的https端口?
浏览到http:// site2.myserver.com -> nginx转发请求 -> 连接到具有site2.myserver.com证书的容器2(192.168.0.10)的443端口。
谢谢, Greggy

我会使用 nginx-proxySSL - rdupz
4个回答

17

据我理解,您的nginx反向代理与容器位于同一网络中,因此不需要使用TLS加密来保护它们之间的连接(因为这是一个私有网络,如果攻击者能够访问该网络,他也将能够访问服务器以及所有未加密的数据)。

如果您绝对需要有效的证书来保护本地网络上的连接,您可以创建附加的子域名,将其解析为本地IP。然后,您需要使用手动DNS选项来获取证书(这是certbot的一个选项,您需要手动输入作为TXT条目的密钥)。

Nginx配置示例:将http重定向到https

server {
    listen 80;

    server_name example.com;
    return 301 https://example.com/;
}
server{
    listen 443 ssl http2;

    server_name  example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

    location / {
        proxy_pass http://container:8080/;
        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;
    }

    include tls.conf;
}

所有容器都在一个主机上运行。由于容器正在运行其自己的虚拟192.168.0.x网络上,因此不需要加密nginx和容器之间的数据。IP地址由docker分配。我只想将浏览器的http连接重定向到https。我发现很多文档都是关于如何在同一主机上执行此操作,但没有针对另一个IP地址(虚拟地址)的文档。 - Greggy
你可以使用设置Nginx来返回301状态码,重定向到HTTPS页面。 - Paul Trehiou
这对我没用:(它会自动重定向到其自己的443端口上的nginx容器。这是我的配置:site2.myserver.com { server 192.168.0.13:8069; } server { server_name site2.myserver.com; listen 80 ; return 301 https://site2.myserver.com$request_uri; location / { proxy_pass http://site2.myserver.com; }} 是否有“位置/”并不影响任何事情。当我删除“return 301”时,http连接将建立到该容器。因此,一旦连接被加密,它就指向了它自己而不是容器。 - Greggy
第一行应该是:upstream site2.myserver.com { server 192.168.0.13:8069; } - Greggy
1
是的,您需要另一个服务器部分来配置HTTPS。 - Paul Trehiou

3

我会选择开箱即用的解决方案:

JWilder Nginx + Lets Encrypt。

首先,我们启动NGINX容器作为反向代理:

docker run -d -p 80:80 -p 443:443 \
    --name nginx-proxy \
    -v /path/to/certs:/etc/nginx/certs:ro \
    -v /etc/nginx/vhost.d \
    -v /usr/share/nginx/html \
    -v /var/run/docker.sock:/tmp/docker.sock:ro \
    jwilder/nginx-proxy

接下来我们启动 Let's Encrypt 容器:

docker run -d \
-v /path/to/certs:/etc/nginx/certs:rw \
--volumes-from nginx-proxy \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
jrcs/letsencrypt-nginx-proxy-companion

对于您的网站,您需要设置一些环境变量:

docker run -d \
--name website1 \
-e "VIRTUAL_HOST=website1.com" \
-e "LETSENCRYPT_HOST=website1.com" \
-e "LETSENCRYPT_EMAIL=webmaster@website1" \
tutum/apache-php

Nginx容器将创建新的配置项,而 Let's Encrypt容器将请求证书(并完成续订)。更多信息请参见:Nginx+Let's Encrypt

我认为这个解决方案很适合快速实现某些东西,但这并不是学习的方式。 - Paul Trehiou
1
如果你想从头开始学习如何设置这些东西,那么这不是最好的解决方案 :-) 但是创建这样的设置需要很长时间(注册每个新容器,调整nginx模板,请求ssl证书..)。 - opHASnoNAME
感谢您的回答,但我最终又遇到了同样的问题。访问 Web 服务器的 80 端口,我可以看到 Tutum 的默认页面,这说明对 tutum 容器的重定向工作正常。但是当我尝试访问 https 页面时,出现“无法连接”的错误提示。 - Greggy

2
以下是我的做法:

NGINX配置文件(default.conf)

使用来自https://github.com/KyleAMathews/docker-nginx的Docker镜像,我按照以下方式进行了自定义默认文件:

server {
    root /var/www;
    index index.html index.htm;

    server_name localhost MYHOST.COM;

    # Add 1 week expires header for static assets
    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires 1w;
    }

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to redirecting to index.html
        try_files $uri $uri/ @root;

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

    # If nginx can't find a file, fallback to the homepage.
    location @root {
        rewrite .* / redirect;
    }

    include /etc/nginx/basic.conf;
}

Dockerfile

这是我的Dockerfile,考虑到我的静态内容在html/目录下。

COPY conf/default.conf /etc/nginx/sites-enabled/default

ADD certs/myhost.com.crt /etc/nginx/ssl/server.crt
ADD certs/myhost.com.key /etc/nginx/ssl/server.key
RUN ln -s /etc/nginx/sites-available/default-ssl /etc/nginx/sites-enabled/default-ssl

COPY html/ /var/www

CMD 'nginx'

测试

为了进行本地测试,请将文件/etc/hosts更改为在127.0.0.1上添加myhost.com,并运行以下命令:

curl -I http://www.myhost.com/

结果

HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sun, 04 Mar 2018 04:32:04 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: https://www.myhost.com/
X-UA-Compatible: IE=Edge

1

很好,最终我通过合并opHASnoNAME和Paul Trehiou的答案得到了想要的结果。我为opHASnoNAME的回答额外添加了一个步骤,即在nginx和letsencrypt docker之间挂载文件系统。这使得可以将nginx的配置文件与正确的证书链接起来(稍后会提到)。

以下是我的操作步骤:

docker run --name nginx-prod --restart always -d -p 80:80 -p 443:443 -v /choose/your/dir/letsencrypt:/etc/nginx/certs:ro -v /etc/nginx/vhost.d -v /usr/share/nginx/html -v /var/run/docker.sock:/tmp/docker.sock:ro -e DEFAULT_HOST=myserver.com jwilder/nginx-proxy

docker run --name letsencrypt --restart always -d -v /choose/your/dir/letsencrypt:/etc/nginx/certs:rw --volumes-from nginx-prod -v /var/run/docker.sock:/var/run/docker.sock:ro jrcs/letsencrypt-nginx-proxy-companion

然后运行带有Web服务器的任何容器;无需设置 LETSENCRYPT 变量。我的当前容器可以在不设置它们的情况下访问。
jwilder/nginx-proxy 将列出 /etc/nginx/conf.d/default.conf 中所有正在运行的容器。不要添加任何内容到此文件中,因为它将在下次重新启动时被覆盖。 为每个 Web 服务器在相同目录中创建一个新的 .conf 文件。该文件将包含由 Paul Trehiou 建议的 HTTPS 信息。例如,我创建了 site2.conf 文件:
server{
    listen 443 ssl http2;
    server_name  site2.myserver.com;
    ssl_certificate /etc/nginx/certs/live/myserver.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/live/myserver.com/privkey.pem;
    ssl_trusted_certificate /etc/nginx/certs/live/myserver.com/fullchain.pem;

    location / {
        proxy_pass http://192.168.0.10/;
        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;
    }
}

代理传递地址是从default.conf文件中获取的,每个容器的IP地址在那里列出。为了能够备份这些.conf文件,我将重新创建我的nginx容器,并将本地文件系统挂载到/etc/nginx/conf.d上。如果容器因.conf文件中的错误而无法启动,这也会使生活变得更加轻松。感谢大家的帮助,现在难题已经解决了;-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接