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

Rob*_*ber 0 ssl https nginx amazon-ec2 puma

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

这是我第一次迁移到HTTPS,我一点也不熟练。读了很多书,我取得了一些进步,但现在我陷入了困境。

我想确认我是正确的方法还是错误的方法。

在这一刻:

  1. 我获得了一个Let's加密证书,安装并更新了我的Nginx服务器。现在,服务器正在侦听443,而SSL正在前端工作。
  2. 我以为足以保护一个站点,但我意识到从前端发送到后端的请求中出现了blocked:mixed-content错误。
  3. 因此,我想我也需要保护后端。(请确认)
  4. 现在(这仅在开发中,而不是在AWS实例中工作)为了保护后端Puma服务器,我采用了一种非常简单的方法:由于Rails API是私有的,因此我创建了一个自签名SSL证书,并仅使用它来启动这样的Puma服务器:

    捆绑执行程序puma -b'ssl://127.0.0.1:3000?key = puma.server.key&cert = puma.server.crt'-e开发-S〜/ puma -C config / puma / development.rb

如前所述,这正在开发中。我的Nginx服务器正在侦听443,并将请求发送到在端口3000侦听的Puma。

  1. 在这一点上,我尝试在EC2生产实例中执行相同的操作。但是在这里,我有堵墙。我刚刚创建了另一个自签名证书,并以相同的方式启动了puma服务器。但是连接被拒绝了。我了解问题是EC2安全规则中的端口3000未打开。我试图同时在HTTPS中打开该端口,但EC2不允许我使用该端口,因为它仅适用于端口443和HTTP,但是由于SSL无效而无法启动服务器。

因此,在这一点上,我想知道我是否采用了正确的方法,但是我只是缺少了一些东西,否则,对于EC2实例来说这是错误的方法,我需要做一些真正不同的事情。注意:我已经阅读了一些有关如何配置Nginx服务器以代理https的内容,但目前还不太了解。我应该这样走吗?

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

我了解您的做法。我建议采用第二种方法。但是,我有一些问题:

  1. 我是否可以解决通过这种方法获得的blocked:mixed-content错误?为什么?我的意思是,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以及代理传递配置应如何匹配这些请求。

您能否澄清一下可能会更新您的示例?假设目前的请求就像// domain-name:8080 / action

更新 我正在尝试配置Ningx以将请求传递给PUMA套接字(目前正在开发中)。我遇到了错误,请查看一篇新文章以保持此整洁:Nginx代理传递给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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

我唯一的问题是:如果我这样注释所有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;
            }
Run Code Online (Sandbox Code Playgroud)

它也可以。我了解它们不是强制性的,应该包含在内,因为它们有一定好处?

与:

ssl_session_timeout  5m;
 ssl_protocols  SSLv2 SSLv3 TLSv1;
 ssl_ciphers  HIGH:!aNULL:!MD5;
 ssl_prefer_server_ciphers   on;
Run Code Online (Sandbox Code Playgroud)

ffe*_*ast 5

你走错路了。

在大多数情况下,您无需保护后端应用程序,这是为什么:

  1. 流量将通过本地连接或内部网络进行传输,因此通过安全连接进行路由没有意义
  2. 您会妨碍nginx <->后端通信性能(可以忽略不计,但仍然可以)
  3. 您将获得其他内部证书带来的各种问题
  4. 甚至更多-并非所有后端服务器都支持https处理-因为这根本不是他们的工作
  5. 从开发的角度来看,您的后端Web应用程序应该不在乎它是运行在http还是httpS上-这些是环境问题,应与您的应用程序逻辑完全分开

因此,任务归结为在nginx中配置https并通过与HTTP感知的后端服务器的不安全连接进行proxy_passhttps://nginx.ru/en/docs/http/ngx_http_proxy_module.html#proxy_pass)。

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

  1. 您的后端服务器不知道的Web主机名
  2. 是否生成httphttpsURL
  3. 什么是真正的客户端IP,因为您的后端应用程序会看到Nginx的IP地址

通常是这样解决的:

  1. Host标头通过proxy_set_header传递并由后端服务器接收
  2. X-Forwarded-Proto 标头被传递,通常由后端服务器尊重
  3. X-Forwarded-For 标头包含原始用户的ip

我在google上搜索了与的相关设置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;
}
Run Code Online (Sandbox Code Playgroud)

现在介绍您的角度应用程序。它需要知道用于API请求的网址。它可以通过多种方式解决,但要点是,没有URL应该在代码中进行硬编码,可以使用环境变量代替。这是描述的方法之一-它使用您的angular应用程序之前包含的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));
Run Code Online (Sandbox Code Playgroud)

另外:由于您的后端似乎正在使用没有API前缀的URL,因此您可以对nginx使用以下技巧:

location /api/ {        
  ...
  proxy_pass http://api.development/;
  ...
}
Run Code Online (Sandbox Code Playgroud)

请注意proxy_pass之后的斜杠-在这种情况下,位置前缀将被删除。从这里

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