http內網穿透CYarp[開源]

jiulang發表於2024-03-19

0 前言

在物聯網領域中,mqtt訊息一直是海量裝置連線到平臺的標配協議,而平臺向移動端開放的操作介面往往是http協議,這就要求平臺為兩種協議作訊息一一適配。在某些情況下,這些裝置是有作業系統的linux或安卓裝置,如果我們換個思路,讓這些裝置直接提供http協議的操作介面(httpd伺服器),平臺端使用隧道打通裝置與移動端的傳輸層,做為平臺開發人員,就可以省去時間做這些繁瑣的訊息級別的一一適配工作了。

1 面臨的問題

1.1 海量連線

mqtt預設基於tcp傳輸,一臺裝置與平臺維持一個tcp連線即可高效的上下並行傳輸訊息。而http/1.1在協議層限制了單個連線序列請求響應,但我們可以使用http/2.0或http/3.0做為http/1.1的傳輸層,也能達到一個物理連結多路傳輸http/1.1的目的。這樣下來內網http/1.1穿透也和mqtt一樣,只需要一個物理長連線。

1.2 身份認證

我嘗試使用frp來承載物聯網裝置的http內網穿透,但至今還沒成功完成海量裝置的一機一密的身份認證,它裡面的token認證方式只適合一平臺對一個裝置的安全要求,而OIDC驗證我至今仍然看不明白怎麼使用。對於一款內網穿透元件或應用,能像asp.netcore的身份驗證強大,對平臺端而言是非常需要的。

1.3 安全傳輸

裝置內建的http伺服器,一般都是沒有https,因為它本身就不考慮公網傳輸的能力。但在內網穿透之後,我們必須要考慮裝置到公網平臺這段的傳輸安全。

1.4 開放協議

內網穿透元件涉及到平臺的服務端和整合在裝置裡的客戶端庫,平臺端一般只有二次開發的需求。而裝置端由於晶片、系統和指令集、記憶體限制等等因素,只提供客戶端庫或二進位制可執行檔案是不夠的,還可能需要客戶端開發者根據互動協議來自行開發客戶端元件。這就要求內網穿透元件提供客戶端與服務端的互動協議,且最好是設計為非常簡單的協議。

2 CYarp出場

CYarp基於Yarp的http內網穿透中介軟體,支援tcp、http/2.0或http/3.0作為http/1.1的傳輸層,其具有以下特徵

  1. 是一個標準的asp.netcore中介軟體
  2. 能完整使用asp.netcore框架和其它中介軟體
  3. 使用服務端的tls(https)做為安全傳輸層
  4. 單連線多路複用,無需客戶端提供http2伺服器
  5. 協議透明簡單,參考了WebSocket升級和Bootstrapping WebSockets with HTTP/2

2.1 服務端開發

CYarp中介軟體其依賴於Authentication身份認證中介軟體,使用如下方法進行註冊和中介軟體的配置。

builder.Services.AddCYarp(cyarp=>
{
    ...
});

中介軟體配置順序如下:

...
app.UseAuthentication();
...
app.UseCYarp();
...

最後在Controller、minapi的處理者或中介軟體中處理http轉發

// 請求者的授權驗證
[Authorize(Roles = "Mobile")]
public class CYarpController : ControllerBase
{ 
    private static readonly string clientIdClaimType = "ClientId";

    /// <summary>
    /// 處理cyarp
    /// 核心操作是從請求上下文獲取clientId
    /// 然後使用clientId從clientManager獲取client來轉發http
    /// </summary>
    /// <param name="clientManager"></param>
    /// <returns></returns>
    [Route("/{**cyarp}")]
    public async Task InvokeAsync([FromServices] IClientManager clientManager)
    {
        var clientId = this.User.FindFirstValue(clientIdClaimType);
        if (clientId != null && clientManager.TryGetValue(clientId, out var client))
        {
            this.Request.Headers.Remove(HeaderNames.Authorization);
            await client.ForwardHttpAsync(this.HttpContext);
        }
        else
        {
            this.Response.StatusCode = StatusCodes.Status502BadGateway;
        }
    }
}

2.2 客戶端開發

使用CYary.Client.CYarpClient很方便完成客戶端開發

 using var client = new CYarpClient();
 while (true))
 {
     await client.TransportAsync(this.clientOptions.CurrentValue, stoppingToken).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
 }

2.3 客戶端協議

由於篇幅有限,不在這裡展開,可以查閱CYarp握手協議

3 同源產品

Yarp是dotnet平臺下堪比nginx的一個元件,其它融入asp.netcore框架作為裡面的中介軟體共享asp.netcore生態。

davidfowl大神也曾經小手一揮造就了YarpTunnelDemo專案,也是實現了http內網穿透的能力。但是他的實現方案要求客戶端方能要執行asp.netcore監聽做為客戶端元件,由於asp.netcore的runtime只適配了桌面系統,且編譯出的二進位制檔案很大,此方法自然無法在物聯網裝置中執行了。

相關文章