서비스를 운영할 때 admin tool은 필수입니다. 운영진은 사용자의 개인정보에 접근하거나 사용자의 요청에 따른 수정, 환불과 같은 이슈를 해결해야 합니다. 개발자에게 요청하여 수동으로 처리하는 경우가 많은가요? 개발자의 시간과 집중력은 소중하기에 수동으로 처리해야 하는 일은 최소로 줄여야 합니다.
그렇다면 운영진이 중요한 정보에 접근하거나 수정하는 것을 어떻게 기록할까요? 이럴 경우 저는 쉽고 빠르게 적용 가능한 audit log를 사용합니다. audit log에 기록하는 내용:
- 접근한 페이지의 controller와 params
- 접근한 사용자(운영진)의 이름, 이메일, 아이디
- 접근한 디바이스의 IP 주소, useragent
Rails는 controller에서 before_action
을 이용하면 쉽게 구현할 수 있습니다. 아래는 제가 실서버 환경에서 구현한 코드 일부입니다.
# Gemfile
gem 'geoip'
# app/controllers/concerns/audit_log.rb
module AuditLog
extend ActiveSupport::Concern
included do
before_action :audit_log
end
def audit_log
logs = {
controller: params[:controller],
action: params[:action],
params: filtered_params,
user_id: current_user&.id,
user_email: current_user&.email,
user_name: current_user&.name,
ip_address: request.remote_ip,
geo_ip: geoip.city(request.remote_ip).to_h,
user_agent: request.user_agent
}
logger.tagged('audit_log') { logger.info logs }
end
private
def geoip
GeoIP.new(File.join(Rails.root, '/lib/GeoLiteCity.dat'))
end
def filtered_params
params.except(:controller, :action, :utf8, :authenticity_token, :commit)
end
end
# app/controllers/admin_controller.rb
class AdminController < ActionController::Base
include AuditLog
end
admin 페이지에서의 로그인, 수정 등 모든 요청은 log 파일에 아래와 같이 빠짐없이 기록됩니다.
[audit_log] {"controller":"users/sessions","action":"create",
"params":{"user":{"email":"kimsuelim@gmail.com","password":"hvgE9WBV7UnNLmSDBKQi"}},
"user_id":"1","user_email":"kimsuelim@gmail.com","user_name":"lim",
"ip_address":"127.0.0.1", "geo_ip":{"request":"127.0.0.1","ip":"127.0.0.1","country_code2":"KR","country_code3":"KOR","country_name":"Korea, Republic of","continent_code":"AS","region_name":"11","city_name":"Seoul","postal_code":"","latitude":37.5985,"longitude":126.97829999999999,"dma_code":null,"area_code":null,"timezone":"Asia/Seoul","real_region_name":"Seoul-t'ukpyolsi"},
"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.113 Safari/537.36"}
위 로그를 자세히 보면 운영진의 비밀번호가 기록됩니다.
"password":"hvgE9WBV7UnNLmSDBKQi"
운영진의 로그인 비밀번호까지 기록해야 할까요? 친절하게도 Rails는 이미 로그 파일에서 민감한 정보를 필터링하고 있습니다.
config/initializers/filter_parameter_logging.rb
에서 필터링이 필요한 parameter를 설정할 수 있습니다.
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]
그러나 custom logging에는 설정이 적용되지 않아 로그인 시, 비밀번호가 로그에 기록되는 문제가 있습니다. 이럴 경우 Rails에서 제공하는 ActionDispatch::Http::ParameterFilter API를 이용하여 수동으로 parameter를 필터링해주면 됩니다.
module AuditLog
...
private
def filtered_params
rails_params = params.except(:controller, :action, :utf8, :authenticity_token, :commit).to_unsafe_h
parameter_filter = ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters)
parameter_filter.filter(rails_params)
end
end
수정한 코드를 적용하면 password 파라미터만 필터된 후에[FILTERED]
기록됩니다. cheers! 🍻
[audit_log] {"controller":"users/sessions","action":"create",
"params":{"user":{"email":"kimsuelim@gmail.com","password":"[FILTERED]"}},
...
audit log에 기록만 한다고 되는 것은 아닙니다. 기록된 로그를 어떻게 저장하고 분석하는
문제가 남아있습니다. 이는 또 다른 topic이기에 여기에서 자세히 다루기는 어렵습니다.
직접 ELK(Elasticsearch + Logstash + Kibana)
환경을 setup 하거나 Papertrail와 같은 SaaS를 이용할 수 있습니다.
개인적으로는 실서버 환경에서 Papertrail 를 사용하고 있으며 아주 만족하고 있습니다.