部署 分享几个部署时解决 ENV 变量问题的方案

ruohanc · 2014年07月01日 · 最后由 lulalala 回复于 2018年03月09日 · 6967 次阅读

比如 Rails 4.1 引入的 secrets.yml 中, production 默认的 SECRET_KEY_BASE 是 <%= ENV["SECRET_KEY_BASE"] %>. 大家在线上部署时候是如何解决这个问题的呢.?

我搜了几个方案分享一下

通过设置对端 sshd_config 接受本端环境变量实现: http://overmind.ws/blog/2014/04/27/capistrano-3/

大意是通过设置 sshd_config 的 PermitUserEnvironment yes 后, 再在本端的 ~/.ssh/environment 中手动写环境变量 SECRET_KEY_BASE=66a2caf0d57e376cae89a297656c1224e5951609591374f9ba0a31a77ba36c4fcafd2f

这样通过 capistrano ssh 到对端后对端的环境中就带上了这个变量, 于是搞定.

缺点在于如果你要在一个机器上向对端部署多个应用就跪了

通过 rbenv 插件 rbenv-vars 实现: https://intercityup.com/blog/deploying-app-env-variables-with-rbenv-passenger-and-capistrano/

rbenv 插件可以读取应用根目录下的 .rbenv-vars 文件, 根据文件内容设置环境变量. 于是只要在 capistrano 的 deploy.rb 中加上 set :linked_files, %w{config/database.yml .rbenv-vars} 就可以了

缺点是必须要安装 rbenv 和 rbenv-vars. 我在生产环境是直接用自编译的 ruby rpm 包, 所以没 rbenv....

通过 dotenv 或者 figaro gem 实现

两个 gem 的功能都类似, 都是提供一种机制能帮你预先把对应的变量放进 ENV Hash 中, 其中最大的区别是 dotenv 鼓励你把 .env 文件放入 repo, 方便新开发者直接上手, 而 figaro 默认把配置文件加入了 .gitignore. 见仁见智吧.

另外 figaro 还有一个特性是 integrated with heroku, 通过 figaro heroku:set -e production 命令直接搞定.

bash_profile 里直接 export SECRET_KEY_BASE=xxx 不可以吗

直接本地生成一串,然后拷贝到服务器上,放到 shared_path 里面去就可以了。

#1 楼 @hooopo 可以呀, 就是有点小缺陷

  1. 难以适应大规模部署 (直接修改 bash_profile 方法不太适合运维)
  2. 同一个机器上难以两个不同应用中使用两个不同的 KEY

#2 楼 @hz_qiuyuanxin 你这个方案跟 2,3 一样, 就看你怎么做的优雅了..

直接写 cap 命令呢

namespace :deploy do
  desc "Start unicorn"
  task :start do
    run "cd #{deploy_to}/current/; RAILS_ENV=#{rails_env}  SECRET_KEY_BASE=#{ENV['SECRET_KEY_BASE']} bundle exec unicorn_rails -c #{unicorn_path} -D"
  end
end

多应用部署同一机器上的情况,在实际很少见吧。。如果有,加前缀也很容易解决。

其实 env 又不是经常改动的,做一个 heroku 那样的 cli 多方便:my_heroku set -e SECRET_KEY_BASE=xxx

我的解决方法是,另外建立了一个 secrets.production.yml,部署文件里边让它在最后自动上传覆盖 secrets.yml,而这个文件在 git 里边忽略了的。

#7 楼 @raofeng 嗯...这跟直接用 capistrano 的 linked_files 效果一样..

#6 楼 @hooopo 后来想想, 如果从安全性的角度上来说, 还是第一种方案最好, 这样 ENV 就只保存在部署机工具机上, 在应用机上完全找不到对应的信息. 即是应用机被完全攻破也拿不到更多信息. 后两种方案 (包括你说的方案) 就是会在应用机上留下信息, 违背了使用 ENV 变量的初衷.

#3 楼 @ruohanc 多个应用可以用不同的环境变量 APP1_SECRET_KEY_BASE APP2_SECRET_KEY_BASE

secrets.yml 里面也相应修改即可

#12 楼 @quakewang 你意思是放在 ~/.bash_profile 里面?

我在想有没有办法在应用机上不放任何敏感信息 (数据库密码这样)..敏感信息最多只保留在部署工具机上

#10 楼 @ruohanc 应用被完全攻破了,应该先看 database.yml 吧。谁还关注那个 key 啊。其实不用想太多,对于绝大多数 Rails 开发者,你就算把 production secret key 交给他,他会用吗?

PS: 我们目前 2 台后端服务器做负载均衡,用的方法和 #2 楼一样。

#14 楼 @Victor 所以 database.yml 也应该是 ENV 定义啊....

环境变量用 ansible 来配到 shell 中

#14 楼 @Victor 可是威胁您系统安全的不是 绝大多数 Rails 开发者

应用机都被攻破了,别人也是可以轻易通过控制台获取环境变量的。只要查询到你用的环境变量的 key,查询其值不是很简单的么?所以,应用机都被攻破了,说这些就没得意思了。

#17 楼 @hooopo 威胁系统安全的,应该是弱智的系统管理员吧。我的意思跟 #18 楼 一样。

#19 楼 @Victor 同意,更危险的是变节的系统管理员

#18 楼 @outman 有道理诶....那为什么推荐 ENV 方式呢...还不如直接用 capistrano 的 linked_files #19 楼 @Victor #20 楼 @swordray

#21 楼 @ruohanc 意思是,为了安全性,我们已经尽力了,只能帮到这里了 :)

#22 楼 @outman god damn cool....

#22 楼 @outman 不对啊..被你绕进去了..如果是第一种方式的话, 直接攻破应用机是没办法方便的看到 ENV 数据的. 除非要 attach 进那个正在运行的进程才能找到了. 直接自己运行 rails c 是重新初始化的 rails 进程, 是看不到"当时"的 ENV 的

#24 楼 @ruohanc 哦,哦,你说的是使用本地的环境变量去启动远程服务器的 rails 应用吧?你说的是这个吗?如果是这个,确实,即使攻破应用机器也没用,要 attach 到那个进程。我看成使用远程服务器上得环境变量了。如果是使用远程机器的 ENV,并且被攻破,还猜到 key 的话,直接 echo $key 就可以了。看来最靠谱的方式,确实是 cap 启动脚本里面带上那个环境变量的参数。

我的方案也是把本地的设定转成环境变量去启动远程服务器的 rails 应用

安装 settei gem 以后

先把设定放在 config/environments/production.yml 跟 config/environments/default.yml

rails:
  secret_token: foooooo
  secret_key_base: barrrrr

然后让 Rails 去读取这设定

# secret_token.rb
Foo::Application.config.secret_token = Setting.dig(:rails, :secret_token)
Foo::Application.config.secret_key_base = Setting.dig(:rails, :secret_key_base)

这样 deploy 时就自动会用 #5 推荐的方式把设定传给远程服务器, 当然要是你使用 heroku 也有类似的功能把设定传给远程服务器。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号