Cisco Meraki

Cisco Merakiネットワーク認証情報をローテーション

概要

本ページでは、Cisco Merakiネットワーク認証情報をローテーションするためのパスワードローテーションを設定する方法について解説します。

要件

  • KSMアプリケーション: Keeperシークレットマネージャー (KSM) アプリケーションが設定されていること。

  • 共有フォルダ: すべてのレコードが保存される共有フォルダを設定されていること。

  • PAM構成: PAM構成が設定されており、ゲートウェイが実行中でこの構成に接続されていること。

  • requestsライブラリ: Python環境にリクエストライブラリがインストールされていること。このライブラリは、CiscoデバイスへのHTTPリクエストを行うために必要となります。

  • AnyConnect Cisco VPNの設定: Ciscoデバイスに接続するために、KeeperゲートウェイをホストしているマシンにCisco AnyConnect VPNがインストールされ、適切に設定されていること。

  • Ciscoデバイスの接続テスト

requrstライブラリをインストール

requestsライブラリを使用すると、HTTPリクエストを簡単に送信できます。Keeperゲートウェイ環境で Python仮想環境をアクティブ化し、以下のコマンドを使用してライブラリをインストールします。

pip install requests

AnyConnect Cisco VPNの設定

KeeperゲートウェイをホストしているマシンにCisco AnyConnect VPNがインストールされており、Ciscoデバイスに接続できるように適切に設定されていることを確かにします。この設定は、Ciscoデバイスへの安全な接続を確立するために必要となります。

Ciscoデバイスのテスト手順

以下は、Cisco デバイスをテストし、Ciscoサンドボックス環境で新しいユーザーを作成する手順です。

注: 仮想環境を使用する場合は、 Python環境設定に基づいてスクリプトの先頭にシェバン行を追加します。

1. Cisco Sandboxにログインする

  • Ciscoアカウントの認証情報を使用してログインします。

  • サンドボックスを選択して起動します。

2. デバイスを選択して起動する

  • サンドボックスカタログに移動します。

  • Ciscoデバイス (Cisco IOS XE など) に適したサンドボックスを選択します。

  • サンドボックスを起動します。

3. メールまたはDevNet環境で詳細情報を受け取る

サンドボックスを起動すると、接続の詳細情報が記載されたメールが届きます。または、DevNet環境の「Quick Access」で詳細情報を確認できます。

4. Cisco AnyConnect VPNをダウンロード

  • Cisco AnyConnect Secure Mobility Clientをダウンロードしてインストールします。

  • 接続手順について、詳しくはこちらのウェブサイトをご覧ください。

5. VPNに接続

  • Cisco AnyConnectセキュアモビリティクライアントを開きます。

  • メールまたはDevNet 環境からのVPN接続の詳細情報を入力します。

  • 提供されたユーザー名とパスワードを使用して接続します。

6. 開発者の認証情報を保存する

この時点で、開発者認証情報 (ホスト、ユーザー名、パスワード) が表示されます。これらの値を、Cisco Authentication Recordという名前でLoginタイプのKeeperレコードに保存します。ポストローテーションスクリプトを実行するには、このKeeperレコード名が必要になります。

7. Cisco Authentication Recordにカスタムフィールドを追加

Cisco Authentication Recordにhost_endpointという名前のカスタムフィールドを追加し、その値をホストアドレス (例: 10.10.20.48) に設定します。

8. ユーザーを作成

  • ターミナルまたはSSHクライアントを開きます。

  • 提供されたIP アドレスと認証情報を使用してCiscoデバイスに接続します。

9. ユーザー作成手順

  1. 管理者ユーザー (開発者) でログインします。

    ssh developer@<device-ip>
  2. 特権コマンドを有効にします。

    enable
  3. 構成モードに入ります。

    configure terminal
  4. パスワードを使用する新しいユーザーを作成します。

    username <user> password <pass>

10. 新しいユーザーをテスト

新しいユーザーでログインします。

ssh <user>@<device-ip>

注:<user>を作成したユーザー名に、<device-ip>をCiscoデバイスのIPアドレスに置き換えます。

ボルトでローテーションを設定

上記の準備が完了した後、以下を確認します。

1

すべての要件が満たされていることを確かにします。

2

ポストローテーションスクリプトが、Cisco管理者の認証情報を含むKeeperレコードを参照するようにします。

3

Keeper PAMユーザーレコードにポストローテーション スクリプトを添付します。このレコードのシークレットがローテーションされると、ポストローテーションスクリプトが実行され、指定されたCiscoデバイスユーザーのパスワードが更新されます。

1. ローテーションレコードをセットアップ

新しいPAMユーザーレコードを作成し、パスワードをローテーションするSnowflakeユーザーの詳細情報を保存します。

  • Ciscoデバイスの管理者認証情報と一致するようにユーザー名を設定します。

  • パスワードを、ユーザーに設定されている現在のパスワードに設定します。

  • Cisco Authentication Recordにnetwork_idという名前のカスタムフィールドを追加し、その値をホストアドレス (例: 13.0.0.1) に設定します。

2. PAMスクリプトを追加

  • パスワードのローテーションを実行する以下のPythonスクリプトを添付します。スクリプトにはコメントが含まれています。

3. NOOPカスタムフィールドを追加

  • ノーオペレーション (NOOP) アトミック実行を有効にします。

    • ユーザーの詳細情報が保存されている現在のPAMユーザーレコードで、 NOOPという名前の新しいカスタムテキストフィールドを作成し、その値をTrueに設定します。

4. パスワードローテーション設定を構成

  1. Rotation Typo (ローテーションタイプ): この例では、「On-Demand」に設定します。

  2. Password Complexity (パスワードの複雑さ): 特別な要件がない限り、デフォルトのままにしておきます。

  3. Rotation Settings (ローテーション設定): 先ほど設定したPAM構成を指定します。

  4. Administrative Credentials Record (管理者認証情報レコード): 空白のままでも構いません。

Pythonスクリプト

以下は、Cisco Merakiユーザーの認証情報をローテーションするPAMスクリプトです。

'''
Cisco merakiユーザーアカウントのパスワードローテーションスクリプト

このスクリプトで、指定されたCisco merakiユーザーのパスワードがローテーションされます。

注: Pythonインタープリタへのパスにスペースが含まれている場合、スクリプトの実行が失敗します。
これはLinuxのシバン行における既知の制限であり、
スペースを含まないパスにPythonインタープリタへのシンボリックリンクを作成する必要があります。
    例: sudo ln -s "/usr/local/bin/my python3.7" /usr/local/bin/pam_rotation_venv_python3
'''

import sys
import base64
import json
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
'''
Optionally display installed packages for debugging. Uncomment if needed.
import pkg_resources
print("# \n# Installed packages for debugging:")
installed_packages = pkg_resources.working_set
installed_packages_list = sorted(["%s==%s" % (i.key, i.version) for i in installed_packages])
for m in installed_packages_list:
    print(f"  {m}")
'''

# requestsパッケージをインポート
try:
    import requests
except ImportError:
    print("# Error: The 'requests' package is not installed. Run 'pip install requests' to install it.")
    exit(1)

def fetch_meraki_user_by_email(api_key, network_id, email):
    """
    Fetches User details by email.
    
    Args:
    - api_key (str): The Meraki API key.
    - network_id (str): The network ID to search within.
    - email (str): The email of the user to fetch.
    
    Returns:
    - User details if found, otherwise None.
    """
    if not network_id:
        print("Invalid network ID.")
        return None

    # Merakiダッシュボードユーザーを取得するためのURL
    users_url = f"https://api.meraki.com/api/v1/networks/{network_id}/merakiAuthUsers"
    headers = {
        'X-Cisco-Meraki-API-Key': api_key,
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    }

    try:
        # ユーザーを取得するためのGETリクエストを送信します。
        response = requests.get(users_url, headers=headers)
        response.raise_for_status()
        # Parse response JSON
        users = response.json()

        if users:
            for user in users:
                if user['email'] == email:
                    print("\nUser found for the email-", email)
                    return user
        return None

    except requests.exceptions.RequestException as e:
        print(f"Error fetching Meraki dashboard users: {e}")
        return None

def update_meraki_user_password(api_key, network_id, user_id, new_password):
    """
    Updates the password for a Meraki dashboard user.
    
    Args:
    - api_key (str): The Meraki API key.
    - network_id (str): The network ID the user belongs to.
    - user_id (str): The ID of the user to update.
    - new_password (str): The new password to set.
    
    Returns:
    - bool: True if successful, otherwise False.
    """
    if not network_id:
        print("Invalid network ID.")
        return False

    # 特定のユーザーのパスワードを更新するためのURL
    user_url = f"https://api.meraki.com/api/v1/networks/{network_id}/merakiAuthUsers/{user_id}"
    headers = {
        'X-Cisco-Meraki-API-Key': api_key,
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    }

    payload = {'password': new_password}

    # ユーザーのパスワードを更新するためのPUTリクエストを送信します。
    response = requests.put(user_url, headers=headers, json=payload)
    
    return response

def rotate(meraki_network_id, meraki_api_key, meraki_user_email, new_password):
    """
    Rotate the password for a given Cisco user.
    Args:
    - meraki_network_id (str): Network ID of the network where the user is located.
    - meraki_api_key (str): API access key for authorization.
    - meraki_user_email (str): Email of the user whose password needs to be rotated.
    - new_password (str): The new password to be set for the Cisco user.
    Returns:
    - None
    """
    
    # 関数fetch_meraki_user_by_emailを呼び出して、ユーザーのメールアドレスを使用してユーザーの詳細を取得します。
    user = fetch_meraki_user_by_email(meraki_api_key, meraki_network_id, meraki_user_email)

    # ユーザーが存在しない場合、メッセージを表示してプログラムを終了します。
    if not user:
        print(f"No user found with the email: {meraki_user_email}")
        exit(1)
    
    try:
        meraki_user_id = user['id']

        # 指定されたユーザーIDを使用してパスワードを更新します。
        response = update_meraki_user_password(meraki_api_key, meraki_network_id, meraki_user_id, new_password)
        if response.status_code == 200:
            print(f"Password updated successfully for user with email {meraki_user_email}")
        else:
            print(f"Failed to update password. Status code: {response.status_code}, Error: {response.text}")

    except requests.exceptions.HTTPError as http_err:
        print(f"HTTP error occurred while updating the password for the given user email: {http_err}")
    except Exception as err:
        print(f"An error occurred: {err}")

def main():
    """
    Main function to rotate the password for a Cisco meraki user.

    Reads and decodes input parameters from stdin, including the authentication record details
    and the new password. Then, updates the password of the specified Cisco meraki user.
    """
    record_title = 'Cisco Authentication Record' #This should be same as the title of the record containing meraki api key and network ID details. 
    api_access_token_record = None
    params = None
    
    # 標準入力(stdin)から入力パラメータを読み取り、デコードします。
    for base64_params in sys.stdin:
        params = json.loads(base64.b64decode(base64_params).decode())

        # PAMスクリプトセクションから渡されたJSON文字列をデコードし、「Rotation Credential」レコードとして読み込みます。
        records = json.loads(base64.b64decode(params.get('records')).decode())
        # 指定されたタイトルと一致するレコードを検索します。
        api_access_token_record = next((record for record in records if record['title'].lower() == record_title.lower()), None)
        break

    if api_access_token_record is None:
        print(f"# Error: No Record with the access token found. Title: {record_title}")
        exit(1)
    
    # レコードから詳細情報を抽出します。
    
    # ユーザーが存在するネットワークのネットワークIDです。
    meraki_network_id = api_access_token_record.get('network_id')
    
    # Cisco Meraki API認証のためのAPIキーです。
    meraki_api_key = api_access_token_record.get('password')

    # パスワードをローテーションする必要があるCisco Merakiユーザーのメールアドレスです。
    meraki_user_email = params.get('user')

    # Cisco Merakiユーザーに設定する新しいパスワードです。
    new_password = params.get('newPassword')
    
    # 必要なすべてのフィールドが存在するか確認します。
    if not all([meraki_network_id, meraki_api_key, meraki_user_email]):
        print("# Error: One or more required fields are missing in the access token record.")
        exit(1)

    # 指定されたCisco Merakiユーザーのパスワードをローテーションします。
    rotate(meraki_network_id, meraki_api_key, meraki_user_email, new_password)

if __name__ == "__main__":
    main()

上記のSnowflakeポストローテーションスクリプトは、以下でも入手できます。

Cisco Merakiネットワークユーザー認証情報のローテーション

注: パスワードがローテーションされるユーザーは管理者であってはならず、クライアントVPNに対して認証されている必要があります (ユーザー管理ポータルでユーザーを追加する際、認証オプションに「Yes」を選択する必要があります)。

PAMユーザーレコードで Ciscoユーザー認証情報のローテーションを設定した後、[スクリプトのみを実行]をクリックすると認証情報がローテーションします。

最終更新