2020年9月17日木曜日

Ruby waterfall を使ってフロー処理を実装してみる

概要

apneadiving/waterfall は Ruby でフロー処理を実現するためのライブラリです
今回は簡単なサンプルを作成して挙動を確認してみました

環境

  • macOS 10.15.6
  • Ruby 2.7.1p83
    • waterfall 1.3.0

簡単なサンプルコード

まずは成功する簡単なサンプルコードを作成しました
waterfall は chain を使って処理を数珠つなぎにすることでフロー処理を実現します
またメインの処理は Waterfall モジュールを include しクラスにすることで管理しやすくしています
また call メソッドを実装することでオブジェクトが生成された際に自動的に処理を開始することができます

結果を受け取る場合は chain にシンボルで引数を与えその名前のシンボルに結果が格納されるようにします

フローを開始する場合は Flow.new します
最後まで成功した場合は最後の chain で結果を受け取ることができます

require 'waterfall'

class MyClass
  include Waterfall

  def initialize(value)
    @value = value
  end

  def call
    self.chain(:value) do
      @value ** 2
    end
  end
end

f = Flow.new
ret = f.chain(value1: :value) do
  MyClass.new(2)
end
.chain(value2: :value) do
  MyClass.new(3)
end
.chain do |result|
  puts result.value1
  puts result.value2
end

失敗した場合

次に途中でフローが失敗した場合のサンプルです
フローが失敗した場合には dam に流すことでフローを失敗にすることができます
前の chain の結果がどうだったか判定するのに when_falsy を使います
このブロック内の結果が false だっと場合に .dam が実行されてエラー処理に流れます

Flow.new 側では dam に流れた処理を受け取るために .on_dam を実装します
dam で返却された内容とエラーの箇所がブロックの引数として渡ってきます

require 'waterfall'

class MyClass
  include Waterfall

  def initialize(value)
    @value = value
  end

  def call
    chain do
      begin
        @value / 0
      rescue ZeroDivisionError
        @value = false
      end
    end
    when_falsy do
      @value
    end
      .dam do
        "ZeroDivisionError"
      end
    chain(:value) do
      @value ** 2
    end
  end
end

f = Flow.new
ret = f.chain(value1: :value) do
  MyClass.new(2)
end
.chain(value2: :value) do
  MyClass.new(3)
end
.chain do |result|
  puts "SUCCESS"
  puts result.value1
  puts result.value2
end
.on_dam do |error, context| 
  puts "ERROR"
  puts error
  puts context
end

halt_chain

dam に流したあとに続けて別のエラー処理をしたい場合に使います
例えば以下のようにエラーになって dam に流れた場合は @value にエラーコード的な値を設定することができます

require 'waterfall'

class MyClass
  include Waterfall

  def initialize(value)
    @value = value
  end

  def call
    chain do
      begin
        @value / 0
      rescue ZeroDivisionError
        @value = false
      end
    end
    .when_falsy do
      @value
    end
      .dam do
        "ZeroDivisionError"
      end
      .halt_chain do |outflow, error_pool, error_pool_context|
        outflow.value = 9999 if error_pool
      end
    chain(:value) do
      @value ** 2
    end
  end
end

f = Flow.new
ret = f.chain(value1: :value) do
  MyClass.new(2)
end
.chain(value2: :value) do
  MyClass.new(3)
end
.chain do |result|
  puts "SUCCESS"
  puts result.value1
  puts result.value2
end
.on_dam do |error, error_pool_context, outflow, waterfall|
  puts "ERROR"
  p waterfall.outflow.value1
end

最後に

Ruby の waterfall を使ってフロー処理を実現してみました
今回は丁寧に do ... end で書きましたが見やすくするために {} で 1 行ブロックで書いてもいいかもしれません
フロー間の引数のやり取りが少し面倒なのでそれに慣れないと使いこなすのは難しいかもしれません
Sidekiq などを使ってもフロー処理は実現できますがブローカが必要だったりするので簡単なフロー処理を実現したいのであれば waterfall でもいいかもしれません

2020年9月16日水曜日

Sinatra の Rack::Protection で有効になっているパストラバーサルの機能を無効にしてどのような影響があるのか確認してみた

概要

パストラバーサル は脆弱性の一つでリクエストに相対パスや絶対パスを入れることで意図しないファイルにアクセスされてしまう脆弱性です
Sinatra にもかつでパストラバーサルの脆弱性があり現在では対応されています
今回はあえてパストラバーサルの保護機能を無効化しどのような影響があるのか確認してみました

環境

  • macOS 10.15.6
  • Ruby 2.7.1p83
    • sinatra 2.0.7

Rack::Protection::PathTraversal の効果

試してみた感じ request.path_info を見て %2e のドットの URL エンコードした文字をデコードするかしないかくらいの効果しかなさそうです
以下で例を紹介します

パストラバーサルを無効にしたコード

とりあえず動作確認してみました
Sinatra でパストラバーサルを無効にする方法は set :protection, :except => :path_traversal を追記するだけです

  • vim config.ru
require './app'
run App
  • vim app.rb
require 'sinatra/base'

class App < Sinatra::Base
  set :protection, except: :path_traversal

  get '/test' do
    env['PATH_INFO']
  end
end

set の部分をコメントアウト/インして動作確認してみます

テストコード/動作確認

以下のような curl を実行するスクリプトを作成しました

curl "localhost:9292/test"
curl "localhost:9292/test/../test"
curl "localhost:9292/test/%2e%2e/test"

これを使ってパストラバーサルが有効なときと無効なときで動作確認してみました
結果は以下の通りでした

  • パストラバーサル有効・・・すべて 200 でアクセスできる
  • パストラバーサル無効・・・1, 2 番目が 200 でアクセスできる、3 番目は 404 になる

という感じでした
要するにパストラバーサルが無効になっている場合は %2e -> . に変換していないことがわかります
また . を直接指定した場合はパストラバーサルの有効/無効に関わらず畳み込みをしてくれるようです

そもそもパストラバーサルに脆弱なサンプルコード

ちょっと話は逸れますがそもそもパストラバーサルに脆弱なコードを紹介します
パストラバーサルを無効にし脆弱性のあるコードをあえて作成します
指定されたファイルを読み込んで返却するようなアプリケーションになっています

  • vim app.rb
require 'sinatra/base'

class App < Sinatra::Base
  set :protection, :except => :path_traversal

  get '/test' do
    File.read(params["path"])
  end
end
  • vim config.ru
require './app'
run App

脆弱性の確認

例えばアプリケーションのカレントディレクトリにある hoge.txt というファイルを読み込む場合には以下のようにリクエストすると思います

  • curl "localhost:9292/test?path=hoge.txt"

app.rb と同じディレクトリに hoge.txt があれば問題なく読み込みレスポンスとしてファイルの内容を返却します
ただこのコードには脆弱性がありフルパスなどを入れるとアプリケーションの上位のファイルも簡単に参照できてしまいます

  • curl "localhost:9292/test?path=/etc/hosts"
  • curl "localhost:9292/test/../open?path=../../../../../etc/hosts"

これで hosts ファイルの内容を簡単に確認できてしまいます
簡単なサンプルですがこれがパストラバーサルに脆弱なサンプルコードになります

Sinatra の Rack::Protection::PathTraversal では上記のようなコードは対処できない

もうここまでくればわかると思いますが params などのリクエストを使ってファイルなどを参照する場合は Rack::Protection::PathTraversal が使えないことがわかると思います
というか Rack::Protection::PathTraversal はほぼ何もしていない感じなのでパストラバーサル対策は自信のアプリケーション側でしっかり行う必要があります

ちなみに Sinatra の Rack::Protection::PathTraversal で対処できそうな脆弱なコード

こんな感じのコードをもし書いていたとすれば path_traversal を有効にするだけで対応はできそうです

require 'sinatra/base'

class App < Sinatra::Base
  set :protection, except: :path_traversal

  get '/*' do
    File.read(env['PATH_INFO'].gsub('%2e', '.'))
  end
end
  • curl "localhost:9292/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/hosts"

そもそもデフォルトの Webrick で動作させていると上記のリクエストは Bad Request で弾かれます
thin を使った場合は弾かれないので path_traversal を使うことが有効ですがそもそもこんなコードは書かないかなと思います

最後に

Siantra のパストラバーサル防御機能の動作確認をしてみました
デフォルトは有効なので気にする必要はないのですが Rack::Protection::PathTraversal だけではパストラバーサルの脆弱性を完全に防げているわけではないので注意が必要だと言うことがわかりました

参考サイト

2020年9月15日火曜日

Ruby から docker-compose を操作してみた

概要

docker-compose.yml を作成して CLI からではなく Ruby からコンテナの作成などを行ってみました
ただ使った gem のバージョンが古くあまり使えなさそうな印象です

環境

  • macOS 10.15.6
  • docker 19.03.12
  • Ruby 2.7.1p83
    • docker-compose-api 1.1.8

docker-compose.yml の作成

何でも OK です
今回は nginx が立ち上がるだけの YAML ファイルを作成しました

version: '3'
services:
  web:
    image: nginx
    ports:
      - 80:80

Ruby からコンテナを立ち上げてみる

では Ruby から docker-compose.yml を使ってコンテンを立ち上げてみます

  • vim app.rb
require 'docker-compose'

compose = DockerCompose.load('./docker-compose.yml')
ret = compose.start
puts ret

これで nginx のコンテナが立ち上がります

コンテナの一覧を確認する

containers を使います
ハッシュで返ってきます

require 'docker-compose'

compose = DockerCompose.load('./docker-compose.yml')
ret = compose.containers
pp ret

コンテナを停止する

stop を使うだけです
ラベルと名前でコンテナを指定することができます

require 'docker-compose'

compose = DockerCompose.load('./docker-compose.yml')
# compose.start
ret = compose.stop
puts ret

別プロセスになるとなぜかコンテナに振られるインデックスが変わるのでその場合は start した同じプロセス内で停止しましょう

Tips: バージョンの表示など

puts DockerCompose.version
puts DockerCompose.docker_client

Tip: AttributeError: ‘ComposeVersion’ object has no attribute ‘version’

なぜか Ruby で実行すると CLI 側で docker-compose を実行できなくなりました
おそらくバージョンの違いや Ruby 側だと処理が足りていないためだと思います

AttributeError: 'ComposeVersion' object has no attribute 'version'
[2692] Failed to execute script docker-compose

最後に

既存の docker-compose.yml があればそれを簡単に Ruby から呼び出すことはできそうです

ただ残念ですが現在は gem のバージョンが古くメンテナンスもされていないようです
docker-compose v3 に対応していませんでした
また docker-compose.yml なしで docker-compose 的なことをするのはこの gem ではできないようです

2020年9月14日月曜日

Flask-Marshmallow でデータベースに格納されている配列の文字列データを配列にする方法

概要

データベースに文字列で配列の情報が格納されている場合にレスポンスには文字列ではなく配列として返却したい場合があります
Flask-Marshmallow で文字列から配列にコンバートして返却する方法を紹介します

環境

  • macOS 10.15.5
  • MySQL 8.0.19
  • Python 3.8.3
    • Flask-Migrate 2.5.3
    • Flask-SQLAlchemy 2.5.3
    • Flask-Marshmallow 0.13.0

やり方

ma.Function とラムダ式を使って実現できます
引数にレコードのオブジェクトが入ってくるのでそこから指定のカラムの情報を抜き出して処理します

class UserCustomSchema(ma.Schema):
    my_id = ma.Integer(attribute="id")
    my_name = ma.Function(lambda obj: json.loads(obj.name))

例えばデータベースの name カラムのフィールドには ["hawk","snowlog"] という文字列情報が入っている場合は json.loads を使いましょう
単純に文字列で CSV が入っている場合は .split などを使いましょう

2020年9月11日金曜日

grape-swagger で Swagger ドキュメントを自動生成する方法

概要

前回は grape を使って RESTful API を構築してみました
grape には grape-swagger がありこれを使うと swagger のドキュメントを自動生成してくれます
今回は導入方法と使い方のポイントを紹介します
また説明は前回のアプリをそのまま使います

環境

  • macOS 10.15.6
  • Ruby 2.7.1p83
    • grape 1.4.0
    • grape-swagger 1.3.0

とりあえず使ってみる

既存の grape アプリがあれば簡単に導入できます
まずは gem を追加しましょう

  • vim Gemfile
gem "grape"
gem "grape-swagger"

そしてアプリに add_swagger_documentation を追加するだけです

  • vim app.rb
require 'grape'
require 'grape-swagger'

module Test
  class API < Grape::API
    format :json
    prefix :api
    articles = []
    @username = ""

    resource :blog do
      desc 'Return an article.'
      get :article do
        articles
      end

      desc 'Create new article.'
      params do
        requires :title, type: String, desc: 'An article title'
        requires :body, type: String, desc: 'An article body'
      end
      post do
        articles.append({
          title: params[:title],
          body: params[:body]
        })
      end

      desc 'Delete an article.'
      params do
        requires :index, type: Integer, desc: 'An article index'
      end
      delete ':index' do
        error!("Does not found index", 404) if articles[params[:index]].nil?
        articles.delete_at(params[:index])
      end

      desc 'Update an article.'
      params do
        requires :index, type: Integer, desc: 'An article index'
        requires :title, type: String, desc: 'An article title'
        requires :body, type: String, desc: 'An article body'
      end
      put ':index' do
        error!("Does not found index", 404) if articles[params[:index]].nil?
        articles[params[:index]] = {
          title: params[:title],
          body: params[:body]
        }
      end
    end

    add_swagger_documentation # New!!

  end
end

あとは普通にアプリを起動しましょう

  • bundle exec rackup config.ru

すると localhost:9292/api/swagger_doc にアクセスするだけで swagger の JSON が返っくることが確認できると思います

CORS に対応する

SwaggerUI を使って確認する場合に CORS に対応しておかないと JSON にアクセスできません
わざわざファイルに落として読み込ませるのも面倒なので CORS に対応しておきます

  • vim Gemfile
gem "rack-cors"
gem "grape"
gem "grape-swagger"
  • vim config.ru
require 'rack/cors'

use Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [ :get, :post, :put, :delete, :options ]
  end
end

require './app'

Test::API.compile!
run Test::API

これで SwaggerUI から確認してみましょう

SwaggerUI から確認してみる

grape-swagger は単純に swagger 定義の JSON を出力してくれるだけで SwaggerUI の機能はありません
なので確認する場合は別途 SwaggerUI を使いましょう

  • docker run -d -p 8080:8080 swaggerapi/swagger-ui

あとは localhost:8080 にアクセスして grape-swagger で出力される JSON の URL を入力すれば OK です

こんな感じで API の定義を確認することができます

レスポンスのサンプルを追加してみる

このままでも十分といえば十分ですがレスポンスにボディがある場合にそれを表示することができません
そんな場合は grape-entity を使います

  • vim Gemfile
gem "rack-cors"
gem "grape"
gem "grape-swagger"
gem "grape-entity"
gem "grape-swagger-entity"

まずは Entity を追加します
必ず Grape::Entity クラスを継承して作成します
expose を使うことでフィールドとして登録することができます

  • vim app.rb
require 'grape'
require 'grape-swagger'
require 'grape-entity'
require 'grape-swagger-entity'

module Test
  module Entities
    class Article < Grape::Entity
      expose :title, documentation: { type: 'string', desc: 'Blog article title.', required: true }
      expose :body, documentation: { type: 'string', desc: 'Blog article body', required: true }
    end
  end
  # 省略...
end

次に定義した Entity を登録します
これも簡単で既存の grape アプリで定義した desc の引数に entity: ハッシュとして追加するだけです

  • vim app.rb
desc 'Create new article.',
  entity: Test::Entities::Article
params do
  requires :title, type: String, desc: 'An article title'
  requires :body, type: String, desc: 'An article body'
end
post do
  articles.append({
    title: params[:title],
    body: params[:body]
  })
end

これで OK です
あとはアプリを起動して localhost:9292/api/swagger_doc を確認するとちゃんとレスポンスサンプルとして含まれていることが確認できると思います

パラメータとしても使える

既存の grape アプリの params の代替にもなります
desc の引数のハッシュに params: として追加するだけで OK です

desc 'Create new article.',
  entity: Test::Entities::Article,
  params: Test::Entities::Article.documentation
post do
  articles.append({
    title: params[:title],
    body: params[:body]
  })
end

こちらのほうが管理的にもきれいになるので良いかなと思います

全体のソースコード

一応紹介しておきます

  • app.rb
require 'grape'
require 'grape-swagger'
require 'grape-entity'
require 'grape-swagger-entity'

module Test
  module Entities
    class Article < Grape::Entity
      expose :title, documentation: { type: 'string', desc: 'Blog article title.', required: true }
      expose :body, documentation: { type: 'string', desc: 'Blog article body', required: true }
    end
  end

  class API < Grape::API
    format :json
    prefix :api
    articles = []
    @username = ""

    resource :blog do
      desc 'Return an article.'
      get :article do
        articles
      end

      desc 'Create new article.',
        entity: Test::Entities::Article,
        params: Test::Entities::Article.documentation
      post do
        articles.append({
          title: params[:title],
          body: params[:body]
        })
      end

      desc 'Delete an article.'
      params do
        requires :index, type: Integer, desc: 'An article index'
      end
      delete ':index' do
        error!("Does not found index", 404) if articles[params[:index]].nil?
        articles.delete_at(params[:index])
      end

      desc 'Update an article.'
      params do
        requires :index, type: Integer, desc: 'An article index'
        requires :title, type: String, desc: 'An article title'
        requires :body, type: String, desc: 'An article body'
      end
      put ':index' do
        error!("Does not found index", 404) if articles[params[:index]].nil?
        articles[params[:index]] = {
          title: params[:title],
          body: params[:body]
        }
      end
    end

    add_swagger_documentation

  end
end
  • config.ru
require 'rack/cors'

use Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [ :get, :post, :put, :delete, :options ]
  end
end

require './app'

Test::API.compile!
run Test::API
  • vim Gemfile
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rack-cors"
gem "grape"
gem "grape-swagger"
gem "grape-entity"
gem "grape-swagger-entity"

最後に

grape-swagger を使って grape のアプリから swagger のドキュメントを自動生成する方法を紹介しました
手動で swagger ファイルを書くよりかは重複を避けられたり管理が楽になので良いかなと思います

定義した Entity をレスポンスのサンプルにしたり実際に API で扱うパラメータにする方法も紹介しました
今回は調査しなかったのですが作成した Entity をデータベースのモデルにしてオブジェクトを生成しフィールドにアクセスすることができれば更に便利な使い方ができるかなと思います

参考サイト

2020年9月10日木曜日

certbot の standalone プラグインで証明書を取得する

概要

DNS を使った manual プラグインでの取得方法を過去に紹介しました
今回は standalone プラグインを使った方法を紹介します
standalone プラグインは DNS ではなく HTTP/80 ポートを使って証明書の取得を行います

環境

  • Ubuntu18.04
  • certbot 1.7.0

事前準備: A レコードを登録する

取得するドメイン or サブドメインの A レコードを DNS に登録しましょう
standalone プラグインは証明書取得時に指定したドメインの 80 番ポートにアクセスするため DNS で引けることが必須条件になります

certbot-auto インストール

  • cd /usr/local/bin
  • sudo git clone https://github.com/certbot/certbot.git
  • sudo ln -s /usr/local/bin/certbot/certbot-auto /usr/local/bin/

HTTP/80 ポートを停止

Nginx なり Apache Httpd なりですでに 80 番ポートを LISTEN している場合は停止しましょう
docker の場合はコンテナを停止しておきます

  • docker-compose stop proxy

証明書取得

では証明書を取得します
今回はインタラクティブモードで取得します

  • sudo certbot-auto certonly --standalone -t

メールアドレスと取得する証明書のドメインを入力します
問題なければ証明書が取得できます
取得後に証明書が配置されるパスは /etc/letsencrypt/live/domain.name になります
fullchain.pem や privkey.pem がシンボリックされているのでこれを使います

トラブルシュート

主な原因としては以下が考えられると思います

  • Let’sEncrypt から指定のドメインの 80 番ポートにアクセスできない
    • ファイアウォールの設定を見直しましょう
  • A レコードが引けない
    • 少し待ってから再度実施しましょう
    • dig コマンドなどで A レコードが引けるか確認しましょう

HTTP/80 ポートの再起動

止めていた Nginx や httpd を再起動しましょう

  • docker-compose start proxy

もし証明書のパスが変わって docker-compose.yml などを書き換えた場合はコンテナを再作成しましょう

  • docker-compose rm proxy
  • docker-compose create proxy
  • docker-compose start proxy

Tips: 古い証明書の削除

今回は standalone プラグインを使って新規で証明書を作成しました
もし manual プラグインを使って別環境で同一ドメインの証明書を取得していた場合は manual プラグイン側の証明書はもう使わないので削除しましょう

  • sudo certbot-auto delete --cert-name domain.name

Let’sEncrypt はすでに同一ドメインで証明書を発行していても別プラグインを使えば取得できてしまいます
なので一時的に同一ドメインの複数証明書ができてしまいます
管理用のメールアドレスなども同一のものが使えてしまうので混乱しないように使わなくなったプラグイン側の証明書は削除しておきましょう

このあとは自動更新をする

基本は renew コマンドで OK です

  • sudo certbot-auto renew

証明書を新規で作成したばかりだと証明書の更新の必要がないというエラーになるので更新できません
また standalone プラグインを使った更新は結局 80 番ポートの停止が必要なので renew 前に同じように停止し更新後に再起動が必要になります

自動更新の方法はまた別記事でまとめる予定ですが --pre-hook と --post-hook オプションを使って自動更新することになると思います

最後に

certbot の standalone プラグインを使って証明書の取得を行ってみました
一瞬 http が止まるので 80 番ポートで動作しているサービスの場合は前に紹介した manual プラグインや webroot プラグインを使ったほうが良いかなと思います
ただ自動更新まで考えるとやはり一番簡単なのは standalone プラグインなんじゃないかなと思います

参考サイト

2020年9月9日水曜日

Ruby の Paint を使ってコンソール表示をカラフルにする

概要

Paint は ANSI カラーコードを使った文字列を簡単に生成することができるライブラリです
コンソール表示をカラフルにしてロギングなどを見やすくすることができます
今回は簡単な使い方を紹介します

環境

  • macOS 10.15.6
  • Ruby 2.7.1p83
    • Paint 2.2.0

インストール

  • bundle init
  • vim Gemfile
gem "paint"
  • bundle install

赤色にする

まずは単純に文字を赤色にしてみます

require 'paint'

painted_str = Paint['hoge', :red]
puts painted_str

Paint.[] というクラスメソッドを使うだけで指定の文字を ANSI カラーコード付きの文字に変換することができます
返り値が String クラスのオブジェクトなので単純に puts すれば色付き文字が表示されます

背景色を設定する

引数を 1 つ増やすだけで OK です
文字色を赤、背景をシアンにする場合は以下のようにします

require 'paint'

puts Paint['hoge', :red, :cyan]

色を反転する

文字色と背景色を反転するには :inverse を指定します

require 'paint'

puts Paint['hoge', :red, :cyan, :inverse]

HTML カラーコードを使う

デフォルトの設定であれば HTML のカラーコードをを指定することもできます

require 'paint'

puts Paint['hoge', "#dc143c"]

:inverse もそのまま使えます

puts Paint['hoge', "#dc143c", :inverse]

モードを設定する

Paint にはモードがありデフォルトは 16777215 色使えます
モードを確認するには Paint.mode を参照します
また設定する場合は 256 or 16 or 8 or 0 を設定するだけです

require 'paint'

puts Paint.mode
Paint.mode = 16
puts Paint['hoge', "#dc143c"] # 強制的に赤色になる

環境によってはフルカラー使えない環境もあるので一度使える色の種類を確認すると良いと思います

おまけ: logger をカスタムして使う

例えば Ruby 標準の logger を Paint でカスタムして使うこともできます
コンソールにだけ表示する分にはこれだけで見やすくなると思います

require 'paint'
require 'logger'

class PaintedLogger < Logger
  def info(str)
    super(Paint[str, :red])
  end
end

plogger = PaintedLogger.new(STDOUT)
plogger.info("hoge")

最後に

Ruby の Paint を使って簡単に ANSI カラーコードを使う方法を紹介しました
今回は紹介していませんが :bright (太字)、Paint.random (ランダムカラー)、Paint.unpaint (カラーコードの削除) など他にも便利な機能が多くあります
コンソールに表示するロガーは Paint してファイルに出力するログはプレーンテキストにするなどすると使い勝手も良くなるかなと思います

ANSI カラーコードを直接記載するとごちゃごちゃになるので Ruby が使える環境であれば /etc/motd などでカラフルなログインメッセージを表示したりするのに使えると思います