如何使用SSL HTTPS保护Amazon EC2中的Nginx前端和Puma后端AWS

时间:2017-12-26 13:29:25

标签: ssl nginx amazon-ec2 https puma

我正在尝试将我的网站从http迁移到https。该架构是:在Nginx服务器上运行的angularJS webapp前端(侦听端口80),它将请求发送到在Puma服务器上运行的Rails API应用程序(侦听端口8080)。两台服务器都在Amazon EC2实例中共存。

这是我第一次迁移到HTTPS并且我根本不熟练。读了很多,我取得了一些进展,但现在我被卡住了。

我想确认我是否采用了正确的方法,或者我的方法是错误的。

目前:

  1. 我有一个让加密证书,安装并更新了我的Nginx服务器。现在,服务器正在侦听443,SSL正在前端工作。
  2. 我认为这足以保护网站,但我意识到我从前端发送到后端的请求中出现了阻塞:混合内容错误。
  3. 所以,我认为我还需要保护后端。 (请确认)
  4. 现在(这只是在开发中工作,而不是AWS实例)试图保护后端Puma服务器我采用了一种非常简单的方法:由于Rails API是私有的,我创建了一个自签名的SSL证书,只是用它来启动Puma服务器,如下所示:

    捆绑exec puma -b'sssl://127.0.0.1:3000?key = puma.server.key& cert = puma.server.crt'-e development -S~ / puma -C config / puma / development .RB

  5. 如上所述,这是在开发中。我的Nginx服务器正在443中监听,并​​向Puma监听端口3000发送请求。

    1. 此时,我试图在我的EC2生产实例中做同样的事情。但在这里,我有一堵墙,我被困住了。我刚刚创建了另一个自签名证书,并以同样的方式启动了puma服务器。但是连接被拒绝了。我明白问题是EC2安全规则中的端口3000没有打开。我尝试在两个HTTPS中打开端口,EC2没有允许我,因为只适用于端口443和HTTP,但是因为SSL不起作用而启动服务器。
    2. 所以,在这一点上,我想知道我是否采用正确的方法,但我只是遗漏了一些东西,否则,对于EC2实例来说这是一种错误的方法,我需要做一些与众不同的事情。注意:我已经阅读了有关如何配置Nginx服务器以代理https的内容,但目前还不太了解。我应该这样走吗?

      @ffeast回答后的评论和问题:

      我理解你的方法。我建议,它阻碍了第二种方法。但是,我有一些问题:

      1. 我会解决使用此方法遇到的阻止:混合内容错误吗?为什么?我的意思是,Rails API请求是否会发生变化?注意:目前,在Angular中,我有资源发送请求,例如:// domain-name:8080 / action,Puma服务器正在侦听该端口中的请求。我目前没有任何代理通行证。

      2. 我认为你包括连接到Puma套接字的de Nginx配置,我想我需要创建这个Puma套接字,我需要检查一下。如果你知道如何包含一个例子会有所帮助。如果我用这个套接字配置Puma,我是否需要在特定端口启动Puma?

      3. 我试着理解整个画面,但我仍然感到困惑: 3.1对API(来自Angular Nginx)的请求应该如何? 3.2如果我应该在特定端口启动Puma?我不知道是否需要套接字方法...... 3.3代理传递配置应该如何匹配这些请求。

      4. 您能澄清一下可能更新您的示例吗?我们假设此时请求类似于//域名:8080 / action

        更新 我正在尝试配置Ningx代理将请求传递给PUMA套接字(目前处于开发阶段)。我收到错误,请参阅新帖子以保持清洁:Nginx proxy pass to Rails API

        更新2 它工作!我的网站安全无误!这是我的配置。

        upstream api.development {
            # Path to Puma SOCK file, as defined previously
            server unix:/tmp/puma.sock fail_timeout=0;
        }
        
        server {
                listen       443 ssl;
                server_name  localhost;
        
                ssl_certificate      /keys/ssl/development.server.crt;
                ssl_certificate_key  /keys/ssl/development.server.key;
        
            #    ssl_session_cache    shared:SSL:1m;
            #    ssl_session_timeout  5m;
        
            #    ssl_ciphers  HIGH:!aNULL:!MD5;
            #    ssl_prefer_server_ciphers  on;
        
                location / {
                    root   /path-to-app;
                    index  index.html index.htm;
                    try_files $uri $uri/ /index.html =404;
                }
        
                # Proxy pass requests to Yanpy API
                location /api {
                   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                   proxy_set_header Host $host;
                   #proxy_set_header X-Forwarded-Proto https;
                   proxy_redirect off;
                   rewrite ^/api(.*) /$1 break;
                   proxy_pass http://api.development;
                }
            }
        

        我唯一的问题是:如果我评论所有的proxy_set_header指令:

        location /api {
                       #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                       #proxy_set_header Host $host;
                       #proxy_set_header X-Forwarded-Proto https;
                       #proxy_redirect off;
                       rewrite ^/api(.*) /$1 break;
                       proxy_pass http://api.development;
                    }
        

        它也有效。我知道他们不是强制性的工作,应该包括在内,因为他们有一些好处吗?

        同样:

        ssl_session_timeout  5m;
         ssl_protocols  SSLv2 SSLv3 TLSv1;
         ssl_ciphers  HIGH:!aNULL:!MD5;
         ssl_prefer_server_ciphers   on;
        

2 个答案:

答案 0 :(得分:4)

你走错了路。

在大多数情况下,您无需保护后端应用,这就是原因:

  1. 流量可以通过本地连接或内部网络进行,因此通过安全连接进行路由是没有意义的
  2. 你会妨碍nginx< - >后端通信性能(它可以忽略不计但仍然存在)
  3. 您将获得其他内部证书的各种问题
  4. 甚至更多 - 并非所有后端服务器都支持https处理 - 因为它根本不是他们的工作
  5. 从开发角度来看,您的后端Web应用程序不应该关心它是通过http还是httpS运行 - 这些是应该从应用程序的逻辑中完全剖析的环境问题
  6. 因此,任务归结为在nginx中配置https并通过与HTTP感知后端服务器的不安全连接执行proxy_passhttps://nginx.ru/en/docs/http/ngx_http_proxy_module.html#proxy_pass)。

    问题是如何处理以下问题:

    1. 您的后端服务器一无所知的网络主机名
    2. 是否生成httphttps网址
    3. 真正的客户端ip是什么,因为你的后端应用程序会看到nginx的ip地址
    4. 以下是它通常如何解决的问题:

      1. Host标头通过proxy_set_header传递并由后端服务器接收
      2. X-Forwarded-Proto标头已被传递并且通常受后端服务器的尊重
      3. X-Forwarded-For标头包含原始用户的IP
      4. 我搜索了与puma相关的设置,这与它最终的样子非常接近(从这里借来https://gist.github.com/rkjha/d898e225266f6bbe75d8),@myapp_puma部分对你的案例特别感兴趣:

        upstream myapp_puma {
          server 127.0.0.1:8080 fail_timeout=0;
        }
        server {
         listen  443 default ssl;    
         server_name example.com;
         root /home/username/example.com/current/public;
         ssl on;
         ssl_certificate /home/username/.comodo_certs/example.com.crt;
         ssl_certificate_key /home/username/.comodo_certs/example.com.key;
         ssl_session_timeout  5m;
         ssl_protocols  SSLv2 SSLv3 TLSv1;
         ssl_ciphers  HIGH:!aNULL:!MD5;
         ssl_prefer_server_ciphers   on;
        
         try_files $uri/index.html $uri @myapp_puma;
        
         location @myapp_puma {
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header Host $host;
           proxy_set_header X-Forwarded-Proto https;
           proxy_redirect off;
           proxy_pass http://myapp_puma;
         }
        
         error_page 500 502 503 504 /500.html;
         client_max_body_size 4G;
         keepalive_timeout 10;
        }
        

        现在关于你的角度应用程序。 它需要知道它应该用于API请求的URL。 它可以通过多种方式解决,但重点是NO URL应该在代码中进行硬编码,您可以使用环境变量代替。这是其中一种方法described - 它使用角度应用之前包含的 env.js 文件,然后依赖于整个代码中定义的常量。在您的情况下,apiUrl应指向所需的https端点

        (function (window) {
          window.__env = window.__env || {};
        
          // API url
          window.__env.apiUrl = 'http://dev.your-api.com';
        
          // Base url
          window.__env.baseUrl = '/';
        
          // Whether or not to enable debug mode
          // Setting this to false will disable console output
          window.__env.enableDebug = true;
        }(this));
        

        增加: 因为你的后端似乎正在处理没有API前缀的URL, 你可以使用nginx的以下技巧:

        location /api/ {        
          ...
          proxy_pass http://api.development/;
          ...
        }
        

        注意到proxy_pass之后的尾部斜杠 - 在这种情况下,将删除位置前缀。来自here

          

        如果使用URI指定了proxy_pass指令,那么当a。时   请求被传递给服务器,这是规范化请求URI的一部分   匹配该位置将被指令

        中指定的URI替换

答案 1 :(得分:1)

听起来整个考验都是关于你在某个地方收到的blocked:mixed-content错误:

  • 是否在浏览器中?无论好坏,浏览器实际上都不会知道前端服务器与后端服务器之间的连接是否超过{{1} }或http。换句话说,更有可能的是,错误是由网页某处仍在https上提取内容的内容引起的,无论是来自您自己的域,还是来自第三方,而不是任何事情后端通过http上的前端服务器代理。例如,您可能希望在前端代码中将http更改为src="http://cdn.example.org/….js",以使所有JavaScript脚本都满意。

  • 它在后端吗?一般来说,为了告诉"在连接安全的后端,前端代理必须添加适当的src="//cdn.example.org/….js"标头。

否则,http://nginx.org/r/proxy_pass确实在最新版本的nginx中支持经过验证的https,您可以使用X-Forwarded-Proto指令的组合来配置安全策略,例如proxy_ssl_…与公共版本您的自签名证书和/或CA,以及proxy_ssl_trusted_certificate

P.S。实际上,我不同意关于后端和前端服务器之间加密性能影响的优点的其他答案。如果你有理由相信这种联系可能会被破坏(甚至谷歌发现他们的数据中心之间的专用光纤线已被政府利用),那么,确实在后端之间使用自签名的proxy_ssl_verify on;和前端服务器 在性能方面,https的主要成本来自建立连接;但是在服务器场景中,那个连接可以重新用于服务多个客户端(例如,可能通过https控制);即使连接无法重用,后端和前端服务器之间的延迟通常也足够小,能够在没有性能影响的情况下维持一些额外的往返(特别是考虑到最终用户的延迟通常在订单上比那更大的数量。) 话虽如此,https address scheme如今被​​高度滥用 - 大多数公共网站都没有被强制使用proxy_ssl_session_reuse的好处,因为它只会减少可以访问这些网站的人数(请参阅HTTP/2.0: The IETF Is Phoning It In仅列出基于策略的问题列表,并且还存在许多TLS不兼容的技术问题),而且面向客户的https确实会因为需要额外的往返行程而降低性能因此,如果您不从https获得任何额外的好处,那么它通常只是一种责任。总而言之,如果没有它,偶尔拍摄猫图片的博客可能会更好。