yujiro's blog

webエンジニアをしています。

【Rails】 scope は 常に ActiveRecord_Relation を返す素晴らしいやつ

Rails で様々な条件で複合検索ができる画面を実装していて、フォームから受け取った値をそのままモデルに渡してコントローラは条件分岐なしにスッキリ実装したかった。 何の気なしに以下のようなコードを書いた。

Post.hoge_method(params[:hoge]).search(params[:word]).another_method....

例えば search の部分は form から渡ってきた値をモデルに渡して値がなければ その部分のクエリを組み立てないでほしかった。

def self.search(w)
  if w.present?
    where('title like :title or description = :description', {title: "%#{w}%", description: "%#{w}%"})
  else
    ...
  end
end

ここで少し思考が停止して、

else のとき、何を返せばいいんだ??となった。

調べてみたら大変素晴らしい記事を見つけた。

【Rails】なぜクラスメソッドよりもスコープを使うべきなのか?

スコープの中で発行されたクエリがnilになる場合は、.allを返すように設計されているので、スコープの返り値を気にせずチェーンを繋げることができます。

そうか。。さっきの部分は

else
    all
end

て書けば良いんだけど scope を使えばその必要すらなくて

scope :search, -> (w) { where('title like :title or description = :description', {title: "%#{w}%", description: "%#{w}%"}) if w.present? } 

だけでよいそう。 scope がnilを返したらall ()を返すようになっているのは全然知りませんでした。

Capybara Poltergeist で TypeError: undefined is not a function (evaluating 'Object.assign〜 というエラーが発生した

今まで poltergeist のオプションに js_errors: false を設定していたんだけど、

とある所で詰まって色々調べるために、true にしたら全く動かなくなった。


テストを実行したら下記のエラーが出力された。

TypeError: undefined is not a function (evaluating 'Object.assign(hoge, fuga)')


すぐ、こちらのぺーじを見つけて

https://github.com/es-shims/es5-shim/blob/master/es5-shim.js

からソース落としてきて

application.js に

//= require es5-shim

を入れたんだけど、またもや同じエラーがおきた。


調べたところ

Object.assign は ES6 の構文らしい。


なので

https://raw.githubusercontent.com/paulmillr/es6-shim/master/es6-shim.js

から同じようにソースを落っことしてきて

//= require es6-shim

としたら動いた。


ちなみに 調べてる過程で見つけたんだけど、apt-get で phantomjs をいれるのはよくないらしい。

下記手順でいれなおした

$ sudo apt-get install npm nodejs nodejs-legacy

$ npm install -g phantomjs

$ phantomjs -v

=> 2.1.1


ちなみに capybara の poltergeist の設定で phantomjs_logger 設定すると console.log したものを吐き出してくれるのでかなり便利だった。

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, { debug: true, js_errors: true, phantomjs_logger: File.open("#{Rails.root}/log/test_phantomjs.log", "a")} )
end

参考

CapybaraでReactをテストしようとしたらエラーになった件

PhantomJS を Ubuntu 16.04 LTS にインストールする

iptables についてのメモ

INPUT, OUTPUT ポリシーについて (httpの場合)

INPUT

  • sport => 他ホストの80番から自分への通信
  • dport => 他ホストから自分の80番への通信

OUTPUT

  • sport => 自分の80番から他ホストへの通信
  • dport => 自分からホストへの80番への通信

チェインとは

それぞれのテーブルの中で、どのタイミングでフィルタリングするかを決めるのがチェインです。例えば受信時についてはINPUT、送信時についてはOUTPUTといった具合です。

INPUT, FORWARD の違いについて

パケットを受信した際、まずパケットの送信先をチェックします。送信先がそのパケットを受信したホストそのものであれば、INPUTチェインに送られて定義されているルールに従って処理され、そのパケットを受け取る場合は適切なサービスに渡されます。送信先が別のホストであればFORWARDチェインに送られ、パケットの通過が許可されれば該当するホストへと転送されます。許可されなかったパケットは、そこで破棄されるというわけです。反対に、そのホストのプログラムによって生成されたパケットはOUTPUTチェインでチェックされ、許可されれば送信先へと送り出されます。

state オプション

  • ESTABLISHED
  • INVALID
  • NEW
  • RELATED

上記4つが指定可能

設定を持続させる iptables-persistent について

iptables の設定を /etc/iptables/rules.v4 に保存

$ sudo service iptables-persistent save

/etc/iptables/rules.v4 にかかれた設定を iptables に反映

$ sudo service iptables-persistent reload

俺史上発展途上のiptables をさらす

最後に自分が作ったスクリプトを晒します。一応DOS対策もしています。(このあたりまた書きます)

普通に考えて下記記事で紹介されているものが最強かと思いますが、

自分で試行錯誤して改良していきたいと思いいます。

俺史上最強のiptablesをさらす

#!/bin/bash

# Ports
SSH=19921
#FTP=20,21
DNS=53
SMTP=25,465,587
POP3=110,995
#IMAP=143,993
HTTP=80,443
#IDENT=113
#NTP=123
#MYSQL=3306
#NET_BIOS=135,137,138,139,445
#DHCP=67,68
DKIM=8891

# Initialize 
iptables -F
iptables -X

# Policy
iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

# 自分自身は許可
iptables -A INPUT -m state --state NEW -s 127.0.0.1 -j ACCEPT

# Dos protection
iptables -N Attacker
iptables -A Attacker -m recent --set --name attacker -j LOG --log-level warning --log-prefix 'Attaker:'
iptables -A Attacker -j DROP

iptables -A INPUT -p tcp -m state --state NEW -m recent --rcheck --seconds 15 --name attacker -j DROP

iptables -A INPUT -m multiport -p tcp --dports ${HTTP} -m state --state NEW -m recent --set --name PreAttacker
iptables -A INPUT -m multiport -p tcp --dports ${HTTP} -m state --state NEW -m recent --update --seconds 1 --hitcount 20 --rttl --name PreAttacker -j Attacker

# セッション確立後のパケット疎通は許可
iptables -A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -A OUTPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT

# SSH
iptables -A INPUT -p tcp --dport ${SSH} -j ACCEPT

# HTTP, HTTPS
iptables -A INPUT -p tcp -m multiport --dports ${HTTP} -j ACCEPT

# SMTP
iptables -A INPUT -p tcp -m multiport --dports ${SMTP} -j ACCEPT

# POP3
iptables -A INPUT -p tcp -m multiport --dports ${POP3} -j ACCEPT

# ICMP 
iptables -A INPUT -p icmp -j ACCEPT # ANY -> SELF

# DKIM
iptables -A INPUT -p tcp --dport ${DKIM} -j ACCEPT

# DNS
iptables -A INPUT -p udp --source-port ${DNS} -j ACCEPT

参考

オプションについての説明

上記の魚拓

state について

iptables で不正アクセスをブロック

コピペから脱出!iptablesの仕組みを理解して環境に合わせた設定をしよう

Ubuntuでiptablesを再起動後にも保持する方法

俺史上最強のiptablesをさらす

iptables -F に気をつけよう

Linuxで作るファイアウォール[パケットフィルタリング設定編] (½)

iptables の ipt_recent で ssh の brute force attack 対策

OSI参照モデル

大量のアクセスを試みる迷惑な接続元を制限する(iptables hashlimit)

TCP/IP 4階層モデルとは?

IPアドレスの基礎知識

Flask で MVCライクな実装

github.com

中身の実装については後で書きたいと思います。

AWS S3で静的ファイルを独自ドメインで配信するまで

前提

  • ドメイン名は sample.com と仮定する。
  • S3に画像ファイルを置いて、image.sample.com というドメインでアクセスできるようにする。
  • S3はそれだけだと遅いので、CDNを利用する(CloudFlare)

手順

  • バケット名を image.sample.com に設定する
  • アクセス許可タブを開いて、バケットポリシーを編集をクリック
  • 以下をペースト
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowPublicRead",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::image.sample.com/*"
        }
    ]
}
  • 静的ウェブサイトホスティングタブをクリック
  • [ウェブサイトのホスティングを有効にする] をクリック
  • インデックスドキュメントをindex.htmlなどに設定(このファイルはなくても大丈夫)

AWS 側は上記で終わり つづいて、ドメイン

  • ドメインサービスの管理画面からDNSを CouldFlareのものにする(あとででもよい)
  • CloudFlare 側で image.sample.com というドメインをCNAME のname に設定、hostname に AWS で発行されたエンドポイントを入力 image.sample.com.s3-website-us-east-1.amazonaws.com みたいなやつ

参考

お名前.comのドメインをAWSで使用する4つの方法 Amazon S3でSSL対応の静的ウェブサイトを公開する

Effective Ruby 読書メモ

セッターメソッド

インスタンスメソッド内でセッターメソッドを使う場合はselfをつけないと 単なるローカル変数の定義になってしまう。 ただし、selfを使うのはその場合のみにしよう

class Hoge

  attr_accessor :hoge, :tako

  def initialize
    hoge = 'a' # Hoge.new.hoge => nil
    self.hoge = 'a' # Hoge.new.hoge => a
  end

end

p Hoge.new.hoge

定数参照におけるレキシカルスコープ(スコープ)と 継承階層

Ruby は定数を見つけるために レキシカルスコープと継承階層を利用する

module SuperDumbCrypto
  KEY = "password123"

  class Encrypt
    def initialize (key=KEY)
      # ...
    end
  end
end

上記コードでは KEY という定数と Encryptクラス(定数)が同じスコープ(SuperDumbCrypto)で定義されているため、KEY定数をみつけることができる。

イメージ的には

SuperDumbCrypto
    => KEY定義
    => Encrypt
        => KEY 使用可能

って感じか。 javascript に似てるとおもいます。

var a = 'hoge';
function fuga(){
  console.log(a);
}

↑と同じようなことだと思います

module SuperDumbCrypto
  KEY = "password123"
end

class SuperDumbCrypto::Encrypt
  def initialize (key=KEY) # raises NameError
    # ...
  end
end

こっちはだめ。

SuperDumbCrypto
    => KEY
SuperDumbCrypto::Encrypt
    => KEY 使用エラー

っていうスコープになっているんだと思う。

module SuperDumbCrypto
  KEY = "password123"
end

# <<: qualified-crypto
class SuperDumbCrypto::Encrypt
  def initialize (key=SuperDumbCrypto::KEY)
    # ...
  end
end

上記にすればOK

Hash を使うときは Struct にしたほうがよくないかと考えてみる

Reading = Struct.new(:date, :high, :low) do
  def mean
    (high + low) / 2 
  end
end

Reading クラスを定数のようにあつかう

CSV.foreach(file_name, headers: true) do |row|
  @readings << Reading.new(date: Date.parse(row[2]),
                           high: row[10].to_f,
                            low: row[11].to_f)
end

こうすれば

@readings.first.mean

みたいな感じでメソッドつかえる。これは便利。

続きも頑張って書きます

メールサーバー迷惑メール対策

SPF

下記をTXTレコードに追加する。

v=spf1 +ip4:[ip_address] include:hoge.com ~all

※include をかかないと PASS しなかった

TXT レコード確認コマンド

$ host -t txt hoge.com

DKIM

TXT レコードに NAMEは以下で

mail._domainkey.hoge.com

※ [selector]._domainkey.[domainname] で設定。ここが重要

VALUE は 以下コマンドを実行し、生成されたものをいれる

$ sudo opendkim-genkey -s mail -d hoge.com

$ sudo vi /etc/opendkim

Domain                hoge.com
KeyFile                 /etc/postfix/dkim.key
Selector               mail
SOCKET               inet:8891@localhost

TXT レコード確認コマンド

$ host -t txt mail._domainkey.hoge.com

参考

http://qiita.com/geeorgey/items/450b498d2b98b6b868a8

チェッカー

http://www.mail-tester.com/

https://mxtoolbox.com/SuperTool.aspx?action=spf%3apreview.movie-ec.com&run=toolpage#