填坑 | .NET 在Docker中訪問MSSQL報錯

語言: CN / TW / HK

【.NET Core 作者 

/ Edison Zhou

不知道你有沒有在.NET Core/.NET 5的Docker訪問MS SQL Server資料庫,如果有,那麼很有可能會遇到這個錯誤。

1 SSL版本錯誤

最近在公司用.NET 5重構部分業務服務,由於之前老系統使用了MS SQL Server資料庫,因此本次重構也決定繼續使用。但是,在將.NET 5應用部署到Docker中通過Swagger測試時,卻報了以下一個錯誤:

Microsoft.Data.SqlClient.SqlException (0x80131904): 
A connection was successfully established with the server,
but then an error occurred during the pre-login handshake.
(provider: TCP Provider, error: 35 - An internal exception was caught)
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.

從字面意思來看,看不出來是啥,只能定位這一句 SSL_ERROR_SSL。搜尋一番,發現 在.NET Core/.NET 5的容器映象中的OpenSSL的最低協議版本要求為TLSv1.2,而我們的MS SQL Server所用的版本較低,不支援TLSv1.2只支援TLSv1

我們可以進入容器內部去驗證下:

# docker exec -it <docker-name> /bin/bash
# cat /etc/ssl/openssl.cnf


.......
[system_default_sect]
MinProtocol = TLSv1.2
CipherString = DEFAULT@SECLEVEL=2

因此,明確了問題,直接在容器內部更改一下:將TLSv1.2改為TLSv1即可

# docker exec -it <docker-name> /bin/bash
# vi /etc/ssl/openssl.cnf


.......
[system_default_sect]
MinProtocol = TLSv1
CipherString = [email protected]=2

更改完成後,再次訪問介面,就不會報錯了。

2 修改Dockerfile

上面的方法只是一個臨時方案,重新打映象執行又會恢復為TLSv1.2。因此,我們需要更改Dockerfile,讓其在源映象中就更改為TLSv1。

這裡以一個簡單的Dockerfile為例,只需要在微軟.NET 5映象源的層中增加一行指令即可:

RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf

完整Dockerfile示例:

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf
EXPOSE 80


FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["AuthCenter.API/AuthCenter.API.csproj", "AuthCenter.API/"]
RUN dotnet restore "AuthCenter.API/AuthCenter.API.csproj"
COPY . .
WORKDIR "/src/AuthCenter.API"
RUN dotnet build "AuthCenter.API.csproj" -c Release -o /app/build


FROM build AS publish
RUN dotnet publish "AuthCenter.API.csproj" -c Release -o /app/publish


FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "AuthCenter.API.dll"]

其他相關聯的OpenSSL錯誤:

Microsfot.Data.SqlClient.SqlException(0x80131904):
A connection was successfully established with the server,
but then an error occurred during the pre-login handshake.
(provide:SSL Provider,error:31 - Encryption(ssl/tls) handshake failed)

這個錯誤和上面的error: 35類似,也是TLS協議版本較高,而SQL Server不支援。修改方法也是改為TLSv1,這裡需要注意的是,我發現網上很多文章都是建議改為TLSv1.0,也就是下面的指令:

RUN sed -i 's/TLSv1.2/TLSv1.0/g' /etc/ssl/openssl.cnf

對於網上大多數童鞋,上面的語句是適用的,但是也有一些和我一樣的,即使使用了上面的語句還不行。一番搜尋發現,需要改為TLSv1(即去掉小數點)才能工作。

3 關於TLS協議

TLS是在TCP傳輸層之上,應用層之下實現的網路安全方案。在TCP/IP四層網路模型中屬於應用層協議。TLS協議在兩個通訊應用程式之間提供資料保密性和資料完整性,另外還提供了連線身份可靠性方案。

UDP則使用DTLS協議實現安全傳輸,和TLS協議類似。

TLS協議的設計目的如下:

(1)加密安全:TLS應用於雙方之間建立安全連線,通過加密,簽名,資料摘要保障資訊保安。

(2 互操作性:程式設計師在不清楚TLS協議的情況下,只要對端程式碼符合RFC標準的情況下都可以實現互操作。

(3 可擴充套件性:在必要時可以通過擴充套件機制新增新的公鑰和機密方法,避免建立新協議。

(4 相對效率:加密需要佔用大量CPU,尤其是公鑰操作。TLS協議握手完成後,通過對稱金鑰加密資料。TLS還集成了會話快取方案,減少需要從頭建立連線的情況。

TLS協議所處的位置如下所示:

更多關於TLS協議的介紹:

http://www.cnblogs.com/Jack-Blog/p/13170728.html

End 總結

在要求安全性越來越高的前提下,TLSv1.2被廣泛應用,為了適配MS SQL Server的低版本,可以選擇在Dockefile中降低TLS協議最低版本要求來解決問題。不過,這畢竟是一個不安全的方法,如果有條件,還是建議升級MS SQL Server所在伺服器的TLS配置,使其支援TLSv1.2。

年終總結: Edison的2020年終總結

數字化轉型: Edison的數字化轉型之旅總結

C#刷題: C#刷劍指Offer演算法題系列文章目錄

.NET面試: .NET開發面試知識體系八股文

.NET大會: 2020年中國.NET開發者大會PDF資料