はじめに
GCP上でWindows Serverを運用する際、企業のセキュリティポリシーに準拠するためセキュリティBaselineを適用することが一般的です。しかし、このBaseline適用時には多くの罠が待ち受けており、適切な準備を怠るとシステムに接続できなくなる深刻な事態に陥ることがあります。
本記事では、実際のプロジェクトで遭遇した問題事例を基に、セキュリティBaseline適用時の注意点とトラブルシューティング方法を詳細に解説します。
セキュリティBaselineとは
概要
セキュリティBaseline(セキュリティベースライン)とは、Microsoftが提供するWindows環境のセキュリティ強化設定テンプレートです。これには以下のようなセキュリティポリシーが含まれています:
- アカウントポリシー: パスワード複雑度、ロックアウト設定
- ローカルポリシー: 監査設定、ユーザー権利の割り当て
- Windowsファイアウォール: 詳細な通信制御ルール
- システムサービス: 不要サービスの無効化
- レジストリ設定: セキュリティ関連の細かな設定
よくある問題とその影響
1. packer_userの自動削除問題
問題の詳細:
Packerでイメージ作成時に使用するpacker_user
が、Baseline適用により「不要なローカルユーザー」として削除されてしまう問題です。
影響範囲:
- Packerの後続プロビジョニング処理が全て失敗
- sysprep実行前に接続が切断される
- イメージビルドプロセスが途中で停止
発生メカニズム:
1
2
3
4
5
6
7
8
9
|
# Baselineに含まれる設定例
secedit /configure /db secedit.sdb /cfg baseline.inf
# baseline.inf の問題箇所例
[System Access]
MinimumPasswordAge = 1
MaximumPasswordAge = 42
MinimumPasswordLength = 14
PasswordComplexity = 1
|
2. WinRM/RDP通信の無効化
問題の詳細:
セキュリティBaseline適用により、PackerやTerraformが依存するWinRM通信やRDP接続が無効化される問題です。
影響範囲:
- Packerのプロビジョニング通信が切断
- 管理者によるリモート接続が不可能
- Terraformのremote-execプロビジョナーが失敗
原因となる設定例:
1
2
3
4
5
6
7
|
; baseline.inf での制限設定例
[Registry Values]
; WinRM無効化
MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy=4,0
; RDP制限
MACHINE\System\CurrentControlSet\Control\Terminal Server\fDenyTSConnections=4,1
|
3. Administrator権限の剥奪
問題の詳細:
Baseline適用により、作成したユーザーがAdministratorsグループから自動的に除外される問題です。
影響範囲:
- 管理者権限が必要な操作が全て失敗
- システム設定変更が不可能
- セキュリティポリシーの再変更ができない
事前対策の実装
1. ユーザー保護設定
Packer設定での対策
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
# windows-2022.pkr.hcl での対策
source "googlecompute" "windows-2022" {
project_id = var.project_id
source_image_family = "windows-2022"
zone = var.zone
machine_type = var.machine_type
communicator = "winrm"
winrm_username = var.winrm_username
winrm_password = var.winrm_password
winrm_timeout = "45m" # タイムアウトを延長
# ユーザー保護のメタデータ
metadata = {
sysprep-specialize-script-cmd = <<-EOF
REM ユーザーの確実な作成と権限付与
net user ${var.winrm_username} ${var.winrm_password} /add /active:yes /expires:never /passwordchg:no
net localgroup administrators ${var.winrm_username} /add
net localgroup "Remote Desktop Users" ${var.winrm_username} /add
net localgroup "Remote Management Users" ${var.winrm_username} /add
REM WinRM設定の確実な有効化
winrm quickconfig -quiet -force
winrm set winrm/config/service @{AllowUnencrypted="true"}
winrm set winrm/config/service/auth @{Basic="true"}
winrm set winrm/config/client/auth @{Basic="true"}
REM ファイアウォール設定
netsh advfirewall firewall add rule name="Allow WinRM HTTP" dir=in action=allow protocol=TCP localport=5985
netsh advfirewall firewall add rule name="Allow WinRM HTTPS" dir=in action=allow protocol=TCP localport=5986
EOF
}
}
|
2. セキュリティBaseline適用スクリプト
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
# scripts/apply-security-baseline.ps1
param(
[string]$BaselineFile = "C:\temp\security-baseline.inf",
[string]$BackupPath = "C:\temp\backup",
[switch]$CreateBackup = $true
)
Write-Output "Applying security baseline with safeguards..."
# バックアップの作成
if ($CreateBackup) {
if (-not (Test-Path $BackupPath)) {
New-Item -Path $BackupPath -ItemType Directory -Force
}
# 現在の設定をバックアップ
secedit /export /cfg "$BackupPath\current-config-$(Get-Date -Format 'yyyyMMdd-HHmmss').inf"
Write-Output "Current configuration backed up to $BackupPath"
}
# Baseline設定ファイルの検証と修正
if (Test-Path $BaselineFile) {
$BaselineContent = Get-Content $BaselineFile
# 危険な設定のコメントアウト
$ModifiedContent = @()
foreach ($Line in $BaselineContent) {
# WinRM無効化設定をコメントアウト
if ($Line -match "fDenyTSConnections.*=.*1") {
$ModifiedContent += "; MODIFIED: $Line"
Write-Warning "Disabled RDP restriction: $Line"
}
# ローカルアカウントフィルター無効化をコメントアウト
elseif ($Line -match "LocalAccountTokenFilterPolicy.*=.*0") {
$ModifiedContent += "; MODIFIED: $Line"
Write-Warning "Disabled local account token filter: $Line"
}
else {
$ModifiedContent += $Line
}
}
# 修正された設定ファイルを作成
$SafeBaselineFile = "$BackupPath\safe-baseline.inf"
$ModifiedContent | Set-Content $SafeBaselineFile
# 安全なBaseline設定を適用
try {
secedit /configure /db secedit.sdb /cfg $SafeBaselineFile /log "$BackupPath\baseline-apply.log"
Write-Output "Security baseline applied successfully"
} catch {
Write-Error "Failed to apply security baseline: $($_.Exception.Message)"
# 失敗時はバックアップから復旧
if (Test-Path "$BackupPath\current-config-*.inf") {
$BackupFile = Get-ChildItem "$BackupPath\current-config-*.inf" | Sort-Object CreationTime -Descending | Select-Object -First 1
secedit /configure /db secedit.sdb /cfg $BackupFile.FullName
Write-Output "Rolled back to previous configuration"
}
}
}
|
トラブルシューティング方法
1. 接続不能になった場合の復旧手順
GCP Console経由でのシリアルコンソール接続
1
2
3
4
5
6
7
|
# GCPのシリアルコンソール機能を有効化
gcloud compute instances add-metadata INSTANCE_NAME \
--metadata serial-port-enable=1
# シリアルコンソール経由で接続
gcloud compute connect-to-serial-port INSTANCE_NAME \
--zone=ZONE_NAME
|
緊急時復旧PowerShellスクリプト
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# emergency-recovery.ps1
Write-Output "Emergency recovery script starting..."
# WinRMサービスの強制有効化
Set-Service -Name WinRM -StartupType Automatic
Start-Service WinRM
# WinRM設定のリセット
winrm delete winrm/config/listener?Address=*+Transport=HTTP
winrm delete winrm/config/listener?Address=*+Transport=HTTPS
winrm quickconfig -quiet -force
# RDPの強制有効化
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
# ローカルアカウントトークンフィルターの無効化
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
# 緊急用管理者アカウントの作成
$EmergencyPassword = "EmergencyPass123!"
net user emergency_admin $EmergencyPassword /add
net localgroup administrators emergency_admin /add
Write-Output "Emergency recovery completed."
|
2. Baseline適用前の検証スクリプト
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
# scripts/validate-baseline-readiness.ps1
param([string]$TestUser = "packer_user")
Write-Output "Validating system readiness for baseline application..."
# 検証結果を格納するハッシュテーブル
$ValidationResults = @{}
# 1. ユーザー存在確認
try {
$User = Get-LocalUser -Name $TestUser -ErrorAction Stop
$ValidationResults["UserExists"] = $true
Write-Output "✅ User $TestUser exists"
} catch {
$ValidationResults["UserExists"] = $false
Write-Warning "❌ User $TestUser does not exist"
}
# 2. 管理者権限確認
try {
$AdminGroup = Get-LocalGroupMember -Group "Administrators" | Where-Object {$_.Name -like "*$TestUser"}
if ($AdminGroup) {
$ValidationResults["AdminRights"] = $true
Write-Output "✅ User $TestUser has administrator rights"
} else {
$ValidationResults["AdminRights"] = $false
Write-Warning "❌ User $TestUser does not have administrator rights"
}
} catch {
$ValidationResults["AdminRights"] = $false
Write-Warning "❌ Could not verify administrator rights for $TestUser"
}
# 3. WinRMサービス状態確認
$WinRMService = Get-Service -Name WinRM
if ($WinRMService.Status -eq "Running") {
$ValidationResults["WinRMRunning"] = $true
Write-Output "✅ WinRM service is running"
} else {
$ValidationResults["WinRMRunning"] = $false
Write-Warning "❌ WinRM service is not running"
}
# 総合判定
$FailedChecks = $ValidationResults.Values | Where-Object {$_ -eq $false}
if ($FailedChecks.Count -eq 0) {
Write-Output "🎉 All validation checks passed. System is ready for baseline application."
exit 0
} else {
Write-Error "⚠️ $($FailedChecks.Count) validation check(s) failed. Please address the issues before applying baseline."
exit 1
}
|
ベストプラクティス
1. 段階的Baseline適用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# staged-baseline-application.ps1
param([string]$Stage = "validation")
switch ($Stage) {
"validation" {
Write-Output "Stage 1: Validation"
& "C:\temp\validate-baseline-readiness.ps1"
}
"user-protection" {
Write-Output "Stage 2: User Protection"
& "C:\temp\pre-baseline-setup.ps1"
}
"baseline-core" {
Write-Output "Stage 3: Core Baseline Application"
& "C:\temp\apply-security-baseline.ps1" -BaselineFile "C:\temp\core-baseline.inf"
}
"verification" {
Write-Output "Stage 4: Final Verification"
& "C:\temp\post-baseline-verification.ps1"
}
}
|
2. ロールバック機能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# rollback-baseline.ps1
param([string]$BackupPath = "C:\temp\backup")
Write-Output "Rolling back security baseline..."
# 最新のバックアップファイルを検索
$BackupFiles = Get-ChildItem "$BackupPath\current-config-*.inf" | Sort-Object CreationTime -Descending
if ($BackupFiles.Count -gt 0) {
$LatestBackup = $BackupFiles[0]
Write-Output "Restoring from backup: $($LatestBackup.Name)"
try {
secedit /configure /db secedit.sdb /cfg $LatestBackup.FullName /log "$BackupPath\rollback.log"
Write-Output "Rollback completed successfully"
# サービスの再起動
Restart-Service WinRM -Force
Restart-Service TermService -Force
} catch {
Write-Error "Rollback failed: $($_.Exception.Message)"
}
} else {
Write-Error "No backup files found in $BackupPath"
}
|
まとめ
セキュリティBaseline適用時の重要なポイント:
事前準備チェックリスト
- ✅ 管理用ユーザーの確実な作成と権限付与
- ✅ WinRM/RDP設定の事前確認
- ✅ ファイアウォールルールの適切な設定
- ✅ 現在の設定のバックアップ作成
- ✅ 緊急時復旧手順の準備
適用時の注意点
- ✅ Baseline設定ファイルの事前レビュー
- ✅ 危険な設定の無効化
- ✅ 段階的な適用とその都度の検証
- ✅ 接続性の継続的な監視
緊急時対応
- ✅ シリアルコンソール経由のアクセス手順
- ✅ 緊急復旧スクリプトの準備
- ✅ ロールバック機能の実装
これらの対策を適切に実装することで、セキュリティBaselineを安全に適用し、システムの可用性を維持しながらセキュリティを強化できます。
次回は、Secret Managerとの連携による更なるセキュリティ向上について解説します。