495 lines
17 KiB
Bash
Executable File
495 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# OpenTofu 密钥上传脚本
|
||
# 用于将 terraform.tfvars 中的敏感配置批量上传到 Consul
|
||
|
||
set -euo pipefail
|
||
|
||
# 配置
|
||
CONSUL_ADDR="${CONSUL_ADDR:-http://master:8500}"
|
||
CONSUL_TOKEN="${CONSUL_TOKEN:-}"
|
||
ENVIRONMENT="${ENVIRONMENT:-dev}"
|
||
TOFU_DIR="${TOFU_DIR:-tofu/environments/${ENVIRONMENT}}"
|
||
TFVARS_FILE="${TFVARS_FILE:-${TOFU_DIR}/terraform.tfvars}"
|
||
|
||
# 颜色输出
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 日志函数
|
||
log_info() {
|
||
echo -e "${BLUE}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# 检查依赖
|
||
check_dependencies() {
|
||
local deps=("curl" "jq")
|
||
for dep in "${deps[@]}"; do
|
||
if ! command -v "$dep" &> /dev/null; then
|
||
log_error "缺少依赖: $dep"
|
||
exit 1
|
||
fi
|
||
done
|
||
}
|
||
|
||
# 检查 Consul 连接
|
||
check_consul() {
|
||
log_info "检查 Consul 连接..."
|
||
if ! curl -s "${CONSUL_ADDR}/v1/status/leader" > /dev/null; then
|
||
log_error "无法连接到 Consul: ${CONSUL_ADDR}"
|
||
exit 1
|
||
fi
|
||
log_success "Consul 连接正常"
|
||
}
|
||
|
||
# 检查 tfvars 文件
|
||
check_tfvars_file() {
|
||
if [[ ! -f "$TFVARS_FILE" ]]; then
|
||
log_error "找不到 terraform.tfvars 文件: $TFVARS_FILE"
|
||
exit 1
|
||
fi
|
||
log_info "找到配置文件: $TFVARS_FILE"
|
||
}
|
||
|
||
# 解析 HCL 配置并转换为 JSON
|
||
parse_hcl_to_json() {
|
||
local tfvars_file="$1"
|
||
local temp_tf_file="/tmp/temp_config.tf"
|
||
local temp_json_file="/tmp/temp_config.json"
|
||
|
||
# 创建临时 .tf 文件,将变量赋值转换为输出
|
||
log_info "解析 HCL 配置..."
|
||
|
||
# 读取 tfvars 文件并转换为 output 格式
|
||
cat > "$temp_tf_file" << 'EOF'
|
||
# 临时配置文件,用于解析 tfvars
|
||
EOF
|
||
|
||
# 解析每个配置块
|
||
while IFS= read -r line; do
|
||
# 跳过注释和空行
|
||
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "${line// }" ]]; then
|
||
continue
|
||
fi
|
||
|
||
# 提取变量名和值
|
||
if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*(.+)$ ]]; then
|
||
local var_name="${BASH_REMATCH[1]}"
|
||
local var_value="${BASH_REMATCH[2]}"
|
||
|
||
echo "output \"$var_name\" {" >> "$temp_tf_file"
|
||
echo " value = $var_value" >> "$temp_tf_file"
|
||
echo "}" >> "$temp_tf_file"
|
||
fi
|
||
done < "$tfvars_file"
|
||
|
||
# 使用 terraform 解析配置
|
||
if command -v terraform &> /dev/null; then
|
||
cd "$(dirname "$temp_tf_file")"
|
||
terraform init -backend=false > /dev/null 2>&1 || true
|
||
terraform output -json > "$temp_json_file" 2>/dev/null || {
|
||
log_warning "无法使用 terraform 解析,尝试手动解析..."
|
||
manual_parse_tfvars "$tfvars_file" "$temp_json_file"
|
||
}
|
||
else
|
||
log_warning "未找到 terraform,使用手动解析..."
|
||
manual_parse_tfvars "$tfvars_file" "$temp_json_file"
|
||
fi
|
||
|
||
echo "$temp_json_file"
|
||
}
|
||
|
||
# 手动解析 tfvars 文件
|
||
manual_parse_tfvars() {
|
||
local tfvars_file="$1"
|
||
local output_file="$2"
|
||
|
||
log_info "手动解析 tfvars 文件..."
|
||
|
||
# 创建基础 JSON 结构
|
||
echo "{" > "$output_file"
|
||
|
||
local first_item=true
|
||
local in_block=false
|
||
local block_name=""
|
||
local block_content=""
|
||
|
||
while IFS= read -r line; do
|
||
# 跳过注释和空行
|
||
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "${line// }" ]]; then
|
||
continue
|
||
fi
|
||
|
||
# 检测配置块开始
|
||
if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*\{[[:space:]]*$ ]]; then
|
||
block_name="${BASH_REMATCH[1]}"
|
||
in_block=true
|
||
block_content=""
|
||
continue
|
||
fi
|
||
|
||
# 检测配置块结束
|
||
if [[ "$in_block" == true && "$line" =~ ^[[:space:]]*\}[[:space:]]*$ ]]; then
|
||
if [[ "$first_item" == false ]]; then
|
||
echo "," >> "$output_file"
|
||
fi
|
||
echo " \"$block_name\": {" >> "$output_file"
|
||
echo "$block_content" >> "$output_file"
|
||
echo " }" >> "$output_file"
|
||
first_item=false
|
||
in_block=false
|
||
continue
|
||
fi
|
||
|
||
# 处理块内容
|
||
if [[ "$in_block" == true ]]; then
|
||
if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*\"([^\"]*)\"|^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*([^[:space:]]+) ]]; then
|
||
local key="${BASH_REMATCH[1]:-${BASH_REMATCH[3]}}"
|
||
local value="${BASH_REMATCH[2]:-${BASH_REMATCH[4]}}"
|
||
|
||
if [[ -n "$block_content" ]]; then
|
||
block_content+=","
|
||
fi
|
||
block_content+="\n \"$key\": \"$value\""
|
||
fi
|
||
continue
|
||
fi
|
||
|
||
# 处理简单变量
|
||
if [[ "$line" =~ ^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*\"([^\"]*)\"|^[[:space:]]*([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*=[[:space:]]*([^[:space:]]+) ]]; then
|
||
local var_name="${BASH_REMATCH[1]:-${BASH_REMATCH[3]}}"
|
||
local var_value="${BASH_REMATCH[2]:-${BASH_REMATCH[4]}}"
|
||
|
||
if [[ "$first_item" == false ]]; then
|
||
echo "," >> "$output_file"
|
||
fi
|
||
echo " \"$var_name\": \"$var_value\"" >> "$output_file"
|
||
first_item=false
|
||
fi
|
||
done < "$tfvars_file"
|
||
|
||
echo "}" >> "$output_file"
|
||
}
|
||
|
||
# 上传配置到 Consul
|
||
upload_config_to_consul() {
|
||
local config_file="$1"
|
||
local uploaded_count=0
|
||
|
||
log_info "开始上传配置到 Consul..."
|
||
|
||
# 读取 JSON 配置
|
||
if [[ ! -f "$config_file" ]]; then
|
||
log_error "配置文件不存在: $config_file"
|
||
return 1
|
||
fi
|
||
|
||
# 上传 Oracle Cloud 配置
|
||
local oci_tenancy=$(jq -r '.oci_tenancy_ocid // empty' "$config_file")
|
||
local oci_user=$(jq -r '.oci_user_ocid // empty' "$config_file")
|
||
local oci_fingerprint=$(jq -r '.oci_fingerprint // empty' "$config_file")
|
||
local oci_private_key_path=$(jq -r '.oci_private_key_path // empty' "$config_file")
|
||
local oci_compartment=$(jq -r '.oci_compartment_ocid // empty' "$config_file")
|
||
local oci_region=$(jq -r '.oci_region // empty' "$config_file")
|
||
|
||
if [[ -n "$oci_tenancy" && "$oci_tenancy" != "null" && "$oci_tenancy" != "" ]]; then
|
||
=======
|
||
# 上传配置到 Consul
|
||
upload_config_to_consul() {
|
||
local config_file="$1"
|
||
local uploaded_count=0
|
||
|
||
log_info "开始上传配置到 Consul..."
|
||
|
||
# 读取 JSON 配置
|
||
if [[ ! -f "$config_file" ]]; then
|
||
log_error "配置文件不存在: $config_file"
|
||
return 1
|
||
fi
|
||
|
||
# 上传 Oracle Cloud 配置
|
||
local oci_tenancy=$(jq -r '.oci_tenancy_ocid // empty' "$config_file")
|
||
local oci_user=$(jq -r '.oci_user_ocid // empty' "$config_file")
|
||
local oci_fingerprint=$(jq -r '.oci_fingerprint // empty' "$config_file")
|
||
local oci_private_key_path=$(jq -r '.oci_private_key_path // empty' "$config_file")
|
||
local oci_compartment=$(jq -r '.oci_compartment_ocid // empty' "$config_file")
|
||
local oci_region=$(jq -r '.oci_region // empty' "$config_file")
|
||
|
||
if [[ -n "$oci_tenancy" && "$oci_tenancy" != "null" && "$oci_tenancy" != "" ]]; then
|
||
log_info "上传 Oracle Cloud 配置..."
|
||
local base_path="config/${ENVIRONMENT}/oracle"
|
||
|
||
local tenancy_ocid=$(jq -r '.oci_config.tenancy_ocid // empty' "$config_file")
|
||
local user_ocid=$(jq -r '.oci_config.user_ocid // empty' "$config_file")
|
||
local fingerprint=$(jq -r '.oci_config.fingerprint // empty' "$config_file")
|
||
local private_key_path=$(jq -r '.oci_config.private_key_path // empty' "$config_file")
|
||
local compartment_ocid=$(jq -r '.oci_config.compartment_ocid // empty' "$config_file")
|
||
local region=$(jq -r '.oci_config.region // "ap-seoul-1"' "$config_file")
|
||
|
||
# 上传非空配置
|
||
[[ -n "$tenancy_ocid" && "$tenancy_ocid" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/tenancy_ocid" -d "$tenancy_ocid" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$user_ocid" && "$user_ocid" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/user_ocid" -d "$user_ocid" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$fingerprint" && "$fingerprint" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/fingerprint" -d "$fingerprint" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$compartment_ocid" && "$compartment_ocid" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/compartment_ocid" -d "$compartment_ocid" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$region" && "$region" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
|
||
# 上传私钥文件内容
|
||
if [[ -n "$private_key_path" && "$private_key_path" != "null" && -f "$private_key_path" ]]; then
|
||
local private_key_content=$(cat "$private_key_path")
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/private_key" -d "$private_key_content" > /dev/null
|
||
((uploaded_count++))
|
||
fi
|
||
|
||
log_success "Oracle Cloud 配置已上传"
|
||
fi
|
||
|
||
# 上传华为云配置
|
||
if jq -e '.huawei_config' "$config_file" > /dev/null 2>&1; then
|
||
log_info "上传华为云配置..."
|
||
local base_path="config/${ENVIRONMENT}/huawei"
|
||
|
||
local access_key=$(jq -r '.huawei_config.access_key // empty' "$config_file")
|
||
local secret_key=$(jq -r '.huawei_config.secret_key // empty' "$config_file")
|
||
local region=$(jq -r '.huawei_config.region // "cn-north-4"' "$config_file")
|
||
local project_id=$(jq -r '.huawei_config.project_id // empty' "$config_file")
|
||
|
||
[[ -n "$access_key" && "$access_key" != "null" && "$access_key" != "" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/access_key" -d "$access_key" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$secret_key" && "$secret_key" != "null" && "$secret_key" != "" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/secret_key" -d "$secret_key" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$region" && "$region" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$project_id" && "$project_id" != "null" && "$project_id" != "" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/project_id" -d "$project_id" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
|
||
log_success "华为云配置已上传"
|
||
fi
|
||
|
||
# 上传 AWS 配置
|
||
if jq -e '.aws_config' "$config_file" > /dev/null 2>&1; then
|
||
log_info "上传 AWS 配置..."
|
||
local base_path="config/${ENVIRONMENT}/aws"
|
||
|
||
local access_key=$(jq -r '.aws_config.access_key // empty' "$config_file")
|
||
local secret_key=$(jq -r '.aws_config.secret_key // empty' "$config_file")
|
||
local region=$(jq -r '.aws_config.region // "ap-northeast-2"' "$config_file")
|
||
|
||
[[ -n "$access_key" && "$access_key" != "null" && "$access_key" != "" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/access_key" -d "$access_key" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$secret_key" && "$secret_key" != "null" && "$secret_key" != "" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/secret_key" -d "$secret_key" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$region" && "$region" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
|
||
log_success "AWS 配置已上传"
|
||
fi
|
||
|
||
# 上传 DigitalOcean 配置
|
||
if jq -e '.do_config' "$config_file" > /dev/null 2>&1; then
|
||
log_info "上传 DigitalOcean 配置..."
|
||
local base_path="config/${ENVIRONMENT}/digitalocean"
|
||
|
||
local token=$(jq -r '.do_config.token // empty' "$config_file")
|
||
local region=$(jq -r '.do_config.region // "sgp1"' "$config_file")
|
||
|
||
[[ -n "$token" && "$token" != "null" && "$token" != "" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/token" -d "$token" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$region" && "$region" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
|
||
log_success "DigitalOcean 配置已上传"
|
||
fi
|
||
|
||
# 上传 Google Cloud 配置
|
||
if jq -e '.gcp_config' "$config_file" > /dev/null 2>&1; then
|
||
log_info "上传 Google Cloud 配置..."
|
||
local base_path="config/${ENVIRONMENT}/gcp"
|
||
|
||
local project_id=$(jq -r '.gcp_config.project_id // empty' "$config_file")
|
||
local region=$(jq -r '.gcp_config.region // "asia-northeast3"' "$config_file")
|
||
local zone=$(jq -r '.gcp_config.zone // "asia-northeast3-a"' "$config_file")
|
||
local credentials_file=$(jq -r '.gcp_config.credentials_file // empty' "$config_file")
|
||
|
||
[[ -n "$project_id" && "$project_id" != "null" && "$project_id" != "" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/project_id" -d "$project_id" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$region" && "$region" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/region" -d "$region" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
[[ -n "$zone" && "$zone" != "null" ]] && {
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/zone" -d "$zone" > /dev/null
|
||
((uploaded_count++))
|
||
}
|
||
|
||
# 上传凭证文件内容
|
||
if [[ -n "$credentials_file" && "$credentials_file" != "null" && -f "$credentials_file" ]]; then
|
||
local credentials_content=$(cat "$credentials_file")
|
||
curl -s -X PUT "${CONSUL_ADDR}/v1/kv/${base_path}/credentials" -d "$credentials_content" > /dev/null
|
||
((uploaded_count++))
|
||
fi
|
||
|
||
log_success "Google Cloud 配置已上传"
|
||
fi
|
||
|
||
log_success "总共上传了 $uploaded_count 个配置项到 Consul"
|
||
}
|
||
|
||
# 列出 Consul 中的配置
|
||
list_consul_configs() {
|
||
log_info "列出 Consul 中的配置..."
|
||
|
||
local base_path="config/${ENVIRONMENT}"
|
||
|
||
echo "=== Consul 中的配置 ==="
|
||
|
||
# 获取所有配置键
|
||
local keys=$(curl -s "${CONSUL_ADDR}/v1/kv/${base_path}/?keys" | jq -r '.[]' 2>/dev/null || echo "")
|
||
|
||
if [[ -z "$keys" ]]; then
|
||
log_warning "Consul 中没有找到配置"
|
||
return
|
||
fi
|
||
|
||
echo "$keys" | while read -r key; do
|
||
local value=$(curl -s "${CONSUL_ADDR}/v1/kv/${key}?raw" 2>/dev/null || echo "无法读取")
|
||
# 隐藏敏感信息
|
||
if [[ "$key" =~ (secret|key|token|password) ]]; then
|
||
echo "$key: [已隐藏]"
|
||
else
|
||
echo "$key: $value"
|
||
fi
|
||
done
|
||
}
|
||
|
||
# 清理 Consul 配置
|
||
cleanup_consul_configs() {
|
||
log_warning "清理 Consul 配置..."
|
||
|
||
read -p "确定要删除环境 '$ENVIRONMENT' 的所有配置吗?(y/N): " confirm
|
||
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||
log_info "操作已取消"
|
||
return
|
||
fi
|
||
|
||
local base_path="config/${ENVIRONMENT}"
|
||
curl -s -X DELETE "${CONSUL_ADDR}/v1/kv/${base_path}?recurse" > /dev/null
|
||
|
||
log_success "环境 '$ENVIRONMENT' 的配置已清理"
|
||
}
|
||
|
||
# 显示帮助信息
|
||
show_help() {
|
||
cat << EOF
|
||
OpenTofu 密钥上传脚本
|
||
|
||
用法: $0 [选项]
|
||
|
||
选项:
|
||
upload 上传 terraform.tfvars 中的配置到 Consul
|
||
list 列出 Consul 中的配置
|
||
cleanup 清理 Consul 中的配置
|
||
help 显示此帮助信息
|
||
|
||
环境变量:
|
||
CONSUL_ADDR Consul 地址 (默认: http://localhost:8500)
|
||
CONSUL_TOKEN Consul ACL Token (可选)
|
||
ENVIRONMENT 环境名称 (默认: dev)
|
||
TOFU_DIR OpenTofu 目录 (默认: tofu/environments/\${ENVIRONMENT})
|
||
TFVARS_FILE 变量文件路径 (默认: \${TOFU_DIR}/terraform.tfvars)
|
||
|
||
示例:
|
||
# 上传配置到 Consul
|
||
$0 upload
|
||
|
||
# 列出 Consul 中的配置
|
||
$0 list
|
||
|
||
# 清理配置
|
||
$0 cleanup
|
||
|
||
# 指定不同环境
|
||
ENVIRONMENT=production $0 upload
|
||
EOF
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
check_dependencies
|
||
|
||
case "${1:-help}" in
|
||
"upload")
|
||
check_consul
|
||
check_tfvars_file
|
||
|
||
log_info "解析配置文件: $TFVARS_FILE"
|
||
local config_json=$(manual_parse_tfvars "$TFVARS_FILE" "/tmp/parsed_config.json")
|
||
upload_config_to_consul "/tmp/parsed_config.json"
|
||
|
||
# 清理临时文件
|
||
rm -f /tmp/parsed_config.json /tmp/temp_config.tf
|
||
;;
|
||
"list")
|
||
check_consul
|
||
list_consul_configs
|
||
;;
|
||
"cleanup")
|
||
check_consul
|
||
cleanup_consul_configs
|
||
;;
|
||
"help"|*)
|
||
show_help
|
||
;;
|
||
esac
|
||
}
|
||
|
||
main "$@" |