gPRC за goproxy возвращает ошибку сертификата, отлично работает без прокси - PullRequest
3 голосов
/ 03 июля 2019

У меня есть клиент и сервер gRPC, оба защищены сертификатом ssl.Без промежуточного прокси эти работают отлично.В качестве теста, когда я намеренно создаю ошибочные сертификаты, он терпит неудачу.Доказательство позже в этом посте - это не проблема с сертификатом.

код сервера gRPC:

// Creates a new gRPC server
// Create the TLS credentials
creds, err := credentials.NewServerTLSFromFile("configs/cert/servercert.pem", "configs/cert/serverkey.pem")
if err != nil {
    log.Fatalf("could not load TLS keys: %s", err)
}
// Create an array of gRPC options with the credentials
opts := []grpc.ServerOption{grpc.Creds(creds)}
// create a gRPC server object
s := grpc.NewServer(opts...)

код клиента gRPC:

// Create the client TLS credentials
creds, err := credentials.NewClientTLSFromFile("configs/cert/servercert.pem", "")
if err != nil {
    log.Fatalf("could not load tls cert: %s", err)
}

conn, err := grpc.Dial(grpcUri, grpc.WithTransportCredentials(creds))
if err != nil {
    log.Fatalf("Unable to connect: %v", err)
}

сейчас я пытаюсь использоватьпрямой прокси (который был протестирован и отлично работает на обычных HTTP-запросах API).Однако он постоянно завершает работу при запросах gRPC через прокси.

Я использую cuttle , который внутренне использует goproxy со следующей настройкой.Обратите внимание, что логический InsecureSkipVerify был опробован как true, так и false.С моим (ограниченным) пониманием SSL, это должно быть false, так как он будет проверять сертификат на наличие сертификата в сети, и они самозаверяющие, поэтому, естественно, он потерпит неудачу.Однако, опять же, я попытался true и false

// Config proxy.
proxy := goproxy.NewProxyHttpServer()
proxy.Tr = &http.Transport{
    // Config TLS cert verification.
    TLSClientConfig: &tls.Config{InsecureSkipVerify: !cfg.TLSVerify},
    Proxy:           http.ProxyFromEnvironment,
}

Запуск прокси между клиентом и сервером gRPC приводит к следующей ошибке:

transport: аутентификациясбой рукопожатия: x509: сертификат подписан неизвестным органом (возможно, из-за «x509: недопустимая подпись: родительский сертификат не может подписать этот тип сертификата» при попытке проверить сертификат кандидата в полномочия «тестовый сервер»

, которыйуказывает на то, что это проблема с сертификатом, однако gRPC безупречно работает без прокси-сервера, как указано и протестировано ранее.

также обратите внимание: я не хочу запускать gRPC за прокси-сервером, но вынужден работать из-за среды разработки.Сервер и прокси-сервер gRPC работают на одной и той же док-машине. Наличие одного и того же IP-адреса приведет к следующей конфигурации, которая просто уничтожит друг друга (поверьте мне, я все равно попробовал).

ENV http_proxy 192.168.99.100:3128
ENV https_proxy 192.168.99.100:3128
ENV no_proxy 192.168.99.100 # <- this would be the gRPC server IP, which is the same as the proxy. resulting in nothing being run through a proxy.

Разделение IP-адресав докере решил бы эту проблему, однако я бы ничего не узнал иВы хотели бы решить это.Я попытался настроить, как ответил здесь , чтобы установить разные внутренние IP-адреса докера, однако, ip останется пустым (будет установлена ​​только сеть), а доступ к новому IP будет просто тайм-аут.

1 Ответ

4 голосов
/ 03 июля 2019

Справочная информация:

Каждому концу соединения TLS требуется предварительно установленное доверие.Большинство клиентов используют цепочку доверия системы при подключении к удаленному хосту (GeoTrust, все доверенные сертификаты DigiCert CA перечислены здесь и позволяют безопасно заходить на сайты, такие как https://facebook.com, https://google.com и т. Д.)

go, при использовании TLS, при обращении к серверам по умолчанию используется цепочка доверия системы.При разработке пользовательских решений есть вероятность, что общедоступный сертификат вашего сервера приложений будет , а не в этой цепочке доверия системы.Таким образом, у вас есть два варианта:

  • Отключить доверие с помощью InsecureSkipVerify: true ( НЕ сделать это!)
  • добавить пользовательское доверие своему клиенту

Скорее всего, у вашего сервера приложений есть самозаверяющий сертификат, поэтому просто получить часть публичного сертификата этого.Вы также можете увидеть публичные сертификаты сервера, используя такие инструменты, как openssl - используя связанное решение, вы можете получить публичные сертификаты не только для своих собственных серверов разработки, но и для любой другой удаленной службы - просто укажите имя хоста и порт.


Итак, просто, чтобы подвести итог вашей ситуации.У вас есть:

Client <- TLS -> Server

Но вы хотите:

Client <-TLS-> Proxy <-TLS-> Server

Так что теперь вашему клиенту, вместо того, чтобы доверять Серверу, теперь нужно просто доверять прокси-серверу - поскольку он только говорит напрямуюна прокси.Прокси, скорее всего, будет иметь самоподписанный сертификат (см. Выше о том, как извлечь доверительный сертификат).Получив это, обновите код go, чтобы использовать этот пользовательский файл доверия, например:

// Get the SystemCertPool, continue with an empty pool on error
rootCAs, err := x509.SystemCertPool() // <- probably not needed, if we're only ever talking to this single proxy
if err != nil || rootCAs == nil {
    rootCAs = x509.NewCertPool()
}

// Read in the custom trust file
certs, err := ioutil.ReadFile(localTrustFile)
if err != nil {
    log.Fatalf("Failed to append %q to RootCAs: %v", localTrustFile, err)
}

// Append our cert to the system pool
if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
    log.Fatalf("failed to append custom cert")
}

tlsConfig := &tls.Config{
    RootCAs: rootCAs,
}

Прокси-сервер также должен будет доверять серверу - так что если сертификат сервера отсутствует в системе-trust-chain, тогда потребуется аналогичная настройка tls.Config, как указано выше.

...