Перекрестная конфигурация SSL/TLS с промежуточными и перекрестными сертификатами

Используя последнюю версию Crossbar (0.13, установлен из apt-get в Ubuntu 14.04) У меня проблемы с установлением соединений с использованием SSL и промежуточных сертификатов.

Если я настрою сервер без ca_certificates недвижимость в tls ключ, то сервер работает нормально, и соединения могут быть сделаны с помощью Google Chrome через wss протокол. Однако попытка установить соединение с использованием thruway завершается неудачно со следующей ошибкой:

Не удалось подключиться: Невозможно завершить рукопожатие SSL/TLS: stream_socket_enable_crypto(): операция SSL не выполнена с кодом 1. Сообщения об ошибках OpenSSL: ошибка:14094410: Подпрограммы SSL:ssl3_read_bytes: ошибка квитирования оповещения sslv3

То, что мы поговорили с командой Thruway, похоже, является проблемой с сертификатом - на нашем живом сайте мы используем промежуточный и кросс-подписанный сертификат от Gandi, который необходим для некоторых браузеров и, следовательно, для некоторых реализаций open-ssl.

Похоже, что в то время как браузеры рады установить соединение TLS только с ключом и сертификатом, Thruway требуется цепочка. Однако приведенная ниже конфигурация с использованием двух сертификатов, предоставленных Gandi, не работает ни для Chrome, ни для Thruway. Chrome показывает ошибку:

не удалось: рукопожатие при открытии WebSocket было отменено

При использовании .crossbar/config.json файл ниже. Итак, это проблема моего конфига, моих сертификатов или какой-то другой части стека Open-SSL?

(Файл ниже был изменен, чтобы удалить любую потенциально конфиденциальную информацию, поэтому может показаться, что он не будет работать по другим причинам. Если подключение работает, то базовая аутентификация и другие компоненты работают нормально, поэтому, пожалуйста, оставляйте ответы / комментарии относительно реализации TLS. Комментарии не являются действительными JSON, но включены, чтобы читатели могли видеть используемые файлы сертификатов)

{
    "version": 2,
    "controller": {},
    "workers": [
        {
            "type": "router",
            "realms": [
                {
                    "name": "test",
                    "roles": [
                        {
                            "name": "web",
                            "authorizer": "test.utils.permissions",
                            "disclose": {
                                "caller": true,
                                "publisher": true
                            }
                        },
                        {
                            "name": "no",
                            "permissions": []
                        }
                    ]
                }
            ],
            "transports": [
                {
                    "type": "websocket",
                    "endpoint": {
                        "type": "tcp",
                        "port": 9001,
                        "interface": "127.0.0.1"
                    },
                    "auth": {
                        "wampcra": {
                            "type": "static",
                            "users": {
                                "authenticator": {
                                    "secret": "authenticator-REDACTED",
                                    "role": "authenticator"
                                }
                            }
                        }
                    }
                },
                {
                    "type": "web",
                    "endpoint": {
                        "type": "tcp",
                        "port": 8089,
                        "tls": {
                            "key": "../ssl/key.pem",
                            "certificate": "../ssl/cert.pem",
                            "ca_certificates": [
                                "../ssl/gandi.pem", // https://www.gandi.net/static/CAs/GandiProSSLCA2.pem
                                "../ssl/gandi-cross-signed.pem" // https://wiki.gandi.net/en/ssl/intermediate#comodo_cross-signed_certificate
                            ],
                            "dhparam": "../ssl/dhparam.pem"
                        }
                    },
                    "paths": {
                        "/": {
                            "type": "static",
                            "directory": "../web"
                        },
                        "ws": {
                            "type": "websocket",
                            "url": "wss://OUR-DOMAIN.com:8089/ws",
                            "auth": {
                                "wampcra": {
                                    "type": "dynamic",
                                    "authenticator": "test.utils.authenticate"
                                }
                            }
                        }
                    }
                }
            ]
        },
        {
            "type": "guest",
            "executable": "/usr/bin/env",
            "arguments": [
                "php",
                "../test.php",
                "ws://127.0.0.1:9001",
                "test",
                "authenticator",
                "authenticator-REDACTED"
            ]
        }
    ]
}

Есть и другие вопросы, которые касаются вопросов, подобных этому.

  • Эта проблема связана с тем фактом, что любая ошибка TLS завершает WSS-соединение без полезной ошибки.
  • Это касается конкретно отмены рукопожатия, но в их случае это была неправильно настроенная библиотека, используемая при компиляции, которая в данном случае не актуальна, так как Crossbar был установлен из apt-get

1 ответ

Это не проблема с Crossbar. Это похоже на проблему с клиентом WAMP - Thruway. Davidwdan является владельцем репо Thruway Github и говорит:

"Транспортный провайдер Thruway Ratchet напрямую не поддерживает SSL. Вам нужно будет установить какой-то прокси-сервер перед ним".

Вы можете найти больше информации о том, что Davidwdan и другие могут сказать по этому поводу прямо здесь, https://github.com/voryx/Thruway/issues/163.

Теперь перейдем к решению. Обратите внимание, что это только для пользователей Apache. Если вы работаете на Nginx, идея почти такая же.

Несколько вещей, чтобы отметить, прежде чем мы начнем.

  1. Следуйте инструкциям Crossbar для установки! Не пытайтесь сделать это самостоятельно! Существует больше настроек Crossbar, чем кажется на первый взгляд. Прекрасные люди в Crossbar выложили подробные инструкции только для вас! https://crossbar.io/docs/Installation/.
  2. Для этого примера у меня есть Crossbar и Apache, работающие на одной машине. Хотя это не является обязательным требованием и не имеет значения!

Первое, что вы хотите сделать, это создать новый виртуальный хост. Я выбрал порт 4043 для этого виртуального хоста, но вы можете выбрать все, что захотите. Этот виртуальный хост будет использоваться для каждой библиотеки WAMP, в которой нет проблем с подключением через wss:// (с SSL). Вот полный список клиентов WAMP: http://wamp-proto.org/implementations/. Убедитесь, что директива ProxyPass и директива ProxyPassReverse имеют IP-адрес, указывающий на компьютер, на котором существует маршрутизатор CROSSBAR. В моем случае, поскольку Apache и Crossbar работают на одной машине, я просто использую 127.0.0.1. Также убедитесь, что порт, используемый в директиве ProxyPass и директиве ProxyPassReverse, точно такой же, как порт, который вы определили в вашем.crossbar/config.json! Вам также потребуется SSL-сертификат, настроенный на этом виртуальном хосте, который, как вы можете видеть, добавлен ниже директив Proxy.

Listen 4043

<VirtualHost *:4043>
ServerName example.org
ProxyRequests off
SSLProxyEngine on
ProxyPass /ws/ ws://127.0.0.1:8000/
ProxyPassReverse /ws/ ws://127.0.0.1:8000/

## Custom fragment
SSLEngine on
SSLCertificateFile /path/to/server_cert.pem
SSLCertificateKeyFile /path/to/server_key.pem
SSLCertificateChainFile /path/to/server_ca.pem
</VirtualHost>

Затем убедитесь, что ваш маршрутизатор Crossbar НЕ настроен с использованием SSL! Это супер важно. Thruway или любая другая библиотека, которая НЕ МОЖЕТ подключиться через SSL, НЕ сможет использовать маршрутизатор, если он настроен на использование SSL! Ниже приведен рабочий файл Crossbar config.json, который вы сможете использовать.

{
 "version": 2,
 "controller": {},
 "workers": [
 {
  "type": "router",
  "realms": [
    {
      "name": "production_realm",
      "roles": [
        {
          "name": "production_role",
          "permissions": [
            {
              "uri": "",
              "match": "prefix",
              "allow": {
                "call": true,
                "register": true,
                "publish": true,
                "subscribe": true
              }
            }
          ]
        }
      ]
    }
  ],
  "transports": [
    {
      "type": "websocket",
      "endpoint": {
        "type": "tcp",
        "port": 8000
        },
        "options": {
            "allowed_origins": ["http://*","https://*"]
      },
      "auth": {
        "ticket": {
          "type": "static",
          "principals": {
            "production_user": {
              "ticket": "tSjlwueuireladgte",
              "role": "production_role"
            }
          }
        }
      }
    }
   ]
  }
 ]
}  

Обратите внимание, что номер порта, определенный выше, совпадает с номером порта, определенным на виртуальном хосте.

./crossbar/config.json:

"endpoint": {
        "type": "tcp",
        "port": 8000
        },

виртуальный хост:

ProxyPass /ws/ ws://127.0.0.1:8000/
ProxyPassReverse /ws/ ws://127.0.0.1:8000/

Кроме того, если вы читаете другие учебные пособия, некоторые люди скажут вам, что вы обязательно должны использовать директиву ProxyPreserveHost в своем файле виртуального хоста. НЕ СЛУШАЙТЕ ИХ! Это даст много неожиданных результатов. Когда эта директива включена, эта опция будет передавать строку Host: из входящего запроса на проксируемый хост вместо имени хоста, указанного в строке ProxyPass! Даже Apache говорит держаться подальше от этой директивы https://httpd.apache.org/docs/2.4/mod/mod_proxy.html. Если он у вас включен, вы получите ошибку, подобную приведенной ниже:

failing WebSocket opening handshake ('missing port in HTTP Host header 
'example.org' and server runs on non-standard port 8000 (wss = 
False)')

И последнее, но не менее важное: убедитесь, что все следующие библиотеки Apache установлены и включены. В последних установках Apache все следующие библиотеки устанавливаются по умолчанию и их необходимо включить:

$ sudo a2enmod proxy
$ sudo a2enmod proxy_http
$ sudo a2enmod proxy_balancer
$ sudo a2enmod lbmethod_byrequests
$ sudo a2enmod proxy_wstunnel

Убедитесь, что вы открываете тот порт, который прослушивает файл виртуального хоста, и порт, который прослушивает ваш маршрутизатор. В моем случае:

$ sudo ufw allow 4043
$ sudo ufw allow 8000

И, наконец, перезапустите Apache, чтобы все ваши изменения вступили в силу.

$ sudo service apache2 restart

И последнее, но не менее важное: я хочу дать краткое объяснение того, почему все это должно быть сделано:

  1. Если на вашем сервере настроен SSL-сертификат, браузер выдаст ошибку при попытке подключения к любому маршрутизатору WAMP без использования wss://.
  2. Обычно решением этой проблемы является настройка маршрутизатора WAMP на использование сертификата SSL, который уже настроен на вашем сервере.
  3. Единственная проблема заключается в том, что Thruway.php (единственный известный мне php-клиент, который работает с WAMP) не очень хорошо работает с wss://. Даже создатели Thruway.php на GitHub говорят, что это не работает.
  4. Решением этой проблемы является использование обратного прокси-сервера.
  5. Сначала вам нужно настроить маршрутизатор WAMP и убедиться, что он не использует сертификат SSL.
  6. Затем вам нужно настроить обратный прокси, чтобы запросы wss: // были преобразованы в ws://. Это позволит вашему браузеру подключаться к маршрутизатору WAMP без жалоб.
  7. Поскольку маршрутизатор WAMP не настроен на использование SSL, Thruway.php также будет работать нормально!

И хорошо.... Это все люди! Я знаю, что мне нужно было дать подробный ответ на этот вопрос, потому что мне потребовалось 5 дней, чтобы понять все это!

Ответ @Tay-Bae был уже очень полезен. Но это не сработало для меня, клиент получал ответ 200 OK. Все, что мне нужно сделать, это перенаправить трафик WSS на мой внутренний клиент WS, который не поддерживает WSS (Thruway). Просмотрев форумы, я наткнулся на ответ: https://serverfault.com/a/846936. Они добавляют часть для перезаписи, которая, по-видимому, необходима для перенаправления запроса. Я думал, что ProxyPassReverse должен сделать это, но это не так. Итак, вот мой рабочий конфиг:

Listen 4043
<VirtualHost *:4043>
        ServerName mydomain.net
        ProxyRequests off
        SSLProxyEngine on
        ProxyPass /ws/ ws://127.0.0.1:8080/
        ProxyPassReverse /ws/ ws://127.0.0.1:8080/

        ## Custom fragment
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/mydomaine.net/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/mydomain.net/privkey.pem
        SSLCertificateChainFile /etc/letsencrypt/live/mydomain.net/chain.pem

        <IfModule mod_rewrite.c>
                RewriteEngine on
                RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
                RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
                RewriteRule .* ws://localhost:8080%{REQUEST_URI} [P]
        </IfModule>

        LogLevel debug
        ErrorLog ${APACHE_LOG_DIR}/error_thruway.log
        CustomLog ${APACHE_LOG_DIR}/access_thruway.log combined
</VirtualHost>
Другие вопросы по тегам