如何设置只读 Rails 控制台
MySQL
Redis
Elasticsearch
其他选项
为了更好地保护我们的生产数据,去年我们选择设置一个只读的生产控制台。这使得开发人员可以随意浏览生产数据,而不必担心意外更改不该更改的内容。在本文中,我将详细介绍我们为 Ruby on Rails 应用程序实现这一目标的具体步骤。
在深入探讨每个数据库的具体细节之前,我想先提一下,我们使用一个完全独立的服务器来访问控制台。使用独立的服务器使我们能够调整应用程序设置以实现只读访问权限。为了部署这些更改,我们使用了Ansible。当 Ansible 运行部署时,它会查找控制台框标签,以了解需要将哪些设置和配置部署到该特定框。
MySQL
我们做的第一件事是在 MySQL 中设置一个具有只读权限的用户。然后,为了让我们的应用程序对 MySQL 只读,我们只需将只读用户凭据写入database.yml
控制台服务器中的文件即可。
production:
adapter: mysql2
encoding: utf8
reconnect: true
pool: 16
database: "prod_db"
username: "readonly"
password: "you_wish"
host: "127.0.0.1"
strict: false
现在,任何时候有人在我们的控制台服务器上打开 Rails 控制台,它都会自动使用配置中的只读凭据。
然而,有时我们仍然希望能够允许开发人员通过控制台编辑数据。为了实现这一点,我们编写了一个用于打开 Rails 控制台的 Bash 脚本。在这个 Bash 脚本中,我们选择使用 Rails 提供的一项鲜为人知的功能。DATABASE_URL
如果您DATABASE_URL
在环境中设置了该变量,Rails 将使用它来连接数据库,而不是从database.yml
文件中读取。这使我们能够在需要时覆盖数据库配置。我们在 Bash 脚本中像这样设置它:
#!/bin/bash
cd /application_path
if [ "$1" = 'write' ]; then
export DATABASE_URL="mysql2://write_username:write_password@host/db_name"
fi
RAILS_ENV=production /usr/local/bin/bundler exec rails console
现在,如果开发人员需要编辑数据,他们只需使用命令打开写入控制台即可console write
。
Redis
为了将 Redis 设置为只读,我们选择覆盖 Redis 客户端,以明确阻止任何写入命令。由于我们使用的是 Ruby on Rails 应用程序,因此我们使用redis-rbcommand
gem 与 Redis 通信。为了阻止写入命令,我们首先通过调用Redis 客户端上的方法来收集所有基于写入的命令。作为参考,Rails.cache.data
我们将直接提供您的 Redis 客户端。
dev> Rails.cache.data
=> #<Redis client v4.0.3 for redis://127.0.0.1:6379/15>
该command
方法将返回 Redis 实例将响应的所有命令的数组以及有关每个命令的一些附加信息。
dev> Rails.cache.data.command.first(3)
=> [["expireat", 3, ["write", "fast"], 1, 1, 1],
["setnx", 3, ["write", "denyoom", "fast"], 1, 1, 1],
["getrange", 4, ["readonly"], 1, 1, 1]]
为了仅过滤掉写入命令,我们只需检查命令属性列表中的“写入”值。
WRITE_COMMANDS = Rails.cache.data.command.map { |a| a[0] if a[2].include?('write') }.compact.to_set
一旦我们有了写入命令列表,我们就会覆盖gem 中的process
方法,以便在调用任何方法时引发错误。
def process(commands)
if commands.flatten.any? { |c| WRITE_COMMANDS.include?(c.to_s) }
raise NotImplementedError, "REDIS_ACCESS_MODE is set to 'readonly', disallowing writes"
end
# additional method logic
end
这两部分允许我们阻止 Redis 的写入命令。但是,问题仍然存在,如何仅在控制台上阻止这些写入命令?我们再次转向环境变量和 bash 控制台脚本。在控制台脚本中,我们根据控制台是默认的只读控制台还是写入控制台来设置环境变量。
#!/bin/bash
cd /application_path
if [ "$1" = 'write' ]; then
export DATABASE_URL="mysql2://write_username:write_password@host/db_name"
export REDIS_ACCESS_MODE=""
else
export REDIS_ACCESS_MODE="readonly"
fi
RAILS_ENV=production /usr/local/bin/bundler exec rails console
然后,在redis.rb
我们的应用程序的初始化文件中,我们对 process 方法进行了修改,以便在只读访问模式下调用写入命令时返回错误。
if ENV['REDIS_ACCESS_MODE'] == 'readonly'
class Redis
class Client
WRITE_COMMANDS = ::Rails.cache.data.command.map { |a| a[0] if a[2].include?('write') }.compact.to_set.freeze
def process(commands)
if commands.flatten.any? { |c| WRITE_COMMANDS.include?(c.to_s) }
raise NotImplementedError, "REDIS_ACCESS_MODE is set to 'readonly', disallowing writes"
end
# additional method logic
end
end
end
end
轰!控制台现在默认对 MySQL 和 Redis 只读了。只剩下一个难题:Elasticsearch。
Elasticsearch
Elasticsearch 是我们应用程序的基石,因此我们也需要将其设置为只读。为了与 Elasticsearch 通信,我们使用elasticsearch-ruby gem。与 Redis 类似,我们找到了用于向 Elasticsearch 发出外部请求的核心方法,perform_resquest
并对其进行了修补,使其在执行写入方法时都会引发错误。由于我们使用 HTTP 请求与 Elasticsearch 通信,因此我们想要阻止的方法是 PUT、POST 和 DELETE。
module Elasticsearch
module Transport
class Client
if ENV['ELASTICSEARCH_ACCESS_MODE'] == 'readonly'
def perform_request(method, path, params={}, body=nil, headers=nil)
raise 'Elasticsearch is in readonly mode.' if method.to_s.match?(/PUT|POST|DELETE/)
method = @send_get_body_as if 'GET' == method && body
transport.perform_request(method, path, params, body, headers)
end
end
end
end
end
再次,我们选择使用环境变量来决定是否应该修补该perform_request
方法。然后,我们将该环境变量添加到我们的 bash 脚本中。完整的 bash 脚本如下所示:
#!/bin/bash
cd /application_path
if [ "$1" = 'write' ]; then
export DATABASE_URL="mysql2://write_username:write_password@host/db_name"
export REDIS_ACCESS_MODE=""
export ELASTICSEARCH_ACCESS_MODE=""
else
export REDIS_ACCESS_MODE="readonly"
export ELASTICSEARCH_ACCESS_MODE="readonly"
fi
RAILS_ENV=production /usr/local/bin/bundler exec rails console
此脚本确保开发人员或支持人员使用该console
命令打开控制台时,默认情况下该控制台为只读状态。必要时,他们可以console write
在需要更新任何数据时调用此脚本。尽管打开可写入控制台非常容易,但绝大多数情况下人们都在只读控制台中工作。只读控制台已多次证明其有效性,避免了人们在浏览生产数据时犯下愚蠢的错误。
其他选项
在处理生产数据时,还有许多其他方法可以保证数据安全。这只是其中一种方法,也是我们在 Kenna 选择使用的方法。另一个非常流行的选择是复制或克隆数据,然后允许人们针对该副本或克隆运行任何查询。这种方法的缺点是,每次需要时获取最新的副本可能会非常耗时,具体取决于数据集的大小。此外,克隆单个数据库(例如 MySQL)非常简单。但是,当您使用 3 个不同的数据存储时(例如我们在 Kenna 所做的那样),将所有数据复制到一起会更加费力。
在 Kenna,开发人员可以克隆生产数据,但目前该功能仅在 MySQL 中可用。通常,MySQL 克隆仅用于检查高风险迁移或一次性更改大量生产数据的脚本。除此之外,我们发现只读控制台非常适合我们的用例,因为大多数时候开发人员和支持人员只想查看最新的生产数据。
希望这篇文章对你有帮助!一如既往,如果你有任何问题,请随时告诉我!🤗
文章来源:https://dev.to/molly/how-to-setup-a-readonly-rails-console-1j1a