AWS Managed Microsoft AD環境でADFSサーバーを構築する

2019.10.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

注意事項
本記事では構築手順を解説していますがADFSの利用を推奨しているわけではありません。
本記事の内容は本番環境での利用を想定していませんので予めご了承ください。

しばたです。

通常ADFSサーバーを構築するためにはDomain Admin権限が必要であり、Domain Admin権限を利用できないAWS Managed Microsoft AD(以後Microsoft AD)環境において通常の手順でADFSサーバーを構築しようとすると以下の様に権限エラーとなってしまいます。

本記事ではこのエラーを回避してMicrosoft AD環境でADFSサーバーを構築する手順について説明します。

参考情報

非Domain AdminユーザーでADFSサーバーを構築する手順についてドキュメントは非常に少なく、チェコのMicrosoft Docsにある下記のドキュメントとAWS Security Blogの記事があるくらいです。

(一応この他に上記を参考にしたと思しきQiitaの記事もあったりしたのですが、必須でない手順が混ざっているなど不正確な記述が多かったのでここでは紹介を控えておきます)

また、この他にAWSが公式に提供しているクイックスタートのリポジトリがあり、こちらの内容も一部参考にしています。

(ただし、このクイックスタートはEC2 Active Directory環境前提でありそのままの内容では使えませんのでご注意ください)

検証環境

今回の検証環境は下図となります。

パブリックサブネットにADFS Web Application Proxy(ADFS WAP)サーバー、プライベートサブネットにADFSサーバーとMicrosoft ADを配置します。
ADFSサーバーがインターネットアクセス可能にするためにNAT Gatewayを配置しています。
加えてMicrosoftのドキュメントを基に各サービス間で必要なポートを開けるセキュリティグループを設定しておきます。

その他詳細は以下。

Microsoft AD

項目 設定値 備考
エディション Standard
ドメイン名 corp.shibata.tech
NetBIOSドメイン名 CORP
ADFS用gMSA corp\adfs-srevice 詳細は後述

ADFSサーバー

項目 設定値 備考
インスタンスタイプ t3.medium
OS 日本語版 Windows Server 2016 AMI : ami-0b26f62143241dc18
コンピューター名 ADFS01
ドメイン corp.shibata.tech に参加
Windows Firewall 無効に変更 通信制御はセキュリティグループで実施

ADFS WAPサーバー

項目 設定値 備考
インスタンスタイプ t3.medium
OS 日本語版 Windows Server 2016 AMI : ami-0b26f62143241dc18
コンピューター名 WAP01
ドメイン corp.shibata.tech に参加 ADFS WAPはドメイン参加必須ではない
Windows Firewall 無効に変更 通信制御はセキュリティグループで実施

【2019.10.09追記】
ADFS WAPはドメイン参加必須ではありません。むしろDMZに配置する場合はドメインに参加しない方が良いでしょう。
本記事ではその点を忘れたままドメインに参加させて検証してました...
【追記ここまで】

その他

  • 外部に公開するサーバー名は sts.shibata.tech
  • 証明書は自己署名証明書を使用
    • このためADFS WAPサーバーやクライアント端末で証明書を無条件信頼する様にしています
  • 各サーバーでの作業は Admin ユーザーで実施
    • PowerShellコンソールは「管理者として実行」が基本

ADFSサーバー、ADFS WAPサーバー構築に至るまでの環境構築については割愛します。

ADFSサーバー (ADFS01) の構築

サーバーがcorp.shibata.techドメインに参加した時点をスタート地点とします。
ADFSサーバー構築前に幾つか前準備を行います。

前準備1 : gMSAの作成

本記事では参考資料とは異なりADFSサービスの実行ユーザーをグループ管理サービスアカウント(gMSA)にします。
Microsoft AD環境ではKDSルートキーは作成済みですのでいきなりNew-ADServiceAccountコマンドレットを実行しgMSAを作成します。

New-ADServiceAccount -Name 'adfs-service' –DNSHostName 'adfs01'

アカウント名をadfs-service、利用可能なサーバーをADFSサーバー(adfs01)とします。

前準備2 : 証明書の作成

ADFSでは通常「サービス通信証明書」にパブリックな証明書を使用しますが、本記事では自己証明書を使い検証します。
また「トークン署名証明書」および「トークン暗号化解除」証明書は通常であればADFSが自動生成するのですが、本記事では後述の都合によりこの証明書を使います。

証明書はPowerShellからNew-SelfSignedCertificateコマンドレットを使いざっくり以下の様に作成しました。

$params = @{
    Subject = 'sts.shibata.tech'
    DnsName = 'sts.shibata.tech'
    CertStoreLocation = 'cert:\LocalMachine\My'
    KeyAlgorithm = 'RSA'
    KeyLength = 2048
    KeyExportPolicy = 'Exportable'
    NotAfter = (Get-Date).AddYears(5)
}
$cert = New-SelfSignedCertificate @params
$cert

また、ADFS WAPの構築時にこの証明書を使いますのでExport-PfxCertificateコマンドレットを実行して適当なディレクトリにエクスポートし保存しておいてください。

# WAPおよびクライアント用にエクスポートしておく (要管理者権限)
# ※エクスポート先およびパスワードは適当なので適宜変更してください
mkdir C:\Temp
Export-PfxCertificate -Cert $cert -FilePath "C:\temp\adfs.pfx" -Password  (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force)

1. ADFSサーバーの構築 (機能の追加)

ここからADFSサーバーの構築を開始します。

はじめにInstall-WindowsFeatureコマンドレットを使い「Active Directory Federation Services」の機能を追加します。

Install-WindowsFeature -Name 'ADFS-Federation' -IncludeManagementTools

2. ADFSサーバーの構築 (ADFSコンテナーの作成)

ここからADFSサーバーの設定に入るのですが、通常ADFSファームを作る場合はドメイン内の[ドメイン名]/Program Data/Microsoftに2つのコンテナーオブジェクトを作成します。

(オンプレ環境でADFSサーバーを構築した場合)

で、この[ドメイン名]/Program Data/Microsoftにオブジェクトを作成するにはDomain Admin権限が必要であり、Microsoft ADでエラーとなる原因でもあります。
このため参考元の記事ではコンテナーをMicrosoft ADでアクセス可能な[ドメイン名]/[NetBIOSドメイン名]/(既定のOU)配下に作成しています。
本記事ではMicrosoft Docsの記事で紹介されているスクリプトをgMSA専用にカスタマイズし関数化しました。

以下の関数を実行すると[ドメイン名]/[NetBIOSドメイン名]/配下にADFSランダムなGUIDのコンテナーを作成し、その設定情報を$adminConfig変数に格納します。
※$adminConfig変数は後で使います

# gMSA専用関数
function New-NonDADkmContainerForgMSA {
    [CmdletBinding()]
    param (
       [Parameter(Mandatory=$True)]
       [string]$AcctToAclDkmContainer
    )
    
    $userNameSplit = $AcctToAclDkmContainer.Split("\")
    if ($userNameSplit.Length -ne 2)
    {
        Write-error "Specify non-DA local admin user in 'domain\username' format"
        return $false
    }
    
    # The OU Name is a randomly generated Guid
    [string]$guid = (New-Guid).Guid
    Write-Host ("OU Name" + $guid)
    
    $ouName = $guid
    $initialPath = "OU=$($userNameSplit[0])," + (Get-ADDomain).DistinguishedName
    $ouPath = "CN=ADFS," + $initialPath
    $ou = "CN=" + $ouName + "," + $ouPath
    
    Write-Host ("Creating organizational unit with DN: " + $ou)
    
    if ($null -eq (Get-ADObject -Filter {distinguishedName -eq $ouPath}))
    {
        Write-Host ("First creating initial path " + $ouPath)
        New-ADObject -Name "ADFS" -Type Container -Path $initialPath
    }
    New-ADObject -Name $ouName -Type Container -Path $ouPath
    
    # get gMSA SID
    $strSID = (Get-ADServiceAccount -Identity $userNameSplit[1]).SID
    
    # set acl to ADFS cottainer object
    Import-Module ActiveDirectory
    try {
        Push-Location ad:
        [System.DirectoryServices.ActiveDirectorySecurityInheritance]$adSecInEnum = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::All
        $ace1 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $strSID,"GenericRead","Allow",$adSecInEnum
        $ace2 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $strSID,"CreateChild","Allow",$adSecInEnum
        $ace3 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $strSID,"WriteOwner","Allow",$adSecInEnum
        $ace4 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $strSID,"DeleteTree","Allow",$adSecInEnum
        
        $acl = Get-Acl -Path $ou
        
        $acl.AddAccessRule($ace1)
        $acl.AddAccessRule($ace2)
        $acl.AddAccessRule($ace3)
        $acl.AddAccessRule($ace4)
        
        Set-Acl -Path $ou -AclObject $acl
    } finally {
        Pop-Location
    }
    
    return @{"DKMContainerDn" = $ou}
}

# ADFSコンテナーを作成し、設定情報を変数に格納
# ※ $adminConfig 変数は後で使用する
$adminConfig = New-NonDADkmContainerForgMSA 'corp\adfs-service'

結果この様にコンテナーが作成されます。

3. ADFSサーバーの構築 (ADFSファームの構築)

ここまでを踏まえてADFSファームの構築に取り掛かれます。
ADFSファームを作るにはADFSモジュールにあるInstall-AdfsFarmコマンドレットを使用します。
本記事では構築前にTest-AdfsFarmInstallationコマンドレットを実行し事前検証をしています。

細かい解説は後述しますが以下のコマンドを実行するとADFSファームを構築できます。

# 証明書情報を取得
$cert = Get-ChildItem Cert:\LocalMachine\My\ | Where-Object { $_.Subject -eq 'CN=sts.shibata.tech' }

# ADFSファームを構築する際のローカル管理者権限を持つユーザーの認証情報
$localAdminCred = Get-Credential 'corp\admin'

# SigningCertificateThumbprint, DecryptionCertificateThumbprint を指定してADFSファームを作成
$params = @{
    CertificateThumbprint = $cert.Thumbprint
    SigningCertificateThumbprint = $cert.Thumbprint
    DecryptionCertificateThumbprint = $cert.Thumbprint
    FederationServiceName = 'sts.shibata.tech'
    GroupServiceAccountIdentifier = 'corp\adfs-service$'
    Credential = $localAdminCred
    OverwriteConfiguration = $true
    AdminConfiguration = $adminConfig
    Verbose = $true
}
# 検証
Test-AdfsFarmInstallation @params
# インストール
Install-AdfsFarm @params

Install-AdfsFarmでは証明書の指定に-CertificateThumbprint(サービス通信証明書)のみ指定し、-SigningCertificateThumbprint(トークン署名証明書)および-DecryptionCertificateThumbprint(トークン暗号化解除証明書)は未指定でADFSサーバーが自動生成する証明書を利用するのがデフォルトなのですが、ここで両パラメーターを指定しない場合、デフォルトの証明書を[ドメイン名]/Program Data/Microsoft配下に作成しようとしエラーとなってしまいます。

このためMicrosoft ADでADFSを作成する場合はトークン署名証明書、トークン暗号化解除証明書ともに自前で設定してやる必要があります。

この場合証明書の自動更新機能は使えない様です。
ADFSサーバーの設定も自動更新がオフにされ、Update-ADFSCertificateコマンドレットを使い自動更新を試みても下図の様にエラーとなってしまいます。

少し余談が入ってしまいましたがInstall-AdfsFarmに成功すると下図の様になります。

これでADFSサーバーの構築が完了し管理コンソールから各種設定を行うことができます。

ADFS WAPサーバー (WAP01) の構築

前準備1 : HOSTSファイルの設定

ADFS WAPサーバーではsts.shibata.techは内部のADFSサーバー(ADFS01)を指す必要があります。
最終的にsts.shibata.techはDNSからはグローバルIPアドレス(EIP)を返す様にすることになるので、ADFS WAPサーバーではHOSTSファイルを設定してやる必要があります。
HOSTSファイルに

<ADFS01のIP> sts.shibata.tech

の設定を追加します。

前準備2 : 証明書のインポート

今回は検証用に自己署名証明書を使うので最初にエクスポートしておいた証明書を「ローカルコンピューター\個人」にインポートしておきます。

# エクスポートした証明書が C:\temp\adfs.pfx にある前提
$params = @{
    FilePath = 'C:\temp\adfs.pfx'
    CertStoreLocation = 'cert:\localMachine\my'
    Password = (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force)
}
Import-PfxCertificate @params

また、この証明書を信頼済み扱いするために「信頼されたルート証明機関」にもインポートしておいてください。(本記事ではこの手順は割愛します)

1. ADFS WAPサーバーの構築 (機能の追加)

ここからADFS WAPサーバーの構築を開始します。

はじめにInstall-WindowsFeatureコマンドレットを使い「Web アプリケーション プロキシ」の機能を追加します。

Install-WindowsFeature Web-Application-Proxy -IncludeManagementTools

2. ADFS WAPサーバーの構築 (WAPの構成)

続けてWebApplicationProxyモジュールInstall-WebApplicationProxyコマンドレットでWAPを構成します。

こちらは細かい説明は不要かと思います。

# ローカル管理者権限を持つユーザーの認証情報
$localAdminCred = Get-Credential 'corp\admin'
$params = @{
    FederationServiceName = 'sts.shibata.tech'
    CertificateThumbprint = (Get-ChildItem Cert:\LocalMachine\My\ | Where-Object { $_.Subject -eq 'CN=sts.shibata.tech' }).Thumbprint
    FederationServiceTrustCredential = $localAdminCred
}
Install-WebApplicationProxy @params

インストールに問題なければ下図の様になります。

ADFSの管理コンソールからWAPが有効になっていることが確認できればOKです。

動作確認

これでADFSおよびADFS WAP環境が構築できましたので簡単に動作確認をしてみます。

の内容を参考にAWSマネジメントコンソールへのSSO環境を作ってみました。
こちらの細かい手順は割愛しますが、その結果作成したADFS WAPのサインイン画面から

ドメインユーザー(tarou.yamada@corp.shibata.tech)でログインし、

SSOによりマネジメントコンソールにアクセスできました。

【追記】各サーバーの冗長化

ADFSサーバーおよびADFS WAPサーバーを冗長化する手順について別記事を書きましたのでこちらも併せてご覧ください。

最後に

以上となります。

Microsoft ADではDomain Admin権限を利用することができないため、ADFSの構築にはひと手間かけてやる必要あり制約も付きますが決して不可能ではありません。
需要は多くないかもしれませんが本記事の内容が誰かの役に立てば幸いです。