问题描述
为了对Docker进行远程控制,Docker可以提供一个Web API。
此API可能根本不需要身份验证(不建议使用),也可以不使用证书身份验证。
问题在于本机证书身份验证不提供证书吊销检查。 这可能会带来严重的后果。
我想告诉我如何解决这个问题。
解决问题
首先,我要说的是关于Windows的Docker。 Linux可能并没有那么糟糕,但现在还不是。
我们有什么? 我们有一个带有这种配置的Docker:
{ "hosts": ["tcp://0.0.0.0:2376", "npipe://"], "tlsverify": true, "tlscacert": "C:\\ssl\\ca.cer", "tlscert": "C:\\ssl\\server.cer", "tlskey": "C:\\ssl\\server.key" }
客户端可以使用其证书进行连接,但是不会检查这些证书是否吊销。
解决该问题的想法是编写自己的代理服务,该代理服务将充当中介。 我们的服务将与Docker安装在同一服务器上,它将拾取端口2376,并将通过//./pipe/docker_engine与Docker通信。
我三思而后行,创建了一个ASP.NET Core项目并进行了最简单的代理:
最简单的代理代码 app.Run(async (context) => { var certificate = context.Connection.ClientCertificate; if (certificate != null) { logger.LogInformation($"Certificate subject: {certificate.Subject}, serial: {certificate.SerialNumber}"); } var handler = new ManagedHandler(async (host, port, cancellationToken) => { var stream = new NamedPipeClientStream(".", "docker_engine", PipeDirection.InOut, PipeOptions.Asynchronous); var dockerStream = new DockerPipeStream(stream); await stream.ConnectAsync(NamedPipeConnectTimeout.Milliseconds, cancellationToken); return dockerStream; }); using (var client = new HttpClient(handler, true)) { var method = new HttpMethod(context.Request.Method); var builder = new UriBuilder("http://dockerengine") { Path = context.Request.Path, Query = context.Request.QueryString.ToUriComponent() }; using (var request = new HttpRequestMessage(method, builder.Uri)) { request.Version = new Version(1, 11); request.Headers.Add("User-Agent", "proxy"); if (method != HttpMethod.Get) { request.Content = new StreamContent(context.Request.Body); request.Content.Headers.ContentType = new MediaTypeHeaderValue(context.Request.ContentType); } using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted)) { context.Response.ContentType = response.Content.Headers.ContentType.ToString(); var output = await response.Content.ReadAsStreamAsync(); await output.CopyToAsync(context.Response.Body, 4096, context.RequestAborted); } } } });
这足以应付来自Docker API的简单GET和POST请求。 但这还不够,因为 对于更复杂的操作(需要用户输入),Docker使用类似于WebSocket的方法。 伏击是,Kestrel断然拒绝接受来自Docker客户端的请求,理由是请求中的连接可能没有正文:Upgrade标头。 但是确实如此。
我不得不放弃Kestrel并编写更多代码。 实际上-您自己的Web服务器。 独立地打开端口,创建TLS连接,解析HTTP标头,与Docker建立内部连接并交换输入/输出流。 而且有效。
资料来源可以在这里找到。
因此,该应用程序已编写,因此有必要以某种方式运行它。 这个想法是用我们的应用程序创建一个容器,将npine抛出://内部并发布端口2376
构建一个Docker镜像
要构建映像,我们需要证书颁发机构(ca.cer)的公共证书,该证书将向用户颁发证书。
该证书将安装在将启动我们的代理的容器的受信任的根证书颁发机构中。
它的安装对于证书验证过程是必需的。
我没有费心编写自己创建应用程序的Docker文件。
因此,必须独立组装。 从带有dockerfile的文件夹中运行:
dotnet publish -c Release -o ..\publish .\DockerTLS\DockerTLS.csproj
现在,我们应该有: Dockerfile
, publish
, ca.cer
。 我们收集图像:
docker build -t vitaliyorg.azurecr.io/docker/proxy:1809 . docker push vitaliyorg.azurecr.io/docker/proxy:1809
当然,图像的名称可以是任何名称。
发射
要启动容器,我们需要服务器certificate.pfx
和密码为password.txt
的文件。 所有文件内容均视为密码。 因此,不应有多余的换行符。
让所有这些东西都在文件夹中: c:\data
安装Docker的服务器上的c:\data
。
在同一服务器上,运行:
docker run --name docker-proxy -d -v "c:/data:c:/data" -v \\.\pipe\docker_engine:\\.\pipe\docker_engine --restart always -p 2376:2376 vitaliyorg.azurecr.io/docker/proxy:1809
记录中
使用docker logs
您可以看到谁做了什么。 您还可以查看失败的连接尝试。