読者です 読者をやめる 読者になる 読者になる

あすたぴのブログ

astap(あすたぴ)のブログ

プログラミングを魔法だと思っていた

昔、すごいプログラムだったりすごいシステムを魔法のように感じていた。 自分には全くどうやっているのか想像がつかなかった。 今からすると、なぜ魔法だと思っていたのかなっと思って、考えてみた。

フレームワークなんて、よくわからずにアクションを書いたら、 なぜかテンプレートに移動して、テンプレートを書くとブラウザにHTMLが表示された。 特定のURLを叩いたときに、特定のActionが呼ばれる理由もわからない。 そこにあるのは命名ルールだけだった。 特に疑問も持たずにルールに沿ってプログラムを書いて、 なんとなく画面が表示されてた。 プログラミングは辛かった。 もともとあるプログラムをコピペしていじって、なんとなく作っていた。 新しいものは作れない。自分にはわからない魔法だから。

ここまで読んでわかったヒトもいると思うけど、おれが触っていたのはJavaだった。 正直いまでもJavaはわからない。あれ以降やってないから。 Javaが苦手だったのは、外部ライブラリのコードが読みにくいから。 jarに固められた状態で手元に渡ってきて、その状態でなんとなく使う。 ドキュメントもあんまりないイメージだった。 jarという圧縮ファイルのブラックボックスだった。 たまに、逆コンパイルでコードを読んだりもしたけど、全然楽しくなかった。 なんで、わざわざ固められているものを戻して、解読みたいなことしてんだろって。 たぶん、これは自分の知識不足なだけで、きっと、元ソースもどっかにあったんだろうし、きっと、ドキュメントも探せばあったんだと思う。Javadocが。

そういう経緯があって、phpを触ったときはコードを奥深くまで読み進められるのが嬉しかった。 この頃から少しずつ、魔法ではなくなってきたかもしれない。

あるときちゃんと魔法に向き合おうと思ってコンピュータサイエンスとかhttpの仕様とか学ぶようになった。 そこからは、いろいろなことがクリアになったと思う。 蓋を開けてみれば、魔法だと思っていたことが実は泥臭いコードの塊だったり、 簡単なプログラムをうまく組み合わせることで魔法のように見えていただけだったりした。

魔法が魔法ではなかったと気づいた時から、仕組みがわからないものは仕組みを理解するように心がけるようになった。 そうした事を続けていると面白いことに、初めて触るライブラリやツールでも、 こういう機能がたぶんあるはず。こういう風に使うといいはず。 内部的にはたぶんこうなってるんだろうな。 みたいな事がなんとなくわかるようになっていた。 今はプログラミングはすごく楽しい。

自分のように、わけもわからずコードを書いている人がこの世にはまだいっぱいいると思う。 そういう人たちにも、プログラミングが楽しめるようになってほしい。

AWSのIAMベストプラクティスをちゃんとやる(見る)

IAM のベストプラクティスをちゃんと理解しようというお話。

目的

このエントリでは、細かいやり方は書かない。 (ドキュメントを見ればいいし、コンソールを開けばわかる) こういうことをしたほうがいいんだよ。という内容を把握し、いざ、必要な時がきたときに知ってるいる状態にすることが目的

対象読者

  • AWSはEC2とかたてたりするけど、*1ルートアカウントでコンソールをいじってる人
  • EC2にIAMインスタンスプロファイルのロールを付けるとは??な人
  • 権限とか難しいから、フルアクセス付けてるよ?な人

IAMとは?

AWS Identity and Access Management (IAM) の略 アイアムって読んでます。(正しいかは知らない)

IAMを使うと、どんな事ができるのか

  • ルートアカウント以外にアカウントを作成できる。
    • 会社のAWSアカウントを複数人で触る際に、個人個人にアカウントを発行する。
    • 外部(CI等)から、AWSにアクセスする必要がある際に使うアカウントを発行する。
  • 作成したアカウントに対して、*2リソースごとにアクセスを制限できる

ルートアカウントを共有しない。 フルアクセス権限を全員に渡さない。 誰が何のリソースをどこまで操作できるか。を制限しよう。 というのがIAMを使う理由の認識です。

やること

AWS アカウント(ルート)のアクセスキーをロックする

ルートアカウントはすべての権限があるため、 ルートアカウントの権限を用いて、AWSコマンド等を使用すると、請求情報まで取得できる。 見られて困るものは、見れないようにしましょうね。という話し

  • アクセスキーを持ってるなら削除する
  • IAMで、管理権限を持つユーザーを作成する。
  • ルートアカウントのパスワードは強度の高いものを設定しよう。
  • ルートアカウントにMFAを設定する。

個々の IAM ユーザーを作成する

AWSにアクセスする必要がある場合は、IAMでユーザーを作成する。 IAMユーザーのアクセス許可は、いつでも、有効、無効にすることができる。 ※ルートアカウントは他人に絶対に教えてはいけない。 たとえ、個々のIAMユーザーに管理権限を付与するとしても、 ルートアカウントでない限りはアクセスを制限できるので、 必ず、IAMユーザーを都度、作る。

AWS 定義のポリシーを使用して可能な限り権限を割り当てる

ポリシーとは AWSではアクセス権限を制御するのにポリシーという定義を利用する。

ポリシー例

{
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action": [
            "aws-portal:*Billing",
            "aws-portal:*Usage",
            "aws-portal:*PaymentMethods",
            "budgets:ViewBudget",
            "budgets:ModifyBudget"
        ],
        "Resource": "*"
    }]

}

AWS内でデフォルトで定義されているポリシーを使って可能な限り、権限を割り当てる。 メリットとしては、簡単。 デメリットとしては、権限の単位がざっくりしている。

例 - AdministratorAccess - フルアクセス権限 - AWSCloudTrailReadOnlyAccess - ClourTrailの読み取り権限

職務によって、ポリシーがAWSで定義されている。 職務機能の AWS 管理ポリシー

IAM ユーザーへのアクセス許可を割り当てるためにグループを使います。

ポリシーをIAMユーザー一人ひとりに付けるのは面倒だし、 複数人いても大体同じ権限になる。という場合に、グループを使う。

グループの作成

ここらへんで、単語が増えてきて混乱してきたと思うので、 以下にユーザ、ポリシー、グループの関係を整理。

  1. グループ作成
  2. グループにポリシーをアタッチ(付与】
  3. ユーザーをグループに追加

ポリシーはユーザーに直接アタッチも可能。

最小限の特権を認める。

まずは、最小限のアクセス(特定サービスへの権限、リードオンリー)を割当て、 必要になった際に権限を解放していくようにしよう。という話し

Access Advisorを使う アクセスアドバイザーは、コンソールでIAMユーザーを見る場合にタブの1つである。 該当ユーザーが、権限が付与されているサービスにいつアクセスしたかがわかる。 この項目を参照し、1度もアクセスをしていないサービスがあれば、 その権限はそのユーザーには不要な可能性があるため、 権限の見直しに使える。

ユーザーのために強度の高いパスワードポリシーを設定する。

IAM ユーザー用のアカウントパスワードポリシーの設定

IAMユーザーのパスワードに、パスワードポリシーを設定できる

特権ユーザーに対して、MFA を有効化する。

AWS での多要素認証 (MFA) の使用 ルートアカウント同様に、重要なリソースへの権限を持っているユーザーにはMFAを設定しようという話し。 例えば、(どのサービスでも)削除権限を持ってる人とか。

Amazon EC2 インスタンスで作動するアプリケーションに対し、ロールを使用する。

EC2上から別AWSサービスにアクセスすることはよくある。 そのアクセス権限は適切に定義しよう。という話し

ロールとは、AWSのリソースにアクセスできる権限の役割の定義

また新しい言葉が出てきたので、整理する。

  • IAM ポリシー
    • AWS上で扱う権限の一番小さい単位
  • IAM ユーザー
    • AWSにアクセスするユーザーの単位
    • グループに含めたり、ポリシーをアタッチして権限を付与する
  • IAM グループ
    • 複数のポリシーをまとめたもの
    • 複数のユーザーに同一の権限を与えるために使う
  • IAM ロール
    • 複数のポリシーをまとめたもの
    • ロールを付与したものに権限を委任することができる。
    • 主にはEC2インスタンスへ権限を付与する場合に使う
    • AWSアカウントのIAMユーザーにロールを付与することもできる。

認証情報を共有するのではなく、ロールを使って委託する。

さっきのロールの使い方での他AWSアカウントへは、アクセスキーを共有するんじゃなくて、 ロールを使おうね。という話し

認証情報を定期的にローテーションする。

定期的に認証情報を更新しようという話し

不要な認証情報の削除

使われていない認証情報があったら消そう。という話し

認証情報レポートや、awsコマンドで最後に使用した日付とかがわかるので、 それを見て、消す対象を精査できる。

追加セキュリティに対するポリシー条件を使用する。

ポリシーに対して、IPアドレスの制限が時間の制限を行うことができるので、 可能な限り、制限したほうがいいよ。という話し

AWS アカウントのアクティビティの監視

  • cloudfront
  • cloudtrail
  • cloudwatch
  • aws config
  • S3

の、アクセスログを利用して、不正なアクセス、利用と監視しようという話し

まとめ

全部やるのは過剰なのかな?という気もしますが、 そこは自分たちのやり方に合わせて、うまくやっていくのがいいのかなと思います。

*1:ルートアカウントとは、メールアドレス、パスワードでログインをするユーザーのこと

*2:AWSでは、ARNごとに、1リソースとして扱う。

がんばるための行動指針

本エントリは「行動指針 Advent Calendar 2016」の16日目の記事です。

キリがいいので、3つ書きます。

がんばらない

残業したり、がんばったり、根性論が嫌いなので、極力がんばらない為にどうするかをがんばります。 嫌いだと言いましたが、詳しく言うと、"出来ない"です。 体力がなさ過ぎて、がんばることが出来ないから、手段、ツール、知識、テクニックを用いて、がんばらないようにがんばります。 がんばれる人は本当にすごいと思います。おれはそれがしたくでも出来ないです。 当たり前ですが、あらゆる事を駆使して効率的に長時間がんばれたらそれが最強です。

これは、仕事に限らない話しです。 ゲームとかもそうですが、最近だと、効率を求めずにだらだらやる楽しみ。というのもわかってきました。 エンジョイ勢ってやつですね。

がんばらない2

努力ができることは才能だと思います。 周りがあの人は努力家だ。と思っていても、本人は努力だとは思っていないことが多いと思います。

自然にできること。がんばらなくてもできること。が自分に合ってることだと思います。 なので、今自分、がんばってるな。って思ったら、続けるか考えます。 がんばらない程度に続けるか、違うことに時間を使うか。 これもなんでもそうです。 仕事、人付き合い、ゲーム etc・・

物事の背景、理由、先にあることを考える。

これは仕事だけに限定しておきます。 我々エンジニアの仕事は、コードを書いて何かを作ること。だけではないです。 何のためにこの機能を実装するのか。を考えたり、 この機能を実装したあとで、何を期待しているのか。を考えたり、 そもそもこの機能はいるんだっけ。を考えたり、 技術的な観点で、この機能よりこっちのほうがいいかも。を考えたり、、、

全ての、仕事や行動などには理由があるべき、あるはずで、それをする理由がないならしないほうがいいかもしれません。 ここらへんは、状況に寄りすぎるので断定はしません。

また、これは自分が誰かに仕事を依頼する場合にも重要です。 背景、理由、あとの展望は成果物の質に大きく影響します。

まとめ

まとめると、全部自分を中心に考えています。 3つ目は物事の本質を捉えようとしている。とかかっこよくいえますが、 こういう考え方で仕事をしたほうが仕事量は減るからです。 自分を安定させてないと、他の(人の)事を考えられないので。

factory_girlの使い方を全体的にまとめた

factory_girlとは

factory_girlとは、 Rspec等の、Rubyのテストフレームワークで使用する、 テストデータ生成のためのライブラリ。

使い所

おもに、Activerecordのデータを生成することに長けている。 Activerecord以外のデータも生成可能だが、 生成ロジック等は自身で書くことになる。

併用ライブラリ

テストデータ生成といっても、データの中身は別途用意する必要がある。 そこで使われるのは、faker。 fakerの使い方は公式のReadmeを一部引用して軽く説明。

以下のような、記述で簡単に、ダミーデータを作成できる。

Faker::Name.name      #=> "Christophe Bartell"
Faker::Internet.email #=> "kirsten.greenholt@corkeryfisher.info"

fakerと組み合わせてfactorygirlを使っていく。

単一テーブルのデータ生成

テーブル

+-----------------+--------------+------+-----+---------+---------------------+
| Field           | Type         | Null | Key | Default | Extra               |
+-----------------+--------------+------+-----+---------+---------------------+
| user_id         | int(11)      | NO   | PRI | NULL    | auto_increment      |
| nickname        | varchar(100) | NO   |     | NULL    |                     |
| created_at      | datetime     | NO   |     | NULL    |                     |
| updated_at      | datetime     | NO   |     | NULL    |                     |
+-----------------+--------------+------+-----+---------+---------------------+

モデル

class User < ActiveRecord::Base
end

上記のような、usersテーブルがあったとする。 これを、factorygirlで生成する。

FactoryGirl.define do
  factory User do
    nickname { Faker::Name.name }
  end
end

上記がfactorygirlの定義

id, created_at, updated_at は activerecord側で生成されるので、 nicknameだけを指定すればよい。

user = create(:user)

生成時は上記のように書けばいい。

補助的なデータの生成

例えば、user生成時に指定したユーザーとフレンド関係になっていてほしいとか

Friendsテーブルがあるとする。

id
user_id
friend_id
created_at
updated_at
FactoryGirl.define do
  factory User do
    nickname { Faker::Name.name }
    
    factory :friend do
      transient do
        friend_id ''
      end
      
      after(:create) do |user, evaluator|
        Friend.new(user_id: user.id, friend_id: evaluator.friend_id)
      end
    end
  end
end
user = create(:user)
friend = create(:friend, friend_id: user.id)

factory定義中にfactoryを定義することができ、 これを行うとfactoryを継承することができる。

上記の場合は、Userを継承し:friendを作成している。

factoryの定義では、factoryの生成周りで、callbackが呼ばれる。

  • after(:build)
  • before(:create)
  • after(:create)
  • after(:stub)

stub以外は、activerecodのbuild,create前後のことだと考えてOK stubについては今回は扱わない。

transientは一時データを、初期から定義、外部から渡すことが可能。 今回は、friendになるユーザーのIDを外部から渡している。

最初からフレンドを持っているユーザーを生成する

ユーザー生成時に、フレンドもついでに作成したいケースもある。 factoryの定義に、traitを指定して、factory生成にパターンを持たせる事ができる。

FactoryGirl.define do
  factory User do
    nickname { Faker::Name.name }
    
    trait :with_friends do
      transient do
        friend_count 1
      end
      
      after(:create) do |user, evalutor|
        evalutor.friend_count.times.each do |_i|
          Friend.new(user_id: user.id, firned_id: create(:user).id)
        end
      end
    end
  end
end
user = create(:user, :with_friends, friend_count: 2)

これで、user生成時に、friendを指定数だけ一緒に生成する定義が出来る。 今回は追加データを生成するパターンに使っているが、 作成するデータのデータ内容のパターンに使うことも可能。

belongs_toが関係のデータ生成

Userモデルに従属しているテーブルがあったとします。

PostsテーブルとUserモデル、 Postモデル定義

id
user_id
created_at
updated_at
class User < ActiveRecord::Base
  has_many :posts
end
class Post < ActiveRecord::Base
  belongs_to :user
end

Postモデルのデータ生成時に、依存しているデータの生成も併せて行えます。

FactoryGirl.define do
  factory Post do
    user
  end
end
post = create(:post)

userモデルに従属しているということをPostファクトリ定義時に指定している。 これは、以下の記述と同様の意味になっている

FactoryGirl.define do
  factory Post do
    association :user, factory: :user
  end
end
user = create(:user)
poset = create(:post, user: user)

逆に、Userのデータはすでに生成済で、 そのUserに対してPostのデータを生成したい場合は上記のように、引数として渡すことができる。

従属している側から生成することはあんまりない気がするので、 たいていの場合は後者の記述。もしくは、friendのときのように、 :with_postsみたいにtraitでpost持ちのUserを生成する定義を書いたほうが使いやすい。

その他

別のテーブルの関係とかは、だいたい、after(:create)とか before(:create)とか、使えば、なんでも作れる。

traitでパターンを定義したが、複数パターンを組み合わせたい場合は、 別途定義ができる

factory :male_admin,   traits: [:male, :admin]   # login will be "admin-John Doe"
factory :female_admin, traits: [:admin, :female] # login will be "Jane Doe (F)"  

単一のtraitの場合は、factoryの引数に指定していたが、 複数を組み合わせる場合は別factoryとなる。

user = create(:male_admin)

まとめ

factorygirlの機能を組み合わせると、細かくパターンごとにテストデータの生成が可能になる。 うまく定義することで、テストコードのデータ作成部分がスッキリするのでおすすめ。

ここに書いたことは、全部公式にのっている