Выполнение запроса https tls от openresty lua

Я пытаюсь найти способ сделать безопасный запрос GET из моего модуля nginx/lua на сервер для проверки аутентификации входящего вызова. Кажется, там очень мало информации о том, как это сделать. Мои текущие попытки сосредоточены на использовании resty.http и следующих.

-- Create http client
local httpc = http.new();
httpc:set_timeout(500)

ngx.log(ngx.DEBUG, '**** Connect with TLS ****');
ok, err = httpc:connect(my_server, port);

Однако my_server требует сертификат, ca и ключ на входе, но опять же, не знаю, как это сделать, используя ["ca"] = myca.pem; и т.д... не работает. Если я установлю

lua_ssl_trusted_certificate=myca.pem

запрос завершается со следующим:

2018/02/23 19:22:17 [crit] 19#0: *4 SSL_shutdown() failed (SSL: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init), client: 127.0.0.1, server: my_server

Я просмотрел https://github.com/brunoos/luasec/archive/luasec-0.6, но, честно говоря, не смог заставить его скомпилироваться в моем контейнере alpine linux. Не знаете, какой модуль использовать или как действовать на этом этапе, есть идеи?

ОБНОВЛЕНИЕ Дополнительная информация на основе комментариев и ответов, которые я получил. Попытки использовать openresty pintsized lua-resty-http с https не увенчались успехом, в настоящее время просто нет способа заставить работать взаимный поток TLS cert/key/ca. Используя приведенный ниже ответ, я смог настроить восходящий прокси-вызов для моего микросервиса внутреннего приложения для правильного обслуживания запросов. Мои фрагменты файла proxy.conf, чтобы заставить это работать, выглядят следующим образом. Примечание: вышестоящий сервер должен совпадать с CN в сертификатах, иначе он не будет работать, или, по крайней мере, я ничего не получил из-за сбоев сертификатов, указывающих на это.

http {
    # Upstream backend for services
    upstream apimy {
        server apimy:3003;
    }
...
        location /api/analytics/token-info {
            internal; # Specifies that a given location can only be used for internal requests

            set $bearerToken $arg_token;
            set $args "";

            proxy_pass_request_headers on;

            proxy_ssl_protocols            TLSv1 TLSv1.1 TLSv1.2;
            proxy_ssl_ciphers              HIGH:!aNULL:!MD5;
            proxy_ssl_certificate          /etc/certs/analytics_proxy_client_public.cert.pem;
            proxy_ssl_certificate_key      /etc/certs/analytics_proxy_client_private.key.pem;
            proxy_ssl_trusted_certificate  /etc/certs/analytics_proxy_client_ca.cert.pem;

            proxy_ssl_verify         on;
            proxy_ssl_verify_depth    2;
            proxy_ssl_session_reuse off;

            proxy_set_header Host apimy;
            proxy_set_header Content-Type "application/json";
            proxy_set_header Accept "application/json";
            proxy_set_header Authorization "Bearer $bearerToken";

            proxy_pass    https://apimy/api/analytics/token-info; # trailing slash
        }

Еще одно замечание: для того, что бы я не мог заставить BearerToken работать, передавая его как vars со стороны lua, мне пришлось передать его как аргумент, а затем очистить аргументы после этого, чтобы любые другие не были переданы в вызов. Мой код lua, который вызывает путь.

-- Connect --
ngx.log(ngx.DEBUG, '**** Connect with TLS ****');

res = ngx.location.capture('/api/analytics/token-info',
{
    method = ngx.HTTP_POST,
    body = json.encode(query),
    args = {
        token = accessToken;
    }
})

person doktoroblivion    schedule 23.02.2018    source источник
comment
Луасек - это боль... к сожалению. Я использую curl или libcurl - например. luarocks.org/modules/luarocks/luacurl (хотя я не знаю, какой из них я на самом деле использовал)   -  person dualed    schedule 24.02.2018
comment
вы должны использовать https://github.com/pintsized/lua-resty-http#ssl_handshake   -  person Alexander Altshuler    schedule 24.02.2018
comment
@AlexanderAltshuler Я посмотрел на это, а также на luasec (что выглядит как боль), если я использую ssl_handshake, какой параметр я использую для полезной нагрузки json, содержащей cert/ca/key?   -  person doktoroblivion    schedule 25.02.2018
comment
@ErickGriffin у вас есть выбор - проверять сертификат сервера или нет. Если установлено значение true, сертификат сервера будет проверен в соответствии с сертификатами ЦС, указанными в директиве lua_ssl_trusted_certificate. Вам также может понадобиться настроить файл lua_ssl_verify_depth. Если вашему серверу требуется сертификат клиента (очень редко используется IMO), я не знаю, как это сделать с помощью resty.http.   -  person Alexander Altshuler    schedule 25.02.2018
comment
@AlexanderAltshuler, я попробовал рукопожатие, и, похоже, оно не работает. Я проверил свой lua_ssl_protocols, и он не установлен, поэтому я не могу объяснить ошибку, которая указывает на проблему с протоколом. Я обновляю проблему выше для любых дополнительных комментариев/идей от вас.   -  person doktoroblivion    schedule 25.02.2018
comment
@ErickGriffin Очевидно, вы должны подключиться до рукопожатия   -  person Alexander Altshuler    schedule 26.02.2018
comment
@AlexanderAltshuler, если я поставлю ssl_handshake после подключения, окажется, что подключение пытается сделать это от моего имени и, конечно, терпит неудачу. Поэтому я предполагаю, что я должен использовать что-то вроде luasec, чтобы получить полный контроль над фактическим развитием событий здесь?   -  person doktoroblivion    schedule 26.02.2018


Ответы (1)


Забудьте о luasec, ваш последний фрагмент кода не имеет смысла.

LuaSec совместим с LuaSocket и определенно несовместим с API-интерфейсом nginx cosocket.

Используйте общий интерфейс resty-http request_uri():

  local http = require "resty.http"
  local httpc = http.new()
  local res, err = httpc:request_uri("https://example.com:443/helloworld", {
    method = "POST",
    body = "a=1&b=2",
    headers = {
      ["Content-Type"] = "application/x-www-form-urlencoded",
    },
    ssl_verify = true
  })

Кстати, request_uri() источник — прекрасный пример использования универсального API resty-http (connect/ssl_handshake/request)

Обновление:

Если вам определенно нужно аутентифицировать клиента по сертификату, вы можете использовать следующий подход:

Создадим апстрим, настроим клиентский сертификат:

  location /my_upstream/ {
      internal; # Specifies that a given location can only be used for internal requests
      proxy_pass_request_headers off;
      proxy_set_header Host backend.example.com;
      proxy_set_header Content-Type "application/json";
      proxy_set_header Accept "application/json";
      proxy_set_header Authorization "Bearer $bearerToken"
      proxy_pass                https://backend.example.com/; # trailing slash
      proxy_ssl_certificate     /etc/nginx/client.pem;
      proxy_ssl_certificate_key /etc/nginx/client.key
      proxy_ssl_verify on;
      proxy_ssl_verify_depth 2; #just example
      proxy_ssl_trusted_certificate /etc/nginx/ca.pem;
  }

Используйте API ngx.location.capture для выдачи синхронного, но не блокирующего Подзапрос Nginx:

local res = ngx.location.capture('/my_upstream/login',
    {
        method = ngx.HTTP_POST,
        body = "some text",
        vars = {
                    bearerToken = "12345"
               }
    })

Пожалуйста, не флудьте, RTFM обо всех деталях ngx.location.capture API и proxy_pass

PS: Глядя на ваше последнее обновление - не могу поверить, что кому-то потребуется и сертификат клиента, и авторизация по токену вместе. Обычно HTTPS без клиентского сертификата используется для безопасного транспорта и заголовка авторизации с токеном для аутентификации.

person Alexander Altshuler    schedule 26.02.2018
comment
Я согласен и хотел бы использовать описанный выше подход, но, как я упоминал в своем обновлении выше, для подключения требуется не только ЦС, но также сертификат, ключ и файл пароля. Каким образом они предоставляются по вышеуказанному звонку? - person doktoroblivion; 27.02.2018
comment
Я использовал приведенную выше реализацию, и с учетом того, что местоположение/сервер должны соответствовать CN, все будет работать. Я немного опубликую свое официальное исправление для других. - person doktoroblivion; 27.02.2018