The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何在 Windows Containers 安全的調整 IIS 站台設定

當你想要對 IIS 站台調整許多設定,又怕調整到一半突然出問題,這時候會殘留許多調整到一半的設定,如果想要做到 IaC (Infrastructure as code) 自動化,這個問題就必須被解決。還好 IISAdministration 模組中有提供「交易」相關的 Cmdlets 可用,本篇文章就來說說如何使用它。

建立容器

官方的 mcr.microsoft.com/dotnet/framework/aspnet:4.8 容器映象,已經預先安裝好 Web Server (IIS)ASP.NET 4.8 角色服務,我將會使用這個 image 為主要的執行環境。

docker run --name=mysite -d --isolation=process -p 80:80 -p 443:443 -v C:\Projects\WebApplication1:C:\Inetpub\wwwroot mcr.microsoft.com/dotnet/framework/aspnet:4.8

以下對上述參數進行說明:

  • --name=mysite 設定容器好記名稱為 mysite,方便後續命令說明。
  • -d 代表我們要將容器執行在 detach 模式(背景執行)。
  • --isolation=process 由於我們要複製現有的 PFX 憑證進容器,在 Windows 10 的 Windows Containers 必須要使用 proccess 隔離模式執行,才能執行 docker cp 命令複製檔案。
  • -p 80:80 -p 443:443 由於 Windows Containers 執行容器時,預設採用 nat 模式,所以需要將容器的 IP:Ports 對應到本機 Ports 比較方便測試。
  • -v C:\Projects\WebApplication1:C:\Inetpub\wwwroot 將一個現有的 ASP.NET MVC 5 專案對應到容器的 C:\Inetpub\wwwroot 路徑。
  • --entrypoint powershell 因為微軟官方的 aspnet 容器映象預設只能跑在 detach (-d) 模式,為了方便我們測試,我想改用 powershell 來當成預設進入程式。
  • mcr.microsoft.com/dotnet/framework/aspnet:4.8 已經預載 Web Server (IIS)ASP.NET 4.8 角色服務。這是微軟官方的「多架構」容器映象,詳見 Windows Container 版本相容性與多重架構容器映像介紹 文章。

接著透過 docker exec 進入容器中操作:

docker exec -it -w c:\inetpub\wwwroot mysite powershell

新增站台繫結

  1. 新增 HTTP 站台繫結

    綁定特定域名 ( 以 blog.miniasp.com 為例 )

    New-IISSiteBinding -Name "Default Web Site" -Protocol http -BindingInformation "*:80:blog.miniasp.com"
    

    綁定特定IP ( 以 192.168.9.100 為例 )

    New-IISSiteBinding -Name "Default Web Site" -Protocol http -BindingInformation "192.168.9.100:80:"
    

    綁定特定埠號 ( 以 8080 為例 )

    New-IISSiteBinding -Name "Default Web Site" -Protocol http -BindingInformation ":8080:"
    

    移除綁定

    Remove-IISSiteBinding -Name "Default Web Site" -Protocol http -BindingInformation "*:80:blog.miniasp.com" -Confirm:$false
    Remove-IISSiteBinding -Name "Default Web Site" -Protocol http -BindingInformation "192.168.9.100:80:" -Confirm:$false
    Remove-IISSiteBinding -Name "Default Web Site" -Protocol http -BindingInformation ":8080:" -Confirm:$false
    
  2. 新增 HTTPS 站台繫結

    綁定 IP-based SSL (不指定特定域名)

    $cert = New-SelfSignedCertificate -DnsName blog.miniasp.com -CertStoreLocation cert:\LocalMachine\My
    
    New-IISSiteBinding -Name "Default Web Site" -Protocol https -BindingInformation "*:443:" -CertificateThumbPrint $cert.Thumbprint -CertStoreLocation My
    

    綁定 SNI-based SSL (指定特定域名)

    $cert = New-SelfSignedCertificate -DnsName blog.miniasp.com -CertStoreLocation cert:\LocalMachine\My
    
    New-IISSiteBinding -Name "Default Web Site" -Protocol https -BindingInformation "*:443:blog.miniasp.com" -SslFlag Sni -CertificateThumbPrint $cert.Thumbprint -CertStoreLocation My
    

    移除綁定

    Remove-IISSiteBinding -Name "Default Web Site" -Protocol https -BindingInformation "*:443:" -Confirm:$false
    Remove-IISSiteBinding -Name "Default Web Site" -Protocol https -BindingInformation "*:443:blog.miniasp.com" -Confirm:$false
    

批次新增站台繫結

如果你一次需要新增好幾個站台繫結,或是一口氣要用 PowerShell 調整多項 IIS 設定的話,透過一行一行執行其實是不太好的。因為這些 PowerShell Cmdlets 每次執行時,都會確實寫入到 applicationHost.config 設定檔中,若你有其中一段設定掛掉的話,就可能會有設定不一致的問題!

IISAdministration 有兩個 Cmdlets 可以讓你的批次作業擁有「交易特性」(Transaction)。

以下是使用範例:

Start-IISCommitDelay

New-IISSite -Name "Test" -PhysicalPath "c:\Inetpub\wwwroot" -BindingInformation ":80:www.example.com" -Protocol http
New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i1.example.com"
New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i2.example.com"
New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i3.example.com"
New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i4.example.com"
New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i5.example.com"

Stop-IISCommitDelay -Commit $True

如果你發現設定的過程有問題發生,就可以用 Stop-IISCommitDelay -Commit $False 來取消取消之前所有的變更!

以下是完整的使用範例:

$ErrorActionPreference = "Stop"

Get-IISSite

try
{
  Write-Host "開始建立站台"
  Start-IISCommitDelay

  New-IISSite -Name "Test" -PhysicalPath "c:\Inetpub\wwwroot" -BindingInformation ":80:www.example.com" -Protocol http
  New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i1.example.com"
  New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i2.example.com"
  New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i2.example.com" # 重複的域名
  New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i4.example.com"
  New-IISSiteBinding -Name "Test" -Protocol http -BindingInformation "*:80:i5.example.com"

  Write-Host "站台建立成功!"
  Stop-IISCommitDelay -Commit $True
}
catch
{
  Write-Host "建立站台發生錯誤,復原所有設定"
  Stop-IISCommitDelay -Commit $False
}

Get-IISSite

使用 Get-IISServerManager 批次設定

透過 IISAdministration 模組下的 Get-IISServerManager 也可以讓你的批次作業擁有「交易特性」(Transaction)。

透過 Get-IISServerManager 可以取回 .NET 的 Microsoft.Web.Administration 物件,讓你直接透過 .NET 物件來操作 IIS 內的各項設定。以下是使用範例:

$sm = Get-IISServerManager

$sm.Sites["Default Web Site"].Bindings.Add("*:80:www.example.com", "http")
$sm.Sites["Default Web Site"].Bindings.Add("*:80:a.example.com", "http")
$sm.Sites["Default Web Site"].Bindings.Add("*:80:b.example.com", "http")
$sm.Sites["Default Web Site"].Bindings.Add("*:80:c.example.com", "http")
$sm.Sites["Default Web Site"].Bindings.Add("*:80:d.example.com", "http")
$sm.Sites["Default Web Site"].Bindings.Add("*:80:e.example.com", "http")

$sm.CommitChanges();

我個人還是比較喜歡用 IISAdministration 模組下標準的 Cmdlets 來調整 IIS 設定,一來比較簡單,二來就算沒有 .NET 經驗也可以使用。

不過,透過 .NET 物件操作的彈性,會比用 PowerShell 來的強大很多,至於你想用哪種方式調整設定,就全看你個人喜好囉! 🙂

相關連結