Кажется, что в настоящее время нет способа достичь этого с помощью входа GKE L7.Но я успешно развернул NGINX Ingress Controller .У Google есть неплохое руководство по развертыванию одного здесь .
. Это устанавливает балансировщик нагрузки TCP L4 без проверок работоспособности сервисов, оставляя NGINX для обработки завершения и маршрутизации L7.Это дает вам гораздо больше гибкости, но дьявол кроется в деталях, а детали не так легко найти.Большая часть того, что я обнаружил, была изучена при обходе проблем с github.
Мне удалось добиться того, чтобы NGINX обрабатывал завершение TLS и по-прежнему передавал сертификат на сервер, так что вы можете обрабатывать такие вещи, каккак аутентификация пользователя через CN или проверка серийного сертификата по CRL.
Ниже указан мой входной файлАннотации - это минимум, необходимый для аутентификации mTLS, и они по-прежнему имеют доступ к сертификату на внутреннем сервере.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: grpc-ingress
namespace: master
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
nginx.ingress.kubernetes.io/auth-tls-secret: "master/auth-tls-chain"
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "2"
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
nginx.ingress.kubernetes.io/backend-protocol: "GRPCS"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/grpc-backend: "true"
spec:
tls:
- hosts:
- grpc.example.com
secretName: auth-tls-chain
rules:
- host: grpc.example.com
http:
paths:
- path: /grpc.AwesomeService
backend:
serviceName: awesome-srv
servicePort: 9999
- path: /grpc.FantasticService
backend:
serviceName: fantastic-srv
servicePort: 9999
Несколько замечаний:
-
auth-ls-chain
секрет содержит 3 файла.ca.crt
является цепочкой сертификатов и должна включать любые промежуточные сертификаты.tls.crt
содержит сертификат вашего сервера, а tls.key
содержит ваш закрытый ключ. - Если этот секрет находится в пространстве имен, которое отличается от входа NGINX, то вы должны указать полный путь в аннотации.
- Моя глубина проверки равна 2, но это потому, что я использую промежуточные сертификаты.Если вы используете самозаверяющий текст, вам понадобится только глубина 1.
backend-protocol: "GRPCS"
требуется, чтобы NGINX не завершил TLS.Если вы хотите, чтобы NGINX прерывал TLS и запускал ваши службы без шифрования, используйте GRPC
в качестве протокола. grpc-backend: "true"
требуется, чтобы NGINX знал, что для HTTP-запросов бэкэнда используется HTTP2. - Вы можете перечислить несколько путей и напрямую к нескольким службам.В отличие от входа GKE, эти пути не должны иметь косую черту или суффикс звездочки.
Самое приятное то, что если у вас несколько пространств имен или вы также используете службу REST (например,gRPC Gateway), NGINX будет использовать тот же балансировщик нагрузки.Это обеспечивает некоторую экономию по сравнению со входом GKE, при котором для каждого входа используется отдельный LB.
Выше приведено из основного пространства имен, а ниже - вход REST из промежуточного пространства имен.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
namespace: staging
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- api-stage.example.com
secretName: letsencrypt-staging
rules:
- host: api-stage.example.com
http:
paths:
- path: /awesome
backend:
serviceName: awesom-srv
servicePort: 8080
- path: /fantastic
backend:
serviceName: fantastic-srv
servicePort: 8080
Для HTTP я использую LetsEncrypt, но есть много информации о том, как его настроить.
Если вы выполните модуль ingress-nginx
, вы сможетечтобы увидеть, как был настроен NGINX:
...
server {
server_name grpc.example.com ;
listen 80;
set $proxy_upstream_name "-";
set $pass_access_scheme $scheme;
set $pass_server_port $server_port;
set $best_http_host $http_host;
set $pass_port $pass_server_port;
listen 442 proxy_protocol ssl http2;
# PEM sha: 142600b0866df5ed9b8a363294b5fd2490c8619d
ssl_certificate /etc/ingress-controller/ssl/default-fake-certificate.pem;
ssl_certificate_key /etc/ingress-controller/ssl/default-fake-certificate.pem;
ssl_certificate_by_lua_block {
certificate.call()
}
# PEM sha: 142600b0866df5ed9b8a363294b5fd2490c8619d
ssl_client_certificate /etc/ingress-controller/ssl/master-auth-tls-chain.pem;
ssl_verify_client on;
ssl_verify_depth 2;
error_page 495 496 = https://help.example.com/auth;
location /grpc.AwesomeService {
set $namespace "master";
set $ingress_name "grpc-ingress";
set $service_name "awesome-srv";
set $service_port "9999";
set $location_path "/grpc.AwesomeServices";
rewrite_by_lua_block {
lua_ingress.rewrite({
force_ssl_redirect = true,
use_port_in_redirects = false,
})
balancer.rewrite()
plugins.run()
}
header_filter_by_lua_block {
plugins.run()
}
body_filter_by_lua_block {
}
log_by_lua_block {
balancer.log()
monitor.call()
plugins.run()
}
if ($scheme = https) {
more_set_headers "Strict-Transport-Security: max-age=15724800; includeSubDomains";
}
port_in_redirect off;
set $proxy_upstream_name "master-analytics-srv-9999";
set $proxy_host $proxy_upstream_name;
client_max_body_size 1m;
grpc_set_header Host $best_http_host;
# Pass the extracted client certificate to the backend
grpc_set_header ssl-client-cert $ssl_client_escaped_cert;
grpc_set_header ssl-client-verify $ssl_client_verify;
grpc_set_header ssl-client-subject-dn $ssl_client_s_dn;
grpc_set_header ssl-client-issuer-dn $ssl_client_i_dn;
# Allow websocket connections
grpc_set_header Upgrade $http_upgrade;
grpc_set_header Connection $connection_upgrade;
grpc_set_header X-Request-ID $req_id;
grpc_set_header X-Real-IP $the_real_ip;
grpc_set_header X-Forwarded-For $the_real_ip;
grpc_set_header X-Forwarded-Host $best_http_host;
grpc_set_header X-Forwarded-Port $pass_port;
grpc_set_header X-Forwarded-Proto $pass_access_scheme;
grpc_set_header X-Original-URI $request_uri;
grpc_set_header X-Scheme $pass_access_scheme;
# Pass the original X-Forwarded-For
grpc_set_header X-Original-Forwarded-For $http_x_forwarded_for;
# mitigate HTTPoxy Vulnerability
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
grpc_set_header Proxy "";
# Custom headers to proxied server
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_buffer_size 4k;
proxy_buffers 4 4k;
proxy_request_buffering on;
proxy_http_version 1.1;
proxy_cookie_domain off;
proxy_cookie_path off;
# In case of errors try the next upstream server before returning an error
proxy_next_upstream error timeout;
proxy_next_upstream_tries 3;
grpc_pass grpcs://upstream_balancer;
proxy_redirect off;
}
location /grpc.FantasticService {
set $namespace "master";
set $ingress_name "grpc-ingress";
set $service_name "fantastic-srv";
set $service_port "9999";
set $location_path "/grpc.FantasticService";
...
Это просто фрагмент сгенерированного nginx.conf
.Но вы должны увидеть, как одна конфигурация может обрабатывать несколько сервисов в нескольких пространствах имен.
Последний фрагмент - фрагмент кода о том, как мы получаем сертификат через контекст.Как видно из конфигурации выше, NGINX добавляет аутентифицированный сертификат и другие детали в метаданные gRPC.
meta, ok := metadata.FromIncomingContext(*ctx)
if !ok {
return status.Error(codes.Unauthenticated, "missing metadata")
}
// Check if SSL has been handled upstream
if len(meta.Get("ssl-client-verify")) == 1 && meta.Get("ssl-client-verify")[0] == "SUCCESS" {
if len(meta.Get("ssl-client-cert")) > 0 {
certPEM, err := url.QueryUnescape(meta.Get("ssl-client-cert")[0])
if err != nil {
return status.Errorf(codes.Unauthenticated, "bad or corrupt certificate")
}
block, _ := pem.Decode([]byte(certPEM))
if block == nil {
return status.Error(codes.Unauthenticated, "failed to parse certificate PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return status.Error(codes.Unauthenticated, "failed to parse certificate PEM")
}
return authUserFromCertificate(ctx, cert)
}
}
// if fallen through, then try to authenticate via the peer object for gRPCS,
// or via a JWT in the metadata for gRPC Gateway.