APIキーを管理してくれるシェルスクリプトを書いてみた
今回は私が普段利用しているシェルスクリプトを紹介しようと思います。
紹介するもの!
今回紹介するのはAPIキーを管理してくれる機能になります。私は普段ブログを書くにあたり様々なサービスを利用しており、各サービスごとにAPIキーの利用があります。例えばAPIキーを利用しているサービスは以下になります。
- Datadog
- Gemini
- OpenAI
- Traceloop
これらのAPIキーを利用するときに、毎回ウェブサイトに行ってチェックするのも嫌ですし、平文でローカルに保存するのもよろしくないです。かといってAPIキーを暗記するなんて普通に考えてできないので実質不可能です。
これらの課題を解決するためにシェルスクリプトでAPIキーを管理するための機能を作りました。以前に作ったのでプロンプトは残ってないですが、claude codeとやり取りしながら実装しました。実装されている機能は以下になります。
- APIキーの暗号化・復号化
- APIキーの登録
- APIキーを現在のシェルの環境変数に登録する
- 登録されているAPIキーの一覧取得(登録名のみ)
- APIの平文を取得する
- 登録しているAPIキーの削除
実装紹介
仕組みの説明
まずはざっくり仕組みを説明します。シェルスクリプトは$HOME/.config/zsh/api_key_manager.zsh
に管理されており、APIキーは$HOME/.config/zsh/api_keys
に保存されるようになっています。APIキーはAES256を用いて暗号化され、暗号化する時に任意のパスワードを利用する必要があります。パスワードを利用することにより、仮に暗号化されたコードの場所がバレてしまってもキーの復号をできないようにしています。
暗号化・復号化
APIキーの暗号化と復号化は以下で実施しています。
# Configuration
API_KEY_DIR="${HOME}/.config/zsh/api_keys"
API_KEY_INDEX="${API_KEY_DIR}/.index"
# Ensure directory exists
[[ ! -d "$API_KEY_DIR" ]] && mkdir -p "$API_KEY_DIR"
# Function to encrypt data using openssl
_encrypt_data() {
local data="$1"
local key_name="$2"
echo "$data" | openssl enc -aes-256-cbc -salt -pbkdf2 -out "${API_KEY_DIR}/${key_name}.enc" 2>/dev/null
}
# Function to decrypt data using openssl
_decrypt_data() {
local key_name="$1"
local key_file="${API_KEY_DIR}/${key_name}.enc"
[[ -f "$key_file" ]] && openssl enc -aes-256-cbc -d -pbkdf2 -in "$key_file" 2>/dev/null
}
APIキーは*.enc
ファイルで保存され、利用時はパスワードが求められます。
APIキーの登録
以下にて新たなAPIキーを登録することができます。実行時にパスワードが聞かれるので、入力すると一覧にAPIキーを登録することができます。
register_api_key() {
local key_name="$1"
local key_content="$2"
if [[ -z "$key_name" || -z "$key_content" ]]; then
echo "Usage: register_api_key <key_name> <key_content>" >&2
return 1
fi
# Check if key already exists
if [[ -f "${API_KEY_DIR}/${key_name}.enc" ]]; then
echo "Error: API key with name '$key_name' already exists" >&2
return 1
fi
# Encrypt and save the key
echo "Enter encryption password for '$key_name':"
if _encrypt_data "$key_content" "$key_name"; then
# Add to index
echo "$key_name" >> "$API_KEY_INDEX"
# Sort and remove duplicates
sort -u "$API_KEY_INDEX" -o "$API_KEY_INDEX"
echo "API key '$key_name' registered successfully"
else
echo "Error: Failed to encrypt API key" >&2
return 1
fi
}
指定したAPIキーたちを環境変数に登録する
以下を実行すると、指定したAPIキーの名前とその値をまとめて環境変数に登録できます。set_api_keys_env AAA BBB CCC
のように複数指定するとAAA、BBB、CCCそれぞれについてパスワードが聞かれた上で登録することができます。
set_api_keys_env() {
if [[ $# -eq 0 ]]; then
echo "Usage: set_api_keys_env <key_name[:env_var_name]> ..." >&2
echo "Example: set_api_keys_env openai:OPENAI_API_KEY github:GITHUB_TOKEN" >&2
return 1
fi
local success_count=0
local total_count=$#
for key_spec in "$@"; do
# Parse key_name:env_var_name format
local key_name="${key_spec%%:*}"
local env_var_name="${key_spec#*:}"
# If no colon, use key name as env var name
[[ "$env_var_name" == "$key_spec" ]] && env_var_name="$key_name"
# Check if key exists
if [[ ! -f "${API_KEY_DIR}/${key_name}.enc" ]]; then
echo "Error: API key '$key_name' not found, skipping" >&2
continue
fi
# Decrypt and set as environment variable
echo "Enter decryption password for '$key_name':"
local decrypted_key=$(_decrypt_data "$key_name")
if [[ -n "$decrypted_key" ]]; then
export "$env_var_name"="$decrypted_key"
echo "✓ Environment variable '$env_var_name' set successfully"
((success_count++))
else
echo "Error: Failed to decrypt API key '$key_name'" >&2
fi
done
echo "Successfully set $success_count out of $total_count environment variables"
}
登録されているAPIキーの一覧を取得する
以下を実行すると登録されているAPIキーの一覧が取得できます。この実行ではパスワードの入力は求められません。
get_api_keys() {
if [[ ! -f "$API_KEY_INDEX" ]]; then
echo "No API keys registered"
return 0
fi
echo "Registered API keys:"
cat "$API_KEY_INDEX" | while read -r key_name; do
if [[ -f "${API_KEY_DIR}/${key_name}.enc" ]]; then
echo " - $key_name"
fi
done
}
私の環境で実行するとこんな感じになります。
get_api_keys
# 結果
Registered API keys:
- DATADOG_API_KEY
- GEMINI_API_KEY
- OPENAI_API_KEY
- TRACELOOP_API_KEY
APIキーを取得する
指定したAPIキーの内容を取得するには以下を呼び出します。こちらをget_api_key_row AAA
のようにすると、パスワードを入力し正しい値であればAPIキーが平文でコンソールに表示されます。間違っていても表示されますが文字化けして何かわからない情報になります。
get_api_key_row() {
local key_name="$1"
if [[ -z "$key_name" ]]; then
echo "Usage: get_api_key_row <key_name>" >&2
return 1
fi
# Check if key exists
if [[ ! -f "${API_KEY_DIR}/${key_name}.enc" ]]; then
echo "Error: API key '$key_name' not found" >&2
return 1
fi
# Decrypt and display
echo "Enter decryption password for '$key_name':"
local decrypted_key=$(_decrypt_data "$key_name")
if [[ -n "$decrypted_key" ]]; then
echo "API Key '$key_name': $decrypted_key"
else
echo "Error: Failed to decrypt API key" >&2
return 1
fi
}
APIキーを削除する
最後に以下を実行するとAPIキーを削除できます。実行すると本当に削除するか確認が表示され、y/Y
を入力すると削除が実行されます。なお復元はできないです。
delete_api_key() {
local key_name="$1"
if [[ -z "$key_name" ]]; then
echo "Usage: delete_api_key <key_name>" >&2
return 1
fi
# Check if key exists
if [[ ! -f "${API_KEY_DIR}/${key_name}.enc" ]]; then
echo "Error: API key '$key_name' not found" >&2
return 1
fi
# Confirm deletion
echo -n "Are you sure you want to delete API key '$key_name'? (y/N): "
read -r confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
# Remove encrypted file
rm -f "${API_KEY_DIR}/${key_name}.enc"
# Remove from index
if [[ -f "$API_KEY_INDEX" ]]; then
grep -v "^${key_name}$" "$API_KEY_INDEX" > "${API_KEY_INDEX}.tmp"
mv "${API_KEY_INDEX}.tmp" "$API_KEY_INDEX"
fi
echo "API key '$key_name' deleted successfully"
else
echo "Deletion cancelled"
fi
}
まとめ
今回は私が普段使っているAPIキー管理方法を共有しました。完全な平文で保存するよりはまだマシだと思ったのと、毎回APIキーわからなくなって新しいものを発行するよりはこのような方法を用意したほうがいいと思って作ってみました。しかし一般的にはパスワード管理ツールでやるのが得策だと思うので、興味ある人はぜひ試してもらいたいと思いつつ、業務で利用する場合は控えたほうがいいかもです(これはあくまで個人PCでのみ使っている手法です)。
Discussion