LinuxサーバーをActive Directoryで管理してみる(トラブル編)
はじめに
プログデンスの佐藤です。
前回、「LinuxサーバーをActive Directoryで管理してみる」という記事を投稿しました。
運用面やセキュリティ面でメリットを得られましたが、今回はその過程の裏側 - トラブルについて書いていきたいと思います。
Kerberos認証の要件
トラブルについて触れる前に、Active Directoryに参加するために必須であるKerberos認証の要件を整理します。(TCP/UDPポートについては割愛します)
- ドメインコントローラーと時刻のズレが5分以内であること
- DNSによる正引きと逆引きでの名前解決が可能であること
- クライアントとKDC(Kerberos v5 Key Distribution Center)で暗号化方式が揃っていること
トラブル概要
そして、Kerberos認証の要件を踏まえた上で、どういうトラブルが起きたかというと、
ほぼ全部の要件に引っかかり
- AD(Active Directory)の参加に失敗する(症状は多岐に渡ります)
- sudo認証でKerberos認証に失敗する
厳密には違うところもあるので、最初から書いていきます。
自宅ラボの環境起因や時間を開けて対応していたりするため、少々ややこしい部分もありますが、なるべく時系列と因果関係が分かりやすくなるように記載しています。
なお、自宅ラボの環境は前回と同様ですが、サーバーが複数台ある関係上、
- ドメインコントローラー:Windows Server 2022/2025 Standard Edition
- Linuxクライアント:RHEL9.6, AlmaLinux9.6
とそれぞれで若干異なる混在環境となっています。
ADの参加に失敗する
実は今も一部のサーバーで起きているため、確実に解消したわけではなく、個体差?があるかもしれません。
そのため、同じように悩んでいる方の参考になれば、幸いです。
症状としては、AD参加時に以下のようになります。([]内は実際のログから変更しています)
# realm join [example.localdomain] -U [administrator]
See: journalctl REALMD_OPERATION=r574246.118048
realm: レルムに参加できませんでした: Failed to join the domain
Please check
https://red.ht/support_rhel_ad
to get help for common issues.
確認先のURLがメッセージ内に記載されていますが、以下の一般的な内容となっており、原因が非常に分かりにくいです。
1. 時刻同期
2. DNSによる名前解決可否確認
3. AD参加に使おうとしているユーザーの権限確認
4. AD/Kerberosで利用するポートへの到達性確認
このうち、3と4については問題無いことは確認済であったため、切り分けとして、順に1と2に対応していきました。
・・・そう、最初はずっと気づかなかったんです。
暗号化方式のことは・・・
ドメインコントローラーとの時刻ズレへの対応
こちらについては厳密には引っかかっていたわけでは無かったですが、自宅ラボではWindowsとLinuxが混在している環境だったため、時刻同期は以下のように同期先が分かれていました。
- Windows環境:Windowsクライアント -> ドメインコントローラー
- Linux環境:Linuxクライアント -> 自宅ラボNTPサーバー
そのため、「ドメインコントローラーと時刻同期はしていないけど、時刻のズレもそれほどない」という状態になっていました。
実際のLinuxクライアントとドメインコントローラー間の時刻のズレは数秒程度だったので、この状態でも問題は無かったのではないかとは思いますが、切り分けを進める上で、時刻同期先はドメインコントローラーに揃えました。
Linuxクライアント側はそう難しいことをするわけではなく、/etc/chrony.confを以下のように変更するだけです。
/etc/chrony.confの内容
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
server ads01 iburst ★ ここをドメインコントローラーに指定
server ads02 iburst ★ ここをドメインコントローラーに指定
# Use NTP servers from DHCP.
sourcedir /run/chrony-dhcp
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
(以下略)
と、軽く考えていたのですが、いくら待っても時刻同期をしてくれません。
力業でdateコマンドを使用して可能な限り、ズレを極小化し、slewからstepに変えて、強制的に同期させようとしても無理でした。
結局、ドメインコントローラー側でもNTPサーバーとして時刻同期されるための設定が必要であり、w32timeを以下の設定を以下のように変更し、サービスを再起動させました。
ドメインコントローラーでのw32timeの変更対応
> w32tm /config /manualpeerlist:"0.pool.ntp.org" /syncfromflags:manual /reliable:yes /update
コマンドは正しく完了しました。
>
> net stop w32time
Windows Time サービスを停止中です.
Windows Time サービスは正常に停止されました。
> net start w32time
Windows Time サービスを開始します.
Windows Time サービスは正常に開始されました。
> w32tm /resync /force
再同期コマンドをローカル コンピューターに送信しています
コマンドは正しく完了しました。
>
> w32tm /query /status
閏インジケーター: 0 (警告なし)
階層: 4 (二次参照 - (S)NTP で同期)
精度: -23 (ティックごとに 119.209ns)
ルート遅延: 0.0046738s
ルート分散: 7.7940503s
参照 ID: 0x8BA2512D (ソース IP: 139.162.81.45)
最終正常同期時刻: 2025/08/12 15:33:52
ソース: 0.pool.ntp.org
ポーリング間隔: 6 (64s)
>
> w32tm /query /configuration
[構成]
EventLogFlags: 2 (ローカル)
AnnounceFlags: 5 (ローカル)
TimeJumpAuditOffset: 28800 (ローカル)
MinPollInterval: 6 (ローカル)
MaxPollInterval: 10 (ローカル)
MaxNegPhaseCorrection: 172800 (ローカル)
MaxPosPhaseCorrection: 172800 (ローカル)
MaxAllowedPhaseOffset: 300 (ローカル)
FrequencyCorrectRate: 4 (ローカル)
PollAdjustFactor: 5 (ローカル)
LargePhaseOffset: 50000000 (ローカル)
SpikeWatchPeriod: 900 (ローカル)
LocalClockDispersion: 10 (ローカル)
HoldPeriod: 5 (ローカル)
PhaseCorrectRate: 7 (ローカル)
UpdateInterval: 100 (ローカル)
FileLogName: (ローカル)
FileLogEntries: (ローカル)
FileLogSize: 0 (ローカル)
[タイム プロバイダー]
NtpClient (ローカル)
DllName: C:\WINDOWS\system32\w32time.dll (ローカル)
Enabled: 1 (ローカル)
InputProvider: 1 (ローカル)
AllowNonstandardModeCombinations: 1 (ローカル)
ResolvePeerBackoffMinutes: 15 (ローカル)
ResolvePeerBackoffMaxTimes: 7 (ローカル)
CompatibilityFlags: 2147483648 (ローカル)
EventLogFlags: 1 (ローカル)
LargeSampleSkew: 3 (ローカル)
SpecialPollInterval: 1024 (ローカル)
Type: NTP (ローカル)
NtpServer: 0.pool.ntp.org (ローカル)
NtpServer (ローカル)
DllName: C:\WINDOWS\system32\w32time.dll (ローカル)
Enabled: 1 (ローカル)
InputProvider: 0 (ローカル)
AllowNonstandardModeCombinations: 1 (ローカル)
VMICTimeProvider (ローカル)
DllName: C:\WINDOWS\System32\vmictimeprovider.dll (ローカル)
Enabled: 1 (ローカル)
InputProvider: 1 (ローカル)
>
文章にするとあっさりですが、トラブル時は
- ドメインコントローラーのFWを疑ったり
- chronyc makestepの実行結果が「200 OK」と返ってきてるのにダメだったり
と、トラブル対応の中でトラブルにハマるということで、無駄に疲れました。
無事に時刻同期できるようになった後で、
昔、Cisco CatalystスイッチのNTPサーバーをWindowsで対応しようとして疲れたことを思い出しました…
(昔すぎて、完全に忘れてました)
まあ、時刻同期が原因というわけもなく、
# realm join [example.localdomain] -U [administrator]
See: journalctl REALMD_OPERATION=r574246.118048
realm: レルムに参加できませんでした: Failed to join the domain
Please check
https://red.ht/support_rhel_ad
to get help for common issues.
という結果に終わりました。
DNSによる正引きと逆引きでの名前解決が可能であること
逆引きが必要であることは普通に知らなかったのですが、これはKerberosが「本当にそのサーバーなのか」を確認するためのセキュリティチェックです。IPアドレスから逆引きで得られるホスト名と、サーバーが名乗っているホスト名が一致することで、なりすましを防いでいます。
さらにこの要件、厳密に言うと、
DNSでの正引きと逆引きでの名前解決の結果が、Linuxサーバーの実際のホスト名と一致すること
になります。
そのため、切り分けによる確認としては、以下まで見ておくのが確実です。
名前解決可否の確認
# hostname -f ★Linuxサーバー自身のホスト名確認
[hostname].[example.localdomain]
#
# host $(hostname -f) ★DNSの正引き確認
[hostname].[example.localdomain] has address [192.168.0.1]
#
# host $(hostname -I) ★DNSの逆引き確認
[1.0.168.192].in-addr.arpa domain name pointer [hostname].[example.localdomain.]
#
なお、これは余談になるのですが、一部のサーバーはなぜか逆引き登録無しでもADに参加していました。(原因は未だに不明です)
ただ、名前解決ができて、時刻同期ができていても…
# realm join [example.localdomain] -U [administrator]
See: journalctl REALMD_OPERATION=r574246.118048
realm: レルムに参加できませんでした: Failed to join the domain
Please check
https://red.ht/support_rhel_ad
to get help for common issues.
となり、状況は変わらずでした。
クライアントとKDCで暗号化方式が揃っていること
その後、SSSDのキャッシュクリアを実施するも失敗し、
※参考までにSSSDのキャッシュクリアを載せておきますが、この状態ではほぼsssdの起動で失敗します。
SSSDのキャッシュクリア
# systemctl stop sssd
# sss_cache -E
# systemctl start sssd
AD側でコンピュータオブジェクトを作成して、強引にAD参加を試みるも失敗に終わります。
暗号化方式の問題に行き当たったのは、realm joinに-vオプションを付けて、デバッグログを出してからです。
realm join時のデバッグログ
# realm join [example.localdomain] -U [administrator] -v
* Resolving: _ldap._tcp.[example.localdomain]
* Performing LDAP DSE lookup on: [192.168.0.44]
* Successfully discovered: [example.localdomain]
* Required files: /usr/sbin/oddjobd, /usr/libexec/oddjob/mkhomedir, /usr/sbin/sssd, /usr/sbin/adcli
* LANG=C /usr/sbin/adcli join --verbose --domain [example.localdomain] --domain-realm [EXAMPLE.LOCALDOMAIN] --domain-controller [192.168.0.44] --login-type user --login-ccache=/var/cache/realmd/realm-ad-kerberos-SJWI92
* Using domain name: [example.localdomain]
* Calculated computer account name from fqdn: [HOSTNAME]
* Using domain realm: [example.localdomain]
* Sending NetLogon ping to domain controller: [192.168.0.44]
* Received NetLogon info from: ads01.[example.localdomain]
* Wrote out krb5.conf snippet to /var/cache/realmd/adcli-krb5-OjP1bp/krb5.d/adcli-krb5-conf-eSzuDF
* Using GSS-SPNEGO for SASL bind
* Looked up short domain name: [EXAMPLE]
* Looked up domain SID: S-1-5-21-3508781286-2196938217-79326079
* Received NetLogon info from: ads01.[example.localdomain]
* Using fully qualified name: [hostname].[example.localdomain]
* Using domain name: [example.localdomain]
* Using computer account name: [HOSTNAME]
* Using domain realm: [example.localdomain]
* Calculated computer account name from fqdn: [HOSTNAME]
* Generated 120 character computer password
* Using keytab: FILE:/etc/krb5.keytab
* A computer account for [HOSTNAME]$ does not exist
* Found well known computer container at: CN=Computers,DC=[example],DC=[localdomain]
* Calculated computer account: CN=[HOSTNAME],CN=Computers,DC=[example],DC=[localdomain]
* Encryption type [16] not permitted. ★暗号化方式(16)が許可されていない
* Encryption type [23] not permitted. ★暗号化方式(23)が許可されていない
* Encryption type [3] not permitted. ★暗号化方式(3)が許可されていない
* Encryption type [1] not permitted. ★暗号化方式(1)が許可されていない
* Created computer account: CN=[HOSTNAME],CN=Computers,DC=[example],DC=[localdomain]
* Trying to set computer password with Kerberos
! Couldn't set password for computer account: [HOSTNAME]$: Message stream modified
adcli: joining domain [example.localdomain] failed: Couldn't set password for computer account: [HOSTNAME]$: Message stream modified
Please check
https://red.ht/support_rhel_ad
to get help for common issues.
! Failed to join the domain
realm: レルムに参加できませんでした: Failed to join the domain
Please check
https://red.ht/support_rhel_ad
to get help for common issues.
なお、暗号化方式についてはこちらに一覧があるので分かりやすいです。
表示されている暗号化方式は以下の通り。
- 1 : DES_CBC_CRC(古い暗号化方式、セキュリティ上非推奨)
- 3 : DES_CBC_CRC, DES_CBC_MD5(古い暗号化方式、セキュリティ上非推奨)
- 16 : AES 256(現在推奨される強固な暗号化)
- 23 : DES_CBC_CRC, DES_CBC_MD5, RC4, AES 256(互換性重視だがセキュリティ面で課題あり)
RHEL9系でデフォルトでサポートしている暗号化方式はここを見る限り、以下となります。
By default, Identity Management establishes a cross-realm trust with support for AES-128 and AES-256 Kerberos encryption types. Additionally, by default SSSD and Samba Winbind support AES-128 and AES-256 Kerberos encryption types.
- AES128
- AES256
さらに
RC4 encryption is deprecated and disabled by default since RHEL 8.3 and RHEL 9, as it is considered less secure than the newer AES-128 and AES-256 encryption types. In contrast, Active Directory (AD) user credentials and trusts between AD domains support RC4 encryption and they might not support all AES encryption types.
とあるので、暗号化方式の違いで間違いなさそうです。
一応、AD側で拡張表示を有効にして、ADUC(Active Directory Users and Computers)のコンピューターオブジェクトのプロパティの属性エディタ―を確認すると、"msDS-SupportedEncryptionTypes"の項目があるのですが、
ここは変更してもあまり有効ではなさそうでした。
そのため、ドメインコントローラーに適用するグループポリシーを以下のように新規(図中のPrivate Policy)で作成しました。
・設定場所
[コンピューターの構成]
- [ポリシー]
- [Windowsの設定]
- [セキュリティの設定]
- [ローカルポリシー]
- [セキュリティオプション]
・設定項目
[ネットワークセキュリティ:Kerberosで許可する暗号化の種類を構成する]
・設定値
☑ AES128_HMAC_SHA1
☑ AES256_HMAC_SHA1
このグループポリシーを適用するために、ドメインコントローラー全台でgpupdate /forceを実行し、それから改めて、Linuxサーバー側でrealm joinを実行し、前回の投稿内容に至るとなります。
まとめ
前回の投稿では、LinuxサーバーをActive Directoryで管理することのメリットについて記載しましたが、システム間での連携ということで、一度つまずくと切り分けに苦労しました。
途中で触れましたが、なぜか上手くいってしまうサーバーが出てきました。(未だに原因不明です)
と思えば、NTPのようにどうでもいいところでハマってしまったりしてと実はかなりの長期戦をやっていました。類似のトラブルを検索しても日本語ではあまり情報が無いのも苦労した点の一つです。
(本当に疲れました、この件…)
ただ、トラブってみないと気づけないシステムのバックエンドの動作を知るには非常に良い機会になったと思います。
今回も書けていませんでしたが、管理面はある程度落ち着いたので、CI/CD周りで進めていることを今後を書いていければと考えています。
最後に、今回のトラブルシュートにあたって、かなりAIを利用しました。(感謝)
おそらくAIを使っていなかったら、解決までもっと時間がかかっていたと思います。トラブルシュートがループしたこともありましたが。
ローカルLLMも大分身近になってきていますので、AI方面にも手を広げていきたいと思っています。
末筆ですが、最後までお読みいただき、ありがとうございました。