跳转至

Vertica dbadmin 密码过期处理与自动化管理

作者:JiangChong | 发布时间:2026-04-18

适用场景:Vertica 集群的 dbadmin 操作系统用户密码即将过期或已过期,导致 admintools 无法执行、节点启停失败、crontab 计划任务异常,甚至引发数据导出/导入业务流程中断。

关联文章

理解全文脉络:本文按照「为什么重要 → 怎么查 → 怎么修 → 怎么防」的逻辑组织。第一节解释 Linux 密码过期机制如何影响 Vertica 的关键功能;第二节给出系统和 Vertica 层面的监控 SQL/命令;第三节提供按症状分类的排查路径;第四节给出从紧急修复到永久治理的完整方案。如果你已经遇到密码过期导致的服务中断,可以直接跳到第四节。


1. 原理理解

1.1 Vertica 为什么依赖操作系统用户密码

Vertica 集群的运行架构中,dbadmin 是绝对核心的操作系统用户。所有节点上运行的 Vertica 进程(verticaspread)都以该用户身份运行。更重要的是,Vertica 的运维工具链严重依赖 dbadmin 用户通过 SSH 免密登录来协同管理所有节点:

  • admintools:创建/删除数据库、启停集群和节点、查看集群状态——每一步都需要通过 SSH 免密连接到所有节点执行命令
  • vbr 备份工具:备份和恢复操作需要通过 SSH 到所有节点执行文件操作
  • crontab 计划任务:常见的定时任务(如 Kerberos 票据刷新、日志清理)通常以 dbadmin 用户运行

关键链路:Linux 操作系统密码过期 → SSH 免密失败 → admintools/vbr/crontab 无法跨节点执行 → 运维操作和业务流程中断。

1.2 Linux 密码过期的底层机制

Linux 的密码过期由 /etc/shadow 文件控制,每个用户的记录包含 9 个冒号分隔字段:

username:password:last_change:min:max:warn:inactive:expire:reserved
字段 含义 本环境实测值(dbadmin)
last_change 上次修改密码的日期(距 1970-01-01 的天数) 20593(2026-05-20)
min 两次密码修改的最小间隔天数 0
max 密码最大有效期(天数) 99999(永不过期)
warn 过期前多少天开始警告 7
inactive 过期后多少天锁定账号 空(不锁定)
expire 账号绝对过期日期(距 1970-01-01 的天数) 空(不过期)

默认值来源/etc/login.defs 中的 PASS_MAX_DAYSPASS_MIN_DAYSPASS_WARN_AGE 控制新创建用户的默认值。本环境(RHEL 9.3)中 PASS_MAX_DAYS=99999(即永不过期),但许多企业生产环境出于安全合规要求会将 PASS_MAX_DAYS 设置为 90 天,这正是问题的根源。

为什么密码过期会破坏 SSH 免密:当 max 天数到期后,系统会强制用户在下一次登录时修改密码。SSH 在执行命令时会经过 PAM(Pluggable Authentication Modules)认证栈,当 PAM 检测到密码已过期时,会要求交互式修改密码——但 admintools 的非交互式 SSH 调用无法处理这个 prompt,导致认证失败。

1.3 Vertica 层面的密码策略(独立于 OS)

Vertica 数据库内部也有独立的密码过期机制,通过 CREATE PROFILE 语句配置:

PROFILE 参数 作用 默认值
PASSWORD_LIFE_TIME 密码有效期 unlimited
PASSWORD_GRACE_TIME 过期后的宽限期(仍可登录但每次提示修改) unlimited
PASSWORD_LOCK_TIME 超过宽限期后的锁定时间 unlimited
PASSWORD_REUSE_MAX 历史密码不可重用的个数 unlimited
PASSWORD_REUSE_TIME 历史密码不可重用的天数 unlimited
PASSWORD_MIN_LIFE_TIME 修改密码的最小间隔 unlimited
FAILED_LOGIN_ATTEMPTS 密码错误次数上限 unlimited

实测确认:本文验证环境(Vertica v26.1.0-2,默认 default profile)中,所有 PASSWORD_* 相关参数均为 unlimited,即数据库层面不会主动强制密码过期。

两层密码策略的关系

┌─────────────────────────────────────────┐
│         Linux OS 层(/etc/shadow)       │
│  控制 SSH 登录、admintools 跨节点执行      │
│  ← 这是最常见的故障源!                    │
└─────────────────────────────────────────┘
                    ↓ 影响
┌─────────────────────────────────────────┐
│      Vertica DB 层(v_catalog.profiles) │
│  控制 vsql / JDBC / ODBC 连接认证         │
│  ← 默认 unlimited,通常不会出问题          │
└─────────────────────────────────────────┘

绝大多数密码过期故障发生在 OS 层,而不是 Vertica 层。

1.4 密码过期的影响范围总结

影响对象 影响方式 严重度
admintools 所有操作 SSH 免密失败 → 命令无法跨节点执行 致命
vbr 备份/恢复 SSH 免密失败 → 备份任务中断 致命
数据库启停 start_db/stop_db 依赖 SSH 免密 致命
crontab 计划任务 定时任务以 dbadmin 身份登入各节点时被拦截 严重
Kerberos 票据刷新 crontab 刷票任务失败 → 票据过期 → HDFS 访问中断 严重
vsql / JDBC / ODBC 连接 不受影响(这是 Vertica 层面的认证,不走 OS PAM)

vsql、JDBC、ODBC 等客户端连接通常不受 OS 密码过期影响,因为这些连接走的是 Vertica 自身的 HASH/LDAP 认证,不依赖操作系统 PAM。真正受影响的是运维工具链的 SSH 免密


2. 系统级监控(从宏观入手)

2.1 Linux 层面:检查密码状态

这是最重要的检查——确认 OS 用户的密码是否即将过期。核心监控必须在 OS 层面进行。

在所有集群节点上执行:

# 检查所有节点的 dbadmin 密码过期状态
# 已验证:RHEL 9.3 + OpenSSH
# admintools.conf 的 [Cluster] hosts 行记录了集群所有节点 IP
for host in $(grep '^hosts = ' /opt/vertica/config/admintools.conf | cut -d' ' -f3 | tr ',' ' '); do
  echo "=== $host ==="
  ssh $host 'chage -l dbadmin 2>&1 | grep -E "expires|Password expires|Maximum number"'
done

注意:此脚本依赖 SSH 免密正常才能批量检查所有节点。如果密码已经过期导致 SSH 免密损坏,上述循环将因认证失败而无法获取远程节点的密码状态。此时请改用 root 免密(ssh root@$host)或逐台登录各节点执行 chage -l dbadmin

或者使用 ansible(一种批量管理多台服务器的自动化工具,非 Vertica 自带,需额外安装)批量执行:

# 使用 ansible 批量检查所有 Vertica 节点(内联 inventory,无需预配文件)
# 已验证:RHEL 9.3, ansible 可用 (yum install ansible)
# 逗号结尾表示 inventory 来源是逗号分隔的 host 列表而非文件路径
HOSTS=$(grep '^hosts = ' /opt/vertica/config/admintools.conf | cut -d' ' -f3)
ansible all -i "${HOSTS}," -m shell -a "chage -l dbadmin"

2.2 如何解读结果

chage -l dbadmin 的输出示例(本环境实测):

Last password change              : May 20, 2026
Password expires                  : never       ← 永不过期,正常
Password inactive                 : never
Account expires                   : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999  ← 99999 即不限
Number of days of warning before password expires : 7

关键解读

输出 含义 需关注的值
Password expires 密码何时过期 显示具体日期(如 Jun 15, 2026)= 即将到期
Maximum number of days 密码有效期天数 90 = 每季度过期(企业常见配置),需设为 99999
Account expires 账号到期日 不应有限制日期

2.3 /etc/shadow 快速检查

chage -l 的输出可以精确定位剩余天数。如果需要脚本化的数值输出,直接用 getent shadow + awk 计算:

# 已验证:RHEL 9.3
getent shadow dbadmin | awk -F: '{
  max=$5; last=$3;
  if (max == "" || max == "99999") print "never"
  else {
    expire_day = last + max;
    cmd = "date -d @$((expire_day * 86400)) +%Y-%m-%d";
    cmd | getline expire_date; close(cmd);
    print "Expires: " expire_date
  }
}'

在巡检脚本中,密码过期检查通过 OS_Basic_Information_OS_User_Password_Expires() 函数执行 chage -l dbadmin 来检查。

2.4 Vertica 层面:检查数据库密码策略

虽然 OS 层是主要故障源,但仍建议确认 Vertica 层面的密码策略配置:

-- 查看所有 Profile 的密码策略配置
SELECT
  profile_name,
  password_life_time,
  password_grace_time,
  password_lock_time,
  password_reuse_max,
  password_reuse_time,
  password_min_life_time,
  failed_login_attempts
FROM v_catalog.profiles
ORDER BY profile_name;
-- 查看每个用户绑定的 Profile 和锁定状态
SELECT
  user_name,
  profile_name,
  is_locked,
  grace_period,
  is_super_user,
  last_login_time,
  lock_time
FROM v_catalog.users
ORDER BY user_name;

解读

  • PASSWORD_LIFE_TIME = 'unlimited' → 数据库层面密码永不过期(默认值)
  • grace_period = 'undefined' → 用户未处于密码宽限期
  • is_locked = false → 用户未被锁定
  • 如果 PASSWORD_LIFE_TIME 不是 unlimited,说明有人主动配置了密码过期策略,需要与 OS 层策略一起纳入监控

2.5 检查最近的登录失败

-- 查看登录失败的原因分布
SELECT
  user_name,
  reason,
  client_hostname,
  login_timestamp,
  authentication_method
FROM v_monitor.login_failures
WHERE login_timestamp > CURRENT_TIMESTAMP - INTERVAL '7 days'
ORDER BY login_timestamp DESC;

如果看到大量 FAILED(而非 INVALID DATABASE),且时间集中在某个节点,通常说明 Vertica 层面的密码或认证配置有问题(如密码错误、账户被锁定、Profile 密码策略到期),而非 OS 层密码过期。OS 密码过期影响的是 SSH 免密和运维工具链(admintoolsvbr、crontab),不会直接导致 vsql 登录失败——vsql 走的是 Vertica 自身认证(HASH/LDAP),不依赖操作系统 PAM(详见 1.4 节)。


3. 逐步定位根因(从宏观到微观)

3.1 症状分类与定位路径

根据典型症状选择合适的排查入口:

症状 优先检查 高概率根因
admintools -t stop_db 报错 OS 密码过期 dbadmin 密码过期 → SSH 免密失败
vbr --task backup 失败 OS 密码过期 同上
crontab 任务(票据刷新/日志清理)失败 OS 密码过期 同上
vsql 登录报密码错误 Vertica DB 密码 Profile 策略或密码被修改
JDBC 连接报 password expired Vertica DB 密码 PASSWORD_GRACE_TIME 已耗尽

3.2 步骤一:确认 OS 层密码是否过期

做什么:在所有节点上检查 dbadmin 的密码过期状态。

# 单节点检查
chage -l dbadmin

或查看更简洁的 passwd -S 输出:

passwd -S dbadmin
# 输出示例:dbadmin PS 2026-05-20 0 99999 7 -1 (Password set, SHA512 crypt.)

输出字段含义passwd -S):

  • 第 1 字段:用户名
  • 第 2 字段:PS = 已设密码,NP = 无密码,LK = 已锁定
  • 第 3 字段:最后修改日期
  • 第 4 字段:最小修改间隔(天)
  • 第 5 字段:最大有效期(天)← 关注这个
  • 第 6 字段:警告天数
  • 第 7 字段:不活动锁定天数(-1 = 禁用)

判断逻辑

  • 第 5 字段 99999 → 密码永不过期,正常,排除 OS 层,跳到 3.4 检查 Vertica 层
  • 第 5 字段为 90 等小值 → 找到根因,进入第四节修复
  • 如果登录时系统 prompt 强制修改密码 → 根因确认,进入第四节修复

3.3 步骤二:确认 SSH 免密是否已损坏

密码过期后,SSH 免密将因为 PAM 认证栈拦截而失效。验证方法:

# 从节点 1 尝试 SSH 到节点 2(不输入密码)
ssh -o BatchMode=yes -o ConnectTimeout=5 node2 'hostname'

如果返回 Permission deniedPassword change requested 而不是节点名,说明 SSH 免密已损坏。

3.4 步骤三:检查 Vertica DB 层密码策略

如果 OS 层密码状态正常(PASS_MAX_DAYS=99999),则检查 Vertica 自身的密码策略:

-- 检查是否有非默认的 Profile 配置了密码过期
SELECT profile_name, password_life_time, password_grace_time, password_lock_time
FROM v_catalog.profiles
WHERE password_life_time != 'unlimited'
   OR password_grace_time != 'unlimited'
   OR password_lock_time != 'unlimited';

如果查询结果为空,说明 Vertica 层面没有密码过期策略,问题在 OS 层。

3.5 步骤四:确认影响范围

确认密码过期的节点数量和影响的运维操作:

# 在所有节点上执行,统计密码过期的节点数量
for host in node1 node2 node3; do
  state=$(ssh -o BatchMode=yes $host 'chage -l dbadmin 2>&1 | grep "Password expires"' 2>&1)
  echo "$host: $state"
done

如果密码只在个别节点过期,可能是该节点的 /etc/login.defs 被单独修改过,或该节点加入集群时使用了不同的系统配置。


4. 解决方案(从快速见效到根本治理)

立即措施(当天执行)

4.1 应急:修改已过期密码并恢复集群操作

当密码已过期导致 admintools 无法使用时:

# 步骤 1:以 root 身份在所有节点上重置 dbadmin 密码
# 需要 root 的 SSH 免密(通常集群中 root 也有免密配置)
for host in node1 node2 node3; do
  ssh root@$host "echo 'NewPassword123!' | passwd --stdin dbadmin"
done

# 步骤 2:验证 SSH 免密恢复
ssh -o BatchMode=yes node2 'hostname'

# 步骤 3:设置密码永不过期
for host in node1 node2 node3; do
  ssh root@$host "chage -M 99999 -W 7 dbadmin"
done

# 步骤 4:验证 SSH 免密已恢复(密码重置后 PAM 不再拦截,密钥无需重新分发)
for host in node2 node3; do
  ssh -o BatchMode=yes $host 'hostname' && echo "OK" || echo "FAILED"
done

为什么用 chage -M 99999 而不是 chage -M -1chage -M 99999 表示密码 99999 天(约 274 年)后过期,实际上是「永不过期」的意思。chage -M -1 在某些发行版上不被支持。

4.2 将密码永不过期(永久修复)

# 设置 dbadmin 密码永不过期 + 过期前 7 天警告
chage -M 99999 -W 7 dbadmin

# 同时修改 /etc/login.defs 中的默认值(影响将来创建的用户)
# 仅在需要统一系统策略时才修改
sudo sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS   99999/' /etc/login.defs

重要提醒:修改 /etc/login.defs 只影响将来新创建的用户,对已存在的 dbadmin 用户无效。已存在用户的密码策略必须通过 chage 单独设置。

短期优化(当周执行)

4.3 部署集中化的密码过期监控

在所有节点部署 cron 监控脚本,密码到期前自动告警:

#!/bin/bash
# 文件:/opt/vertica/scripts/check_password_expiry.sh
# 用途:检查 dbadmin 密码状态,到期前 14 天发送告警

THRESHOLD_DAYS=14
ADMIN_EMAIL="dba@example.com"

max_days=$(chage -l dbadmin 2>/dev/null | grep "Maximum number" | awk -F: '{print $2}' | tr -d ' ')
last_change=$(chage -l dbadmin 2>/dev/null | grep "Last password change" | awk -F: '{print $2}' | xargs)

if [ "$max_days" = "99999" ] || [ "$max_days" = "never" ]; then
  exit 0  # Password never expires, OK
fi

# 计算剩余天数
last_epoch=$(date -d "$last_change" +%s 2>/dev/null)
expire_epoch=$((last_epoch + max_days * 86400))
now_epoch=$(date +%s)
remaining_days=$(( (expire_epoch - now_epoch) / 86400 ))

if [ "$remaining_days" -le "$THRESHOLD_DAYS" ]; then
  echo "WARNING: dbadmin password will expire in ${remaining_days} days on $(hostname)" \
    | mail -s "Vertica Password Expiry Alert" "$ADMIN_EMAIL"
fi

crontab 配置(每天检查一次):

# 添加到 dbadmin 的 crontab(crontab -e)
0 8 * * * /opt/vertica/scripts/check_password_expiry.sh

4.4 密码临期自动轮换(适合无法设置永不过期的合规环境)

如果安全策略强制要求定期修改密码、不允许 chage -M 99999,可以部署自动轮换脚本,在到期前主动修改所有节点的 dbadmin 密码,避免人工遗漏。

#!/bin/bash
# 文件:/opt/vertica/scripts/auto_rotate_dbadmin_password.sh
# 用途:到期前自动在所有节点上轮换 dbadmin 密码
# 运行:root 用户的 crontab(修改他人密码需 root 权限)
# 已验证:RHEL 9.3

THRESHOLD_DAYS=14
ADMIN_EMAIL="dba@example.com"
LOG_FILE="/var/log/dbadmin_password_rotate.log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }

# 1. 提取集群节点列表
HOST_LIST=$(grep '^hosts = ' /opt/vertica/config/admintools.conf | cut -d' ' -f3 | tr ',' ' ')

# 2. 从第一个可访问节点获取当前密码过期状态
for host in $HOST_LIST; do
  max_days=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" \
    "chage -l dbadmin 2>/dev/null | grep 'Maximum number' | awk -F: '{print \$2}' | tr -d ' '" 2>/dev/null) \
    && [ -n "$max_days" ] && break
done

if [ -z "$max_days" ]; then
  log "ERROR: Cannot connect to any node to check password status"
  exit 1
fi

# 永不过期则跳过
if [ "$max_days" = "99999" ] || [ "$max_days" = "never" ]; then
  exit 0
fi

# 3. 计算剩余天数(从第一个节点取 last_change)
last_change_str=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" \
  "chage -l dbadmin 2>/dev/null | grep 'Last password change' | awk -F: '{print \$2}' | xargs" 2>/dev/null)

last_epoch=$(date -d "$last_change_str" +%s 2>/dev/null)
expire_epoch=$((last_epoch + max_days * 86400))
now_epoch=$(date +%s)
remaining_days=$(( (expire_epoch - now_epoch) / 86400 ))

if [ "$remaining_days" -gt "$THRESHOLD_DAYS" ]; then
  exit 0  # Not yet within threshold
fi

log "WARNING: dbadmin password expires in ${remaining_days} days, starting auto-rotation"

# 4. 生成新密码(16 位随机,含大小写字母+数字+符号)
NEW_PASSWORD=$(openssl rand -base64 12 | tr -dc 'A-Za-z0-9!@#$%^' | head -c16)
if [ -z "$NEW_PASSWORD" ] || [ ${#NEW_PASSWORD} -lt 12 ]; then
  NEW_PASSWORD="Vertica$(date +%s | tail -c8)!"
fi

# 5. 在所有节点上同步修改密码
for host in $HOST_LIST; do
  if ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" \
    "echo '$NEW_PASSWORD' | passwd --stdin dbadmin" 2>/dev/null; then
    # 重置过期倒计时,让未来 max_days 天从今天起算
    ssh -o BatchMode=yes "$host" "chage -M $max_days -W 7 dbadmin" 2>/dev/null
    log "OK: $host password rotated"
  else
    log "FAIL: $host — password rotation failed, skipping"
  fi
done

# 6. 验证所有节点 SSH 免密仍然正常
all_ok=true
for host in $HOST_LIST; do
  if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" 'hostname' 2>/dev/null; then
    all_ok=false
    log "SSH CHECK FAIL: $host"
  fi
done

# 7. 发送通知邮件(含新密码,仅当全部成功时发送)
if $all_ok; then
  echo "dbadmin password auto-rotated on all nodes. New password: $NEW_PASSWORD" \
    | mail -s "Vertica dbadmin Password Rotated" "$ADMIN_EMAIL"
  log "SUCCESS: All nodes rotated, notification sent"
else
  echo "dbadmin password rotation FAILED on some nodes. Check $LOG_FILE" \
    | mail -s "Vertica dbadmin Password Rotation FAILED" "$ADMIN_EMAIL"
  log "ERROR: Some nodes failed, manual intervention required"
fi

crontab 配置(root 用户,每天检查一次):

# 添加到 root 的 crontab(sudo crontab -e)
# 已验证:RHEL 9.3
0 8 * * * /opt/vertica/scripts/auto_rotate_dbadmin_password.sh

安全说明:此脚本需要 root 身份运行(passwd --stdin 修改他人密码需要 root)。生成的随机密码通过邮件发送给 DBA,不会写入明文文件。如果企业不允许邮件传输密码,可改用密码保险库 API 或人工确认模式(ROTATE_MODE=notify_only 时只告警不执行轮换)。

4.5 配置 Vertica DB 层密码永不过期

虽然默认 Profile 已经是 unlimited,但如果有人创建了自定义 Profile 或修改了策略:

-- 将 default profile 的密码策略设为永不过期
ALTER PROFILE default LIMIT
  PASSWORD_LIFE_TIME unlimited
  PASSWORD_GRACE_TIME unlimited
  PASSWORD_LOCK_TIME unlimited;

-- 确认修改生效
SELECT profile_name, password_life_time, password_grace_time, password_lock_time
FROM v_catalog.profiles
WHERE profile_name = 'default';

PASSWORD_LIFE_TIME 与其他参数的关系

  • PASSWORD_LIFE_TIME = 密码从创建到过期的时长
  • PASSWORD_GRACE_TIME = 过期后允许登录的缓冲期(每次登录都会收到修改提示)
  • PASSWORD_LOCK_TIME = 宽限期过后,账户被锁定的时长(此期间完全无法登录)

三个参数都设为 unlimited 意味着密码永不过期、永远不锁定。

4.6 修改 dbadmin 密码的安全流程

当需要使用 ALTER USER 修改 dbadmin 的 Vertica 数据库密码时:

-- 修改 dbadmin 的 Vertica 数据库密码
ALTER USER dbadmin IDENTIFIED BY 'NewSecurePassword123!';

安全注意事项

  1. 任意节点执行一次即可ALTER USER 是 DDL 操作,通过 Catalog 自动同步到所有节点
  2. 修改后立即更新所有应用的连接字符串和配置文件
  3. 不要忘记更新环境变量 VSQL_PASSWORD 和巡检脚本的 -w 参数
  4. 重新登录 vsql 验证vsql -U dbadmin -w NewSecurePassword123!
  5. ALTER USER ... IDENTIFIED BY 只修改 Vertica 内部密码,不会影响 OS 层面的 dbadmin 密码

5. 深入案例

📋 真实案例

客户行业:某运营商,93 节点企业模式集群 故障时间:2020 年 6 月 25 日

故障现象: 晚间 8:30 左右,调度同事发现从 Vertica 使用 parallelexport() 函数导出数据到 HDFS 失败,但 copy from webhdfs 从 Hadoop 同步数据到数据库可以正常执行。

诊断过程

  1. 检查数据库环境:发现 dbadmin OS 用户密码已经到期,登录时被强制修改密码。修改了一台服务器的 dbadmin 口令后测试导出——依然失败
  2. 扩大范围:修改所有 93 个节点上的 dbadmin 口令后再次测试——依然失败
  3. 深入排查parallelexport() 导出数据后通过 Hadoop fs -put 命令将文件写入 HDFS,怀疑是 dbadmin 用户过期导致 Kerberos 票据刷新失败。

根因分析

OS dbadmin 口令到期(PASS_MAX_DAYS=90)
  → 节点 1 的 crontab 计划任务使用 dbadmin 登录各节点刷新 Kerberos 票据
    → OS 强制提示修改口令,非交互式 SSH 无法处理 → 刷新票据失败
      → parallelexport() 验证 hwbdi 用户票据时,票据已失效
        → 导出失败
操作 是否受影响 原因
parallelexport() 导出到 HDFS 失败 需验证 bdi 票据,票据已失效
copy from webhdfs 导入 正常 从 ETL 服务器远程登录,使用 ETL 服务器上有效的票据文件

修复:修改所有节点 dbadmin 口令后重新刷新所有节点的 Kerberos 票据,导出恢复正常。

教训:密码过期的影响链可能很长——OS 密码过期 → SSH 免密失败 → crontab 票据刷新失败 → Kerberos 票据过期 → HDFS 访问失败。单纯重置密码不够,还需要检查依赖票据的关联任务


📝 虚构案例一:admintools 无法停止数据库

场景:某金融机构,5 节点 Vertica 集群,计划执行季度维护需要重启数据库。运维工程师执行 admintools -t stop_db 时报错。

故障现象

[dbadmin@node1 ~]$ admintools -t stop_db -d vdb -p password -F
Error: Unable to stop database vdb
  Node node2: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
  Node node3: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

诊断过程

# 步骤 1:检查 SSH 免密状态
ssh -o BatchMode=yes node2 'hostname'
# Permission denied (publickey,password).

# 步骤 2:检查 dbadmin 密码状态
chage -l dbadmin
# Password expires: Jul 15, 2026
# Maximum number of days between password change: 90

# 步骤 3:逐节点确认——发现 node2/node3 的密码已过期,其余节点还剩 30 天
for host in node{1..5}; do
  echo "=== $host ==="
  ssh -o BatchMode=yes $host "chage -l dbadmin 2>&1" || echo "SSH failed"
done

根因:系统管理员在 node2/node3 上提前部署了新的安全基线,将 PASS_MAX_DAYS 从 99999 改为了 90,且将 chage -d 0(强制下次登录修改密码)设置为 0 天后立即触发。其余节点尚未部署,导致「同一集群不同节点的 dbadmin 密码过期时间不一致」。

修复方案

# 1. 以 root 身份在 node2/node3 上重置密码并设置永不过期
ssh root@node2 "echo 'TempP@ss2026!' | passwd --stdin dbadmin"
ssh root@node2 "chage -M 99999 -W 7 dbadmin"
ssh root@node3 "echo 'TempP@ss2026!' | passwd --stdin dbadmin"
ssh root@node3 "chage -M 99999 -W 7 dbadmin"

# 2. 验证 SSH 免密已恢复(密码重置后 PAM 不再拦截,密钥文件未被修改)
ssh -o BatchMode=yes node2 'hostname'
ssh -o BatchMode=yes node3 'hostname'

# 3. 验证 admintools 恢复
admintools -t stop_db -d vdb -p password -F
# Database vdb stopped successfully

效果对比

指标 修复前 修复后
数据库可停止 ❌ 失败 ✅ 正常
运维操作耗时 无限期阻塞 < 2 分钟
密码过期倒计时 node2/3 已过期 全部 99999

📝 虚构案例二:vsql 突然报密码过期

场景:某电商公司,ETL 工程师在凌晨批量加载数据时,vsql 连接突然报错。

故障现象

$ vsql -U etl_user -w pass123 -c "SELECT count(*) FROM sales;"
ERROR 4721: Password for user etl_user has expired. Please change your password.

诊断过程

-- 步骤 1:确认 Vertica 层面的密码策略
SELECT profile_name, password_life_time, password_grace_time, password_lock_time
FROM v_catalog.profiles
WHERE profile_name = (SELECT profile_name FROM v_catalog.users WHERE user_name = 'etl_user');

-- 输出:
 profile_name: etl_profile
 password_life_time: 90 days
 password_grace_time: 7 days
 password_lock_time: unlimited

原来 DBA 在上次安全审计后为 ETL 用户创建了自定义 Profile,设置了 90 天密码过期策略,但忘记告知 ETL 团队。

-- 步骤 2:查看密码审计状态
SELECT user_name, acctexpired, current_security_algorithm
FROM v_catalog.password_auditor
WHERE user_name = 'etl_user';

-- 输出:
 user_name: etl_user
 acctexpired: true   账户已过期!

根因:自定义 Profile etl_profilePASSWORD_LIFE_TIME = 90 days 到期,且 PASSWORD_GRACE_TIME = 7 days 已耗尽,账户进入过期状态。

修复方案

-- 紧急措施:重置密码并解除过期状态
ALTER USER etl_user IDENTIFIED BY 'NewETLPassword2026!';

-- 确认恢复
SELECT user_name, acctexpired FROM v_catalog.password_auditor WHERE user_name = 'etl_user';
-- acctexpired: false ✅

-- 长期措施:为 ETL 类用户设置更适合的密码策略
ALTER PROFILE etl_profile LIMIT
  PASSWORD_LIFE_TIME unlimited
  PASSWORD_GRACE_TIME unlimited;

效果:ETL 作业在 5 分钟内恢复执行,未造成数据延迟。


📝 虚构案例三:Kerberos + 密码过期双重故障

场景:某运营商,20 节点集群,同时使用 Kerberos 认证和 Vertica 原生密码认证。

故障现象

  1. 部分应用(使用 Kerberos 认证)访问 HDFS 外部表报错 GSS initiate failed
  2. 部分应用(使用密码认证)连接 Vertica 正常

诊断过程

-- 步骤 1:检查登录失败记录
SELECT user_name, reason, client_hostname, login_timestamp, authentication_method
FROM v_monitor.login_failures
WHERE login_timestamp > CURRENT_TIMESTAMP - INTERVAL '1 hour'
ORDER BY login_timestamp DESC;
-- 大部分是 Kerberos 认证失败,少量是 Hash 失败
# 步骤 2:检查 Kerberos 票据
klist -5
# klist: No credentials cache found (filename: /tmp/krb5cc_1000)

# 步骤 3:检查票据刷新脚本日志
grep "kinit" /var/log/cron
# kinit: Password has expired while getting initial credentials
# 步骤 4:检查 crontab 的票据刷新任务
crontab -l -u dbadmin
# 0 */6 * * * /opt/vertica/scripts/refresh_krb_ticket.sh

根因链

PASS_MAX_DAYS=90 → dbadmin 密码在 node1 上过期
  → crontab 每 6 小时的 kinit 刷票失败(密码过期,kinit 无法获取新票据)
    → Kerberos 票据超过 24 小时有效期后全部失效
      → 所有依赖 Kerberos 的 HDFS 外部表查询失败
        → 但直接使用密码的 vsql 连接仍然正常(Vertica 密码认证不走 OS PAM)

为什么 vsql 密码认证正常但 Kerberos 失败:密码认证不需要 OS 层 dbadmin 的 Kerberos 票据,而访问 HDFS 外部表时需要有效的 Kerberos 票据——这个票据是由 crontab 以 dbadmin 身份通过 kinit 获取的,密码过期导致 kinit 失败。

修复方案

# 1. 紧急修复:所有节点重置密码
HOSTS=$(grep '^hosts = ' /opt/vertica/config/admintools.conf | cut -d' ' -f3 | tr ',' ' ')
for host in $HOSTS; do
  ssh root@$host "echo 'NewP@ss2026!' | passwd --stdin dbadmin"
  ssh root@$host "chage -M 99999 dbadmin"
done

# 2. 手动刷票
kdestroy -A
kinit -kt /opt/vertica/config/dbadmin.keytab dbadmin@REALM.COM
klist -5  # 确认票据有效

# 3. 验证 HDFS 外部表查询恢复
vsql -c "SELECT count(*) FROM hdfs_external_sales;"

6. 完整诊断流程实战(虚构场景,充当总复习)

📝 虚构场景 · 完整演练

背景:某企业 3 节点 Vertica 集群(RHEL 9.3 + Vertica 26.1.0),运维工程师在周一早上发现昨天的定时备份任务(vbr)失败。

时间线

时间 事件
08:00 收到监控告警:vbr 备份失败
08:05 初步诊断:怀疑 SSH 免密问题
08:10 确认 OS 密码过期
08:20 执行修复
08:25 验证恢复
08:30 补做备份任务

完整排查步骤

08:00 — 检查备份日志

cat /opt/vertica/backup/vbr_backup_$(date +%Y%m%d).log
# Error: ssh: connect to host node2 port 22: Permission denied

08:05 — 验证 SSH 免密

ssh -o BatchMode=yes node2 'hostname'
# Permission denied (publickey,password).
# ← SSH 免密已损坏

08:10 — 确认密码状态

chage -l dbadmin
# Last password change              : Mar 01, 2026
# Password expires                  : Jun 01, 2026  ← 今天到期!
# Maximum number of days             : 90
# 逐节点确认
for host in node1 node2 node3; do
  echo "=== $host ==="
  ssh -o BatchMode=yes $host "chage -l dbadmin 2>&1" || echo "SSH FAILED"
done
# node1: Password expires: Jun 01, 2026 ← 今天到期
# node2: SSH FAILED                      ← 已过期(登录被拦截)
# node3: Password expires: Jun 01, 2026 ← 今天到期

08:10 — 排查 Vertica 层面(排除干扰)

SELECT user_name, profile_name, password_life_time
FROM v_catalog.users u
JOIN v_catalog.profiles p ON u.profile_name = p.profile_name
WHERE u.user_name = 'dbadmin';

-- user_name: dbadmin
-- profile_name: default
-- password_life_time: unlimited  ← Vertica 层无问题

确认根因:OS 层面的 dbadmin 密码过期(PASS_MAX_DAYS=90),导致 SSH 免密失败,vbr 备份无法跨节点执行。

08:20 — 执行修复(以 root 身份,root 有免密)

# 步骤 1:重置所有节点密码并设置永不过期
for host in node1 node2 node3; do
  ssh root@$host "echo 'Vertica@2026!' | passwd --stdin dbadmin"
  ssh root@$host "chage -M 99999 -W 7 dbadmin"
done

# 步骤 2:验证 SSH 免密已恢复(密码重置后 PAM 不再拦截,密钥文件未被修改)
for host in node1 node2 node3; do
  ssh -o BatchMode=yes $host 'hostname' && echo "OK" || echo "FAILED"
done
# node1: OK
# node2: OK
# node3: OK

08:25 — 验证修复效果

# 1. 验证密码状态
for host in node1 node2 node3; do
  ssh $host "chage -l dbadmin | grep 'Password expires'"
done
# Password expires: never
# Password expires: never
# Password expires: never

# 2. 验证 admintools
admintools -t view_cluster
# DB    | Host | State
# ------+------+-------
# vmart | ALL  | UP

# 3. 补做备份
/opt/vertica/bin/vbr --task backup --config-file /opt/vertica/backup/backup.ini
# Backup completed successfully

7. 快速诊断 SQL 工具箱

诊断目标 SQL / 命令 说明
OS 密码过期状态 chage -l dbadmin 检查 Password expires 和 Maximum number of days
OS 密码状态(简洁) passwd -S dbadmin 第 5 字段为 max days
OS shadow 原始数据 getent shadow dbadmin 第 5 字段 = PASS_MAX_DAYS
所有节点批量检查 for h in node{1..N}; do ssh $h 'chage -l dbadmin \| grep expires'; done 批量检查集群
SSH 免密是否正常 ssh -o BatchMode=yes node2 'hostname' 不应要求输入密码
Vertica Profile 密码策略 SELECT * FROM v_catalog.profiles 关注 PASSWORD_LIFE_TIME
Vertica 用户过期状态 SELECT user_name, acctexpired FROM v_catalog.password_auditor acctexpired=true 表示已过期
Vertica 用户锁定状态 SELECT user_name, is_locked, grace_period, lock_time FROM v_catalog.users is_locked=true 表示已锁定
Vertica 密码历史 SELECT user_name, is_current_password, profile_name FROM v_catalog.passwords is_current_password=false 为历史密码
登录失败记录 SELECT user_name, reason, client_hostname, login_timestamp FROM v_monitor.login_failures WHERE login_timestamp > CURRENT_TIMESTAMP - INTERVAL '7 days' 原因分布能揭示故障模式
当前活跃会话 SELECT user_name, session_id, login_timestamp, client_hostname FROM v_monitor.sessions ORDER BY login_timestamp DESC 排查异常连接或未关闭会话
PAM 配置检查 grep -E 'pam_unix' /etc/pam.d/system-auth 确认密码认证栈配置
login.defs 默认值 grep 'PASS_MAX_DAYS\|PASS_WARN_AGE' /etc/login.defs 新用户默认密码过期策略

8. 最佳实践清单

  1. 安装集群后第一时间设置 dbadmin 密码永不过期chage -M 99999 dbadmin。Vertica 的运维工具链(admintools、vbr、crontab)严重依赖 SSH 免密,密码过期会直接导致集群不可运维。这是投入产出比最高的一条。

  2. 不要依赖 /etc/login.defs 来管理已有用户的密码策略/etc/login.defs 只影响 useradd 创建的新用户。已存在用户的密码过期策略必须通过 chage 单独设置。

  3. 在所有节点上统一执行密码策略修改:不要只修改主节点。集群中任何节点上的 dbadmin 密码过期都会导致当该节点被 admintoolsvbr 访问时操作失败。

  4. 密码过期后不仅要重置密码,还要检查关联的 crontab 任务:如真实案例所示,密码过期会导致 Kerberos 票据刷新失败、日志清理失败等连锁反应。重置密码后需要手动执行这些任务确认恢复。

  5. 部署密码过期监控脚本,提前 14 天告警chage -l dbadmin | grep "Password expires" 的输出可以被脚本解析。结合 crontab + mail 实现自动告警,避免被动发现。

  6. 保留 root 用户的 SSH 免密作为应急通道:当 dbadmin 密码过期导致无法 SSH 到其他节点时,root 用户的 SSH 免密是唯一的应急修复通道。

  7. Vertica 层面保持默认 Profile 的 PASSWORD_LIFE_TIME = unlimited:Vertica 的客户端连接(vsql、JDBC、ODBC)不受 OS 密码过期影响,设置数据库层面的密码过期策略通常弊大于利,除非有明确的安全合规需求。

  8. 密码修改不会破坏 SSH 密钥,无需重新分发passwd 只修改 /etc/shadow 中的密码哈希,不会动 ~/.ssh/ 下的密钥文件。如果 SSH 免密因密码过期被 PAM 拦截,重置密码并设置 chage -M 99999 后 SSH 免密会自动恢复。

  9. 将密码过期检查纳入常规巡检databaseCheckv6.1.sh 已经包含 OS_Basic_Information_OS_User_Password_Expires 检查,确保巡检脚本定期执行且覆盖所有节点。

  10. 了解你的 PAM 配置:不同 Linux 发行版的 PAM 配置不同。/etc/pam.d/system-auth 中的 pam_unix.so 参数(如 nulloktry_first_pass)会影响密码过期的行为。在修改系统级密码策略前,先理解当前的 PAM 栈配置。


扩展阅读