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

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

比如 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 也有类似的功能把设定传给远程服务器。

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