【AWX Operator】Azure AD連携でSSOを導入！実践ガイド

# 概要
[AWX Operator](https://github.com/ansible/awx-operator)は、Ansible Towerのオープンソース版であるAWXをKubernetes環境で簡単に管理・運用できる便利なツールですが、ユーザー認証を効率化するためにはSSO（シングルサインオン）の導入が効果的です。
本記事では、Kubernetes上にデプロイしたAWX OperatorのDashboardに、Azure ADを利用してSSO（シングルサインオン）認証を設定する方法を解説します。

## 利用環境　
* Amazon EKS：v1.29
* AWX Operator： v2.10.0

## 構成図
今回の設定で実現する構成の全体像を示します。
以下は筆者の担当システム構成となりますので、参考までの情報としてご参照ください。
下図では、AWX OperatorがKubernetes上にデプロイされ、Azure ADを使用してSSO認証を行う仕組みを示しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/cd0dcbea-3433-4f23-a821-d2ef04963f73.png)

### 接続の流れ
ユーザーがSSO認証を行ってログインするまでの流れを示すと、以下の通りとなります。
1. ユーザーがAWX Operator Dashboardにアクセス
2. Ingressコントローラーがリクエストを受け取る
3. AWX OperatorがAzure ADにリダイレクト
4. 既存のセッションを確認し認証（必要に応じて再認証）
5. Azure ADが認証トークンを発行
6. Azure ADがAWXにリダイレクト
7. AWX Operatorが認証トークンを検証
8. ユーザーがAWX Operator Dashboardにログイン

## 事前準備
設定を進める前に、以下の準備を完了させておく必要があります。
詳細な手順は割愛しますが、必要に応じて関連資料をご参照ください。

* DNSゾーンとドメインの設定
* DNSレコードの登録
* SSL証明書の用意

https://docs.aws.amazon.com/ja_jp/route53/?id=docs_gateway

https://docs.aws.amazon.com/ja_jp/acm/

# AWX Operator × Azure ADのセットアップ
以下の流れで、AWX OperatorのDashboardにAzure ADを使用したSSOを設定します。

## 01. App Registrationsへアプリケーションの登録（Azure AD）
公式ドキュメントを参考にAzure ADのApp Registrationsへアプリケーションを登録、SSOグループの作成を行います。

https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app

## 02. IngressおよびExternal DNS設定追加（Kubernetes環境）
### External DNS設定追加
AWX OperatorのIngress設定を構成する際には、DNSの自動登録を行うために[External DNS](https://github.com/kubernetes-sigs/external-dns)の設定も併せて行います。External DNSは、Kubernetesリソース（IngressやServiceなど）を基にDNSレコードを自動的に管理してくれるツールであり、AWS Route 53などのDNSプロバイダーと統合することが可能です。

https://github.com/kubernetes-sigs/external-dns


以下はExternal DNSの設定例です。
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      containers:
        - name: external-dns
          image: k8s.gcr.io/external-dns/external-dns:v0.13.2 # ExternalDNSの公式イメージを指定
          args:
            - --source=service
            - --source=ingress
            - --domain-filter=<ドメイン名> # 管理対象のドメイン名を指定
            - --provider=aws # 使用するDNSプロバイダー（この例ではAWS Route 53）
            - --policy=upsert-only # DNSレコードの削除を防ぎ、作成と更新のみを許可
            - --aws-zone-type=public # パブリックホストゾーンのみ操作
            - --registry=txt # TXTレコードを使用して競合を防止
            - --txt-owner-id=xxxx # DNSレコードの所有者を識別するID
            - --txt-prefix=%{record_type}-record. # TXTレコードに付加するプレフィックス
          resources:
            limits:
              cpu: 100m
              memory: 300Mi
```
#### 設定のポイント
* `--provider=aws`:
  * AWS Route 53をDNSプロバイダーとして使用する場合に指定します。
* `--policy=upsert-only`:
  * AWSリソースの削除を防ぐ安全設定。
* `--txt-owner-id=xxxx`:
  * 管理対象レコードを追跡する識別子。複数インスタンス運用時に必須
* `--txt-prefix=%{record_type}-record.`:
  * 命名規則をカスタマイズする場合に使用（任意）

External DNSを設定することで、Kubernetesリソースの変更に応じてDNSレコードが自動的に更新され、手動での設定が不要になります。
後述するIngressの`external-dns.alpha.kubernetes.io/hostname`アノテーションと組み合わせて使用することで、より柔軟な運用が可能になります。

### Ingressの構成
AWX Operatorが提供するCRD（Custom Resource Definition）設定の一部として、Ingressの構成を行います。[公式ドキュメント](https://github.com/ansible/awx-operator/blob/devel/docs/user-guide/advanced-configuration/mesh-ingress.md)によれば、`ingress_annotations`を利用することで、ALB（Application Load Balancer）やExternal DNSに関連する細かい設定をCRDに組み込む形で簡潔に記述できます。

https://github.com/ansible/awx-operator/blob/devel/docs/user-guide/advanced-configuration/mesh-ingress.md

#### 接続フローと概要
IngressとExternal DNSを組み合わせた接続フローを以下に示します。

```scss
[ユーザー] --> [Route 53] (External DNSでDNS登録) --> [ALB] --> [Ingress] --> [AWX Pod]
```

#### 接続フローの詳細解説
1. **External DNSによるDNS登録**  
   KubernetesのIngressやService情報を元に、External DNSがRoute 53にDNSレコードを自動登録します。

2. **Route 53で名前解決**  
   ユーザーが指定したドメイン名でアクセスすると、Route 53がALBのエンドポイントに名前解決を行います。

3. **ALBを経由してトラフィックを処理**  
   ALBはHTTPS通信を処理し、トラフィックをKubernetesのIngressリソースにルーティングします。

4. **HTTPS化の対応**  
   HTTPS通信を有効化するためには、AWS Certificate Manager（ACM）で発行したSSL証明書をALBに関連付け、Ingressリソース内でその証明書を指定します。

#### AWX OperatorのIngress設定例
AWX Operatorのデプロイ時に、Ingressリソースを設定する際の記載例は以下の通りです。
```yaml
  ingress_type: ingress # Exposes AWX using ingress.
  hostname: <ドメイン名>
  ingress_annotations: |
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/target-group-attributes: stickiness.enabled=true,stickiness.lb_cookie.duration_seconds=86400
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-Ext2-2021-06
    alb.ingress.kubernetes.io/certificate-arn: <SSL証明書ARN>
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    external-dns.alpha.kubernetes.io/hostname: <ドメイン名>
  ingress_path: /
  ingress_path_type: Prefix
  ingress_api_version: 'networking.k8s.io/v1'
```

#### 設定のポイント
- `ingress_type: ingress` を設定することで、AWXがIngressを利用して外部に公開されるようにします。
- `hostname`に指定したドメイン名が外部からのアクセスポイントとなります。
- **ingress_annotations**  
  - ALB (Application Load Balancer) に関連する設定を細かく指定します。
    - `stickiness.enabled=true` : セッションの持続性を有効化。
    - `certificate-arn` : HTTPS通信を有効にするためのSSL証明書を指定。
    - `backend-protocol: HTTP` : バックエンドがHTTPで動作している場合に必要。
  - `external-dns.alpha.kubernetes.io/hostname` :
    - 前述したExternal DNS設定との組み合わせ。
 
- **ingress_path および ingress_path_type**
  - `ingress_path: /` により、ルートパスでアクセスできるように設定。
  - `ingress_path_type: Prefix` を設定することで、指定されたパスに基づいたルーティングが可能になります。
- **SSLポリシーの選択**  
  - `alb.ingress.kubernetes.io/ssl-policy`では、TLS 1.3を含む強力なセキュリティポリシーが適用されるように設定されています。


これらの設定により、AWX側でIngress Gatewayをデプロイ管理することができます。

設定を完了した後、Azure ADとAWX Operatorの連携にコールバックURLの確認が必要です。
このURLは、認証後にAzure ADがAWXにリダイレクトする際に使用されます。

### コールバックURLを確認
AWX Operatorの管理画面から「設定 > Azure ADの設定」を開き、コールバックURLを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/b95830ed-b32a-464a-eba9-e6daeea21183.png)

このコールバックURLを後続のリダイレクトURIとして設定します。

:::note info
コールバックURLのデフォルト値はシステム設定から変更可能することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/7337c7b7-a35f-5a2e-72d6-151f43016b57.png)
:::

## 03. リダイレクトURI設定およびAPIアクセス許可（Azure AD）
### リダイレクトURI設定
Azure ポータルの「SSOアプリケーション管理画面」から、「認証 > プラットフォーム構成」にてリダイレクトURI（前述で確認したコールバックURL）を追加します。

「プラットフォームを追加」ボタンをクリックし、「Web」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/78ca201c-5dea-4cd2-7257-3203dc2e7213.png)

リダイレクトURIを入力し、「Configure」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/dada5ac4-de5a-68fc-0ac2-77627cc3feec.png)

https://ansible.readthedocs.io/projects/awx/en/latest/administration/tipsandtricks.html#configuring-the-awxhost-hostname-for-notifications

### クライアントシークレットの発行
リダイレクトURIの設定後、クライアントシークレットを発行します。
アプリケーションの「証明書とシークレット」タブを開き、「新しいクライアントシークレットの追加」を選択します。
シークレットを発行し、その値を安全に保管します（後でKubernetes Secretに登録します）。

操作内容は、以下のブログが参考になりました。

https://devzero.co.za/2020/01/05/ansible-tower-awx-single-sign-on-with-azure-ad/

### APIアクセス許可の追加
APIアクセス許可として`User.Read`、`openid`、`profile`を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/31c9d572-cebc-df73-fc9f-35308a6b395b.png)

## 04. Secretsリソース設定
Azure ADの認証情報（Client IDとClient Secret）をKubernetesのSecretに保存し、AWX Operatorがそれを参照するよう設定します。ExternalSecretを利用して認証情報を保存する例を以下に示します。

https://github.com/external-secrets/external-secrets

```
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: awx-azuread-oauth2-secret
  namespace: awx
spec:
  refreshInterval: "0"
  secretStoreRef:
    name: aws-secret-store
    kind: SecretStore
  target:
    name: awx-azuread-oauth2-secret
    template:
      type: Opaque
  data:
    - secretKey: SOCIAL_AUTH_AZUREAD_OAUTH2_KEY
      remoteRef:
        key: common/awx
        property: client-id-key
    - secretKey: SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET
      remoteRef:
        key: common/awx
        property: client-secret-key

```
:::note info
前述で発行した、Azure ADのclient-idとclient-secretがKubernetesのSecretとして保存され、AWX Operatorがこれを参照して認証に使用します。
:::

AWX Operator CRDの設定で`extra_env`を利用してシークレットを参照するよう設定します。

参考Issueは以下になります。

https://github.com/ansible/awx-operator/issues/1224

```
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
  name: awx
  namespace: awx
spec:
  task_extra_env: |
    - name: SOCIAL_AUTH_AZUREAD_OAUTH2_KEY
      valueFrom:
        secretKeyRef:
          name: awx-azuread-oauth2-secret
          key: SOCIAL_AUTH_AZUREAD_OAUTH2_KEY
    - name: SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET
      valueFrom:
        secretKeyRef:
          name: awx-azuread-oauth2-secret
          key: SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET
  web_extra_env: |
    - name: SOCIAL_AUTH_AZUREAD_OAUTH2_KEY
      valueFrom:
        secretKeyRef:
          name: awx-azuread-oauth2-secret
          key: SOCIAL_AUTH_AZUREAD_OAUTH2_KEY
    - name: SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET
      valueFrom:
        secretKeyRef:
          name: awx-azuread-oauth2-secret
          key: SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET
  extra_settings:
    - setting: SOCIAL_AUTH_AZUREAD_OAUTH2_KEY
      value: 'os.getenv("SOCIAL_AUTH_AZUREAD_OAUTH2_KEY")'
    - setting: SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET
      value: 'os.getenv("SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET")'

```
適用後、AWX Operatorの`「設定」 > 「Azure ADの設定」`からも適用されていることを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/e49776ae-94fb-22b0-3ce6-ae40c43bdd50.png)

:::note info
この設定により、AWXはAzure ADのクライアントIDとクライアントシークレットを使用して、Azure ADと連携し、ユーザー認証を行うことができます。
:::

## 05. SSO認証でログイン
Azure ADのアイコンがログイン画面に表示されます。このアイコンをクリックし、SSO認証によるログインが可能かどうか確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/05b67ded-fb8c-9b2c-38bf-03f844b32517.png)

この際、必要に応じて管理者承認の依頼が必要です。今回は別部門のチームがAzure ADを管理しているため、そちらへ管理者承認依頼を行いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/cfede0e1-5d3c-630c-0cd5-dbce002b2c18.png)

https://jpazureid.github.io/blog/azure-active-directory/azure-ad-consent-framework/

接続／ログインできない場合は、以下の点を確認してください。

* リダイレクトURIが正しいか
* APIアクセス許可が正しく設定されているか
* Azure ADのアプリケーション登録が正しく行われているか
* Azure ADのクライアントIDとクライアントシークレットが正しくKubernetesのSecretに保存されているか

### 参考ドキュメント

https://learn.microsoft.com/ja-jp/troubleshoot/azure/


SSOログイン後、以下のようにAzure AD経由で認証されたユーザーがソーシャルユーザーとして登録されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588696/55d1e33f-6fa0-220b-1896-661e4151a0ce.png)

登録のタイミングで、所属する組織やチームに自動的にマッピングすることも可能です。これにより、アクセス権限の割り当てやユーザー管理を効率化できます。
具体的な設定方法については、以下のドキュメントをご参照ください。

https://docs.ansible.com/ansible-tower/3.8.3/html_ja/administration/social_auth.html#ag-org-team-maps


# 終わりに

本記事では、Kubernetes環境にデプロイしたAWX OperatorのDashboardでAzure ADを使用したSSO認証の設定方法をご紹介しました。この設定を行うことで、シングルサインオンを通じて、ユーザー体験を向上させるとともに、セキュリティの強化や管理の効率化が実現できます。

AWX Operatorに関する情報はまだ少なく、設定に苦労する場面もありましたが、基本的な構造はAnsibleと同様のため、問題が発生した際にはAnsibleの公式ドキュメントが非常に参考になります。
以下にリンクを記載しておきますので、ぜひご活用ください。

https://docs.ansible.com/automation-controller/4.0.0/html_ja/administration/ent_auth.html#azure-ad-settings

最後までお読みいただきありがとうございました。
本記事が少しでも誰かのお役に立てれば幸いです。