编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • ScriptHub 脚本工具箱
  • Python

  • Shell-Bash

    • Shell & Bash 从0到1实战专栏
    • Shell 入门与变量
    • 流程控制与函数
    • 数据与 IO 处理
    • grep 搜索实战
    • sed 与 awk 编程
    • 文件查找与统计
    • 日志监控与告警
    • 备份进程与磁盘
    • 用户与服务管理
      • 9.1 用户管理
        • 9.1.1 useradd / usermod / userdel——用户生命周期
        • 9.1.2 passwd / chage——密码与账户策略
        • 9.1.3 批量创建用户——从 CSV 导入
        • 9.1.4 用户组管理——groupadd / gpasswd
        • 9.1.5 sudo 配置——最小权限原则
        • 9.1.6 SSH 密钥管理——免密登录与批量分发
        • 9.1.7 登录审计与安全——谁在什么时候做了什么
        • 9.1.8 锁定用户与强制踢出
      • 9.2 服务管理
        • 9.2.1 systemctl——现代 Linux 服务管理核心
        • 9.2.2 自定义 systemd Unit 文件
        • 9.2.3 service / chkconfig——兼容传统 SysV Init
        • 9.2.4 服务健康检查——不仅是端口存活
        • 9.2.5 优雅重启——零停机更新策略
        • 9.2.6 灰度发布脚本——分批滚动更新
      • 9.3 新手陷阱与思考题
        • 陷阱 Top 5
        • 综合思考题
    • 网络调度与部署
    • 调试与脚本规范
    • 安全与兼容处理
    • 性能与打包分发
  • 工具脚本

  • ScriptHub
  • Shell-Bash
杨充
2023-01-11
目录

用户与服务管理

# 第 9 章 用户与服务管理

# 目录介绍

  • 9.1 用户管理
    • 9.1.1 useradd / usermod / userdel——用户生命周期
    • 9.1.2 passwd / chage——密码与账户策略
    • 9.1.3 批量创建用户——从 CSV 导入
    • 9.1.4 用户组管理——groupadd / gpasswd
    • 9.1.5 sudo 配置——最小权限原则
    • 9.1.6 SSH 密钥管理——免密登录与批量分发
    • 9.1.7 登录审计与安全——谁在什么时候做了什么
    • 9.1.8 锁定用户与强制踢出
  • 9.2 服务管理
    • 9.2.1 systemctl——现代 Linux 服务管理核心
    • 9.2.2 自定义 systemd Unit 文件
    • 9.2.3 service / chkconfig——兼容传统 SysV Init
    • 9.2.4 服务健康检查——不仅是端口存活
    • 9.2.5 优雅重启——零停机更新策略
    • 9.2.6 灰度发布脚本——分批滚动更新
  • 9.3 新手陷阱与思考题

# 9.1 用户管理

# 9.1.1 useradd / usermod / userdel——用户生命周期

#!/bin/bash

# ===== useradd —— 创建用户 =====
# 基本创建(使用系统默认值)
useradd alice

# 完整创建(所有参数显式指定)
useradd -m \                      # 创建家目录 (/home/bob)
    -s /bin/bash \               # 登录 Shell
    -c "Bob Smith" \              # 注释(全名)
    -u 1500 \                     # 指定 UID
    -g developers \               # 主组
    -G docker,wheel \             # 附加组
    -d /home/bob \                # 指定家目录路径
    -e 2025-12-31 \               # 账户过期日期
    bob

# 创建系统用户(不创建家目录,UID < 1000)
useradd -r -s /usr/sbin/nologin appuser

# ===== 查看默认值 =====
useradd -D                                     # 显示创建用户的默认值
# 输出:GROUP=100  HOME=/home  INACTIVE=-1  EXPIRE=  SHELL=/bin/bash
# 默认配置文件:/etc/default/useradd  和  /etc/login.defs

# ===== usermod —— 修改用户 =====
usermod -l newname oldname                     # 修改用户名
usermod -u 2000 alice                          # 修改 UID
usermod -g staff alice                         # 修改主组
usermod -aG docker alice                       # 追加附加组(-a = append)
usermod -aG sudo,wheel alice                   # 追加多个组
usermod -s /bin/zsh alice                      # 修改 Shell
usermod -L alice                               # 锁定用户
usermod -U alice                               # 解锁用户
usermod -e 2026-12-31 alice                    # 设置账户过期日

# ⚠️ usermod -G 不加 -a 会覆盖所有附加组
usermod -G docker alice                        # ❌ 覆盖——alice 只剩 docker 组
usermod -aG docker alice                       # ✅ 追加——保留原有组 + docker

# ===== userdel —— 删除用户 =====
userdel alice                                  # 只删除用户,保留家目录
userdel -r alice                               # 删除用户 + 家目录 + 邮件池
userdel -f alice                               # 强制删除(即使用户已登录)

# ===== 直接编辑 /etc/passwd 字段含义 =====
# root:x:0:0:root:/root:/bin/bash
# ①   ② ③ ④  ⑤    ⑥       ⑦
# ① 用户名  ② 密码占位符(x=存在shadow中)  ③ UID  ④ GID
# ⑤ 注释    ⑥ 家目录                      ⑦ 登录 Shell

# ===== 查看用户信息 =====
id alice                                       # UID/GID/所属组
finger alice                                   # 用户详细信息
groups alice                                   # 用户所属的组
getent passwd alice                            # 查询 passwd 数据库条目
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 9.1.2 passwd / chage——密码与账户策略

#!/bin/bash

# ===== passwd —— 修改密码 =====
passwd alice                              # root 修改他人密码(不需旧密码)
passwd                                    # 普通用户修改自己密码(需旧密码)
passwd -l alice                           # 锁定密码(等价于 usermod -L)
passwd -u alice                           # 解锁密码
passwd -S alice                           # 查看密码状态
# 输出:alice P 2025-06-01 0 90 7 -1
#      状态:P=密码有效  L=锁定  NP=无密码
#      上次修改  最小天数  最大天数  警告天数  过期宽限天数

passwd -e alice                           # 强制用户下次登录修改密码
passwd -n 7 alice                         # 设置最短密码修改间隔(天)
passwd -x 90 alice                        # 设置密码最长有效期(天)
passwd -w 7 alice                         # 密码过期前 7 天开始警告
passwd -i 14 alice                        # 密码过期后 14 天宽限期(过期还能登录)

# ===== chage —— 账户老化策略 =====
# 查看策略
chage -l alice
# 输出:
#   最近一次密码修改:2025-06-01
#   密码过期时间:2025-08-30
#   密码失效时间:从不
#   账户过期时间:从不
#   两次修改密码最小间隔:7
#   两次修改密码最大间隔:90
#   密码过期前警告:7

# 设置策略
chage -M 90 -m 7 -W 7 alice              # 同上 passwd 参数
chage -E 2025-12-31 alice                # 账户过期日期
chage -I 14 alice                        # 密码过期宽限期
chage -d 0 alice                         # 强制下次登录改密码

# ===== 密码复杂度策略(/etc/security/pwquality.conf)=====
# 或在 /etc/pam.d/common-password(Debian)/ /etc/pam.d/system-auth(RHEL)
# password requisite pam_pwquality.so retry=3 \
#     minlen=12 difok=3 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1
# minlen=12    : 最少 12 个字符
# difok=3      : 新密码至少 3 个字符不同于旧密码
# dcredit=-1   : 至少 1 个数字
# ucredit=-1   : 至少 1 个大写字母
# lcredit=-1   : 至少 1 个小写字母
# ocredit=-1   : 至少 1 个特殊字符

# ===== 检查弱密码用户 =====
check_weak_passwords() {
    echo "检查空密码用户:"
    awk -F: '($2 == "" || $2 == "!") && $1 != "root" {print $1}' /etc/shadow

    echo ""
    echo "检查 UID=0 的非 root 用户:"
    awk -F: '($3 == 0 && $1 != "root") {print $1}' /etc/passwd

    echo ""
    echo "检查可登录的用户(非 nologin 和 false):"
    awk -F: '$7 !~ /(nologin|false)$/ {printf "%-15s %s\n", $1, $7}' /etc/passwd
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# 9.1.3 批量创建用户——从 CSV 导入

#!/bin/bash

# ===== 方式 1:从 CSV 文件批量创建 =====
# 格式:用户名,密码,全名,组1:组2
# alice,Pass1234,Alice Wang,developers:docker
# bob,Pass5678,Bob Li,developers
# carol,Pass9012,Carol Zhang,testers

cat > users.csv << 'CSV'
alice,Pass1234,Alice Wang,developers:docker
bob,Pass5678,Bob Li,developers
carol,Pass9012,Carol Zhang,testers
CSV

batch_create_users() {
    local csv_file="$1"
    local default_shell="${2:-/bin/bash}"

    while IFS=',' read -r username password fullname groups_str; do
        # 跳过空行和注释行
        [[ -z "$username" || "$username" == \#* ]] && continue

        echo "创建用户: $username"

        # 确保附加组存在
        if [[ -n "$groups_str" ]]; then
            IFS=':' read -ra groups <<< "$groups_str"
            for grp in "${groups[@]}"; do
                getent group "$grp" > /dev/null || groupadd "$grp"
            done
        fi

        # 创建用户
        if id "$username" &>/dev/null; then
            echo "  用户 $username 已存在,跳过"
        else
            useradd -m -s "$default_shell" -c "$fullname" "$username"
        fi

        # 添加附加组
        if [[ -n "$groups_str" ]]; then
            usermod -aG "$(echo "$groups_str" | tr ':' ',')" "$username"
        fi

        # 设置密码
        echo "${username}:${password}" | chpasswd

        # 强制首次登录改密码
        chage -d 0 "$username"

        echo "  ✅ $username 创建完成,组: $groups_str"
    done < "$csv_file"
}

# batch_create_users users.csv

# ===== 方式 2:用 newusers 批量导入 =====
# newusers 读取 /etc/passwd 格式的文本文件
cat > users_passwd_fmt.txt << 'TXT'
alice:x:1501:1501:Alice Wang:/home/alice:/bin/bash
bob:x:1502:1502:Bob Li:/home/bob:/bin/bash
TXT
newusers users_passwd_fmt.txt

# ===== 方式 3:循环+变量批量创建 =====
for i in $(seq 1 10); do
    username="dev${i}"
    useradd -m -s /bin/bash -g developers "$username"
    echo "${username}:Password${i}" | chpasswd
done

# ===== 批量删除测试用户 =====
batch_delete_users() {
    local prefix="$1"                    # 用户名前缀(如 dev)
    for user in $(awk -F: -v pre="$prefix" '$1 ~ "^"pre {print $1}' /etc/passwd); do
        echo "删除用户: $user"
        userdel -r "$user"
    done
}
# batch_delete_users "dev"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# 9.1.4 用户组管理——groupadd / gpasswd

#!/bin/bash

# ===== 创建组 =====
groupadd developers
groupadd -g 2000 designers               # 指定 GID

# ===== 修改组 =====
groupmod -n newname oldname              # 重命名组
groupmod -g 3000 developers              # 修改 GID

# ===== 删除组 =====
groupdel developers                       # 删除组(不能是任何用户的主组)

# ===== 组成员管理 =====
# 添加用户到组
gpasswd -a alice developers              # 添加
gpasswd -d alice developers              # 移除
gpasswd -M alice,bob developers          # 设置组员(覆盖)
gpasswd -A alice developers              # 设置组管理员

# ===== 查看组成员 =====
members developers                        # 列出组内所有用户(需安装 members 包)
getent group developers                   # 查看组条目
grep "^developers:" /etc/group            # 直接查看 /etc/group

# ===== /etc/group 格式 =====
# developers:x:2000:alice,bob,carol
# ①          ② ③   ④
# ① 组名  ② 组密码(通常为空)  ③ GID  ④ 成员列表(逗号分隔)

# ===== 实战:项目目录权限设置 =====
# 场景:多个开发者共享 /opt/project 目录
setup_project_dir() {
    local dir="/opt/project"
    local group="developers"

    groupadd -f "$group"                   # -f = 已存在不报错
    mkdir -p "$dir"
    chgrp -R "$group" "$dir"              # 改变目录组
    chmod 2775 "$dir"                     # 2775 = setgid + rwxrwxr-x
    # setgid 位(2) = 在目录下创建的文件自动继承目录的组
    echo "项目目录 $dir 已设置,所有 developers 组成员可读写"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 9.1.5 sudo 配置——最小权限原则

#!/bin/bash

# ===== sudo 相关文件 =====
# /etc/sudoers        主配置文件(用 visudo 编辑)
# /etc/sudoers.d/     分片配置目录(推荐)

# ===== visudo —— 安全编辑 sudoers =====
# 必须用 visudo 编辑——它会语法检查,防止锁死 sudo
visudo                                       # 编辑 /etc/sudoers
visudo -f /etc/sudoers.d/myapp               # 编辑分片文件

# ===== sudoers 语法速查 =====
# 格式:用户  主机=(运行身份)  命令
cat > /etc/sudoers.d/myapp << 'SUDOERS'
# 1. 单用户全权限(危险!)
# alice ALL=(ALL:ALL) ALL

# 2. 单用户无密码执行特定命令(推荐)
alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload nginx
alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl status nginx

# 3. 组权限
%developers ALL=(ALL) /usr/bin/systemctl restart myapp
%developers ALL=(ALL) /bin/journalctl -u myapp

# 4. 禁止特定命令
alice ALL=(ALL) ALL, !/usr/bin/su, !/usr/bin/passwd root

# 5. 免密码执行所有命令(运维用户)
deploy ALL=(ALL) NOPASSWD: ALL

# 6. 只能以特定用户身份运行
webapp ALL=(www-data) NOPASSWD: /usr/bin/systemctl restart php-fpm
SUDOERS

# ===== 查看 sudo 权限 =====
sudo -l                                       # 当前用户能执行哪些 sudo 命令
sudo -l -U alice                              # root 查看 alice 的 sudo 权限

# ===== sudo 日志 =====
# sudo 操作记录在 syslog 或专门的 sudo 日志中
grep sudo /var/log/auth.log                   # Debian/Ubuntu
grep sudo /var/log/secure                     # RHEL/CentOS
journalctl -u sudo                            # systemd 系统

# ===== 实战:创建受限运维用户 =====
create_op_user() {
    local username="op"
    useradd -m -s /bin/bash "$username"
    mkdir -p /home/$username/.ssh
    chmod 700 /home/$username/.ssh

    # 只允许重启和查看日志
    cat > "/etc/sudoers.d/$username" << SUDOERS
$username ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, \
    /usr/bin/systemctl status nginx, \
    /usr/bin/systemctl restart php-fpm, \
    /bin/journalctl -u nginx, \
    /bin/journalctl -u php-fpm, \
    /usr/bin/df -h, /usr/bin/free -h, /usr/bin/uptime
SUDOERS
    echo "运维用户 $username 创建完成"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

# 9.1.6 SSH 密钥管理——免密登录与批量分发

#!/bin/bash

# ===== 生成 SSH 密钥对 =====
ssh-keygen -t ed25519 -C "alice@server01"       # Ed25519(推荐,现代算法)
ssh-keygen -t rsa -b 4096 -C "bob@server01"     # RSA 4096(兼容性好)
# -t = 算法  -b = 密钥长度  -C = 注释  -f = 指定文件名

# 密钥存储位置
# 私钥(绝不可外泄): ~/.ssh/id_ed25519
# 公钥(可以分发):   ~/.ssh/id_ed25519.pub

# ===== 免密登录(手动版)=====
# 方式 1:ssh-copy-id(推荐)
ssh-copy-id user@remote-server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@remote
ssh-copy-id -p 2222 user@remote              # 指定端口

# 方式 2:手动复制(前一种失败时用)
cat ~/.ssh/id_ed25519.pub | ssh user@remote \
    "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

# ===== 批量分发公钥脚本 =====
cat > batch_ssh_copy.sh << 'SCRIPT'
#!/bin/bash
PUB_KEY="$HOME/.ssh/id_ed25519.pub"
SERVERS=("server01" "server02" "server03" "server04")
USER="deploy"

if [[ ! -f "$PUB_KEY" ]]; then
    echo "生成密钥..."
    ssh-keygen -t ed25519 -N "" -f "${PUB_KEY%.pub}"
fi

for server in "${SERVERS[@]}"; do
    echo "分发到 $server ..."
    ssh-copy-id -i "$PUB_KEY" "${USER}@${server}" || {
        echo "ssh-copy-id 失败,尝试手动复制..."
        cat "$PUB_KEY" | ssh "${USER}@${server}" \
            "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && sort -u ~/.ssh/authorized_keys -o ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
    }
done
echo "分发完成"
SCRIPT

# ===== SSH 安全加固 =====
# 编辑 /etc/ssh/sshd_config
cat > /etc/ssh/sshd_config.d/security.conf << 'SSHD'
# 禁止 root 直接登录
PermitRootLogin no

# 禁止密码登录(只用密钥)
PasswordAuthentication no

# 禁止空密码
PermitEmptyPasswords no

# 限制登录尝试
MaxAuthTries 3

# 限制登录用户
AllowUsers alice bob deploy
# AllowGroups developers

# 修改默认端口(可选,减少扫描)
# Port 2222

# 空闲超时
ClientAliveInterval 300
ClientAliveCountMax 2
SSHD

systemctl reload sshd

# ===== 撤销某人的 SSH 公钥 =====
revoke_ssh_key() {
    local username="$1"
    sed -i "/${username}@/d" /home/*/ssh/authorized_keys 2>/dev/null
    # 或精确撤销:从 known_hosts 和 authorized_keys 同时清理
    echo "已撤销 ${username} 的公钥"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# 9.1.7 登录审计与安全——谁在什么时候做了什么

#!/bin/bash

# ===== 查看谁在线 =====
who                                          # 当前登录的用户
w                                            # 更详细(在做什么)
last                                         # 历史登录记录
last -n 20                                   # 最近 20 次
last alice                                   # alice 的登录历史
lastb                                        # 失败登录尝试(需要读取 /var/log/btmp)
lastb -n 20                                  # 最近 20 次失败

# ===== 登录失败排查 =====
# 查看暴力破解
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10
# 查看被攻击 IP
grep "Failed password" /var/log/auth.log | grep -oP '\d+\.\d+\.\d+\.\d+' | sort | uniq -c | sort -rn | head -10

# ===== 命令审计(记录所有用户执行的命令)=====
# 方式 1:在 /etc/profile 最后添加
# export PROMPT_COMMAND='history -a; logger -p local1.notice "[AUDIT] $(whoami) [$(who am i | awk "{print \$1}")]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//")"'
# 用户命令会发送到 syslog (local1.notice)

# 方式 2:使用 auditd 监控关键文件
# auditctl -w /etc/passwd -p wa -k passwd_changes      # 监控 passwd 写操作
# auditctl -w /etc/shadow -p wa -k shadow_changes       # 监控 shadow 写操作
# auditctl -w /etc/sudoers -p wa -k sudoers_changes     # 监控 sudoers 变更

# ===== 登录安全脚本:检测异常登录 =====
cat > login_monitor.sh << 'SCRIPT'
#!/bin/bash
# 放入 /etc/profile.d/ 或 SSH PAM 中执行

THRESHOLD=5                      # 5 分钟内失败 5 次 = 异常

# 检查最近失败登录(仅 root 权限可读 /var/log/btmp)
if [[ -f /var/log/btmp ]]; then
    recent_fails=$(lastb -s -5min 2>/dev/null | wc -l)

    if (( recent_fails > THRESHOLD )); then
        echo "[$(date)] 异常:最近 5 分钟有 ${recent_fails} 次失败登录"
        # 提取攻击来源 IP
        lastb -s -5min 2>/dev/null | awk 'NF>0 {print $3}' | sort | uniq -c | sort -rn \
            | while read count ip; do
                if [[ "$count" -ge 3 ]]; then
                    echo "  攻击者 IP: $ip ($count 次)"
                    # 可选:加入 iptables 黑名单
                    # iptables -A INPUT -s $ip -j DROP
                fi
            done
    fi
fi

# 检查 root 直接登录(如果禁用了 root 登录则告警)
root_logins=$(last root | grep -v "never logged in" | wc -l)
if (( root_logins > 0 )); then
    echo "[$(date)] 警告:root 用户最近有登录记录"
fi
SCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# 9.1.8 锁定用户与强制踢出

#!/bin/bash

# ===== 锁定账户 =====
usermod -L alice                            # 锁定——在 /etc/shadow 密码前加 !
passwd -l alice                             # 同上
# 解锁:
usermod -U alice
passwd -u alice

# ===== 设置账户过期 =====
usermod -e 1970-01-01 alice                 # 设置过期日到过去 = 立即失效

# ===== 修改 Shell 禁止登录 =====
usermod -s /usr/sbin/nologin alice          # 禁止交互登录
usermod -s /bin/false alice                 # 更彻底——连非交互都禁止

# ===== 强制踢出用户 =====
# 查看登录会话
who -u                                       # 显示 PID
# alice  pts/0  2025-06-10 14:30   .   12345

# 给用户发消息
write alice pts/0 <<< "系统维护,5 分钟后将断开连接"

# 踢出指定用户
pkill -KILL -u alice                         # 杀死 alice 所有进程
kill -9 12345                                # 杀死指定 PID 的会话

# 踢出所有除 root 外的用户
kick_all_users() {
    local exclude_user="${1:-root}"
    who | awk -v ex="$exclude_user" '$1 != ex {print $1, $2, $5}' \
        | while read user tty pid; do
            echo "踢出用户 $user (TTY:$tty PID:$pid)"
            kill -9 "$pid" 2>/dev/null || true
        done
}

# ===== 限制用户资源(/etc/security/limits.conf)=====
cat > /etc/security/limits.d/restrict.conf << 'LIMITS'
# 限制 developer 组的资源
@developers    hard    nproc           100       # 最大进程数
@developers    hard    nofile          4096      # 最大打开文件数
@developers    hard    maxlogins       3         # 最大同时登录数
@developers    hard    cpu             60        # CPU 时间限制(分钟)
LIMITS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 9.2 服务管理

# 9.2.1 systemctl——现代 Linux 服务管理核心

#!/bin/bash

# ===== 服务状态操作 =====
systemctl status nginx                       # 查看状态(最常用)
systemctl start nginx                        # 启动
systemctl stop nginx                         # 停止
systemctl restart nginx                      # 重启
systemctl reload nginx                       # 重载配置(不中断服务)
systemctl enable nginx                       # 开机自启
systemctl disable nginx                      # 取消自启
systemctl is-active nginx                    # 检查是否运行
systemctl is-enabled nginx                   # 检查是否开机自启
systemctl is-failed nginx                    # 检查是否启动失败

# ===== 查看列表 =====
systemctl list-units --type=service          # 所有已加载的服务
systemctl list-units --type=service --state=running    # 正在运行的服务
systemctl list-units --type=service --state=failed     # 启动失败的服务
systemctl list-unit-files --type=service     # 所有已安装的服务(含未加载的)

# ===== 日志查看 =====
journalctl -u nginx                          # 查看服务日志
journalctl -u nginx -f                       # 实时跟踪日志(类似 tail -f)
journalctl -u nginx --since "10 minutes ago" # 最近 10 分钟
journalctl -u nginx --since "2025-06-10"     # 指定日期
journalctl -u nginx -n 50                    # 最近 50 行
journalctl -u nginx -p err                   # 只看错误级别日志

# ===== 服务依赖 =====
systemctl list-dependencies nginx            # 查看服务依赖树
systemctl list-dependencies nginx --reverse  # 查看哪些服务依赖 nginx

# ===== 屏蔽服务(比 disable 更强)=====
systemctl mask nginx                         # 屏蔽——禁止手动启动和开机自启
systemctl unmask nginx                       # 解除屏蔽

# ===== 失败服务自动重启(在 Unit 文件中配置)=====
# Restart=on-failure      异常退出自动重启
# RestartSec=10s          重启间隔
# StartLimitBurst=5       允许的重启次数
# StartLimitInterval=60s  计数周期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 9.2.2 自定义 systemd Unit 文件

#!/bin/bash

# ===== 基础服务模板 =====
# 位置:/etc/systemd/system/myapp.service
cat > /etc/systemd/system/myapp.service << 'UNIT'
[Unit]
Description=My Application Service
Documentation=https://example.com/docs
After=network.target
Wants=redis.service

[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python /opt/myapp/main.py
ExecStartPre=/opt/myapp/check_config.sh        # 启动前检查
ExecStartPost=/opt/myapp/notify_started.sh     # 启动后通知
ExecStop=/bin/kill -TERM $MAINPID
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10
StartLimitBurst=5
StartLimitInterval=60s

# 资源限制
LimitNOFILE=65535
LimitNPROC=4096
MemoryMax=1G
MemoryHigh=800M                                  # 软限制——超了会 throttle
CPUQuota=200%

# 环境变量
Environment="APP_ENV=production"
Environment="PORT=8080"
EnvironmentFile=/etc/myapp/env

# 日志
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

# 安全加固
ProtectSystem=strict                             # 只读系统目录
ProtectHome=true                                 # 禁止访问 /home
NoNewPrivileges=true                             # 禁止提权
PrivateTmp=true                                  # 独立 /tmp

[Install]
WantedBy=multi-user.target
UNIT

# 重载并启动
systemctl daemon-reload
systemctl enable myapp
systemctl start myapp

# ===== systemd Unit 类型对比 =====
# Type=simple  :默认,ExecStart 启动的主进程即主服务
# Type=forking :老旧守护进程模式(启动后 fork 到后台),需配合 PIDFile
# Type=oneshot :一次性任务(类似 init.d 脚本),完成后退出
#              配合 RemainAfterExit=yes 表示"执行完也算 running"
# Type=notify  :服务启动后发 sd_notify() 通知 systemd
# Type=idle    :等其他 job 完成再启动

# ===== 定时器 Unit(替代 cron)=====
# 位置:/etc/systemd/system/cleanup.timer
# 位置:/etc/systemd/system/cleanup.service
cat > /etc/systemd/system/cleanup.service << 'TSVC'
[Unit]
Description=Daily cleanup job

[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup.sh
TSVC

cat > /etc/systemd/system/cleanup.timer << 'TIMER'
[Unit]
Description=Run cleanup daily at 3AM

[Timer]
OnCalendar=daily
OnCalendar=*-*-* 03:00:00
Persistent=true                    # 错过了补执行
RandomizedDelaySec=600             # 随机延迟 10 分钟(避免所有机器同时执行)

[Install]
WantedBy=timers.target
TIMER

systemctl enable cleanup.timer
systemctl start cleanup.timer
systemctl list-timers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

# 9.2.3 service / chkconfig——兼容传统 SysV Init

#!/bin/bash

# ===== service 命令(SysV Init 时代)=====
# systemd 系统已重定向到 systemctl,但 service 仍可使用
service nginx start
service nginx stop
service nginx restart
service nginx status
service nginx reload
service --status-all                            # 列出所有服务状态

# ===== chkconfig(管理开机自启)=====
chkconfig --list                                # 列出所有服务自启状态
chkconfig nginx on                              # 开机自启
chkconfig nginx off                             # 禁止自启
chkconfig --add myapp                           # 添加服务(从 /etc/init.d/)

# ===== Systemd 兼容命令映射 =====
# service nginx start     → systemctl start nginx
# chkconfig nginx on      → systemctl enable nginx
# service nginx status    → systemctl status nginx
# service --status-all    → systemctl list-units --type=service

# ===== 旧式 init.d 脚本模板 =====
# 仅在无 systemd 的系统上使用(如旧版 CentOS 6、Alpine Linux)
# 位置:/etc/init.d/myapp
cat > /etc/init.d/myapp << 'INITSCRIPT'
#!/bin/bash
# chkconfig: 2345 80 20
# description: My Application Service
# 2345     = 在 runlevel 2,3,4,5 开启
# 80       = 启动优先级(S80)
# 20       = 关闭优先级(K20)

PIDFILE="/var/run/myapp.pid"
LOGFILE="/var/log/myapp.log"
CMD="/usr/bin/python /opt/myapp/main.py"

start() {
    if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
        echo "myapp 已在运行"
        return 1
    fi
    echo "启动 myapp..."
    nohup $CMD >> "$LOGFILE" 2>&1 &
    echo $! > "$PIDFILE"
    echo "启动完成,PID: $(cat "$PIDFILE")"
}

stop() {
    if [[ ! -f "$PIDFILE" ]]; then
        echo "myapp 未运行"
        return 1
    fi
    local pid=$(cat "$PIDFILE")
    echo "停止 myapp (PID: $pid)..."
    kill -TERM "$pid"
    rm -f "$PIDFILE"
    echo "已停止"
}

case "$1" in
    start) start ;;
    stop) stop ;;
    restart) stop; sleep 1; start ;;
    status)
        if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
            echo "myapp 运行中 (PID: $(cat "$PIDFILE"))"
        else
            echo "myapp 已停止"
        fi
        ;;
    *) echo "用法: $0 {start|stop|restart|status}" ;;
esac
INITSCRIPT
chmod +x /etc/init.d/myapp
chkconfig --add myapp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

# 9.2.4 服务健康检查——不仅是端口存活

#!/bin/bash

# ===== 层次 1:端口是否监听 =====
check_port() {
    local port="$1"
    if ss -tlnp | grep -q ":$port "; then
        echo "✅ 端口 $port 在监听"
        return 0
    else
        echo "❌ 端口 $port 未监听"
        return 1
    fi
}

# ===== 层次 2:服务是否有响应(HTTP)=====
check_http_response() {
    local url="$1"
    local timeout="${2:-5}"

    local code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout "$timeout" "$url")
    if [[ "$code" == "200" ]]; then
        echo "✅ HTTP $code: $url"
        return 0
    else
        echo "❌ HTTP $code: $url"
        return 1
    fi
}

# ===== 层次 3:业务功能检查(端到端)=====
check_api_health() {
    local endpoint="$1"

    # 检查业务 API 是否返回预期数据
    response=$(curl -s "$endpoint" 2>/dev/null)
    if echo "$response" | jq -e '.status == "ok"' > /dev/null 2>&1; then
        echo "✅ API 正常: $endpoint"
        return 0
    else
        echo "❌ API 异常: $endpoint (响应: $response)"
        return 1
    fi
}

# ===== 综合健康检查 =====
cat > service_health_check.sh << 'SCRIPT'
#!/bin/bash
# 可放入 cron 每 1 分钟执行一次

HOST="localhost"

# 定义检查项:名称|检查方式|地址|失败动作
CHECKS=(
    "nginx|http|http://${HOST}:80/health|restart_nginx"
    "app_api|api|http://${HOST}:8080/api/health|restart_app"
    "redis|tcp|${HOST}:6379|restart_redis"
    "mysql|tcp|${HOST}:3306|alert_only"
)

restart_nginx()  { systemctl restart nginx; }
restart_app()    { systemctl restart myapp; }
restart_redis()  { systemctl restart redis; }
alert_only()     { echo "[FATAL] $(date) $1 不可达" >> /var/log/health_alert.log; }

for check in "${CHECKS[@]}"; do
    IFS='|' read -r name type target action <<< "$check"

    ok=false
    case "$type" in
        http)
            curl -sf --connect-timeout 3 "http://${target}" >/dev/null 2>&1 && ok=true
            ;;
        tcp)
            timeout 2 bash -c "echo > /dev/tcp/${target%:*}/${target#*:}" 2>/dev/null && ok=true
            ;;
        api)
            curl -sf "$target" 2>/dev/null | jq -e '.status=="ok"' >/dev/null && ok=true
            ;;
        process)
            pgrep -x "$target" >/dev/null && ok=true
            ;;
    esac

    if $ok; then
        echo "[$(date)] ✅ $name 正常"
    else
        echo "[$(date)] ❌ $name 异常,执行恢复动作: $action"
        eval "$action $name"
    fi
done
SCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

# 9.2.5 优雅重启——零停机更新策略

#!/bin/bash

# ===== 策略 1:reload(重载配置,不中断连接)=====
# Nginx
nginx -t && systemctl reload nginx           # 先测试配置,再重载
# PHP-FPM
systemctl reload php-fpm                     # 等待旧 worker 处理完再退出

# ===== 策略 2:pre-fork 预热(重启前启动新进程)=====
# 场景:更新 Node.js 应用(不支持 reload)
graceful_restart_node() {
    local app_dir="$1"
    local port="$2"
    local new_port="$((port + 1))"

    # 1. 在新端口启动新进程
    cd "$app_dir"
    PORT="$new_port" nohup node server.js > /tmp/app_new.log 2>&1 &
    new_pid=$!

    # 2. 等待新进程就绪
    for i in $(seq 1 30); do
        if curl -sf "http://localhost:$new_port/health" >/dev/null 2>&1; then
            echo "新进程就绪 (PID: $new_pid)"
            break
        fi
        sleep 1
    done

    # 3. 切换:用 iptables 把流量转到新端口
    # 或通知 Nginx upstream 切换到新端口
    iptables -t nat -D PREROUTING -p tcp --dport "$port" -j REDIRECT --to-port "$port"
    iptables -t nat -A PREROUTING -p tcp --dport "$port" -j REDIRECT --to-port "$new_port"

    # 4. 等待旧端口连接耗尽
    sleep 5
    old_pid=$(lsof -ti :$port)
    if [[ -n "$old_pid" ]]; then
        kill -TERM "$old_pid"
    fi

    echo "优雅重启完成(旧端口 $port → 新端口 $new_port)"
}

# ===== 策略 3:Nginx upstream 平滑切换 =====
cat > nginx_rolling_restart.sh << 'SCRIPT'
#!/bin/bash
# 每次重启一个 upstream server,逐个完成
# 需 Nginx upstream 配置 health_check

UPSTREAM_SERVERS=("192.168.1.10" "192.168.1.11" "192.168.1.12")

for server in "${UPSTREAM_SERVERS[@]}"; do
    echo "从 upstream 移除: $server"
    # 修改 Nginx 配置标记该 server down
    ssh "$server" "systemctl restart myapp"

    # 等待服务恢复
    echo "等待服务恢复..."
    for i in $(seq 1 30); do
        if ssh "$server" "curl -sf http://localhost:8080/health" >/dev/null 2>&1; then
            echo "$server 已恢复"
            break
        fi
        sleep 2
    done

    # 恢复 upstream
    echo "$server 重新加入 upstream"
    sleep 10    # 给一些时间让连接分发稳定
done

echo "所有 server 优雅重启完成"
SCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

# 9.2.6 灰度发布脚本——分批滚动更新

#!/bin/bash

# ===== 灰度发布策略 =====
# 10% → 观察 5 分钟 → 50% → 观察 5 分钟 → 100%
# 每个阶段检查错误率,异常则自动回滚

cat > canary_deploy.sh << 'SCRIPT'
#!/bin/bash
# 灰度发布入口脚本

APP="myapp"
NEW_VERSION="$1"
OLD_VERSION=$(cat /opt/${APP}/current_version 2>/dev/null || echo "unknown")

if [[ -z "$NEW_VERSION" ]]; then
    echo "用法: $0 <新版本号>"
    exit 1
fi

DEPLOY_DIR="/opt/${APP}"
HEALTH_URL="http://localhost:8080/health"
METRICS_URL="http://localhost:8080/metrics"
ROLLBACK_SCRIPT="${DEPLOY_DIR}/rollback.sh"

# ---- 阶段定义 ----
declare -A STAGES=(
    ["canary"]="10%"
    ["half"]="50%"
    ["full"]="100%"
)

# ---- 工具函数 ----
log() { echo "[$(date '+%H:%M:%S')] $*"; }

check_error_rate() {
    local max_rate="${1:-5}"     # 最大允许错误率(%)
    # 从 metrics 端点获取 1 分钟错误率
    local err_rate=$(curl -sf "$METRICS_URL" 2>/dev/null | jq -r '.error_rate_1m // 0')
    if (( $(echo "$err_rate > $max_rate" | bc -l) )); then
        return 1    # 错误率过高
    fi
    return 0
}

health_wait() {
    local timeout="${1:-30}"
    for i in $(seq 1 "$timeout"); do
        if curl -sf "$HEALTH_URL" >/dev/null 2>&1; then
            return 0
        fi
        sleep 1
    done
    return 1
}

rollback() {
    log "💀 执行回滚!"
    if [[ -x "$ROLLBACK_SCRIPT" ]]; then
        bash "$ROLLBACK_SCRIPT" "$OLD_VERSION"
    else
        log "没有回滚脚本,手动回滚到版本 $OLD_VERSION"
    fi
    exit 1
}

# ---- 灰度流程 ----
log "===== 灰度发布 $APP v${NEW_VERSION} (旧版: v${OLD_VERSION}) ====="

for stage in canary half full; do
    percentage="${STAGES[$stage]}"
    log "当前阶段: $stage (${percentage})"

    # 部署新版本到该阶段节点
    # 这里假设用 ansible 或 ssh 分发(简化为本地)
    bash "${DEPLOY_DIR}/deploy_stage.sh" "$stage" "$NEW_VERSION"

    # 等待服务健康
    if ! health_wait 30; then
        log "阶段 ${stage} 健康检查失败"
        rollback
    fi

    # 观察期
    log "观察期 5 分钟..."
    sleep 300

    # 检查错误率
    if ! check_error_rate 3; then
        log "阶段 ${stage} 错误率过高"
        rollback
    fi

    log "阶段 ${stage} 通过 ✅"
done

log "===== 灰度发布完成: v${OLD_VERSION} → v${NEW_VERSION} ====="
echo "$NEW_VERSION" > "${DEPLOY_DIR}/current_version"
SCRIPT

# ===== 配合的 Nginx upstream 权重配置 =====
# 灰度发布需要 Nginx 支持动态调整 upstream 权重
cat > /etc/nginx/conf.d/upstream_weight.conf << 'NGINX'
upstream app_backend {
    # canary 阶段:新版本权重 10%
    # server 192.168.1.10:8080 weight=9;    # 旧版
    # server 192.168.1.10:8081 weight=1;    # 新版(canary)

    # half 阶段:50%
    # server 192.168.1.10:8080 weight=1;
    # server 192.168.1.10:8081 weight=1;

    # full 阶段:100%
    server 192.168.1.10:8080 weight=1;       # 新版
    # server 192.168.1.10:8081 down;         # 旧版下线

    keepalive 32;
}
NGINX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

# 9.3 新手陷阱与思考题

# 陷阱 Top 5

陷阱 1:usermod -G 忘记 -a 导致丢失所有附加组

# ❌ 覆盖附加组——用户可能失去 sudo/docker 权限
usermod -G docker alice               # alice 只剩 docker 组

# ✅ 追加组
usermod -aG docker alice              # 保留原有组 + docker

# 修复:查看用户当前组
id alice                              # 看缺少什么组
usermod -aG sudo,wheel alice          # 补回来
1
2
3
4
5
6
7
8
9

陷阱 2:SSH 安全加固后自己也被锁在外面

# ❌ 改了 /etc/ssh/sshd_config 后没测试就直接重启 sshd
# 如果配置有误——再也连不上了!

# ✅ 在另一个终端保持 SSH 连接,测试新配置
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
# 修改配置...
sshd -t                                # 先测试语法
systemctl reload sshd                  # 用 reload 而不是 restart
# 在新终端测试能否登录
ssh localhost                          # 确认能登录后再关旧终端
1
2
3
4
5
6
7
8
9
10

陷阱 3:userdel 不删家目录留下安全隐患

# ❌ 只删除用户,家目录残留(含 SSH 密钥等敏感信息)
userdel alice

# ✅ 彻底删除
userdel -r alice
# 或事后手动清理
find /home -maxdepth 1 -nouser -exec rm -rf {} \;
1
2
3
4
5
6
7

陷阱 4:systemctl mask 后忘记 unmask 导致服务启动不了

# ❌ mask 后再 start 无任何提示但就是启动不了
systemctl mask nginx
systemctl start nginx                  # 静默失败

# 检查:
systemctl status nginx
# 输出:Loaded: masked (Reason: Unit nginx.service is masked.)

# ✅ 排查:
systemctl list-unit-files | grep masked
systemctl unmask nginx
1
2
3
4
5
6
7
8
9
10
11

陷阱 5:健康检查只检查端口,服务实际上是假死

# ❌ 端口在监听但不响应——假死
ss -tlnp | grep 8080                   # 端口监听 ✓
curl http://localhost:8080/            # 但实际不响应 ✗

# ✅ 分层检查
# 1. 端口
# 2. HTTP 响应码
# 3. 业务数据正确性
# 4. 响应时间阈值
1
2
3
4
5
6
7
8
9

# 综合思考题

  1. 用户安全审计:写脚本列出系统上所有 UID=0 的用户、无密码用户、可登录但 90 天未修改密码的用户、家目录权限为 777 的用户。

  2. 批量迁移:将一批用户从旧服务器迁移到新服务器(包括用户条目、密码 hash、组关系、家目录数据),写出迁移脚本。

  3. SSH 入侵防护:用 iptables + 脚本实现:对同一 IP 60 秒内 SSH 失败超过 5 次,自动封禁该 IP 1 小时,并记录到日志。

  4. 自定义健康检查服务:写一个 systemd service + timer,每 30 秒对关键 API 做健康检查,失败时自动重启对应服务,且连续失败 3 次时发送告警。

  5. 零停机部署完整方案:设计一个完整的零停机部署流程,包含:

    • 健康检查端点
    • Nginx upstream 动态切换
    • 旧进程优雅退出(等待活跃连接完成)
    • 自动回滚机制
#Shell#运维
上次更新: 2026/06/17, 12:47:39
备份进程与磁盘
网络调度与部署

← 备份进程与磁盘 网络调度与部署→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式