PowerShell DSC SendReport

背景

PowerShell DSC Pull Server因为证书过期挂了一个月,重新好了,会收到所有已经注册的server(称之为Node,或者Agent)这段时间积累的请求。为啥?问就是By design。

ref: https://github.com/PowerShell/PowerShell/issues/2978

Nginx

然后Nginx 502错误

  1. upstream timed out
    加大nginx读取updtream的时间
    proxy_read_timeout 3600;

  2. no live upstreams while connecting to upstream
    这个本质问题是#1引发的

  3. worker_connections are not enough while connecting to upstream
    加大worker_connections
    events {
    worker_connections 20000;
    }

  4. WSARecv() failed (10054: An existing connection was forcibly closed by the remote host) while reading response header from upstream
    加大keepalive_requests和对应的keepalive_timeout

keepalive_requests 500;
keepalive_timeout 120s;

格式 和 作用,见:https://nginx.org/en/docs/ (别找错模块了)
逐个修复后,请求还是不断,查看Access log

Nginx Access log


可以看到2个请求每10分钟重试一直发,说明Report是失败了。

模拟Send Report

DSC web services没有开源,不清楚如何访问,只知道HTTP差不多可以访问

这个老哥说想要API

ref:https://github.com/dsccommunity/xPSDesiredStateConfiguration/issues/443

那就看看吧。

Specifies the Desired State Configuration Pull Model Protocol, which is used to get a client's configuration and modules from the server and to report the client's status back to the server. The protocol depends on HTTP for the transfer of all protocol messages.
ref: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dscpm/ea744c01-51a2-4000-9ef2-312711dcc8c9?redirectedfrom=MSDN

看到没,这是基于HTTP发明个了协议啊, Desired State Configuration Pull Model Protocol。

很快,我们就找到了SendReport的介绍:https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dscpm/9484a812-d90e-42a8-b4f4-392f2ae1076c
文档写的很好,Demo很到位呀,来,copy...

等等,是不是编码乱了,这特么是啥乱七八糟的...

明察秋毫,下载文档协议文档读读...

打开文档

这个有用的信息,说的描述用的是ABNF语法,看这里:https://tools.ietf.org/search/rfc4234

可以可以,很规范。

高超的英语水平发挥了巨大的作用,终于可以读懂了:

//等号理解为组成
 DSC-SendReport-Request  =  DSC-SendReport-Req-Line DSC-SendReportSetReq-Headers DSC-SendReportReq-Body

//SP 就是空格的意思  
 DSC-SendReport-Req-Line   = "POST" SP Request-URI SP HTTP-Version CRLF
 Request-URI = Request-URI-Start DSC-SendReportRequest-URI-End
            
 DSC-SendReportRequest-URI-End = "Node(AgentId=" SQUOTE AgentID SQUOTE RBRACKET FSLASH "SendReport"

//分号就是注释的意思
 SQUOTE = %x27  ;  ' (Single Quote)
 RBRACKET = %x29 ; ) (Closing Bracket)
 FSLASH = %x2F ; / (Forward Slash)
 AgentID = UUID ; as specified in [RFC4122] 

//星号就是一个或多个的意思
//斜杠就是或者的意思            
 DSC-SendReportSetReq-Headers = *( DSC-SendReportSetReq-Header-REQ
      / DSC-SendReportSetReq-Header-OPT ) 
            
 DSC-SendReportSetReq-Header-REQ    = Host    ; section 14.23 of [RFC2616] 
           / Accept ; section 14.1 of [RFC2616] 
           / ContentType ; section 2.2.2.1.2
           / Content-Length ; section 14.13 of [RFC2616] 
            
 DSC-SendReportSetReq-Header-OPT    = Connection  ; section 14.10 of [RFC2616] 
            / Expect ; section 14.20 of [RFC2616] 
            
 DSC-SendReportReq-Body = ReportRequest ; section 3.10.5.1.1.1

RequestBody也找到了JSON描述

 {
     "title": "SendReport request schema",
     "type": "object",
     "properties": {
         "JobId": {
             "type": [ "string", "null" ],            
             "required": "true"
         },
         "OperationType": {
             "type": [ "string", "null" ]
         },
         "RefreshMode": {
             "enum": [ "Push", "Pull" ]
         },
         "Status": {
             "type": [ "string", "null" ]
         },
         "LCMVersion": {
             "type": [ "string", "null" ]
         },
         "ReportFormatVersion": {
             "type": [ "string", "null" ]
         },
         "ConfigurationVersion": {
             "type": [ "string", "null" ]
         },
         "NodeName": {
             "type": [ "string", "null" ]
         },        
         "IpAddress": {
             "type": [ "string", "null" ]
         },
         "StartTime": {
             "type": [ "string", "null" ]
         },
         "EndTime": {
             "type": [ "string", "null" ]
         },
         "RebootRequested": {
             "enum": [ "True", "False" ]
         },
         "Errors": {
             "type": [ "string", "null" ]
         },
         "StatusData": {
             "type": [ "string", "null" ]
         },
         "AdditionalData": {
             "type": "array",
             "required": false,
             "items": [
                 {
                     "type": "object",
                     "required": true,
                     "properties": {
                         "Key": {
                             "type": "string",
                             "required": true
                         },
                         "Value": {
                             "type": "string",
                             "required": true
                         }
                     }
                 }
             ]
         }
     }
 }

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dscpm/4cd205ec-5b1c-426e-8176-d5e02a91106b

好的,有了以上的铺垫,我们就开始发个普通的HTTP请求吧。

按照上面的JSON描述,JobId是必须的,其他字段可以不传入或者为NULL的,可以我只传入JobId的时候,数据库并没有插入任何数据,但是却响应HTTP 200, 还是提示SavedReport。(这不是坑爹呢么?!)
经过我的验证,NodeName设置上之后,就可以成功入库了。

写入成功,读取当然可以直接读数据库,或者写脚本:https://docs.microsoft.com/en-us/powershell/dsc/pull-server/reportserver?view=dsc-1.1#getting-report-data
但凡认真看下这个,GET请求这么发送,POST还要那么复杂的去读协议?

posted @ 2022-03-17 22:59  talentzemin  阅读(114)  评论(0编辑  收藏  举报