最小权限与 Secrets 基础
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read346 words

最小权限与 Secrets 基础

"最小权限"不是口号,而是让失误成本变小。哪怕有人拿到了一个账号,也不应该拿到整台机器。

权限隔离模型

graph TD A[运维工程师 ubuntu] -->|sudo| B[系统管理操作] C[应用进程 app user] --> D[/opt/myapp/ 仅可读] C --> E[/etc/myapp/myapp.env 仅可读] F[数据库 postgres user] --> G[数据库文件] H[部署脚本 deploy user] --> I[/opt/myapp/releases/ 可写] J[Secret 文件] -->|chmod 640, chown root:app| C

应用专用用户配置

# 创建无 shell 的应用专用用户(不允许直接登录)
sudo useradd --system --shell /usr/sbin/nologin --create-home --home-dir /opt/myapp app
# 应用目录属于 root,但 app 可读
sudo chown -R root:app /opt/myapp/current
sudo chmod -R 750 /opt/myapp/current
# 日志目录属于 app(应用需要写入)
sudo mkdir -p /var/log/myapp
sudo chown app:app /var/log/myapp
sudo chmod 755 /var/log/myapp

Secrets 文件管理

标准目录结构

/etc/myapp/
├── myapp.env          # 环境变量(DB 密码、API 密钥等)
└── myapp.secret.key   # 加密密钥(如有)

创建和保护

# 创建 Secrets 目录
sudo mkdir -p /etc/myapp
sudo chown root:app /etc/myapp
sudo chmod 750 /etc/myapp
# 创建环境变量文件
sudo tee /etc/myapp/myapp.env > /dev/null <<'EOF'
DATABASE_URL=postgresql://app:yourpassword@localhost:5432/myapp
REDIS_URL=redis://localhost:6379
SECRET_KEY=your-secret-key-here
APP_ENV=production
PORT=3000
EOF
# 设置权限:只有 root 和 app 组可读
sudo chown root:app /etc/myapp/myapp.env
sudo chmod 640 /etc/myapp/myapp.env
# 验证
ls -la /etc/myapp/myapp.env
# -rw-r----- 1 root app ... /etc/myapp/myapp.env

在 systemd 中引用

[Service]
EnvironmentFile=/etc/myapp/myapp.env
# 变量自动注入,不出现在进程命令行(ps aux 不可见)

数据库最小权限

-- PostgreSQL:为应用创建只有所需权限的数据库用户
CREATE USER app_user WITH PASSWORD 'secure-password';
CREATE DATABASE myapp OWNER app_user;
-- 如果 app 只读(报表服务)
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_user;
-- 不要给 SUPERUSER 或 CREATEDB 权限
-- 不要用 postgres 超级用户连接应用

防止 Secrets 进入 Git

# .gitignore(根目录)
.env
.env.*
*.env
*.secret
secrets/
config/secrets.yml
# 检查是否已有 Secrets 泄漏到提交历史
git log --all --full-history -- "**/.env*"
git grep -l "password\|secret\|api_key" $(git rev-list --all)
# 如果已经提交了 Secret,必须立即轮转密钥(修改密码/API Key)
# 历史中的 Secret 视为已泄漏,不能通过删除提交来"撤回"

sudo 最小权限

# 查看当前 sudo 权限
sudo -l
# 为部署用户只授权特定命令(而非全部 sudo)
# 编辑 /etc/sudoers.d/deploy(用 visudo 编辑)
sudo visudo -f /etc/sudoers.d/deploy
# /etc/sudoers.d/deploy
# deploy 用户只能重启 myapp 服务,不能做其他 root 操作
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp, /bin/systemctl status myapp

权限检查清单(操作对照表)

对象 推荐权限 命令
应用代码目录 root:app 750 chown root:app /opt/myapp; chmod 750 /opt/myapp
Secrets 文件 root:app 640 chown root:app /etc/myapp/myapp.env; chmod 640 /etc/myapp/myapp.env
日志目录 app:app 755 chown app:app /var/log/myapp; chmod 755 /var/log/myapp
SSH 私钥 user:user 600 chmod 600 ~/.ssh/id_ed25519
SSH authorized_keys user:user 600 chmod 600 ~/.ssh/authorized_keys

常见误区

误区 正确做法
把数据库密码写进 Git .gitignore 排除,用 /etc/myapp/ 存储
所有服务都用同一个 .env 文件 每个应用有独立的 Secret 文件,权限隔离
给应用用户完整 sudo 只授权 systemctl restart myapp 等最小必要命令
Secret 已泄漏到 Git 历史,删掉就行 历史中的 Secret 视为公开,必须立即轮转密钥

本节执行清单

下一节Fail2ban、自动更新与审计——继续做机器层面的基础防护。