Рубрики
Uncategorized

Как настроить консоли Readonly Rails

Как настроить консоль Readonly Rails, чтобы помочь защитить ваши производственные данные. Tagged с DevOps, базой данных, Ruby, Rails.

Стремясь лучше защитить наши производственные данные, в прошлом году мы решили настроить консоль производственной консоли Readonly. Это позволяет разработчикам такать по производственным данным, не беспокоясь о том, что они могут случайно изменить то, что не должны. В этом посте я разбим именно то, что мы сделали, чтобы сделать это для нашего приложения Ruby on Rails.

Прежде чем погрузиться в специфику для каждой базы данных, я хочу сначала упомянуть, что мы используем совершенно отдельный сервер для консоли. Использование отдельного сервера позволяет нам настраивать настройки приложения для достижения доступа к чтениям. Чтобы развернуть эти изменения, мы используем Ansible Анкет Когда Ansible запускает развертывание, он ищет тег для консоли, чтобы узнать, какие настройки и конфигурации должны быть развернуты в этом конкретном поле.

Первое, что мы сделали, — это настроить пользователя с Readonly Access в MySQL. Затем, чтобы сделать наше приложение Readonly для MySQL, все, что нам просто нужно было сделать, это поместить учетные данные пользователя READONLY в нашем 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

Теперь Каждый раз, когда кто -то открывает консоль рельсов на нашем консольном сервере, он автоматически использует учетные данные для чтения из конфигурации.

Тем не менее, есть еще времена, когда мы хотим иметь возможность позволить разработчикам редактировать данные через консоль. Чтобы сделать это, мы установили сценарий 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

Теперь, если разработчик должен редактировать данные, он может просто открыть консоль записи, используя команду Консоль записи Анкет

Чтобы обработать настройку Redis как Readonly, мы решили переопределить нашего клиента Redis, чтобы явно блокировать любые команды записи. Поскольку у нас есть приложение Ruby on Rails, мы используем Redis-rb драгоценный камень, чтобы поговорить с Redis. Чтобы заблокировать команды записи, мы сначала собрали все команды, которые писали, на основе вызова Команда Метод на нашем клиенте Redis. Для справки, Rails.cache.data просто даст вам ваш клиент Redis.

dev> Rails.cache.data
=> #

Команда Метод вернет массив всех команд, на которые ваш экземпляр 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

Как только у нас появился список команд записи, мы отвергли процесс Метод В нашем жемчужине, чтобы выдвинуть ошибку, если был вызван какой -либо из этих методов.

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 записи. Но все еще остается вопрос, как мы можем блокировать эти команды записи только в нашем консольном поле? Еще раз, мы обратились к нашим переменным окружающей среде и нашему сценарию консоли. В нашем консольном скрипте мы устанавливаем переменные среды на основе того, была ли консоль в чтении по умолчанию или если это была консоль записи.

#!/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 Файл инициализатора В нашем приложении мы обезьяны исправлены метод процесса, чтобы вернуть ошибку, если команда записи была вызвана в режиме Readonly Access.

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-ruby жемчужина Во многом так же, как мы делали Redis, мы нашли основной метод, используемый для выполнения внешних запросов в Elasticsearch, exector_resquest и исправил его так, чтобы это выдвинуло ошибку всякий раз, когда был выполнен метод записи. Поскольку мы разговариваем с Elasticsearch, используя HTTP -запросы, методы, которые мы хотели блокировать, были поставлены, публиковать и удалять.

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

Еще раз, мы также решили использовать переменную среды, чтобы определить, должны ли мы исправлять exector_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

Этот сценарий гарантирует, что когда разработчик или поддержка открывают консоль, используя Консоль Команда, по умолчанию, это будет читается. При необходимости они могут позвонить Консоль писать Если им нужно обновить какие -либо данные. Несмотря на то, что очень легко открыть консоли записи, подавляющее большинство людей, которые люди работают в консолях Readonly. Консоли Readonly зарекомендовали себя много раз, спасая людей от глупых ошибок, просматривая данные о производстве.

Есть много других способов подхода к безопасности данных, когда речь идет о работе с производственными данными. Это всего лишь один подход и тот, который мы решили использовать в Кенне. Еще один очень популярный вариант — сделать реплику или клон ваших данных, а затем позволить людям запускать любые запросы, которые они хотят против этой копии или клона. Недостатком этого является то, что получение современной копии каждый раз, когда вам нужно, это может занять много времени в зависимости от размера вашего набора данных. Кроме того, клонирование одной базы данных, такой как MySQL, довольно просто. Однако, когда вы работаете с 3 различными хранилищами данных, например, мы делаем в Кенне, это гораздо больше усилий, чтобы воспроизвести все эти данные вместе.

В Кенне разработчики имеют возможность клонировать данные о производстве, но в настоящее время они доступны только в MySQL. Обычно клон MySQL используется только для проверки миграций высокого риска или сценариев, которые собираются изменить много производственных данных одновременно. Кроме того, мы обнаружили, что наша консоль Readonly отлично работала для нашего варианта использования, потому что большую часть времени разработчиков и поддержки просто хотят взглянуть на актуальные данные о производстве.

Надеюсь, вы нашли этот пост полезным! Как всегда, пожалуйста, дайте мне знать, если у вас есть какие -либо вопросы! 🤗

Оригинал: «https://dev.to/molly/how-to-setup-a-readonly-rails-console-1j1a»