You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform配置Azure应用网关mTLS时出现400 SSL证书错误排查

Azure应用网关mTLS配置400错误排查

问题现象

请求应用网关时返回400错误:

<html>
<head><title>400 The SSL certificate error</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The SSL certificate error</center>
<hr><center>Microsoft-Azure-Application-Gateway/v2</center>
</body>
</html>
  • 网关日志仅显示连接尝试记录,无具体错误详情
  • 已通过openssl verify -CAfile ca.pem client.pem验证客户端证书与CA签名匹配,结果为OK

现有Terraform配置

根CA证书配置

resource "tls_private_key" "gateway_mtls_root_ca_private_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "tls_self_signed_cert" "gateway_mtls_root_ca" {
  private_key_pem = tls_private_key.gateway_mtls_root_ca_private_key.private_key_pem

  subject {
    common_name  = "root-ca"
    organization = "test"
  }

  validity_period_hours = 24 * 90 # 3 months for testing phase
  is_ca_certificate     = true

  allowed_uses = [
    "cert_signing",
    "crl_signing",
    "digital_signature"
  ]
}

客户端证书配置

resource "tls_private_key" "gateway_mtls_client_cert_private_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "tls_cert_request" "gateway_mtls_client_cert_request" {
  private_key_pem = tls_private_key.gateway_mtls_client_cert_private_key.private_key_pem

  subject {
    common_name  = "client"
    organization = "test"
  }
}

resource "tls_locally_signed_cert" "gateway_mtls_client_cert" {
  cert_request_pem   = tls_cert_request.gateway_mtls_client_cert_request.cert_request_pem
  ca_private_key_pem = tls_private_key.gateway_mtls_root_ca_private_key.private_key_pem
  ca_cert_pem        = tls_self_signed_cert.gateway_mtls_root_ca.cert_pem

  validity_period_hours = 24 * 30 # 1 month for testing

  allowed_uses = [
    "client_auth",
    "key_encipherment",
    "digital_signature",
  ]
}

应用网关配置

resource "azurerm_application_gateway" "container_gateway" {
  name                = "test-gateway"
  location            = var.resource_group_region
  resource_group_name = var.resource_group_name

  sku {
    name     = "Standard_v2"
    tier     = "Standard_v2"
    capacity = 2
  }

  identity {
    type = "UserAssigned"
    identity_ids = [
      azurerm_user_assigned_identity.gateway_identity.id
    ]
  }

  gateway_ip_configuration {
    name      = local.gateway_ip_config_name
    subnet_id = var.gateway_subnet_ids[0]
  }

  backend_address_pool {
    name         = local.gateway_backend_pool_name
    ip_addresses = var.container_group_ip_addresses
  }

  backend_http_settings {
    name                  = local.gateway_backend_settings
    cookie_based_affinity = "Disabled"
    port                  = 80
    protocol              = "Http"
    request_timeout       = 20
  }

  frontend_ip_configuration {
    name                 = local.gateway_ip_config_name
    public_ip_address_id = azurerm_public_ip.container_pip.id
  }


  frontend_port {
    name = local.gateway_frontend_https_port_name
    port = 443
  }

  http_listener {
    name                           = local.gateway_https_listener_name
    frontend_ip_configuration_name = local.gateway_ip_config_name
    frontend_port_name             = local.gateway_frontend_https_port_name
    protocol                       = "Https"
    ssl_certificate_name           = local.gateway_ssl_certificate_name
    ssl_profile_name               = local.gateway_ssl_profile_name
  }

  request_routing_rule {
    name               = local.gateway_https_path_based_rules_name
    rule_type          = "PathBasedRouting"
    http_listener_name = local.gateway_https_listener_name
    url_path_map_name  = local.gateway_url_path_map_name

    priority = 1000
  }

  url_path_map {
    name                               = local.gateway_url_path_map_name
    default_backend_address_pool_name  = local.gateway_backend_pool_name
    default_backend_http_settings_name = local.gateway_backend_settings

    path_rule {
      name                       = "test"
      paths                      = ["/*"]
      backend_address_pool_name  = local.gateway_backend_pool_name
      backend_http_settings_name = local.gateway_backend_settings
    }
  }

  ssl_certificate {
    name                = local.gateway_ssl_certificate_name
    key_vault_secret_id = azurerm_key_vault_certificate.gateway_server_certificate.secret_id
  }

  ssl_policy {
    policy_type = "Predefined"
    policy_name = "AppGwSslPolicy20220101"
  }

  trusted_client_certificate {
    name = local.gateway_trusted_client_certificate_name
    data = tls_self_signed_cert.gateway_mtls_root_ca.cert_pem
  }

  ssl_profile {
    name                                 = local.gateway_ssl_profile_name
    trusted_client_certificate_names     = [local.gateway_trusted_client_certificate_name]
    verify_client_certificate_revocation = "OCSP"

    ssl_policy {
      policy_type = "Predefined"
      policy_name = "AppGwSslPolicy20220101"
    }
  }
}

排查方向与解决方案

一、配置可能存在的问题

  1. OCSP验证配置冲突
    SSL profile中开启了verify_client_certificate_revocation = "OCSP",但测试用自签CA和客户端证书未配置OCSP端点,网关无法获取吊销状态会直接拒绝请求。建议暂时关闭OCSP验证,修改为:

    ssl_profile {
      name                                 = local.gateway_ssl_profile_name
      trusted_client_certificate_names     = [local.gateway_trusted_client_certificate_name]
      verify_client_certificate_revocation = "None"
    
      ssl_policy {
        policy_type = "Predefined"
        policy_name = "AppGwSslPolicy20220101"
      }
    }
    
  2. 根CA证书格式问题
    检查传入trusted_client_certificate的CA证书是否为纯PEM格式公钥证书,无多余内容(如私钥)。可通过openssl x509 -in ca.pem -text -noout确认格式正确性。

  3. 客户端证书扩展用法检查
    用openssl x509 -in client.pem -text -noout查看X509v3 Extended Key Usage字段,确认是否明确包含TLS Web Client Authentication。

  4. SSL策略兼容性
    AppGwSslPolicy20220101可能与客户端证书加密套件不兼容,可尝试切换为AppGwSslPolicy20170401S等兼容策略,或自定义策略包含客户端支持的套件。

  5. 前端IP配置重名
    gateway_ip_configuration和frontend_ip_configuration使用了相同的local.gateway_ip_config_name,可能导致网关内部配置冲突,建议给两者设置不同名称。

二、获取更详细的日志

  1. 启用应用网关诊断日志
    在Azure门户开启ApplicationGatewayAccessLog和ApplicationGatewayFirewallLog,发送至Log Analytics工作区,运行以下Kusto查询获取详细错误:

    AzureDiagnostics
    | where ResourceType == "APPLICATIONGATEWAYS" and OperationName == "ApplicationGatewayAccess"
    | where HttpStatus == 400
    | project TimeGenerated, ClientIP, RequestUri, SslErrorMessage, ServerRoutedIP
    

    SslErrorMessage字段会包含具体证书错误原因。

  2. 用openssl调试客户端请求
    执行以下命令获取SSL握手阶段详细信息:

    openssl s_client -connect <gateway-ip>:443 -cert client.pem -key client-key.pem -CAfile ca.pem -state -debug
    

    重点关注Verify return code及握手过程中的错误提示。


内容的提问来源于stack exchange,提问作者UoS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 07:45:58