为 ASP.NET Core Kestrel Web 服务器配置终结点

注意

此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

对于当前版本,请参阅此文的 .NET 8 版本

Kestrel 终结点提供用于侦听传入请求并将其路由到相应中间件的基础结构。 地址和协议的组合定义了终结点。

  • 地址指定服务器侦听传入请求的网络接口,例如 TCP 端口。
  • 协议指定客户端与服务器之间的通信,例如 HTTP/1.1、HTTP/2 或 HTTP/3。
  • 可以使用 https URL 方案或 UseHttps 方法来保护终结点。

可以使用 URL、appsettings.json 中的 JSON 和代码配置终结点。 本文讨论如何使用每个选项来配置终结点:

默认终结点

新 ASP.NET Core 项目配置为绑定到 5000-5300 之间的随机 HTTP 端口和 7000-7300 之间的随机 HTTPS 端口。 所选端口存储在生成的 Properties/launchSettings.json 文件中,开发人员可以对其进行修改。 launchSetting.json 文件仅用于本地开发。

如果没有终结点配置,则 Kestrel 绑定到 http://localhost:5000

配置终结点

Kestrel 终结点侦听传入连接。 创建终结点时,必须使用它将侦听的地址对其进行配置。 通常,这是一个 TCP 地址和端口号。

有多种选项可用于配置终结点:

使用 URL 配置终结点

以下部分介绍如何使用以下方式配置终结点:

  • ASPNETCORE_URLS 环境变量。
  • --urls 命令行参数。
  • urls 主机配置键。
  • UseUrls 扩展方法。
  • WebApplication.Urls 属性。

URL 格式

URL 指示服务器应侦听的端口和协议的 IP 或主机地址。 如果端口是协议的默认值(通常为 80 和 443),则可以省略该端口。 URL 可以采用以下任意格式。

  • 包含端口号的 IPv4 地址

    http://65.55.39.10:80/
    

    0.0.0.0 是一种绑定到所有 IPv4 地址的特殊情况。

  • 包含端口号的 IPv6 地址

    http://[0:0:0:0:0:ffff:4137:270a]:80/
    

    [::] 是 IPv4 0.0.0.0 的 IPv6 等效项。

  • 具有端口号的通配符主机

    http://contoso.com:80/
    http://*:80/
    

    任何未识别为有效 IP 地址或 localhost 的内容被视为绑定到所有 IPv4 和 IPv6 地址的通配符。 有些人喜欢使用 *+ 以便更加明确。 若要将不同主机名绑定到相同端口上的不同 ASP.NET Core 应用,请使用 HTTP.sys 或反向代理服务器。

    反向代理服务器示例包括 IIS、YARP、Nginx 和 Apache。

  • 包含端口号的主机名称 localhost 或包含端口号的环回 IP

    http://localhost:5000/
    http://127.0.0.1:5000/
    http://[::1]:5000/
    

    指定 localhost 后,Kestrel 将尝试绑定到 IPv4 和 IPv6 环回接口。 如果其他服务正在任一环回接口上使用请求的端口,则 Kestrel 将无法启动。 如果任一环回接口出于任何其他原因(通常是因为 IPv6 不受支持)而不可用,则 Kestrel 将记录一个警告。

可以使用分号 (;) 分隔符指定多个 URL 前缀:

http://*:5000;http://localhost:5001;https://hostname:5002

有关详细信息,请参阅重写配置

HTTPS URL 前缀

仅当 HTTPS 终结点配置中提供默认证书时,HTTPS URL 前缀才可用于定义终结点。 例如,使用 KestrelServerOptions 配置或配置文件,如本文稍后所示。

有关详细信息,请参阅配置 HTTPS

仅指定端口

应用和容器通常只能侦听一个端口(比如端口 80),且没有其他限制(比如主机或路径)。 HTTP_PORTS 和 HTTPS_PORTS 是为 Kestrel 和 HTTP.sys 服务器指定侦听端口的配置密钥。 可以将这些密钥指定为使用 DOTNET_ASPNETCORE_ 前缀定义的环境变量,也可以直接通过任何其他配置输入(例如 appsettings.json)指定这些密钥。 每个密钥都是一个以分号分隔的端口值列表,如下例所示:

ASPNETCORE_HTTP_PORTS=80;8080
ASPNETCORE_HTTPS_PORTS=443;8081

上例是以下配置的简写,它指定了方案(HTTP 或 HTTPS)和任何主机或 IP。

ASPNETCORE_URLS=http://*:80/;http://*:8080/;https://*:443/;https://*:8081/

HTTP_PORTS 和 HTTPS_PORTS 配置密钥的优先级较低,会被 URLS 或代码中直接提供的值覆盖。 证书仍需通过特定于服务器的 HTTPS 机制单独配置。

在 appsettings.json 中配置终结点

Kestrel 可以从 IConfiguration 实例加载终结点。 默认情况下,Kestrel 配置从 Kestrel 部分加载,终结点在 Kestrel:Endpoints 中配置:

{
  "Kestrel": {
    "Endpoints": {
      "MyHttpEndpoint": {
        "Url": "http://localhost:8080"
      }
    }
  }
}

上面的示例:

  • 使用 appsettings.json 作为配置源。 但是,可以使用任何 IConfiguration 源。
  • 在端口 8080 上添加名为 MyHttpEndpoint 的终结点。

有关使用 JSON 配置终结点的详细信息,请参阅本文后面的部分,其中讨论了在 appsettings.json 中配置 HTTPS配置 HTTP 协议

从配置重新加载终结点

默认情况下,“在配置源更改时重新加载终结点配置”处于启用状态。 可以使用 KestrelServerOptions.Configure(IConfiguration, Boolean) 来禁用它。

如果发出更改信号,请执行以下步骤:

  • 新配置与旧配置相比,不会修改任何没有配置更改的终结点。
  • 已删除或已修改的终结点将在 5 秒内完成处理请求并关闭。
  • 启动新的或已修改的终结点。

重启终结点时,连接到已修改的终结点的客户端可能会断开连接或被拒绝。

ConfigurationLoader

KestrelServerOptions.Configure 返回 KestrelConfigurationLoader。 加载程序的 Endpoint(String, Action<EndpointConfiguration>) 方法,可用于补充配置的终结点的设置:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    var kestrelSection = context.Configuration.GetSection("Kestrel");

    serverOptions.Configure(kestrelSection)
        .Endpoint("HTTPS", listenOptions =>
        {
            // ...
        });
});

可以直接访问 KestrelServerOptions.ConfigurationLoader 以继续迭代现有加载程序,例如由 WebApplicationBuilder.WebHost 提供的加载程序。

  • 每个终结点的配置节都可用于 Endpoint 方法中的选项,以便读取自定义设置。
  • 可以多次调用 KestrelServerOptions.Configure(IConfiguration),但除非在之前的实例上显式调用了 Load,否则只使用最新配置。 默认主机不会调用 Load,因此可能会替换它的默认配置部分。
  • KestrelConfigurationLoaderKestrelServerOptions 将 API 的 Listen 簇反射为 Endpoint 重载,因此可在同样的位置配置代码和配置终结点。 这些重载不使用名称,且只使用配置中的默认设置。

在代码中配置终结点

KestrelServerOptions 提供用于在代码中配置终结点的方法:

如果同时使用 ListenUseUrls API,Listen 终结点将覆盖 UseUrls 终结点。

绑定到 TCP 套接字

ListenListenLocalhostListenAnyIP 方法绑定到 TCP 套接字:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
});

上面的示例:

在 Windows 上,可以使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不受支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

在 macOS、Linux 和 Windows 上,可以使用 OpenSSL 创建证书。

绑定到 Unix 套接字

可通过 ListenUnixSocket 侦听 Unix 套接字以提高 Nginx 的性能,如以下示例所示:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock");
});
  • 在 Nginx 配置文件中,将 server>location>proxy_pass 条目设置为 http://unix:/tmp/{KESTREL SOCKET}:/;{KESTREL SOCKET} 是提供给 ListenUnixSocket 的套接字的名称(例如,上述示例中的 kestrel-test.sock)。
  • 确保套接字可由 Nginx (例如 chmod go+w /tmp/kestrel-test.sock)进行写入。

配置终结点默认值

ConfigureEndpointDefaults(Action<ListenOptions>) 指定为每个指定的终结点运行的配置。 多次调用 ConfigureEndpointDefaults 将替换以前的配置。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // ...
    });
});

注意

通过在调用 ConfigureEndpointDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

动态端口绑定

如果指定端口号 0,Kestrel 将动态绑定到可用端口。 以下示例演示如何确定 Kestrel 在运行时绑定到的端口:

app.Run(async (context) =>
{
    var serverAddressFeature = context.Features.Get<IServerAddressesFeature>();

    if (serverAddressFeature is not null)
    {
        var listenAddresses = string.Join(", ", serverAddressFeature.Addresses);

        // ...
    }
});

在某些情况下,动态绑定端口不可用:

配置 HTTPS

Kestrel 支持使用 HTTPS 保护终结点。 通过 HTTPS 发送的数据使用传输层安全性 (TLS) 进行加密,以提高在客户端和服务器之间传输的数据的安全性。

HTTPS 需要 TLS 证书。 TLS 证书存储在服务器上,并将 Kestrel 配置为使用该证书。 应用可以在本地开发环境中使用 ASP.NET Core HTTPS 开发证书。 开发证书未安装在非开发环境中。 在生产环境中,必须显式配置 TLS 证书。 至少必须提供默认证书。

配置 HTTPS 和 TLS 证书的方式取决于终结点的配置方式:

在 appsettings.json 中配置 HTTPS

Kestrel 可以使用默认 HTTPS 应用设置配置架构。 从磁盘上的文件或从证书存储中配置多个终结点,包括要使用的 URL 和证书。

任何未指定证书的 HTTPS 终结点(下例中的 HttpsDefaultCert)会回退至在 Certificates:Default 下定义的证书或开发证书。

以下示例适用于 appsettings.json,但可以使用任何配置源:

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertAndKeyFile": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Path": "<path to .pem/.crt file>",
          "KeyPath": "<path to .key file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertStore": {
        "Url": "https://localhost:5003",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },
      "HttpsDefaultCert": {
        "Url": "https://localhost:5004"
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

架构的注意事项

  • 终结点的名称不区分大小写。 例如,由于再也无法解析标识符“Families”,因此 HTTPS and Https 是等效的。
  • 每个终结点都要具备 Url 参数。 此参数的格式和顶层 Urls 配置参数一样,只不过它只能有单个值。 请参阅本文前面部分的 URL 格式
  • 这些终结点不会添加在顶层 Urls 配置中定义的终结点,而是替换它们。 通过 Listen 在代码中定义的终结点与在配置节中定义的终结点相累积。
  • Certificate 部分是可选的。 如果未指定 Certificate 部分,则使用 Certificates:Default 中定义的默认值。 如果没有可用的默认值,则使用开发证书。 如果没有默认值,且开发证书不存在,则服务器将引发异常,并且无法启动。
  • Certificate 部分支持多个证书源。
  • 只要不会导致端口冲突,就能在Configuration中定义任何数量的终结点。

证书源

可以将证书节点配置为从多个源加载证书:

  • PathPassword 用于加载 .pfx 文件。
  • PathKeyPathPassword 用于加载 .pem/.crt 和 .key 文件。
  • SubjectStore 用于从证书存储中加载。

例如,可将 Certificates:Default 证书指定为:

"Default": {
  "Subject": "<subject; required>",
  "Store": "<cert store; required>",
  "Location": "<location; defaults to CurrentUser>",
  "AllowInvalid": "<true or false; defaults to false>"
}

在 appsettings.json 中配置客户端证书

ClientCertificateMode 用于配置客户端证书行为。

{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "ClientCertificateMode": "AllowCertificate",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

默认值为 ClientCertificateMode.NoCertificate,其中 Kestrel 不会从客户端请求或要求证书。

有关详细信息,请参阅在 ASP.NET Core 中配置证书身份验证

在 appsettings.json 中配置 SSL/TLS 协议

SSL 协议是用于在两个对等机(传统上是客户端和服务器)之间加密和解密流量的协议。

{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "SslProtocols": ["Tls12", "Tls13"],
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

默认值 SslProtocols.None 会导致 Kestrel 使用操作系统默认值来选择最佳协议。 除非你有特定原因要选择协议,否则请使用默认值。

在代码中配置 HTTPS

使用 Listen API 时,ListenOptions 上的 UseHttps 扩展方法可用于配置 HTTPS。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
});

ListenOptions.UseHttps 参数:

  • filename 是证书文件的路径和文件名,关联包含应用内容文件的目录。
  • password 是访问 X.509 证书数据所需的密码。
  • configureOptions 是配置 HttpsConnectionAdapterOptionsAction。 返回 ListenOptions
  • storeName 是从中加载证书的证书存储。
  • subject 是证书的主题名称。
  • allowInvalid 指示是否存在需要留意的无效证书,例如自签名证书。
  • location 是从中加载证书的存储位置。
  • serverCertificate 是 X.509 证书。

有关 UseHttps 重载的完整列表,请参阅 UseHttps

在代码中配置客户端证书

ClientCertificateMode 配置客户端证书要求。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
    });
});

默认值为 NoCertificate,其中 Kestrel 不会从客户端请求或要求证书。

有关详细信息,请参阅在 ASP.NET Core 中配置证书身份验证

在代码中配置 HTTPS 默认值

ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions>) 指定要为每个 HTTPS 终结点运行的配置 Action。 多次调用 ConfigureHttpsDefaults,用最新指定的 Action 替换之前的 Action 实例。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // ...
    });
});

注意

通过在调用 ConfigureHttpsDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

在代码中配置 SSL/TLS 协议

SSL 协议是用于在两个对等机(传统上是客户端和服务器)之间加密和解密流量的协议。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.SslProtocols = SslProtocols.Tls13;
    });
});

在代码中配置 TLS 密码套件筛选器

在 Linux 上,CipherSuitesPolicy 可用于针对每个连接筛选 TLS 握手:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.OnAuthenticate = (context, sslOptions) =>
        {
            sslOptions.CipherSuitesPolicy = new CipherSuitesPolicy(
                new[]
                {
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                    // ...
                });
        };
    });
});

配置服务器名称指示

服务器名称指示 (SNI) 可用于承载相同 IP 地址和端口上的多个域。 SNI 可用于通过从一台服务器为多个站点提供服务来节省资源。

为了运行 SNI,客户端在 TLS 握手过程中将进行安全会话的主机名发送至服务器,从而让服务器可以提供正确的证书。 在 TLS 握手后的安全会话期间,客户端将服务器提供的证书用于与服务器进行加密通信。

所有网站必须在相同的 Kestrel 实例上运行。 Kestrel 在无反向代理时不支持跨多个实例共享一个 IP 地址和端口。

可用两种方式配置 SNI:

  • 配置中配置主机名和 HTTPS 选项之间的映射。 例如,appsettings.json 文件中的 JSON。
  • 在代码中创建终结点,并通过 ServerCertificateSelector 回调使用主机名选择证书。

在 appsettings.json 中配置 SNI

Kestrel 支持配置中定义的 SNI。 可以使用包含主机名和 HTTPS 选项之间的映射的 Sni 对象来配置终结点。 连接主机名与选项匹配,并且这些选项用于该连接。

以下配置将添加一个名为 MySniEndpoint 的终结点,该终结点使用 SNI 基于主机名选择 HTTPS 选项:

{
  "Kestrel": {
    "Endpoints": {
      "MySniEndpoint": {
        "Url": "https://*",
        "SslProtocols": ["Tls11", "Tls12"],
        "Sni": {
          "a.example.org": {
            "Protocols": "Http1AndHttp2",
            "SslProtocols": ["Tls11", "Tls12", "Tls13"],
            "Certificate": {
              "Subject": "<subject; required>",
              "Store": "<certificate store; required>",
            },
            "ClientCertificateMode" : "NoCertificate"
          },
          "*.example.org": {
            "Certificate": {
              "Path": "<path to .pfx file>",
              "Password": "$CREDENTIAL_PLACEHOLDER$"
            }
          },
          "*": {
            // At least one subproperty needs to exist per SNI section or it
            // cannot be discovered via IConfiguration
            "Protocols": "Http1",
          }
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

可由 SNI 覆盖的 HTTPS 选项:

主机名支持通配符匹配:

  • 完全匹配。 例如,a.example.org 匹配 a.example.org
  • 通配符前缀。 如果有多个通配符匹配项,则选择最长的模式。 例如,*.example.org 匹配 b.example.orgc.example.org
  • 完整通配符。 * 匹配其他所有内容,包括不使用 SNI 且不发送主机名的客户端。

匹配的 SNI 配置将应用于连接的终结点,并重写终结点上的值。 如果连接与已配置的 SNI 主机名不匹配,则连接将被拒绝。

使用代码配置 SNI

Kestrel 支持具有多个回调 API 的 SNI:

  • ServerCertificateSelector
  • ServerOptionsSelectionCallback
  • TlsHandshakeCallbackOptions

具有 ServerCertificateSelector 的 SNI

Kestrel 通过 ServerCertificateSelector 回调支持 SNI。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var subExampleCert = CertificateLoader.LoadFromStoreCert(
                "sub.example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var certs = new Dictionary<string, X509Certificate2>(
                StringComparer.OrdinalIgnoreCase)
            {
                ["localhost"] = localhostCert,
                ["example.com"] = exampleCert,
                ["sub.example.com"] = subExampleCert
            };

            httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
            {
                if (name is not null && certs.TryGetValue(name, out var cert))
                {
                    return cert;
                }

                return exampleCert;
            };
        });
    });
});

具有 ServerOptionsSelectionCallback 的 SNI

Kestrel 通过 ServerOptionsSelectionCallback 回调支持其他动态 TLS 配置。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书和 TLS 配置。 默认证书和 ConfigureHttpsDefaults 不与此回调一起使用。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);

            listenOptions.UseHttps((stream, clientHelloInfo, state, cancellationToken) =>
            {
                if (string.Equals(clientHelloInfo.ServerName, "localhost",
                    StringComparison.OrdinalIgnoreCase))
                {
                    return new ValueTask<SslServerAuthenticationOptions>(
                        new SslServerAuthenticationOptions
                        {
                            ServerCertificate = localhostCert,
                            // Different TLS requirements for this host
                            ClientCertificateRequired = true
                        });
                }

                return new ValueTask<SslServerAuthenticationOptions>(
                    new SslServerAuthenticationOptions
                    {
                        ServerCertificate = exampleCert
                    });
            }, state: null!);
        });
    });
});

具有 TlsHandshakeCallbackOptions 的 SNI

Kestrel 通过 TlsHandshakeCallbackOptions.OnConnection 回调支持其他动态 TLS 配置。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书、TLS 配置和其他服务器选项。 默认证书和 ConfigureHttpsDefaults 不与此回调一起使用。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);

            listenOptions.UseHttps(new TlsHandshakeCallbackOptions
            {
                OnConnection = context =>
                {
                    if (string.Equals(context.ClientHelloInfo.ServerName, "localhost",
                        StringComparison.OrdinalIgnoreCase))
                    {
                        // Different TLS requirements for this host
                        context.AllowDelayedClientCertificateNegotation = true;

                        return new ValueTask<SslServerAuthenticationOptions>(
                            new SslServerAuthenticationOptions
                            {
                                ServerCertificate = localhostCert
                            });
                    }

                    return new ValueTask<SslServerAuthenticationOptions>(
                        new SslServerAuthenticationOptions
                        {
                            ServerCertificate = exampleCert
                        });
                }
            });
        });
    });
});

配置 HTTP 协议

Kestrel 支持所有常用的 HTTP 版本。 可以使用 HttpProtocols 枚举(指定可用的 HTTP 版本选项)将终结点配置为支持不同的 HTTP 版本。

需要 TLS 才能支持多个 HTTP 版本。 当终结点支持多个协议时,TLS 应用程序层协议协商 (ALPN) 握手可用于协商客户端与服务器之间的连接协议。

HttpProtocols 允许的连接协议
Http1 仅 HTTP/1.1。 可以在具有 TLS 或没有 TLS 的情况下使用。
Http2 仅 HTTP/2。 仅当客户端支持先验知识模式时,才可以在没有 TLS 的情况下使用。
Http3 仅 HTTP/3。 需要 TLS。 可能需要将客户端配置为仅使用 HTTP/3。
Http1AndHttp2 HTTP/1.1 和 HTTP/2。 HTTP/2 要求客户端在 TLS 应用层协议协商 (ALPN) 握手过程中选择 HTTP/2;否则,连接默认为 HTTP/1.1。
Http1AndHttp2AndHttp3 HTTP/1.1、HTTP/2 和 HTTP/3。 第一个客户端请求通常使用 HTTP/1.1 或 HTTP/2,alt-svc 响应头会提示客户端升级到 HTTP/3。 HTTP/2 和 HTTP/3 需要 TLS;否则,连接默认为 HTTP/1.1。

终结点的默认协议值为 HttpProtocols.Http1AndHttp2

HTTP/2 的 TLS 限制:

  • TLS 版本 1.2 或更高版本
  • 重新协商已禁用
  • 压缩已禁用
  • 最小的临时密钥交换大小:
    • 椭圆曲线 Diffie-Hellman (ECDHE) [RFC4492]:最小 224 位
    • 有限字段 Diffie-Hellman (DHE) [TLS12]:最小 2048 位
  • 不禁止密码套件。

默认情况下,支持具有 P-256 椭圆曲线 [FIPS186] 的 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [TLS-ECDHE]。

在 appsettings.json 中配置 HTTP 协议

以下 appsettings.json 示例将为所有指定终结点建立 HTTP/1.1 连接协议:

{
  "Kestrel": {
    "Endpoints": {
      "HttpsDefaultCert": {
        "Url": "https://localhost:5001",
        "Protocols": "Http1"
      }
    }
  }
}

可以在 Kestrel:EndpointDefaults 节中配置默认协议。 以下 appsettings.json 示例将 HTTP/1.1 建立为所有终结点的默认连接协议:

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1"
    }
  }
}

代码中指定的协议覆盖了由配置设置的值。

在代码中配置 HTTP 协议

ListenOptions.Protocols 用于指定具有 HttpProtocols 枚举的协议。

以下示例为端口 8000 上的 HTTP/1.1、HTTP/2 和 HTTP/3 连接配置终结点。 TLS 使用提供的证书来保护连接:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
    });
});

请参阅

ASP.NET Core 项目被配置为绑定到 5000-5300 之间的随机 HTTP 端口和 7000-7300 之间的随机 HTTPS 端口。 这个默认配置是在生成的 Properties/launchSettings.json 文件中指定的,可以被重写。 如果没有指定端口,Kestrel 将绑定到 http://localhost:5000

使用以下内容指定 URL:

  • ASPNETCORE_URLS 环境变量。
  • --urls 命令行参数。
  • urls 主机配置键。
  • UseUrls 扩展方法。

采用这些方法提供的值可以是一个或多个 HTTP 和 HTTPS 终结点(如果默认证书可用,则为 HTTPS)。 将值配置为以分号分隔的列表(例如 "Urls": "http://localhost:8000;http://localhost:8001")。

有关这些方法的详细信息,请参阅服务器 URL重写配置

关于开发证书的创建:

开发证书仅适用于生成证书的用户。 某些浏览器需要授予显式权限才能信任本地开发证书。

项目模板将应用配置为默认情况下在 HTTPS 上运行,并包括 HTTPS 重定向和 HSTS 支持

调用 KestrelServerOptions 上的 ListenListenUnixSocket 方法以配置 URL 前缀和 Kestrel 的端口。

UseUrls--urls 命令行参数、urls 主机配置键以及 ASPNETCORE_URLS 环境变量也有用,但具有本节后面注明的限制(必须要有可用于 HTTPS 终结点配置的默认证书)。

KestrelServerOptions 配置:

ConfigureEndpointDefaults

ConfigureEndpointDefaults(Action<ListenOptions>) 指定一个为每个指定的终结点运行的配置 Action。 多次调用 ConfigureEndpointDefaults 会将之前的 Action 替换为最后指定的 Action

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // ...
    });
});

注意

通过在调用 ConfigureEndpointDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

Configure(IConfiguration)

允许 Kestrel 从 IConfiguration 中加载终结点。 配置必须针对 Kestrel 的配置节。 Configure(IConfiguration, bool) 重载可用于在配置源更改时启用重载终结点。

默认情况下,从 Kestrel 部分加载 Kestrel 配置并启用重载更改:

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "Https": {
        "Url": "https://localhost:5001"
      }
    }
  }
}

如果已启用重载配置并发出更改信号,则会执行以下步骤:

  • 新配置与旧配置相比,不会修改任何没有配置更改的终结点。
  • 已删除或已修改的终结点将在 5 秒内完成处理请求并关闭。
  • 启动新的或已修改的终结点。

重启终结点时,连接到已修改的终结点的客户端可能会断开连接或被拒绝。

ConfigureHttpsDefaults

ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions>) 指定一个为每个 HTTPS 终结点运行的配置 Action。 多次调用 ConfigureHttpsDefaults,用最新指定的 Action 替换之前的 Action

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // ...
    });
});

注意

通过在调用 ConfigureHttpsDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

ListenOptions.UseHttps

将 Kestrel 配置为使用 HTTPS。

ListenOptions.UseHttps 扩展:

  • UseHttps:将 Kestrel 配置为使用 HTTPS,采用默认证书。 如果没有配置默认证书,则会引发异常。
  • UseHttps(string fileName)
  • UseHttps(string fileName, string password)
  • UseHttps(string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(StoreName storeName, string subject)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(X509Certificate2 serverCertificate)
  • UseHttps(X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(Action<HttpsConnectionAdapterOptions> configureOptions)

ListenOptions.UseHttps 参数:

  • filename 是证书文件的路径和文件名,关联包含应用内容文件的目录。
  • password 是访问 X.509 证书数据所需的密码。
  • configureOptions 是配置 HttpsConnectionAdapterOptionsAction。 返回 ListenOptions
  • storeName 是从中加载证书的证书存储。
  • subject 是证书的主题名称。
  • allowInvalid 指示是否存在需要留意的无效证书,例如自签名证书。
  • location 是从中加载证书的存储位置。
  • serverCertificate 是 X.509 证书。

在生产中,必须显式配置 HTTPS。 至少必须提供默认证书。

如果要从磁盘读取证书,而不是从 Windows 证书存储读取证书,则包含目录必须具有适当的权限,以防止未经授权的访问。

下面要描述的支持的配置:

  • 无配置
  • 从配置中替换默认证书
  • 更改代码中的默认值

无配置

Kestrel 侦听 http://localhost:5000

从配置中替换默认证书

Kestrel 可以使用默认 HTTPS 应用设置配置架构。 从磁盘上的文件或从证书存储中配置多个终结点,包括要使用的 URL 和证书。

在以下 appsettings.json 示例中:

  • AllowInvalid 设置为 true,从而允许使用无效证书(例如自签名证书)。
  • 任何未指定证书的 HTTPS 终结点(下例中的 HttpsDefaultCert)会回退至在 Certificates:Default 下定义的证书或开发证书。
{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertAndKeyFile": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Path": "<path to .pem/.crt file>",
          "KeyPath": "<path to .key file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertStore": {
        "Url": "https://localhost:5003",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },
      "HttpsDefaultCert": {
        "Url": "https://localhost:5004"
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作每个证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

架构的注意事项:

  • 终结点的名称不区分大小写。 例如,由于再也无法解析标识符“Families”,因此 HTTPS and Https 是等效的。
  • 每个终结点都要具备 Url 参数。 此参数的格式和顶层 Urls 配置参数一样,只不过它只能有单个值。
  • 这些终结点不会添加进顶层 Urls 配置中定义的终结点,而是替换它们。 通过 Listen 在代码中定义的终结点与在配置节中定义的终结点相累积。
  • Certificate 部分是可选的。 如果未指定 Certificate 部分,则使用 Certificates:Default 中定义的默认值。 如果没有可用的默认值,则使用开发证书。 如果没有默认值,且开发证书不存在,则服务器将引发异常,并且无法启动。
  • Certificate 部分支持多个证书源
  • 只要不会导致端口冲突,就能在配置中定义任何数量的终结点。

证书源

可以将证书节点配置为从多个源加载证书:

  • PathPassword 用于加载 .pfx 文件。
  • PathKeyPathPassword 用于加载 .pem/.crt 和 .key 文件。
  • SubjectStore 用于从证书存储中加载。

例如,可将 Certificates:Default 证书指定为:

"Default": {
  "Subject": "<subject; required>",
  "Store": "<cert store; required>",
  "Location": "<location; defaults to CurrentUser>",
  "AllowInvalid": "<true or false; defaults to false>"
}

ConfigurationLoader

Configure(IConfiguration) 通过 Endpoint(String, Action<EndpointConfiguration>) 方法返回 KestrelConfigurationLoader,可以用于补充已配置的终结点设置:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    var kestrelSection = context.Configuration.GetSection("Kestrel");

    serverOptions.Configure(kestrelSection)
        .Endpoint("HTTPS", listenOptions =>
        {
            // ...
        });
});

可以直接访问 KestrelServerOptions.ConfigurationLoader 以继续迭代现有加载程序,例如由 WebApplicationBuilder.WebHost 提供的加载程序。

  • 每个终结点的配置节都可用于 Endpoint 方法中的选项,以便读取自定义设置。
  • 通过另一节再次调用 Configure(IConfiguration) 可能加载多个配置。 只使用最新配置,除非之前的实例上显式调用了 Load。 元包不会调用 Load,所以可能会替换它的默认配置节。
  • KestrelConfigurationLoaderKestrelServerOptions 将 API 的 Listen 簇反射为 Endpoint 重载,因此可在同样的位置配置代码和配置终结点。 这些重载不使用名称,且只使用配置中的默认设置。

更改代码中的默认值

可以使用 ConfigureEndpointDefaultsConfigureHttpsDefaults 更改 ListenOptionsHttpsConnectionAdapterOptions 的默认设置,包括重写之前的方案指定的默认证书。 需要在配置任何终结点之前调用 ConfigureEndpointDefaultsConfigureHttpsDefaults

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // ...
    });

    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // ...
    });
});

使用服务器名称指示配置终结点

服务器名称指示 (SNI) 可用于承载相同 IP 地址和端口上的多个域。 为了运行 SNI,客户端在 TLS 握手过程中将进行安全会话的主机名发送至服务器,从而让服务器可以提供正确的证书。 在 TLS 握手后的安全会话期间,客户端将服务器提供的证书用于与服务器进行加密通信。

可用两种方式配置 SNI:

  • 在代码中创建终结点,并通过 ServerCertificateSelector 回调使用主机名选择证书。
  • 配置中配置主机名和 HTTPS 选项之间的映射。 例如,appsettings.json 文件中的 JSON。

具有 ServerCertificateSelector 的 SNI

Kestrel 通过 ServerCertificateSelector 回调支持 SNI。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var subExampleCert = CertificateLoader.LoadFromStoreCert(
                "sub.example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var certs = new Dictionary<string, X509Certificate2>(
                StringComparer.OrdinalIgnoreCase)
            {
                ["localhost"] = localhostCert,
                ["example.com"] = exampleCert,
                ["sub.example.com"] = subExampleCert
            };

            httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
            {
                if (name is not null && certs.TryGetValue(name, out var cert))
                {
                    return cert;
                }

                return exampleCert;
            };
        });
    });
});

具有 ServerOptionsSelectionCallback 的 SNI

Kestrel 通过 ServerOptionsSelectionCallback 回调支持其他动态 TLS 配置。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书和 TLS 配置。 默认证书和 ConfigureHttpsDefaults 不与此回调一起使用。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);

            listenOptions.UseHttps((stream, clientHelloInfo, state, cancellationToken) =>
            {
                if (string.Equals(clientHelloInfo.ServerName, "localhost",
                    StringComparison.OrdinalIgnoreCase))
                {
                    return new ValueTask<SslServerAuthenticationOptions>(
                        new SslServerAuthenticationOptions
                        {
                            ServerCertificate = localhostCert,
                            // Different TLS requirements for this host
                            ClientCertificateRequired = true
                        });
                }

                return new ValueTask<SslServerAuthenticationOptions>(
                    new SslServerAuthenticationOptions
                    {
                        ServerCertificate = exampleCert
                    });
            }, state: null!);
        });
    });
});

具有 TlsHandshakeCallbackOptions 的 SNI

Kestrel 通过 TlsHandshakeCallbackOptions.OnConnection 回调支持其他动态 TLS 配置。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书、TLS 配置和其他服务器选项。 默认证书和 ConfigureHttpsDefaults 不与此回调一起使用。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);

            listenOptions.UseHttps(new TlsHandshakeCallbackOptions
            {
                OnConnection = context =>
                {
                    if (string.Equals(context.ClientHelloInfo.ServerName, "localhost",
                        StringComparison.OrdinalIgnoreCase))
                    {
                        // Different TLS requirements for this host
                        context.AllowDelayedClientCertificateNegotation = true;

                        return new ValueTask<SslServerAuthenticationOptions>(
                            new SslServerAuthenticationOptions
                            {
                                ServerCertificate = localhostCert
                            });
                    }

                    return new ValueTask<SslServerAuthenticationOptions>(
                        new SslServerAuthenticationOptions
                        {
                            ServerCertificate = exampleCert
                        });
                }
            });
        });
    });
});

配置中的 SNI

Kestrel 支持配置中定义的 SNI。 可以使用包含主机名和 HTTPS 选项之间的映射的 Sni 对象来配置终结点。 连接主机名与选项匹配,并且这些选项用于该连接。

以下配置将添加一个名为 MySniEndpoint 的终结点,该终结点使用 SNI 基于主机名选择 HTTPS 选项:

{
  "Kestrel": {
    "Endpoints": {
      "MySniEndpoint": {
        "Url": "https://*",
        "SslProtocols": ["Tls11", "Tls12"],
        "Sni": {
          "a.example.org": {
            "Protocols": "Http1AndHttp2",
            "SslProtocols": ["Tls11", "Tls12", "Tls13"],
            "Certificate": {
              "Subject": "<subject; required>",
              "Store": "<certificate store; required>",
            },
            "ClientCertificateMode" : "NoCertificate"
          },
          "*.example.org": {
            "Certificate": {
              "Path": "<path to .pfx file>",
              "Password": "$CREDENTIAL_PLACEHOLDER$"
            }
          },
          "*": {
            // At least one subproperty needs to exist per SNI section or it
            // cannot be discovered via IConfiguration
            "Protocols": "Http1",
          }
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作每个证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

可由 SNI 覆盖的 HTTPS 选项:

主机名支持通配符匹配:

  • 完全匹配。 例如,a.example.org 匹配 a.example.org
  • 通配符前缀。 如果有多个通配符匹配项,则选择最长的模式。 例如,*.example.org 匹配 b.example.orgc.example.org
  • 完整通配符。 * 匹配其他所有内容,包括不使用 SNI 且不发送主机名的客户端。

匹配的 SNI 配置将应用于连接的终结点,并重写终结点上的值。 如果连接与已配置的 SNI 主机名不匹配,则连接将被拒绝。

SNI 要求

所有网站必须在相同的 Kestrel 实例上运行。 Kestrel 在无反向代理时不支持跨多个实例共享一个 IP 地址和端口。

SSL/TLS 协议

SSL 协议是用于在两个对等机(传统上是客户端和服务器)之间加密和解密流量的协议。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.SslProtocols = SslProtocols.Tls13;
    });
});
{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "SslProtocols": ["Tls12", "Tls13"],
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

默认值 SslProtocols.None 会导致 Kestrel 使用操作系统默认值来选择最佳协议。 除非你有特定原因要选择协议,否则请使用默认值。

客户端证书

ClientCertificateMode 配置客户端证书要求

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
    });
});
{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "ClientCertificateMode": "AllowCertificate",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序

默认值为 ClientCertificateMode.NoCertificate,其中 Kestrel 不会从客户端请求或要求证书。

有关详细信息,请参阅在 ASP.NET Core 中配置证书身份验证

连接日志记录

调用 UseConnectionLogging 以发出用于进行连接上的字节级别通信的调试级别日志。 连接日志记录有助于排查低级通信中的问题,例如在 TLS 加密期间和代理后。 如果 UseConnectionLogging 放置在 UseHttps 之前,则会记录加密的流量。 如果 UseConnectionLogging 放置于 UseHttps 之后,则会记录解密的流量。 这是内置连接中间件

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseConnectionLogging();
    });
});

绑定到 TCP 套接字

Listen 方法绑定至 TCP 套接字,且 options lambda 允许 X.509 证书配置:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
});

示例使用 ListenOptions 为终结点配置 HTTPS。 可使用相同 API 为特定终结点配置其他 Kestrel 设置。

在 Windows 上,可以使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不受支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

在 macOS、Linux 和 Windows 上,可以使用 OpenSSL 创建证书。

绑定到 Unix 套接字

可通过 ListenUnixSocket 侦听 Unix 套接字以提高 Nginx 的性能,如以下示例所示:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock");
});
  • 在 Nginx 配置文件中,将 server>location>proxy_pass 条目设置为 http://unix:/tmp/{KESTREL SOCKET}:/;{KESTREL SOCKET} 是提供给 ListenUnixSocket 的套接字的名称(例如,上述示例中的 kestrel-test.sock)。
  • 确保套接字可由 Nginx (例如 chmod go+w /tmp/kestrel-test.sock)进行写入。

端口 0

如果指定端口号 0,Kestrel 将动态绑定到可用端口。 以下示例演示如何确定 Kestrel 在运行时绑定到的端口:

app.Run(async (context) =>
{
    var serverAddressFeature = context.Features.Get<IServerAddressesFeature>();

    if (serverAddressFeature is not null)
    {
        var listenAddresses = string.Join(", ", serverAddressFeature.Addresses);

        // ...
    }
});

在某些情况下,动态绑定端口不可用:

  • ListenLocalhost
  • 将基于 TCP 的 HTTP/1.1 或 HTTP/2 和基于 QUIC 的 HTTP/3 绑定在一起。

限制

使用以下方法配置终结点:

  • UseUrls
  • --urls 命令行参数
  • urls 主机配置键
  • ASPNETCORE_URLS 环境变量

若要将代码用于 Kestrel 以外的服务器,这些方法非常有用。 不过,请注意以下限制:

  • HTTPS 无法与这些方法结合使用,除非在 HTTPS 终结点配置中提供了默认证书(例如,使用 KestrelServerOptions 配置或配置文件,如本文前面的部分所示)。
  • 如果同时使用 ListenUseUrls 方法,Listen 终结点将覆盖 UseUrls 终结点。

IIS 终结点配置

使用 IIS 时,由 ListenUseUrls 设置用于 IIS 覆盖绑定的 URL 绑定。 有关详细信息,请参阅 ASP.NET Core 模块

ListenOptions.Protocols

Protocols 属性建立在连接终结点上或为服务器启用的 HTTP 协议(HttpProtocols)。 从 HttpProtocols 枚举向 Protocols 属性赋值。

HttpProtocols 枚举值 允许的连接协议
Http1 仅 HTTP/1.1。 可以在具有 TLS 或没有 TLS 的情况下使用。
Http2 仅 HTTP/2。 仅当客户端支持先验知识模式时,才可以在没有 TLS 的情况下使用。
Http3 仅 HTTP/3。 需要 TLS。 可能需要将客户端配置为仅使用 HTTP/3。
Http1AndHttp2 HTTP/1.1 和 HTTP/2。 HTTP/2 要求客户端在 TLS 应用层协议协商 (ALPN) 握手过程中选择 HTTP/2;否则,连接默认为 HTTP/1.1。
Http1AndHttp2AndHttp3 HTTP/1.1、HTTP/2 和 HTTP/3。 第一个客户端请求通常使用 HTTP/1.1 或 HTTP/2,alt-svc 响应头会提示客户端升级到 HTTP/3。 HTTP/2 和 HTTP/3 需要 TLS;否则,连接默认为 HTTP/1.1。

任何终结点的默认 ListenOptions.Protocols 值都为 HttpProtocols.Http1AndHttp2

HTTP/2 的 TLS 限制:

  • TLS 版本 1.2 或更高版本
  • 重新协商已禁用
  • 压缩已禁用
  • 最小的临时密钥交换大小:
    • 椭圆曲线 Diffie-Hellman (ECDHE) [RFC4492]:最小 224 位
    • 有限字段 Diffie-Hellman (DHE) [TLS12]:最小 2048 位
  • 不禁止密码套件。

默认情况下,支持具有 P-256 椭圆曲线 [FIPS186] 的 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [TLS-ECDHE]。

以下示例允许端口 8000 上的 HTTP/1.1 和 HTTP/2 连接。 TLS 使用提供的证书来保护连接:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
    });
});

在 Linux 上,CipherSuitesPolicy 可用于针对每个连接筛选 TLS 握手:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.OnAuthenticate = (context, sslOptions) =>
        {
            sslOptions.CipherSuitesPolicy = new CipherSuitesPolicy(
                new[]
                {
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                    // ...
                });
        };
    });
});

连接中间件

必要时,自定义连接中间件可以按连接为特定密码筛选 TLS 握手。

下面的示例针对应用不支持的任何密码算法引发 NotSupportedException。 或者,定义 ITlsHandshakeFeature.CipherAlgorithm 并将其与可接受的密码套件列表进行比较。

没有任何加密使用 CipherAlgorithmType.Null 密码算法。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");

        listenOptions.Use((context, next) =>
        {
            var tlsFeature = context.Features.Get<ITlsHandshakeFeature>()!;

            if (tlsFeature.CipherAlgorithm == CipherAlgorithmType.Null)
            {
                throw new NotSupportedException(
                    $"Prohibited cipher: {tlsFeature.CipherAlgorithm}");
            }

            return next();
        });
    });
});

从配置中设置 HTTP 协议

默认情况下,从 Kestrel 部分加载 Kestrel 配置。 以下 appsettings.json 示例将 HTTP/1.1 建立为所有终结点的默认连接协议:

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1"
    }
  }
}

以下 appsettings.json 示例将为所有指定终结点建立 HTTP/1.1 连接协议:

{
  "Kestrel": {
    "Endpoints": {
      "HttpsDefaultCert": {
        "Url": "https://localhost:5001",
        "Protocols": "Http1"
      }
    }
  }
}

代码中指定的协议覆盖了由配置设置的值。

URL 前缀

如果使用 UseUrls--urls 命令行参数、urls 主机配置键或 ASPNETCORE_URLS 环境变量,URL 前缀可采用以下任意格式。

仅 HTTP URL 前缀是有效的。 使用 UseUrls 配置 URL 绑定时,Kestrel 不支持 HTTPS。

  • 包含端口号的 IPv4 地址

    http://65.55.39.10:80/
    

    0.0.0.0 是一种绑定到所有 IPv4 地址的特殊情况。

  • 包含端口号的 IPv6 地址

    http://[0:0:0:0:0:ffff:4137:270a]:80/
    

    [::] 是 IPv4 0.0.0.0 的 IPv6 等效项。

  • 包含端口号的主机名

    http://contoso.com:80/
    http://*:80/
    

    主机名、*+ 并不特殊。 没有识别为有效 IP 地址或 localhost 的任何内容都将绑定到所有 IPv4 和 IPv6 IP。 若要将不同主机名绑定到相同端口上的不同 ASP.NET Core 应用,请使用 HTTP.sys 或反向代理服务器。 反向代理服务器示例包括 IIS、Nginx 或 Apache。

    警告

    采用反向代理配置进行托管需要主机筛选

  • 包含端口号的主机 localhost 名称或包含端口号的环回 IP

    http://localhost:5000/
    http://127.0.0.1:5000/
    http://[::1]:5000/
    

    指定 localhost 后,Kestrel 将尝试绑定到 IPv4 和 IPv6 环回接口。 如果其他服务正在任一环回接口上使用请求的端口,则 Kestrel 将无法启动。 如果任一环回接口出于任何其他原因(通常是因为 IPv6 不受支持)而不可用,则 Kestrel 将记录一个警告。

ASP.NET Core 项目被配置为绑定到 5000-5300 之间的随机 HTTP 端口和 7000-7300 之间的随机 HTTPS 端口。 这个默认配置是在生成的 Properties/launchSettings.json 文件中指定的,可以被重写。 如果没有指定端口,Kestrel 将绑定到:

  • http://localhost:5000
  • https://localhost:5001(存在本地开发证书时)

使用以下内容指定 URL:

  • ASPNETCORE_URLS 环境变量。
  • --urls 命令行参数。
  • urls 主机配置键。
  • UseUrls 扩展方法。

采用这些方法提供的值可以是一个或多个 HTTP 和 HTTPS 终结点(如果默认证书可用,则为 HTTPS)。 将值配置为以分号分隔的列表(例如 "Urls": "http://localhost:8000;http://localhost:8001")。

有关这些方法的详细信息,请参阅服务器 URL重写配置

关于开发证书的创建:

开发证书仅适用于生成证书的用户。 某些浏览器需要授予显式权限才能信任本地开发证书。

项目模板将应用配置为默认情况下在 HTTPS 上运行,并包括 HTTPS 重定向和 HSTS 支持

调用 KestrelServerOptions 上的 ListenListenUnixSocket 方法以配置 URL 前缀和 Kestrel 的端口。

UseUrls--urls 命令行参数、urls 主机配置键以及 ASPNETCORE_URLS 环境变量也有用,但具有本节后面注明的限制(必须要有可用于 HTTPS 终结点配置的默认证书)。

KestrelServerOptions 配置:

ConfigureEndpointDefaults

ConfigureEndpointDefaults(Action<ListenOptions>) 指定一个为每个指定的终结点运行的配置 Action。 多次调用 ConfigureEndpointDefaults 会将之前的 Action 替换为最后指定的 Action

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // ...
    });
});

注意

通过在调用 ConfigureEndpointDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

Configure(IConfiguration)

允许 Kestrel 从 IConfiguration 中加载终结点。 配置必须针对 Kestrel 的配置节。 Configure(IConfiguration, bool) 重载可用于在配置源更改时启用重载终结点。

默认情况下,从 Kestrel 部分加载 Kestrel 配置并启用重载更改:

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "Https": {
        "Url": "https://localhost:5001"
      }
    }
  }
}

如果已启用重载配置并发出更改信号,则会执行以下步骤:

  • 新配置与旧配置相比,不会修改任何没有配置更改的终结点。
  • 已删除或已修改的终结点将在 5 秒内完成处理请求并关闭。
  • 启动新的或已修改的终结点。

重启终结点时,连接到已修改的终结点的客户端可能会断开连接或被拒绝。

ConfigureHttpsDefaults

ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions>) 指定一个为每个 HTTPS 终结点运行的配置 Action。 多次调用 ConfigureHttpsDefaults,用最新指定的 Action 替换之前的 Action

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // ...
    });
});

注意

通过在调用 ConfigureHttpsDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

ListenOptions.UseHttps

将 Kestrel 配置为使用 HTTPS。

ListenOptions.UseHttps 扩展:

  • UseHttps:将 Kestrel 配置为使用 HTTPS,采用默认证书。 如果没有配置默认证书,则会引发异常。
  • UseHttps(string fileName)
  • UseHttps(string fileName, string password)
  • UseHttps(string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(StoreName storeName, string subject)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(X509Certificate2 serverCertificate)
  • UseHttps(X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(Action<HttpsConnectionAdapterOptions> configureOptions)

ListenOptions.UseHttps 参数:

  • filename 是证书文件的路径和文件名,关联包含应用内容文件的目录。
  • password 是访问 X.509 证书数据所需的密码。
  • configureOptions 是配置 HttpsConnectionAdapterOptionsAction。 返回 ListenOptions
  • storeName 是从中加载证书的证书存储。
  • subject 是证书的主题名称。
  • allowInvalid 指示是否存在需要留意的无效证书,例如自签名证书。
  • location 是从中加载证书的存储位置。
  • serverCertificate 是 X.509 证书。

在生产中,必须显式配置 HTTPS。 至少必须提供默认证书。

下面要描述的支持的配置:

  • 无配置
  • 从配置中替换默认证书
  • 更改代码中的默认值

无配置

Kestrel 在 http://localhost:5000https://localhost:5001 上进行侦听(如果默认证书可用)。

从配置中替换默认证书

Kestrel 可以使用默认 HTTPS 应用设置配置架构。 从磁盘上的文件或从证书存储中配置多个终结点,包括要使用的 URL 和证书。

在以下 appsettings.json 示例中:

  • AllowInvalid 设置为 true,从而允许使用无效证书(例如自签名证书)。
  • 任何未指定证书的 HTTPS 终结点(下例中的 HttpsDefaultCert)会回退至在 Certificates:Default 下定义的证书或开发证书。
{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertAndKeyFile": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Path": "<path to .pem/.crt file>",
          "KeyPath": "<path to .key file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertStore": {
        "Url": "https://localhost:5003",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },
      "HttpsDefaultCert": {
        "Url": "https://localhost:5004"
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作每个证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

架构的注意事项:

  • 终结点的名称不区分大小写。 例如,由于再也无法解析标识符“Families”,因此 HTTPS and Https 是等效的。
  • 每个终结点都要具备 Url 参数。 此参数的格式和顶层 Urls 配置参数一样,只不过它只能有单个值。
  • 这些终结点不会添加进顶层 Urls 配置中定义的终结点,而是替换它们。 通过 Listen 在代码中定义的终结点与在配置节中定义的终结点相累积。
  • Certificate 部分是可选的。 如果未指定 Certificate 部分,则使用 Certificates:Default 中定义的默认值。 如果没有可用的默认值,则使用开发证书。 如果没有默认值,且开发证书不存在,则服务器将引发异常,并且无法启动。
  • Certificate 部分支持多个证书源
  • 只要不会导致端口冲突,就能在配置中定义任何数量的终结点。

证书源

可以将证书节点配置为从多个源加载证书:

  • PathPassword 用于加载 .pfx 文件。
  • PathKeyPathPassword 用于加载 .pem/.crt 和 .key 文件。
  • SubjectStore 用于从证书存储中加载。

例如,可将 Certificates:Default 证书指定为:

"Default": {
  "Subject": "<subject; required>",
  "Store": "<cert store; required>",
  "Location": "<location; defaults to CurrentUser>",
  "AllowInvalid": "<true or false; defaults to false>"
}

ConfigurationLoader

Configure(IConfiguration) 通过 Endpoint(String, Action<EndpointConfiguration>) 方法返回 KestrelConfigurationLoader,可以用于补充已配置的终结点设置:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    var kestrelSection = context.Configuration.GetSection("Kestrel");

    serverOptions.Configure(kestrelSection)
        .Endpoint("HTTPS", listenOptions =>
        {
            // ...
        });
});

可以直接访问 KestrelServerOptions.ConfigurationLoader 以继续迭代现有加载程序,例如由 WebApplicationBuilder.WebHost 提供的加载程序。

  • 每个终结点的配置节都可用于 Endpoint 方法中的选项,以便读取自定义设置。
  • 通过另一节再次调用 Configure(IConfiguration) 可能加载多个配置。 只使用最新配置,除非之前的实例上显式调用了 Load。 元包不会调用 Load,所以可能会替换它的默认配置节。
  • KestrelConfigurationLoaderKestrelServerOptions 将 API 的 Listen 簇反射为 Endpoint 重载,因此可在同样的位置配置代码和配置终结点。 这些重载不使用名称,且只使用配置中的默认设置。

更改代码中的默认值

可以使用 ConfigureEndpointDefaultsConfigureHttpsDefaults 更改 ListenOptionsHttpsConnectionAdapterOptions 的默认设置,包括重写之前的方案指定的默认证书。 需要在配置任何终结点之前调用 ConfigureEndpointDefaultsConfigureHttpsDefaults

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // ...
    });

    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // ...
    });
});

使用服务器名称指示配置终结点

服务器名称指示 (SNI) 可用于承载相同 IP 地址和端口上的多个域。 为了运行 SNI,客户端在 TLS 握手过程中将进行安全会话的主机名发送至服务器,从而让服务器可以提供正确的证书。 在 TLS 握手后的安全会话期间,客户端将服务器提供的证书用于与服务器进行加密通信。

可用两种方式配置 SNI:

  • 在代码中创建终结点,并通过 ServerCertificateSelector 回调使用主机名选择证书。
  • 配置中配置主机名和 HTTPS 选项之间的映射。 例如,appsettings.json 文件中的 JSON。

具有 ServerCertificateSelector 的 SNI

Kestrel 通过 ServerCertificateSelector 回调支持 SNI。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var subExampleCert = CertificateLoader.LoadFromStoreCert(
                "sub.example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var certs = new Dictionary<string, X509Certificate2>(
                StringComparer.OrdinalIgnoreCase)
            {
                ["localhost"] = localhostCert,
                ["example.com"] = exampleCert,
                ["sub.example.com"] = subExampleCert
            };

            httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
            {
                if (name is not null && certs.TryGetValue(name, out var cert))
                {
                    return cert;
                }

                return exampleCert;
            };
        });
    });
});

具有 ServerOptionsSelectionCallback 的 SNI

Kestrel 通过 ServerOptionsSelectionCallback 回调支持其他动态 TLS 配置。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书和 TLS 配置。 默认证书和 ConfigureHttpsDefaults 不与此回调一起使用。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);

            listenOptions.UseHttps((stream, clientHelloInfo, state, cancellationToken) =>
            {
                if (string.Equals(clientHelloInfo.ServerName, "localhost",
                    StringComparison.OrdinalIgnoreCase))
                {
                    return new ValueTask<SslServerAuthenticationOptions>(
                        new SslServerAuthenticationOptions
                        {
                            ServerCertificate = localhostCert,
                            // Different TLS requirements for this host
                            ClientCertificateRequired = true
                        });
                }

                return new ValueTask<SslServerAuthenticationOptions>(
                    new SslServerAuthenticationOptions
                    {
                        ServerCertificate = exampleCert
                    });
            }, state: null!);
        });
    });
});

具有 TlsHandshakeCallbackOptions 的 SNI

Kestrel 通过 TlsHandshakeCallbackOptions.OnConnection 回调支持其他动态 TLS 配置。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书、TLS 配置和其他服务器选项。 默认证书和 ConfigureHttpsDefaults 不与此回调一起使用。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);

            listenOptions.UseHttps(new TlsHandshakeCallbackOptions
            {
                OnConnection = context =>
                {
                    if (string.Equals(context.ClientHelloInfo.ServerName, "localhost",
                        StringComparison.OrdinalIgnoreCase))
                    {
                        // Different TLS requirements for this host
                        context.AllowDelayedClientCertificateNegotation = true;

                        return new ValueTask<SslServerAuthenticationOptions>(
                            new SslServerAuthenticationOptions
                            {
                                ServerCertificate = localhostCert
                            });
                    }

                    return new ValueTask<SslServerAuthenticationOptions>(
                        new SslServerAuthenticationOptions
                        {
                            ServerCertificate = exampleCert
                        });
                }
            });
        });
    });
});

配置中的 SNI

Kestrel 支持配置中定义的 SNI。 可以使用包含主机名和 HTTPS 选项之间的映射的 Sni 对象来配置终结点。 连接主机名与选项匹配,并且这些选项用于该连接。

以下配置将添加一个名为 MySniEndpoint 的终结点,该终结点使用 SNI 基于主机名选择 HTTPS 选项:

{
  "Kestrel": {
    "Endpoints": {
      "MySniEndpoint": {
        "Url": "https://*",
        "SslProtocols": ["Tls11", "Tls12"],
        "Sni": {
          "a.example.org": {
            "Protocols": "Http1AndHttp2",
            "SslProtocols": ["Tls11", "Tls12", "Tls13"],
            "Certificate": {
              "Subject": "<subject; required>",
              "Store": "<certificate store; required>",
            },
            "ClientCertificateMode" : "NoCertificate"
          },
          "*.example.org": {
            "Certificate": {
              "Path": "<path to .pfx file>",
              "Password": "$CREDENTIAL_PLACEHOLDER$"
            }
          },
          "*": {
            // At least one subproperty needs to exist per SNI section or it
            // cannot be discovered via IConfiguration
            "Protocols": "Http1",
          }
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作每个证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

可由 SNI 覆盖的 HTTPS 选项:

主机名支持通配符匹配:

  • 完全匹配。 例如,a.example.org 匹配 a.example.org
  • 通配符前缀。 如果有多个通配符匹配项,则选择最长的模式。 例如,*.example.org 匹配 b.example.orgc.example.org
  • 完整通配符。 * 匹配其他所有内容,包括不使用 SNI 且不发送主机名的客户端。

匹配的 SNI 配置将应用于连接的终结点,并重写终结点上的值。 如果连接与已配置的 SNI 主机名不匹配,则连接将被拒绝。

SNI 要求

所有网站必须在相同的 Kestrel 实例上运行。 Kestrel 在无反向代理时不支持跨多个实例共享一个 IP 地址和端口。

SSL/TLS 协议

SSL 协议是用于在两个对等机(传统上是客户端和服务器)之间加密和解密流量的协议。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.SslProtocols = SslProtocols.Tls13;
    });
});
{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "SslProtocols": ["Tls12", "Tls13"],
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

默认值 SslProtocols.None 会导致 Kestrel 使用操作系统默认值来选择最佳协议。 除非你有特定原因要选择协议,否则请使用默认值。

客户端证书

ClientCertificateMode 配置客户端证书要求

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
    });
});
{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "ClientCertificateMode": "AllowCertificate",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序

默认值为 ClientCertificateMode.NoCertificate,其中 Kestrel 不会从客户端请求或要求证书。

有关详细信息,请参阅在 ASP.NET Core 中配置证书身份验证

连接日志记录

调用 UseConnectionLogging 以发出用于进行连接上的字节级别通信的调试级别日志。 连接日志记录有助于排查低级通信中的问题,例如在 TLS 加密期间和代理后。 如果 UseConnectionLogging 放置在 UseHttps 之前,则会记录加密的流量。 如果 UseConnectionLogging 放置于 UseHttps 之后,则会记录解密的流量。 这是内置连接中间件

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseConnectionLogging();
    });
});

绑定到 TCP 套接字

Listen 方法绑定至 TCP 套接字,且 options lambda 允许 X.509 证书配置:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Loopback, 5000);
    serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
});

示例使用 ListenOptions 为终结点配置 HTTPS。 可使用相同 API 为特定终结点配置其他 Kestrel 设置。

在 Windows 上,可以使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不受支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

在 macOS、Linux 和 Windows 上,可以使用 OpenSSL 创建证书。

绑定到 Unix 套接字

可通过 ListenUnixSocket 侦听 Unix 套接字以提高 Nginx 的性能,如以下示例所示:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock");
});
  • 在 Nginx 配置文件中,将 server>location>proxy_pass 条目设置为 http://unix:/tmp/{KESTREL SOCKET}:/;{KESTREL SOCKET} 是提供给 ListenUnixSocket 的套接字的名称(例如,上述示例中的 kestrel-test.sock)。
  • 确保套接字可由 Nginx (例如 chmod go+w /tmp/kestrel-test.sock)进行写入。

端口 0

如果指定端口号 0,Kestrel 将动态绑定到可用端口。 以下示例演示如何确定 Kestrel 在运行时绑定到的端口:

app.Run(async (context) =>
{
    var serverAddressFeature = context.Features.Get<IServerAddressesFeature>();

    if (serverAddressFeature is not null)
    {
        var listenAddresses = string.Join(", ", serverAddressFeature.Addresses);

        // ...
    }
});

限制

使用以下方法配置终结点:

  • UseUrls
  • --urls 命令行参数
  • urls 主机配置键
  • ASPNETCORE_URLS 环境变量

若要将代码用于 Kestrel 以外的服务器,这些方法非常有用。 不过,请注意以下限制:

  • HTTPS 无法与这些方法结合使用,除非在 HTTPS 终结点配置中提供了默认证书(例如,使用 KestrelServerOptions 配置或配置文件,如本文前面的部分所示)。
  • 如果同时使用 ListenUseUrls 方法,Listen 终结点将覆盖 UseUrls 终结点。

IIS 终结点配置

使用 IIS 时,由 ListenUseUrls 设置用于 IIS 覆盖绑定的 URL 绑定。 有关详细信息,请参阅 ASP.NET Core 模块

ListenOptions.Protocols

Protocols 属性建立在连接终结点上或为服务器启用的 HTTP 协议(HttpProtocols)。 从 HttpProtocols 枚举向 Protocols 属性赋值。

HttpProtocols 枚举值 允许的连接协议
Http1 仅 HTTP/1.1。 可以在具有 TLS 或没有 TLS 的情况下使用。
Http2 仅 HTTP/2。 仅当客户端支持先验知识模式时,才可以在没有 TLS 的情况下使用。
Http1AndHttp2 HTTP/1.1 和 HTTP/2。 HTTP/2 要求客户端在 TLS 应用层协议协商 (ALPN) 握手过程中选择 HTTP/2;否则,连接默认为 HTTP/1.1。

任何终结点的默认 ListenOptions.Protocols 值都为 HttpProtocols.Http1AndHttp2

HTTP/2 的 TLS 限制:

  • TLS 版本 1.2 或更高版本
  • 重新协商已禁用
  • 压缩已禁用
  • 最小的临时密钥交换大小:
    • 椭圆曲线 Diffie-Hellman (ECDHE) [RFC4492]:最小 224 位
    • 有限字段 Diffie-Hellman (DHE) [TLS12]:最小 2048 位
  • 不禁止密码套件。

默认情况下,支持具有 P-256 椭圆曲线 [FIPS186] 的 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [TLS-ECDHE]。

以下示例允许端口 8000 上的 HTTP/1.1 和 HTTP/2 连接。 TLS 使用提供的证书来保护连接:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
    });
});

在 Linux 上,CipherSuitesPolicy 可用于针对每个连接筛选 TLS 握手:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.OnAuthenticate = (context, sslOptions) =>
        {
            sslOptions.CipherSuitesPolicy = new CipherSuitesPolicy(
                new[]
                {
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                    // ...
                });
        };
    });
});

连接中间件

必要时,自定义连接中间件可以按连接为特定密码筛选 TLS 握手。

下面的示例针对应用不支持的任何密码算法引发 NotSupportedException。 或者,定义 ITlsHandshakeFeature.CipherAlgorithm 并将其与可接受的密码套件列表进行比较。

没有任何加密使用 CipherAlgorithmType.Null 密码算法。

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");

        listenOptions.Use((context, next) =>
        {
            var tlsFeature = context.Features.Get<ITlsHandshakeFeature>()!;

            if (tlsFeature.CipherAlgorithm == CipherAlgorithmType.Null)
            {
                throw new NotSupportedException(
                    $"Prohibited cipher: {tlsFeature.CipherAlgorithm}");
            }

            return next();
        });
    });
});

从配置中设置 HTTP 协议

默认情况下,从 Kestrel 部分加载 Kestrel 配置。 以下 appsettings.json 示例将 HTTP/1.1 建立为所有终结点的默认连接协议:

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1"
    }
  }
}

以下 appsettings.json 示例将为所有指定终结点建立 HTTP/1.1 连接协议:

{
  "Kestrel": {
    "Endpoints": {
      "HttpsDefaultCert": {
        "Url": "https://localhost:5001",
        "Protocols": "Http1"
      }
    }
  }
}

代码中指定的协议覆盖了由配置设置的值。

URL 前缀

如果使用 UseUrls--urls 命令行参数、urls 主机配置键或 ASPNETCORE_URLS 环境变量,URL 前缀可采用以下任意格式。

仅 HTTP URL 前缀是有效的。 使用 UseUrls 配置 URL 绑定时,Kestrel 不支持 HTTPS。

  • 包含端口号的 IPv4 地址

    http://65.55.39.10:80/
    

    0.0.0.0 是一种绑定到所有 IPv4 地址的特殊情况。

  • 包含端口号的 IPv6 地址

    http://[0:0:0:0:0:ffff:4137:270a]:80/
    

    [::] 是 IPv4 0.0.0.0 的 IPv6 等效项。

  • 包含端口号的主机名

    http://contoso.com:80/
    http://*:80/
    

    主机名、*+ 并不特殊。 没有识别为有效 IP 地址或 localhost 的任何内容都将绑定到所有 IPv4 和 IPv6 IP。 若要将不同主机名绑定到相同端口上的不同 ASP.NET Core 应用,请使用 HTTP.sys 或反向代理服务器。 反向代理服务器示例包括 IIS、Nginx 或 Apache。

    警告

    采用反向代理配置进行托管需要主机筛选

  • 包含端口号的主机 localhost 名称或包含端口号的环回 IP

    http://localhost:5000/
    http://127.0.0.1:5000/
    http://[::1]:5000/
    

    指定 localhost 后,Kestrel 将尝试绑定到 IPv4 和 IPv6 环回接口。 如果其他服务正在任一环回接口上使用请求的端口,则 Kestrel 将无法启动。 如果任一环回接口出于任何其他原因(通常是因为 IPv6 不受支持)而不可用,则 Kestrel 将记录一个警告。

默认情况下,ASP.NET Core 绑定到:

  • http://localhost:5000
  • https://localhost:5001(存在本地开发证书时)

使用以下内容指定 URL:

  • ASPNETCORE_URLS 环境变量。
  • --urls 命令行参数。
  • urls 主机配置键。
  • UseUrls 扩展方法。

采用这些方法提供的值可以是一个或多个 HTTP 和 HTTPS 终结点(如果默认证书可用,则为 HTTPS)。 将值配置为以分号分隔的列表(例如 "Urls": "http://localhost:8000;http://localhost:8001")。

有关这些方法的详细信息,请参阅服务器 URL重写配置

关于开发证书的创建:

某些浏览器需要授予显式权限才能信任本地开发证书。

项目模板将应用配置为默认情况下在 HTTPS 上运行,并包括 HTTPS 重定向和 HSTS 支持

调用 KestrelServerOptions 上的 ListenListenUnixSocket 方法以配置 URL 前缀和 Kestrel 的端口。

UseUrls--urls 命令行参数、urls 主机配置键以及 ASPNETCORE_URLS 环境变量也有用,但具有本节后面注明的限制(必须要有可用于 HTTPS 终结点配置的默认证书)。

KestrelServerOptions 配置:

ConfigureEndpointDefaults

ConfigureEndpointDefaults(Action<ListenOptions>) 指定一个为每个指定的终结点运行的配置 Action。 多次调用 ConfigureEndpointDefaults,用最新指定的 Action 替换之前的 Action

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // Configure endpoint defaults
    });
});

注意

通过在调用 ConfigureEndpointDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

Configure(IConfiguration)

允许 Kestrel 从 IConfiguration 中加载终结点。 配置必须针对 Kestrel 的配置节。

Configure(IConfiguration, bool) 重载可用于在配置源更改时启用重载终结点。

IHostBuilder.ConfigureWebHostDefaults 在默认情况下调用 Configure(context.Configuration.GetSection("Kestrel"), reloadOnChange: true) 来加载 Kestrel 配置并启用重载。

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "Https": {
        "Url": "https://localhost:5001"
      }
    }
  }
}

如果已启用重载配置并发出更改信号,则会执行以下步骤:

  • 新配置与旧配置相比,不会修改任何没有配置更改的终结点。
  • 已删除或已修改的终结点将在 5 秒内完成处理请求并关闭。
  • 启动新的或已修改的终结点。

重启终结点时,连接到已修改的终结点的客户端可能会断开连接或被拒绝。

ConfigureHttpsDefaults

ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions>) 指定一个为每个 HTTPS 终结点运行的配置 Action。 多次调用 ConfigureHttpsDefaults,用最新指定的 Action 替换之前的 Action

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // certificate is an X509Certificate2
        listenOptions.ServerCertificate = certificate;
    });
});

注意

通过在调用 ConfigureHttpsDefaults 之前调用 Listen 创建的终结点将不会应用默认值。

ListenOptions.UseHttps

将 Kestrel 配置为使用 HTTPS。

ListenOptions.UseHttps 扩展:

  • UseHttps:将 Kestrel 配置为使用 HTTPS,采用默认证书。 如果没有配置默认证书,则会引发异常。
  • UseHttps(string fileName)
  • UseHttps(string fileName, string password)
  • UseHttps(string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(StoreName storeName, string subject)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location)
  • UseHttps(StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(X509Certificate2 serverCertificate)
  • UseHttps(X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions)
  • UseHttps(Action<HttpsConnectionAdapterOptions> configureOptions)

ListenOptions.UseHttps 参数:

  • filename 是证书文件的路径和文件名,关联包含应用内容文件的目录。
  • password 是访问 X.509 证书数据所需的密码。
  • configureOptions 是配置 HttpsConnectionAdapterOptionsAction。 返回 ListenOptions
  • storeName 是从中加载证书的证书存储。
  • subject 是证书的主题名称。
  • allowInvalid 指示是否存在需要留意的无效证书,例如自签名证书。
  • location 是从中加载证书的存储位置。
  • serverCertificate 是 X.509 证书。

在生产中,必须显式配置 HTTPS。 至少必须提供默认证书。

下面要描述的支持的配置:

  • 无配置
  • 从配置中替换默认证书
  • 更改代码中的默认值

无配置

Kestrel 在 http://localhost:5000https://localhost:5001 上进行侦听(如果默认证书可用)。

从配置中替换默认证书

Kestrel 可以使用默认 HTTPS 应用设置配置架构。 从磁盘上的文件或从证书存储中配置多个终结点,包括要使用的 URL 和证书。

在以下 appsettings.json 示例中:

  • AllowInvalid 设置为 true,从而允许使用无效证书(例如自签名证书)。
  • 任何未指定证书的 HTTPS 终结点(下例中的 HttpsDefaultCert)会回退至在 Certificates:Default 下定义的证书或开发证书。
{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertAndKeyFile": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Path": "<path to .pem/.crt file>",
          "KeyPath": "<path to .key file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      },
      "HttpsInlineCertStore": {
        "Url": "https://localhost:5003",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },
      "HttpsDefaultCert": {
        "Url": "https://localhost:5004"
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作每个证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

架构的注意事项:

  • 终结点的名称不区分大小写。 例如,由于再也无法解析标识符“Families”,因此 HTTPS and Https 是等效的。
  • 每个终结点都要具备 Url 参数。 此参数的格式和顶层 Urls 配置参数一样,只不过它只能有单个值。
  • 这些终结点不会添加进顶层 Urls 配置中定义的终结点,而是替换它们。 通过 Listen 在代码中定义的终结点与在配置节中定义的终结点相累积。
  • Certificate 部分是可选的。 如果未指定 Certificate 部分,则使用 Certificates:Default 中定义的默认值。 如果没有可用的默认值,则使用开发证书。 如果没有默认值,且开发证书不存在,则服务器将引发异常,并且无法启动。
  • Certificate 部分支持多个证书源
  • 只要不会导致端口冲突,就能在配置中定义任何数量的终结点。

证书源

可以将证书节点配置为从多个源加载证书:

  • PathPassword 用于加载 .pfx 文件。
  • PathKeyPathPassword 用于加载 .pem/.crt 和 .key 文件。
  • SubjectStore 用于从证书存储中加载。

例如,可将 Certificates:Default 证书指定为:

"Default": {
  "Subject": "<subject; required>",
  "Store": "<cert store; required>",
  "Location": "<location; defaults to CurrentUser>",
  "AllowInvalid": "<true or false; defaults to false>"
}

ConfigurationLoader

options.Configure(context.Configuration.GetSection("{SECTION}")) 通过 .Endpoint(string name, listenOptions => { }) 方法返回 KestrelConfigurationLoader,可以用于补充已配置的终结点设置:

webBuilder.UseKestrel((context, serverOptions) =>
{
    serverOptions.Configure(context.Configuration.GetSection("Kestrel"))
        .Endpoint("HTTPS", listenOptions =>
        {
            listenOptions.HttpsOptions.SslProtocols = SslProtocols.Tls12;
        });
});

可以直接访问 KestrelServerOptions.ConfigurationLoader 以继续迭代现有加载程序,例如由 CreateDefaultBuilder 提供的加载程序。

  • 每个终结点的配置节都可用于 Endpoint 方法中的选项,以便读取自定义设置。
  • 通过另一节再次调用 options.Configure(context.Configuration.GetSection("{SECTION}")) 可能加载多个配置。 只使用最新配置,除非之前的实例上显式调用了 Load。 元包不会调用 Load,所以可能会替换它的默认配置节。
  • KestrelConfigurationLoaderKestrelServerOptions 将 API 的 Listen 簇反射为 Endpoint 重载,因此可在同样的位置配置代码和配置终结点。 这些重载不使用名称,且只使用配置中的默认设置。

更改代码中的默认值

可以使用 ConfigureEndpointDefaultsConfigureHttpsDefaults 更改 ListenOptionsHttpsConnectionAdapterOptions 的默认设置,包括重写之前的方案指定的默认证书。 需要在配置任何终结点之前调用 ConfigureEndpointDefaultsConfigureHttpsDefaults

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        // Configure endpoint defaults
    });

    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.SslProtocols = SslProtocols.Tls12;
    });
});

使用服务器名称指示配置终结点

服务器名称指示 (SNI) 可用于承载相同 IP 地址和端口上的多个域。 为了运行 SNI,客户端在 TLS 握手过程中将进行安全会话的主机名发送至服务器,从而让服务器可以提供正确的证书。 在 TLS 握手后的安全会话期间,客户端将服务器提供的证书用于与服务器进行加密通信。

可用两种方式配置 SNI:

  • 在代码中创建终结点,并通过 ServerCertificateSelector 回调使用主机名选择证书。
  • 配置中配置主机名和 HTTPS 选项之间的映射。 例如,appsettings.json 文件中的 JSON。

具有 ServerCertificateSelector 的 SNI

Kestrel 通过 ServerCertificateSelector 回调支持 SNI。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书。 可以在项目的 Program.cs 文件的 ConfigureWebHostDefaults 方法调用中使用以下回调代码:

// using System.Security.Cryptography.X509Certificates;
// using Microsoft.AspNetCore.Server.Kestrel.Https;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var subExampleCert = CertificateLoader.LoadFromStoreCert(
                "sub.example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var certs = new Dictionary<string, X509Certificate2>(StringComparer.OrdinalIgnoreCase)
            {
                { "localhost", localhostCert },
                { "example.com", exampleCert },
                { "sub.example.com", subExampleCert },
            };            

            httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
            {
                if (name != null && certs.TryGetValue(name, out var cert))
                {
                    return cert;
                }

                return exampleCert;
            };
        });
    });
});

具有 ServerOptionsSelectionCallback 的 SNI

Kestrel 通过 ServerOptionsSelectionCallback 回调支持其他动态 TLS 配置。 每次连接调用一次回调,从而允许应用检查主机名并选择合适的证书和 TLS 配置。 默认证书和 ConfigureHttpsDefaults 不与此回调一起使用。

// using System.Security.Cryptography.X509Certificates;
// using Microsoft.AspNetCore.Server.Kestrel.Https;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5005, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            var localhostCert = CertificateLoader.LoadFromStoreCert(
                "localhost", "My", StoreLocation.CurrentUser,
                allowInvalid: true);
            var exampleCert = CertificateLoader.LoadFromStoreCert(
                "example.com", "My", StoreLocation.CurrentUser,
                allowInvalid: true);

            listenOptions.UseHttps((stream, clientHelloInfo, state, cancellationToken) =>
            {
                if (string.Equals(clientHelloInfo.ServerName, "localhost", StringComparison.OrdinalIgnoreCase))
                {
                    return new ValueTask<SslServerAuthenticationOptions>(new SslServerAuthenticationOptions
                    {
                        ServerCertificate = localhostCert,
                        // Different TLS requirements for this host
                        ClientCertificateRequired = true,
                    });
                }

                return new ValueTask<SslServerAuthenticationOptions>(new SslServerAuthenticationOptions
                {
                    ServerCertificate = exampleCert,
                });
            }, state: null);
        });
    });
});

配置中的 SNI

Kestrel 支持配置中定义的 SNI。 可以使用包含主机名和 HTTPS 选项之间的映射的 Sni 对象来配置终结点。 连接主机名与选项匹配,并且这些选项用于该连接。

以下配置将添加一个名为 MySniEndpoint 的终结点,该终结点使用 SNI 基于主机名选择 HTTPS 选项:

{
  "Kestrel": {
    "Endpoints": {
      "MySniEndpoint": {
        "Url": "https://*",
        "SslProtocols": ["Tls11", "Tls12"],
        "Sni": {
          "a.example.org": {
            "Protocols": "Http1AndHttp2",
            "SslProtocols": ["Tls11", "Tls12", "Tls13"],
            "Certificate": {
              "Subject": "<subject; required>",
              "Store": "<certificate store; required>",
            },
            "ClientCertificateMode" : "NoCertificate"
          },
          "*.example.org": {
            "Certificate": {
              "Path": "<path to .pfx file>",
              "Password": "$CREDENTIAL_PLACEHOLDER$"
            }
          },
          "*": {
            // At least one subproperty needs to exist per SNI section or it
            // cannot be discovered via IConfiguration
            "Protocols": "Http1",
          }
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "$CREDENTIAL_PLACEHOLDER$"
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作每个证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

可由 SNI 覆盖的 HTTPS 选项:

主机名支持通配符匹配:

  • 完全匹配。 例如,a.example.org 匹配 a.example.org
  • 通配符前缀。 如果有多个通配符匹配项,则选择最长的模式。 例如,*.example.org 匹配 b.example.orgc.example.org
  • 完整通配符。 * 匹配其他所有内容,包括不使用 SNI 且不发送主机名的客户端。

匹配的 SNI 配置将应用于连接的终结点,并重写终结点上的值。 如果连接与已配置的 SNI 主机名不匹配,则连接将被拒绝。

SNI 要求

  • 在目标框架 netcoreapp2.1 或更高版本上运行。 在 net461 或最高版本上,将调用回调,但是 name 始终为 null。 如果客户端未在 TLS 握手过程中提供主机名参数,则 name 也为 null
  • 所有网站在相同的 Kestrel 实例上运行。 Kestrel 在无反向代理时不支持跨多个实例共享一个 IP 地址和端口。

SSL/TLS 协议

SSL 协议是用于在两个对等机(传统上是客户端和服务器)之间加密和解密流量的协议。

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.SslProtocols = SslProtocols.Tls13;
    });
});
{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "SslProtocols": ["Tls12", "Tls13"],
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

默认值 SslProtocols.None 会导致 Kestrel 使用操作系统默认值来选择最佳协议。 除非你有特定原因要选择协议,否则请使用默认值。

客户端证书

ClientCertificateMode 配置客户端证书要求

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
    });
});
{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "ClientCertificateMode": "AllowCertificate",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "$CREDENTIAL_PLACEHOLDER$"
        }
      }
    }
  }
}

警告

在上一示例中,证书密码以纯文本形式存储在 appsettings.json 中。 $CREDENTIAL_PLACEHOLDER$ 令牌用作证书密码的占位符。 若要在开发环境中安全地存储证书密码,请参阅在开发过程中保护机密。 若要在生产环境中安全地存储证书密码,请参阅 Azure Key Vault 配置提供程序。 不应将开发机密用于生产或测试。

默认值为 ClientCertificateMode.NoCertificate,其中 Kestrel 不会从客户端请求或要求证书。

有关详细信息,请参阅在 ASP.NET Core 中配置证书身份验证

连接日志记录

调用 UseConnectionLogging 以发出用于进行连接上的字节级别通信的调试级别日志。 连接日志记录有助于排查低级通信中的问题,例如在 TLS 加密期间和代理后。 如果 UseConnectionLogging 放置在 UseHttps 之前,则会记录加密的流量。 如果 UseConnectionLogging 放置于 UseHttps 之后,则会记录解密的流量。 这是内置连接中间件

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseConnectionLogging();
    });
});

绑定到 TCP 套接字

Listen 方法绑定至 TCP 套接字,且 options lambda 允许 X.509 证书配置:

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(serverOptions =>
            {
                serverOptions.Listen(IPAddress.Loopback, 5000);
                serverOptions.Listen(IPAddress.Loopback, 5001, 
                    listenOptions =>
                    {
                        listenOptions.UseHttps("testCert.pfx", 
                            "testPassword");
                    });
            })
            .UseStartup<Startup>();
        });

示例使用 ListenOptions 为终结点配置 HTTPS。 可使用相同 API 为特定终结点配置其他 Kestrel 设置。

在 Windows 上,可以使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不受支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

在 macOS、Linux 和 Windows 上,可以使用 OpenSSL 创建证书。

绑定到 Unix 套接字

可通过 ListenUnixSocket 侦听 Unix 套接字以提高 Nginx 的性能,如以下示例所示:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock");
    serverOptions.ListenUnixSocket("/tmp/kestrel-test.sock", 
        listenOptions =>
        {
            listenOptions.UseHttps("testCert.pfx", 
                "testpassword");
        });
})
  • 在 Nginx 配置文件中,将 server>location>proxy_pass 条目设置为 http://unix:/tmp/{KESTREL SOCKET}:/;{KESTREL SOCKET} 是提供给 ListenUnixSocket 的套接字的名称(例如,上述示例中的 kestrel-test.sock)。
  • 确保套接字可由 Nginx (例如 chmod go+w /tmp/kestrel-test.sock)进行写入。

端口 0

如果指定端口号 0,Kestrel 将动态绑定到可用端口。 以下示例演示如何确定 Kestrel 在运行时绑定到的端口:

public void Configure(IApplicationBuilder app)
{
    var serverAddressesFeature =
        app.ServerFeatures.Get<IServerAddressesFeature>();

    app.UseStaticFiles();

    app.Run(async (context) =>
    {
        context.Response.ContentType = "text/html";
        await context.Response
            .WriteAsync("<!DOCTYPE html><html lang=\"en\"><head>" +
                "<title></title></head><body><p>Hosted by Kestrel</p>");

        if (serverAddressesFeature != null)
        {
            await context.Response
                .WriteAsync("<p>Listening on the following addresses: " +
                    string.Join(", ", serverAddressesFeature.Addresses) +
                    "</p>");
        }

        await context.Response.WriteAsync("<p>Request URL: " +
            $"{context.Request.GetDisplayUrl()}<p>");
    });
}

在应用运行时,控制台窗口输出指示可用于访问应用的动态端口:

Listening on the following addresses: http://127.0.0.1:48508

限制

使用以下方法配置终结点:

  • UseUrls
  • --urls 命令行参数
  • urls 主机配置键
  • ASPNETCORE_URLS 环境变量

若要将代码用于 Kestrel 以外的服务器,这些方法非常有用。 不过,请注意以下限制:

  • HTTPS 无法与这些方法结合使用,除非在 HTTPS 终结点配置中提供了默认证书(例如,使用 KestrelServerOptions 配置或配置文件,如本文前面的部分所示)。
  • 如果同时使用 ListenUseUrls 方法,Listen 终结点将覆盖 UseUrls 终结点。

IIS 终结点配置

使用 IIS 时,由 ListenUseUrls 设置用于 IIS 覆盖绑定的 URL 绑定。 有关详细信息,请参阅 ASP.NET Core 模块

ListenOptions.Protocols

Protocols 属性建立在连接终结点上或为服务器启用的 HTTP 协议(HttpProtocols)。 从 HttpProtocols 枚举向 Protocols 属性赋值。

HttpProtocols 枚举值 允许的连接协议
Http1 仅 HTTP/1.1。 可以在具有 TLS 或没有 TLS 的情况下使用。
Http2 仅 HTTP/2。 仅当客户端支持先验知识模式时,才可以在没有 TLS 的情况下使用。
Http1AndHttp2 HTTP/1.1 和 HTTP/2。 HTTP/2 要求客户端在 TLS 应用层协议协商 (ALPN) 握手过程中选择 HTTP/2;否则,连接默认为 HTTP/1.1。

任何终结点的默认 ListenOptions.Protocols 值都为 HttpProtocols.Http1AndHttp2

HTTP/2 的 TLS 限制:

  • TLS 版本 1.2 或更高版本
  • 重新协商已禁用
  • 压缩已禁用
  • 最小的临时密钥交换大小:
    • 椭圆曲线 Diffie-Hellman (ECDHE) [RFC4492]:最小 224 位
    • 有限字段 Diffie-Hellman (DHE) [TLS12]:最小 2048 位
  • 不禁止密码套件。

默认情况下,支持具有 P-256 椭圆曲线 [FIPS186] 的 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [TLS-ECDHE]。

以下示例允许端口 8000 上的 HTTP/1.1 和 HTTP/2 连接。 TLS 使用提供的证书来保护连接:

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
    });
});

在 Linux 上,CipherSuitesPolicy 可用于针对每个连接筛选 TLS 握手:

// using System.Net.Security;
// using Microsoft.AspNetCore.Hosting;
// using Microsoft.AspNetCore.Server.Kestrel.Core;
// using Microsoft.Extensions.DependencyInjection;
// using Microsoft.Extensions.Hosting;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        listenOptions.OnAuthenticate = (context, sslOptions) =>
        {
            sslOptions.CipherSuitesPolicy = new CipherSuitesPolicy(
                new[]
                {
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                    TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                    // ...
                });
        };
    });
});

连接中间件

必要时,自定义连接中间件可以按连接为特定密码筛选 TLS 握手。

下面的示例针对应用不支持的任何密码算法引发 NotSupportedException。 或者,定义 ITlsHandshakeFeature.CipherAlgorithm 并将其与可接受的密码套件列表进行比较。

没有哪种加密是使用 CipherAlgorithmType.Null 密码算法。

// using System.Net;
// using Microsoft.AspNetCore.Connections;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.UseTlsFilter();
    });
});
using System;
using System.Security.Authentication;
using Microsoft.AspNetCore.Connections.Features;

namespace Microsoft.AspNetCore.Connections
{
    public static class TlsFilterConnectionMiddlewareExtensions
    {
        public static IConnectionBuilder UseTlsFilter(
            this IConnectionBuilder builder)
        {
            return builder.Use((connection, next) =>
            {
                var tlsFeature = connection.Features.Get<ITlsHandshakeFeature>();

                if (tlsFeature.CipherAlgorithm == CipherAlgorithmType.Null)
                {
                    throw new NotSupportedException("Prohibited cipher: " +
                        tlsFeature.CipherAlgorithm);
                }

                return next();
            });
        }
    }
}

连接筛选也可以通过 IConnectionBuilder lambda 进行配置:

// using System;
// using System.Net;
// using System.Security.Authentication;
// using Microsoft.AspNetCore.Connections;
// using Microsoft.AspNetCore.Connections.Features;

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("testCert.pfx", "testPassword");
        listenOptions.Use((context, next) =>
        {
            var tlsFeature = context.Features.Get<ITlsHandshakeFeature>();

            if (tlsFeature.CipherAlgorithm == CipherAlgorithmType.Null)
            {
                throw new NotSupportedException(
                    $"Prohibited cipher: {tlsFeature.CipherAlgorithm}");
            }

            return next();
        });
    });
});

从配置中设置 HTTP 协议

CreateDefaultBuilder 在默认情况下调用 serverOptions.Configure(context.Configuration.GetSection("Kestrel")) 来加载 Kestrel 配置。

以下 appsettings.json 示例将 HTTP/1.1 建立为所有终结点的默认连接协议:

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1"
    }
  }
}

以下 appsettings.json 示例将为所有指定终结点建立 HTTP/1.1 连接协议:

{
  "Kestrel": {
    "Endpoints": {
      "HttpsDefaultCert": {
        "Url": "https://localhost:5001",
        "Protocols": "Http1"
      }
    }
  }
}

代码中指定的协议覆盖了由配置设置的值。

URL 前缀

如果使用 UseUrls--urls 命令行参数、urls 主机配置键或 ASPNETCORE_URLS 环境变量,URL 前缀可采用以下任意格式。

仅 HTTP URL 前缀是有效的。 使用 UseUrls 配置 URL 绑定时,Kestrel 不支持 HTTPS。

  • 包含端口号的 IPv4 地址

    http://65.55.39.10:80/
    

    0.0.0.0 是一种绑定到所有 IPv4 地址的特殊情况。

  • 包含端口号的 IPv6 地址

    http://[0:0:0:0:0:ffff:4137:270a]:80/
    

    [::] 是 IPv4 0.0.0.0 的 IPv6 等效项。

  • 包含端口号的主机名

    http://contoso.com:80/
    http://*:80/
    

    主机名、*+ 并不特殊。 没有识别为有效 IP 地址或 localhost 的任何内容都将绑定到所有 IPv4 和 IPv6 IP。 若要将不同主机名绑定到相同端口上的不同 ASP.NET Core 应用,请使用 HTTP.sys 或反向代理服务器。 反向代理服务器示例包括 IIS、Nginx 或 Apache。

    警告

    采用反向代理配置进行托管需要主机筛选

  • 包含端口号的主机 localhost 名称或包含端口号的环回 IP

    http://localhost:5000/
    http://127.0.0.1:5000/
    http://[::1]:5000/
    

    指定 localhost 后,Kestrel 将尝试绑定到 IPv4 和 IPv6 环回接口。 如果其他服务正在任一环回接口上使用请求的端口,则 Kestrel 将无法启动。 如果任一环回接口出于任何其他原因(通常是因为 IPv6 不受支持)而不可用,则 Kestrel 将记录一个警告。