保护 MOF 文件

适用于:Windows PowerShell 4.0、Windows PowerShell 5.0

DSC 通过应用存储在 MOF 文件中的信息来管理服务器节点的配置,其中本地配置管理器 (LCM) 实现所需的结束状态。 由于此文件包含配置的详细信息,因此请务必确保其安全。 本文介绍如何确保目标节点已加密文件。

从 PowerShell 版本 5.0 开始,默认情况下,使用 cmdlet 将 Start-DSCConfiguration 整个 MOF 文件应用于节点时,会对其进行加密。 仅当使用拉取服务协议实现解决方案时,如果未管理证书,本文中所述的过程才需要,以确保目标节点下载的配置在应用之前可以由系统解密和读取(例如,Windows Server 中可用的拉取服务)。 注册到 Azure 自动化 DSC 的节点将自动安装证书并由服务管理,无需管理开销。

注释

本主题讨论用于加密的证书。 对于加密,自签名证书就足够了,因为私钥始终保密,加密并不意味着对文档的信任。 自签名证书 应用于身份验证目的。 出于任何身份验证目的,应使用来自受信任证书颁发机构 (CA) 的证书。

先决条件

若要成功加密用于保护 DSC 配置的凭据,请确保您具备以下条件:

  • 发和分发证书的一些方式。 本主题及其示例假定您使用的是 Active Directory 证书颁发机构。 有关 Active Directory 证书服务的更多背景信息,请参阅 Active Directory 证书服务概述
  • 对一个或多个目标节点的管理访问权限
  • 每个目标节点都有一个支持加密的证书,保存其个人存储。 在 Windows PowerShell 中,存储的路径为 Cert:\LocalMachine\My。 本主题中的示例使用“工作站身份验证”模板,您可以在 默认证书模板中找到该模板(以及其他证书模板)。
  • 如果要在目标节点以外的计算机上运行此配置,请 导出证书的公钥,然后将其导入到要从中运行配置的计算机。 确保仅导出 钥;确保私钥安全。

注释

脚本资源在加密方面存在局限性。 有关详细信息,请参阅 脚本资源

整个过程

  1. 设置证书、密钥和指纹,确保每个目标节点都有证书的副本,并且配置计算机具有公钥和指纹。
  2. 创建包含公钥的路径和指纹的配置数据块。
  3. 创建一个配置脚本,用于定义目标节点的所需配置,并通过命令本地配置管理器使用证书及其指纹解密配置数据,在目标节点上设置解密。
  4. 运行配置,这将设置本地配置管理器设置并启动 DSC 配置。

凭据加密的流程

证书要求

若要实施凭据加密,目标节点上必须提供公钥证书,该 目标节点 受用于创作 DSC 配置的计算机 所信任 。 此公钥证书对用于 DSC 凭据加密有特定要求:

  1. 主要用法
    • 必须包含:“KeyEncipherment”和“DataEncipherment”。
    • 不应包含:“数字签名”。
  2. 增强的密钥使用
    • 必须包含:文档加密 (1.3.6.1.4.1.311.80.1)。
    • 不应包含:客户端身份验证 (1.3.6.1.5.5.7.3.2) 和服务器身份验证 (1.3.6.1.5.5.7.3.1)。
  3. 证书的私钥在 *Target Node_上可用。
  4. 证书的 提供程序 必须是“Microsoft RSA SChannel 加密提供程序”。

重要

尽管可以使用包含“数字签名”密钥用法或身份验证 EKU 之一的证书,但这将使加密密钥更容易被滥用并容易受到攻击。 因此,最佳做法是使用专门为保护 DSC 凭据而创建的证书,该证书省略了这些密钥用法和 EKU。

目标节点上满足这些条件的任何现有证书都可用于保护 DSC 凭据。

证书创建

可以采用两种方法来创建和使用所需的加密证书(公钥-私钥对)。

  1. 目标节点 上创建它,并仅将公钥导出到 创作节点
  2. 创作节点 上创建它,并将整个密钥对导出到 目标节点

建议使用方法 1,因为用于解密 MOF 中凭据的私钥始终保留在目标节点上。

在目标节点上创建证书

私钥必须保密,因为用于解密 目标节点 上的 MOF最简单的方法是在 目标节点上创建私钥证书,并将 公钥证书 复制到用于将 DSC 配置创作到 MOF 文件中的计算机。 下面的示例:

  1. 目标节点上创建证书
  2. 导出 目标节点上的公钥证书。
  3. 将公钥证书导入到创作节点上的我的证书存储中。

在目标节点上:创建和导出证书

目标节点:Windows Server 2016 和 Windows 10

# note: These steps need to be performed in an Administrator PowerShell session
$cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'DscEncryptionCert' -HashAlgorithm SHA256
# export the public key certificate
$cert | Export-Certificate -FilePath "$env:temp\DscPublicKey.cer" -Force

导出后,需要将 复制 DscPublicKey.cer创作节点

在创作节点上:导入证书的公钥

# Import to the my store
Import-Certificate -FilePath "$env:temp\DscPublicKey.cer" -CertStoreLocation Cert:\LocalMachine\My

在创作节点上创建证书

或者,可以在 创作节点上创建加密证书,将 私钥 导出为 PFX 文件,然后导入到 目标节点上。 这是在 Nano Server 上实现 DSC 凭据加密的当前方法。 尽管 PFX 使用密码进行保护,但在传输过程中应保持安全。 下面的示例:

  1. 创作节点上创建证书。
  2. 导出证书,包括 创作节点上的私钥。
  3. 创作节点中删除私钥,但将公钥证书保留在 我的 存储中。
  4. 将私钥证书导入目标 节点上的“我的(个人)”证书存储中。
    • 它必须添加到根存储中,以便 目标节点信任它。

在创作节点上:创建和导出证书

目标节点:Windows Server 2016 和 Windows 10

# note: These steps need to be performed in an Administrator PowerShell session
$cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'DscEncryptionCert' -HashAlgorithm SHA256
# export the private key certificate
$mypwd = ConvertTo-SecureString -String "YOUR_PFX_PASSWD" -Force -AsPlainText
$cert | Export-PfxCertificate -FilePath "$env:temp\DscPrivateKey.pfx" -Password $mypwd -Force
# remove the private key certificate from the node but keep the public key certificate
$cert | Export-Certificate -FilePath "$env:temp\DscPublicKey.cer" -Force
$cert | Remove-Item -Force
Import-Certificate -FilePath "$env:temp\DscPublicKey.cer" -CertStoreLocation Cert:\LocalMachine\My

导出后,需要将 复制 DscPrivateKey.pfx目标节点

在目标节点上:将证书的私钥导入为受信任的根

# Import to the root store so that it is trusted
$mypwd = ConvertTo-SecureString -String "YOUR_PFX_PASSWD" -Force -AsPlainText
Import-PfxCertificate -FilePath "$env:temp\DscPrivateKey.pfx" -CertStoreLocation Cert:\LocalMachine\My -Password $mypwd > $null

配置数据

配置数据块定义要在哪些目标节点上运行、是否加密凭据、加密方式和其他信息。 有关配置数据块的详细信息,请参阅分离 配置数据和环境数据

可以为与凭证加密相关的每个节点配置的元素包括:

  • NodeName - 为其配置凭据加密的目标节点的名称。
  • PsDscAllowPlainTextPassword - 是否允许将未加密的凭据传递到此节点。 不 建议这样做。
  • 指纹 - 证书的指纹,用于解密 目标节点上 DSC 配置中的凭据。 此证书必须存在于目标节点上的本地计算机证书存储中。
  • CertificateFile - 证书文件(仅包含公钥),应用于加密 目标节点的凭据。 这必须是 DER 编码的二进制 X.509 或 Base-64 编码的 X.509 格式证书文件。

此示例演示了一个配置数据块,该数据块指定要对命名的 targetNode 执行作的目标节点、公钥证书文件的路径(名为 targetNode.cer)以及公钥的指纹。

$ConfigData = @{
    AllNodes = @(
        @{
            # The name of the node we are describing
            NodeName        = "targetNode"

            # The path to the .cer file containing the
            # public key of the Encryption Certificate
            # used to encrypt credentials for this node
            CertificateFile = "C:\publicKeys\targetNode.cer"


            # The thumbprint of the Encryption Certificate
            # used to decrypt the credentials on target node
            Thumbprint      = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8"
        }
    )
}

配置脚本

在配置脚本本身中,使用该 PsCredential 参数来确保凭据的存储时间尽可能短。 运行提供的示例时,DSC 将提示你输入凭据,然后使用与配置数据块中的目标节点关联的 CertificateFile 加密 MOF 文件。 此代码示例从受保护的共享复制文件,该共享保护给用户。

configuration CredentialEncryptionExample
{
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullorEmpty()]
        [PsCredential] $credential
    )

    Node $AllNodes.NodeName
    {
        File exampleFile
        {
            SourcePath = "\\Server\share\path\file.ext"
            DestinationPath = "C:\destinationPath"
            Credential = $credential
        }
    }
}

设置解密

Start-DscConfiguration 工作之前,必须告知每个目标节点上的本地配置管理器使用哪个证书来解密凭据,并使用 CertificateID 资源验证证书的指纹。 此示例函数将查找适当的本地证书(您可能需要对其进行自定义,以便它找到您要使用的确切证书):

# Get the certificate that works for encryption
function Get-LocalEncryptionCertificateThumbprint
{
    (dir Cert:\LocalMachine\my) | %{
        # Verify the certificate is for Encryption and valid
        if ($_.PrivateKey.KeyExchangeAlgorithm -and $_.Verify())
        {
            return $_.Thumbprint
        }
    }
}

使用由指纹标识的证书,可以更新配置脚本以使用以下值:

configuration CredentialEncryptionExample
{
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullorEmpty()]
        [PsCredential] $credential
    )

    Node $AllNodes.NodeName
    {
        File exampleFile
        {
            SourcePath = "\\Server\share\path\file.ext"
            DestinationPath = "C:\destinationPath"
            Credential = $credential
        }

        LocalConfigurationManager
        {
             CertificateId = $node.Thumbprint
        }
    }
}

运行配置

此时,您可以运行配置,它将输出两个文件:

  • 一个文件, *.meta.mof 用于将本地配置管理器配置为使用存储在本地计算机存储中并由其指纹标识的证书解密凭据。 Set-DscLocalConfigurationManager 应用该 *.meta.mof 文件。
  • 实际应用配置的 MOF 文件。 Start-DscConfiguration 应用配置。

这些命令将完成以下步骤:

Write-Host "Generate DSC Configuration..."
CredentialEncryptionExample -ConfigurationData $ConfigData -OutputPath .\CredentialEncryptionExample

Write-Host "Setting up LCM to decrypt credentials..."
Set-DscLocalConfigurationManager .\CredentialEncryptionExample -Verbose

Write-Host "Starting Configuration..."
Start-DscConfiguration .\CredentialEncryptionExample -wait -Verbose

此示例会将 DSC 配置推送到目标节点。 也可以使用 DSC 拉取服务器(如果可用)应用 DSC 配置。

有关使用 DSC 拉取服务器应用 DSC 配置的更多信息,请参阅设置 DSC 拉取客户端

凭证加密模块示例

下面是一个完整的示例,其中包含所有这些步骤,以及用于导出和复制公钥的帮助程序 cmdlet:

# A simple example of using credentials
configuration CredentialEncryptionExample
{
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullorEmpty()]
        [PsCredential] $credential
    )

    Node $AllNodes.NodeName
    {
        File exampleFile
        {
            SourcePath = "\\server\share\file.txt"
            DestinationPath = "C:\Users\user"
            Credential = $credential
        }

        LocalConfigurationManager
        {
            CertificateId = $node.Thumbprint
        }
    }
}

# A Helper to invoke the configuration, with the correct public key
# To encrypt the configuration credentials
function Start-CredentialEncryptionExample
{
    [CmdletBinding()]
    param ($computerName)

    [string] $thumbprint = Get-EncryptionCertificate -computerName $computerName -Verbose
    Write-Verbose "using cert: $thumbprint"

    $certificatePath = join-path -Path "$env:SystemDrive\$script:publicKeyFolder" -childPath "$computername.EncryptionCertificate.cer"

    $ConfigData=    @{
        AllNodes = @(
                        @{
                            # The name of the node we are describing
                            NodeName = "$computerName"

                            # The path to the .cer file containing the
                            # public key of the Encryption Certificate
                            CertificateFile = "$certificatePath"

                            # The thumbprint of the Encryption Certificate
                            # used to decrypt the credentials
                            Thumbprint = $thumbprint
                        };
                    );
    }

    Write-Verbose "Generate DSC Configuration..."
    CredentialEncryptionExample -ConfigurationData $ConfigData -OutputPath .\CredentialEncryptionExample `
        -credential (Get-Credential -UserName "$env:USERDOMAIN\$env:USERNAME" -Message "Enter credentials for configuration")

    Write-Verbose "Setting up LCM to decrypt credentials..."
    Set-DscLocalConfigurationManager .\CredentialEncryptionExample -Verbose

    Write-Verbose "Starting Configuration..."
    Start-DscConfiguration .\CredentialEncryptionExample -wait -Verbose
}

#region HelperFunctions

# The folder name for the exported public keys
$script:publicKeyFolder = "publicKeys"

# Get the certificate that works for encryptions
function Get-EncryptionCertificate
{
    [CmdletBinding()]
    param ($computerName)

    $returnValue= Invoke-Command -ComputerName $computerName -ScriptBlock {
        $certificates = dir Cert:\LocalMachine\my

        $certificates | %{
                    # Verify the certificate is for Encryption and valid
            if ($_.PrivateKey.KeyExchangeAlgorithm -and $_.Verify())
            {
                # Create the folder to hold the exported public key
                $folder= Join-Path -Path $env:SystemDrive\ -ChildPath $using:publicKeyFolder
                if (! (Test-Path $folder))
                {
                    md $folder | Out-Null
                }

                # Export the public key to a well known location
                $certPath = Export-Certificate -Cert $_ -FilePath (Join-Path -path $folder -childPath "EncryptionCertificate.cer")

                # Return the thumbprint, and exported certificate path
                return @($_.Thumbprint,$certPath);
            }
        }
    }

    Write-Verbose "Identified and exported cert..."
    # Copy the exported certificate locally
    $destinationPath = join-path -Path "$env:SystemDrive\$script:publicKeyFolder" -childPath "$computername.EncryptionCertificate.cer"
    Copy-Item -Path (join-path -path \\$computername -childPath $returnValue[1].FullName.Replace(":","$"))  $destinationPath | Out-Null

    # Return the thumbprint
    return $returnValue[0]
}

Start-CredentialEncryptionExample