Created
June 1, 2025 05:18
-
-
Save brunogama/10600356fd7601159b05df23f6378939 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# dump-api-keys - Export API keys from macOS Keychain as environment variables | |
# Usage: | |
# eval $(dump-api-keys) # Load all API keys into environment | |
# dump-api-keys > keys.env # Save to file for sourcing | |
# dump-api-keys --pattern FOO # Only dump keys containing FOO | |
# | |
# This script exports all API keys (service names ending with _KEY) | |
# that were stored using the store-api-key script. | |
set -euo pipefail | |
# Script name for error messages | |
readonly SCRIPT_NAME="$(basename "$0")" | |
# Default pattern matches all keys ending with _KEY | |
PATTERN=".*API_KEY$" | |
SAFE_MODE=false | |
# Function to display usage information | |
usage() { | |
cat << EOF | |
Usage: $SCRIPT_NAME [OPTIONS] | |
Export API keys from macOS Keychain as environment variables. | |
Options: | |
-p, --pattern PATTERN Only export keys matching PATTERN (regex) | |
-s, --safe Generate a sourceable file instead of eval-ready output | |
-h, --help Show this help message | |
Examples: | |
# Load all API keys into current shell | |
eval \$($SCRIPT_NAME) | |
# Save to file and source it (safer) | |
$SCRIPT_NAME > ~/.api-keys.env | |
source ~/.api-keys.env | |
# Only export keys containing "AWS" | |
eval \$($SCRIPT_NAME --pattern AWS) | |
# Export in safe mode (with instructions) | |
$SCRIPT_NAME --safe > api-keys.sh | |
Security Note: | |
Using eval is convenient but can be dangerous. Consider using the | |
file-based approach (saving to a file and sourcing it) for better security. | |
EOF | |
exit 0 | |
} | |
# Function to display error messages | |
error() { | |
echo "Error: $1" >&2 | |
exit "${2:-1}" | |
} | |
# Parse command line arguments | |
while [[ $# -gt 0 ]]; do | |
case "$1" in | |
-p|--pattern) | |
PATTERN="$2" | |
shift 2 | |
;; | |
-s|--safe) | |
SAFE_MODE=true | |
shift | |
;; | |
-h|--help) | |
usage | |
;; | |
*) | |
error "Unknown option: $1" 1 | |
;; | |
esac | |
done | |
# Check if running on macOS | |
if [[ "$OSTYPE" != "darwin"* ]]; then | |
error "This script requires macOS" 2 | |
fi | |
# Check if security command is available | |
if ! command -v security &> /dev/null; then | |
error "The 'security' command is not available" 2 | |
fi | |
# Function to get all API key service names from keychain | |
get_api_key_services() { | |
# Dump keychain and extract service names | |
# The dump-keychain output includes lines like: "svce"<blob>="SERVICE_NAME" | |
security dump-keychain 2>/dev/null | \ | |
awk -F'"' '/"svce"<blob>=/ && $4 ~ /'"$PATTERN"'/ {print $4}' | \ | |
sort -u || true | |
} | |
# Function to escape a value for safe shell usage | |
escape_value() { | |
printf '%q' "$1" | |
} | |
# Header for safe mode | |
if [[ "$SAFE_MODE" == "true" ]]; then | |
cat << 'EOF' | |
#!/bin/bash | |
# Generated by dump-api-keys | |
# Source this file to load API keys into your environment | |
# Usage: source this_file.sh | |
EOF | |
fi | |
# Get list of API key services | |
services=$(get_api_key_services) | |
if [[ -z "$services" ]]; then | |
if [[ "$SAFE_MODE" == "true" ]]; then | |
echo "# No API keys found matching pattern: $PATTERN" >&2 | |
else | |
echo "# No API keys found matching pattern: $PATTERN" >&2 | |
fi | |
exit 0 | |
fi | |
# Export count for reporting | |
count=0 | |
# Process each service | |
while IFS= read -r service; do | |
# Skip empty lines | |
[[ -z "$service" ]] && continue | |
# Validate service name (alphanumeric, underscore, dash only) | |
if ! [[ "$service" =~ ^[A-Za-z0-9_-]+$ ]]; then | |
echo "# Warning: Skipping invalid service name: $service" >&2 | |
continue | |
fi | |
# Retrieve the API key value | |
if api_key=$(security find-generic-password -a "$USER" -s "$service" -w 2>/dev/null); then | |
# Escape the value for safe shell usage | |
escaped_value=$(escape_value "$api_key") | |
# Output the export statement | |
echo "export $service=$escaped_value" | |
((count++)) | |
else | |
echo "# Warning: Could not retrieve key for: $service" >&2 | |
fi | |
done <<< "$services" | |
# Summary in safe mode | |
if [[ "$SAFE_MODE" == "true" ]]; then | |
echo "" | |
echo "# Exported $count API keys" | |
fi | |
# If no keys were exported successfully | |
if [[ $count -eq 0 ]]; then | |
echo "# No API keys could be retrieved" >&2 | |
exit 1 | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# get-api-key - Retrieve API keys from macOS Keychain | |
# Usage: get-api-key <service_name> | |
# Example: get-api-key SERVICEA_API_KEY | |
# | |
# This script uses the macOS security command to retrieve API keys | |
# from the user's login keychain. | |
set -euo pipefail | |
# Script name for error messages | |
readonly SCRIPT_NAME="$(basename "$0")" | |
# Function to display usage information | |
usage() { | |
cat << EOF | |
Usage: $SCRIPT_NAME <service_name> | |
Retrieve an API key from the macOS Keychain. | |
Arguments: | |
service_name The name/identifier for the API key (e.g., SERVICEA_API_KEY) | |
Example: | |
$SCRIPT_NAME SERVICEA_API_KEY | |
Notes: | |
- The key must have been previously stored with store-api-key | |
- You may be prompted to allow access on first use | |
- The API key is printed to stdout | |
EOF | |
exit 1 | |
} | |
# Function to display error messages | |
error() { | |
echo "Error: $1" >&2 | |
exit "${2:-1}" | |
} | |
# Check if running on macOS | |
if [[ "$OSTYPE" != "darwin"* ]]; then | |
error "This script requires macOS" 2 | |
fi | |
# Check if security command is available | |
if ! command -v security &> /dev/null; then | |
error "The 'security' command is not available" 2 | |
fi | |
# Check number of arguments | |
if [[ $# -ne 1 ]]; then | |
usage | |
fi | |
# Assign argument to variable | |
readonly SERVICE_NAME="$1" | |
# Validate service name | |
if [[ -z "$SERVICE_NAME" ]]; then | |
error "Service name cannot be empty" 3 | |
fi | |
# Validate service name format (alphanumeric, underscore, dash) | |
if ! [[ "$SERVICE_NAME" =~ ^[A-Za-z0-9_-]+$ ]]; then | |
error "Service name must contain only letters, numbers, underscores, and dashes" 3 | |
fi | |
# Retrieve the API key from the keychain | |
# -a: account name (using current user) | |
# -s: service name | |
# -w: output password only | |
API_KEY=$(security find-generic-password \ | |
-a "$USER" \ | |
-s "$SERVICE_NAME" \ | |
-w \ | |
2>/dev/null) || { | |
error "API key for '$SERVICE_NAME' not found in keychain" 4 | |
} | |
# Output the API key | |
echo "$API_KEY" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# store-api-key - Store API keys securely in macOS Keychain | |
# Usage: store-api-key <service_name> <api_key> [--force] | |
# Example: store-api-key SERVICEA_API_KEY randomapikey | |
# | |
# This script uses the macOS security command to store API keys | |
# in the user's login keychain securely. | |
set -euo pipefail | |
# Script name for error messages | |
readonly SCRIPT_NAME="$(basename "$0")" | |
# Default settings | |
FORCE_UPDATE=false | |
# Function to display usage information | |
usage() { | |
cat << EOF | |
Usage: $SCRIPT_NAME <service_name> <api_key> [--force] | |
Store an API key securely in the macOS Keychain. | |
Arguments: | |
service_name The name/identifier for the API key (e.g., SERVICEA_API_KEY) | |
api_key The API key value to store | |
Options: | |
--force Override existing key without confirmation | |
Example: | |
$SCRIPT_NAME SERVICEA_API_KEY randomapikey | |
$SCRIPT_NAME SERVICEA_API_KEY newkey --force | |
Notes: | |
- The key is stored in the user's login keychain | |
- If a key with the same service name exists, you'll be prompted to confirm unless --force is used | |
- The script requires macOS and the 'security' command | |
EOF | |
exit 1 | |
} | |
# Function to display error messages | |
error() { | |
echo "Error: $1" >&2 | |
exit "${2:-1}" | |
} | |
# Check if running on macOS | |
if [[ "$OSTYPE" != "darwin"* ]]; then | |
error "This script requires macOS" 2 | |
fi | |
# Check if security command is available | |
if ! command -v security &> /dev/null; then | |
error "The 'security' command is not available" 2 | |
fi | |
# Parse arguments | |
if [[ $# -lt 2 || $# -gt 3 ]]; then | |
usage | |
fi | |
# Check for force flag | |
if [[ $# -eq 3 && "$3" == "--force" ]]; then | |
FORCE_UPDATE=true | |
elif [[ $# -eq 3 ]]; then | |
error "Unknown option: $3" 1 | |
fi | |
# Assign arguments to variables | |
readonly SERVICE_NAME="$1" | |
readonly API_KEY="$2" | |
# Validate service name | |
if [[ -z "$SERVICE_NAME" ]]; then | |
error "Service name cannot be empty" 3 | |
fi | |
# Validate API key | |
if [[ -z "$API_KEY" ]]; then | |
error "API key cannot be empty" 3 | |
fi | |
# Validate service name format (alphanumeric, underscore, dash) | |
if ! [[ "$SERVICE_NAME" =~ ^[A-Za-z0-9_-]+$ ]]; then | |
error "Service name must contain only letters, numbers, underscores, and dashes" 3 | |
fi | |
# Check if the key already exists in the keychain | |
if security find-generic-password -s "$SERVICE_NAME" &>/dev/null; then | |
if [[ "$FORCE_UPDATE" == false ]]; then | |
read -p "API key '$SERVICE_NAME' already exists in keychain. Overwrite? (y/n): " confirm | |
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then | |
echo "Operation cancelled. API key not updated." | |
exit 0 | |
fi | |
fi | |
echo "Updating existing API key for '$SERVICE_NAME'..." | |
else | |
echo "Creating new API key for '$SERVICE_NAME'..." | |
fi | |
# Store the API key in the keychain | |
# -a: account name (using current user) | |
# -s: service name | |
# -w: password/secret | |
# -U: update if exists | |
if security add-generic-password \ | |
-a "$USER" \ | |
-s "$SERVICE_NAME" \ | |
-w "$API_KEY" \ | |
-U \ | |
2>/dev/null; then | |
echo "✅ Successfully stored API key for '$SERVICE_NAME' in keychain" | |
else | |
error "Failed to store API key in keychain" 4 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment