今のうちに見直しておきたいnginx設定集 【セキュリティ編】

こんにちは、Cluexの高橋です。

先日EC2サーバーのOSを新しいものにしまして、その時にサーバーのテストを簡単に行ってくれるQualys SSL Labsで移行前のサーバーをテストしてみたところ、C判定が出てしまったので、nginxの設定も全体的に見直してみました。

     

イメージはこんな感じです f:id:cluex-developers:20160813190017p:plain    

テストしたのはこちら「Qualys SSL Labs」 www.ssllabs.com    

ということで今回はnginxの設定に関してセキュリティ関連でやっておくべき設定集をまとめました。      

nginxのバージョン情報を隠す

nginxのバーション情報を隠蔽します。

server {
  # ~~
  server_tokens off;
  # ~~
}

   

SSL通信に使用するプロトコルを指定する

SSLv2やSSLv3はダウングレード攻撃やPOODLEなどへの脆弱性があるのでTLSのみ許可します。

server {
  # ~~
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  # ~~
}

   

暗号化スイートの設定

SSLの暗号化にする暗号化スイートを明示的に指定し、クライアントサイドで指定した暗号化をしないようにすることで、通信をよりセキュアにすることができます。 ちなみにssl_ciphersの設定方法としては、「:」をデリミタとして使用し、「!」をプレフィックスでつけることで除外対象となります。  

詳細な暗号化スイートの設定内容はこちらのサイトを参考にさせていただきました。

Strong SSL Security on nginx - Raymii.org    

おすすめの設定がこちら。

server {
  # ~~
  ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
  # ~~
}

 

IE6/WinXP などにも対応したいと言う方にはこちらの設定がおすすめとのことですが、SSL3.0などを使用できない設定にしているので今回は特にIE6を気にする必要はないかと思います。

server {
  # ~~

  ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

  # ~~
}

   

サーバーの暗号化設定の優先

先ほどのサーバー側で指定した暗号化スイートを優先して使用するようにします。

server {
  # ~~
  ssl_prefer_server_ciphers on;
  # ~~
}

   

Http Strinct Transport Security(HSTS)ヘッダの追加

サーバーからStrict Transport Securityヘッダを返すことで常にHTTPSで通信を行うようにし、中間者攻撃を防ぎます。これを使用するには Nginxのngx_http_headers_moduleが必要になります。

add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;';

developer.mozilla.org

   

ちなみにpreloadオプションを使用したい場合は、こちらでドメインを登録しておくと宜しいかと思います。

HSTS Preload List Submission

こちらに登録するには下記の要件を満たす必要があります。

Submission Requirements

If a site sends the preload directive in an HSTS header, it is considered be requesting inclusion in the preload list and may be submitted via the form on this site.

In order to be accepted to the HSTS preload list through this form, your site must satisfy the following set of requirements:

  1. Serve a valid certificate.

  2. Redirect from HTTP to HTTPS on the same host.

  3. Serve all subdomains over HTTPS.

 ・In particular, you must support HTTPS for the www subdomain if a DNS record for that subdomain exists.

  1. Serve an HSTS header on the base domain for HTTPS requests:

 ・The max-age must be at least eighteen weeks (10886400 seconds).

 ・The includeSubDomains directive must be specified.

 ・The preload directive must be specified.

If you are serving an additional redirect from your HTTPS site, that redirect must still have the HSTS header (rather than the page it redirects to).      

preloadを指定する場合はこんな感じです。

add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;preload';

       

DH鍵交換時のKeyの強化

TLSプロトコルで採用されているDiffie-Hellman鍵交換は512bitもしくは1024bit以下のDHパラメータにおいてLogjam攻撃を受ける可能性があり、これを防ぐにはDHパラメータに2048bitのものを使用することが望ましいとされています。

   

Diffie-Hellman鍵交換による暗号化時にnginxのデフォルトでは1024ビットの鍵を使用しますが、Google ChromeMozilla Firefoxなどの主要なブラウザでは「最低でも」1024ビット以上のものを使用するよう推奨しており、できれば2048bitのものを独自生成して使用するのが好ましいかと思います。

  一般的なwebサイトでは2048bitもしくは4096bitでpemを作っておくといいようです。

   

まずはpemファイルの作成

$ cd /etc/ssl/certs
$ openssl dhparam -out dhparam.pem 4096

こちらがnginxのconfファイルに追加する内容。

server {
  # ~~
  ssl_dhparam /etc/ssl/certs/dhparam.pem;
  # ~~
}

     

OCSPステープリング(OCSP Stapling)の設定

OCSP(Online Certificate Status Protocol)はSSL/TLS通信の時に証明書が失効していないかを確認するために使用されています。 通常はSSL通信を行う際はクライアント側がこのOSCPを使用してサーバー証明書の正当性を確認する必要がありますが、サーバー側で事前にこの処理を行うことでクライアント側で行う必要がなくなるのでHTTPS通信の開始を高速化できます。

 

具体的にはOCSPステープリングによってクライアント側ではなくサーバー側でOCSPによるサーバー署名書の確認を行い、その結果をクライアントへのレスポンスと一緒に送るようにすることで、これを実現しています。

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/cert/trustchain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s

ssl_staplingを使用するにはDNSリゾルバを resolverディレクティブにて指定する必要があります。googleのパブリックDNSサーバーが使用されることが多いようです。    

X-Frame-Options

レスポンスヘッダにX-Frame-Optionsを設定することでブラウザがiframeなどの内部に自身のサイトを表示を許可するかを設定できます。DENYもしくはSAMEORIGINを指定することで自分のサイトのコンテンツが他のサイトに埋め込まれないように設定でき、クリックジャッキング攻撃を防ぐことができます。

add_header X-Frame-Options SAMEORIGIN;

 

X-XSS-Protection

XSSに対するフィルタ機能を有効化

add_header X-XSS-Protection "1; mode=block";

 

X-Content-Type-Options

Content-Typeに一致しない処理は行わないようにさせることでXSSなどを防ぎます。

add_header X-Content-Type-Options nosniff;

これが設定されていない場合、例えば Content-type="text/plain" のファイルでも、その中にjavascriptのソースがあるとブラウザによって実行される可能性があります。

     

再度テストした結果は・・・

f:id:cluex-developers:20160813185943p:plain    

当時の設定では上記の幾つかはまだ設定していないものもあるのでA+の評価をとることができなかったので、今回あげたものも含めてさらにサーバー周りを強化していきたいなと思います。

     

ちなみにMozilla Foundationが公開しているwebサーバーのConfig Generatorがあるので、そちらで基本的な設定を確認できるので、気になる方はお使いになってみるいいかと思います。

Generate Mozilla Security Recommended Web Server Configuration Files

       

余談)Cluexではエンジニアの方を探しています!

 

See more at cluex.co.jp

assets on S3の導入

こんにちは。エンジニアの志村です。 最近暑くて参りますね…。アイスばっかり食べてます。

さて今回ですが、Assets on S3を導入しましたのでその際のメモです。
結構この形でassetsを配信しているサービスは多いですよね。
今回は、Cloudfront+S3 / asset_sync+capistranoという定番のパターンで実装しております。

assets on S3とは

デプロイ時にassetsファイルをS3に配置し、CDN経由で配信する方法です。
通常であればnginxやApache等のWebサーバーを介して静的ファイルは配信されています。
assets on S3はassetsファイルをS3に配置し、CloudfrontやAkamai等のCDN経由で配信します。

Webサーバー負担軽減やCDNを噛ませることによる高速化を目的として用いられることが多いかと思います。

仕組み

下記のような流れになります。

  1. デプロイ時にrake assets:precompileを走らせる。その際にasset_syncを使用し、S3にassetsファイルをアップロードする。
  2. manifestファイルをEC2にアップロードする。
  3. rake assets:precompile時に作成されるassetファイルを削除する(public/assetsに格納されている)

こんな感じでしょうか。 他にもCloudfrontやS3の設定があるので、下記に記していきます。

Rails側の設定

ではまずasset_syncを導入し、bundle installをします。

asset_syncの導入

gem 'asset_sync'

次にasset_syncのconfigファイルを生成します。

$ rails g asset_sync:install --provider=AWS

今回はAWSを使用するのでproviderオプションにはAWSを設定します。 そうするとasset_sync.rbという設定ファイルが生成されます。

  • config/initializers/asset_sync.rb
AssetSync.configure do |config|
  # fogを使用するサービス
  config.fog_provider = 'AWS'
  # S3の存在するリージョン
  config.fog_region = 'ap-northeast-1'
  # S3のバケット名
  config.fog_directory = 'assets'
  # IAMのアクセスキー
  config.aws_access_key_id = 'xxxx'
  # IAMのシークレットアクセスキー
  config.aws_secret_access_key = 'xxxx'
  # 既にS3上に存在しているファイルの扱い keep, delete, ignoreから選択
  config.existing_remote_files = 'keep'
  # gzip圧縮をするかどうか
  config.gzip_compression = true
end if defined?(AssetSync)

上記で使用してるIAMに関しては、次の章のS3の部分で説明します。 これでasset_sync自体の設定は完了しました!

assetsのURLをCloudfrontにする

assetsファイルはCloudfrontより配信されます。 通常の設定では、http://domain/assets/配下のファイルが配信されますが、これをhttps://Cloudfrontのエンドポイント/assetsに変更し、Cloudfrontから配信するように設定します。

  • config/environments/production.rb
〜中略〜

  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  config.action_controller.asset_host = '//xxxxx.cloudfront.net'

〜中略〜

これでassetsファイルがCloudfrontより配信されるようになりました!

次はcapistranoの設定を行います

capistranoの設定

流れとして、

  1. cap 〜 deployコマンドを叩いた時に、ローカルにてrake assets:precompileを走らせる(asset_syncが自動的にS3にアップロードしてくれる)
  2. manifestファイルをサーバのpublic/assetsディレクトリに転送する
  3. ローカルで生成されたpublic/assetsディレクトリを消去する

この中で特に大切なのは2.かと思います。
railsは、productionにデプロイされる際にprecompileを行います。
その際にdigestを付与してファイル名-digest.scssのようなファイル名に変更します。
このdigestはprecompile時にランダムに設定されるのでrails側で管理が必要になります。
そのファイル名を管理するのがmanifestファイルです。これが存在しないと、railsはassetのパスを正しく指定出来ません。

ではcapistranoの設定に移っていきます。 こちらのコードを参考にさせて頂きました。

qiita.com

  • config/deploy.rb
〜中略〜

namespace :deploy do
  # rake db:assets:precompileを実行する
  task :asset_sync do
    run_locally do
      Bundler.with_clean_env do
        execute :rake, 'assets:precompile'
      end
    end
  end

  # manifestファイルをアップロードする
  task :upload_manifest do
    on roles(:app) do |host|
      if test "[ ! -d #{release_path}/public/assets ]"
        execute "mkdir -p #{release_path}/public/assets"
      end
      file_path = Dir::glob('public/assets/.sprockets-manifest*').first
      upload!(file_path, "#{release_path}/public/assets")
    end
  end

  # ローカルに残されたprecompile後のassetsファイルを削除する
  task :assets_cleanup do
    run_locally do
      Bundler.with_clean_env do
        execute :rake, 'assets:clobber'
      end
    end
  end

  task :restart do
    invoke 'unicorn:restart'
  end
end

before 'deploy:starting', 'deploy:asset_sync'
after 'deploy:publishing', 'deploy:upload_manifest'
after 'deploy:publishing', 'deploy:assets_cleanup'
after 'deploy:publishing', 'deploy:restart'

〜中略〜

上記のようになります。
rakeタスクに関しては新しくファイルに切り分けても良いかと思いますが、そんなにデプロイスクリプトが多いわけでも無かったので僕はそのままdeploy.rbに書いてしまっています。
release_pathの部分はそれぞれの環境に合わせたpathを指定すればOKです。
developmentではsprocketsを使用しているのでそれをそのままuploadしております。

AWS側の設定

S3の設定

S3はバケットを作成し、CORSの設定を行います。 CORSの設定をしないと、Font Awesomeが正しく表示されません。 詳しくは

dev.classmethod.jp

を御覧ください。

  1. AWSコンソールの「S3」を選択

  2. 「バケットの作成」を選択

  3. バケット名、リージョンを設定

  4. 作成したバケットを選択し、「プロパティ」→「アクセス許可」→「CORS設定の編集」を選択 f:id:cluex-developers:20160810172755p:plain

  5. ここに下記のコードを貼り付け、「保存」をします 下記のコードを使用させて頂きました。

sora33.hatenadiary.com

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Cloudfrontの設定

基本設定

assets用のディストリビューションを作成します。

  1. AWSコンソールの「Cloudfront」を選択

  2. 「Web」を選択 f:id:cluex-developers:20160810152149p:plain

  3. Origin Settingsを入力する

項目 設定 説明
Origin Domain Name 先ほど作成したS3バケット名 オブジェクトの取得先
Origin Path 空のまま S3のルートディレクトリの設定
Origin-ID 分かりやすい名前 オリジンを区別する一意のID
Restrict Bucket Access Yes CloudfrontのURLからのみS3のオブジェクトにアクセス出来る
Comment 分かりやすい名前 identityを区別するIDのようなもの
Grant Read Permissions on Bucket Yes バケット内のオブジェクトの読み取り許可
Origin Custom Headers 空のまま リクエストをオリジンに転送する際にカスタムヘッダーを含めるかどうか

f:id:cluex-developers:20160810152400p:plain

※ Default Cache Behavior Settings, Distribution Settingsに関しては環境に応じて入力して下さい。今回は特にいじらずに行きます。

「Create Distributions」を選択するとCloudfrontのDistributionが作成されます。

Behaviorの設定

さて、Behaviorの設定を行っていきましょう。 Behaviorは振り分けのルールです。
例えば、

  • https://xxxx.cloudfront.net/assetsにリクエストが来た場合にはassetsバケットに転送
  • https://xxxx.cloudfront.net/imagesにリクエストが来た場合にはimagesバケットに転送

のようなことが可能になります。

  1. 「Behavior」タブの「Create Behavior」を選択 f:id:cluex-developers:20160810155354p:plain

  2. Settingsを入力

項目 設定 説明
Path Pattern /assets/* 振り分けパターン。今回は/assets配下のオブジェクト全てをS3から配信したいので/assets/*とする
Origin 先ほど設定したOrigin-ID どのオリジンからオブジェクトを取得するかを選択
Viewer Protocol Policy 環境によって選択 アクセスする際のプロトコルの選択
Allowed HTTP Methods GET, HEAD オリジンに転送するHTTPメソッド。今回は取得のみなのでGET, HEADを選択
Forward Headers Whitelist -> Origin どのヘッダを転送するか 今回CORSの設定を行っているので、OriginをAddする
Grant Read Permissions on Bucket Yes バケット内のオブジェクトの読み取り許可
Object Caching Use Origin Cache Headers MaximumTTL等を変更しないのであればUse Origin Cache Headersのままで良い

f:id:cluex-developers:20160810173203p:plain

「Create」を選択すると、このBehaviorが有効になります。

設定の反映までは10分〜20分ぐらいかかるので気長に待ちましょう!

以上です。 asset_syncのおかげでそこまで多くの手順を踏むこと無く、assets on S3の導入が出来ました。 次回はLambdaを使用してInvalidationを走らせる処理について書きたいと思います。

インターン生の村田です!

はじめまして、慶應義塾大学2(3)年の村田正之です。

2016年4月から株式会社Cluexでエンジニアとしてインターンをしております。 よろしくお願いします。あと、上のカッコはあまり気にしないでください。

株式会社Cluexで働く前

Cluexにジョインするまでは所属していた学生団体の活動に多くの時間を割いていました。大学と深い関係もありその歴史は10年以上という、学生団体としては比較的しっかりとした方だったと思います。そこではビジネスコンテストの運営、もう少し堅い言葉を使ってしまうとインキュベーション活動をしておりまして、最終的にはその代の副代表も務めておりました。

当時はあまり意識していなかったですが、その学生団体では本当にたくさんのことを勉強させていただきました。 リーンスタートアップやデザイン思考といったアイデアやビジネスの創造においての基本的な概念を学ぶことができましたし、僅かではありますが、マネジメントや組織の形成の仕方、ファシリテーションと言った汎用的な能力も身につけることができました。 ファシリテーションなどの能力はエンジニアと無縁に見えることもあるかもしれませんが、チームで開発をしていく上では非常に大切な能力の一つであり、現在でも活かされている場面が多いと思います。 そのようなことを学ばせていただけたという意味で、現在でもその学生団体には感謝を忘れることはできません。 しかし活動期間がありましたので、活動終了後は特別に打ち込むものもなく普通の学生生活を送っていました。

株式会社Cluexのインターンに応募したきっかけ

その学生団体のビジネスコンテストを運営していく中で、ものづくりができる人の強さを目の当たりにしてきました。 特に能力の高いエンジニアを抱えているチームはアイデアにとどまらず素早く行動に移すことができるため、短期間でピボットを繰り返していることがとても衝撃的でした。 

実はそのあたりから密かに、「エンジニアってかっこいいなあ」という憧れを抱き始めました。

そこで密かにプログラミングの本を購入して独学で勉強を始めました。 ただ、自分はエンジニアのコミュニティに属していなかったので、チャレンジしても誰にも相談できないまま解決できないということが何度か続き、結局挫折してしまいました。 そうしている中で、「もしプログラミングを学ぶなら優秀なエンジニアがいてスピード感のある環境に身を置きたい」と思いインターンを探していたところCluexというスタートアップを発見して応募しました。

当時は初心者でも受け入れてくれたので面接の応募をしたのですが、面接に行った際にサービスとプログラミングが大好きな人たちが働いている様子を見て、「ここでエンジニアとして働きたい!!」と感化されてしまいました。

いまやっていることと将来やりたいこと

Cluexでは2つのチームに分かれて開発を進めております。 1つがWebAppTeam、もう1つがNativeAppTeam(iOS)です。 私は前者のWebAppTeamでRubyonRailsを使って開発を進めています。 インフラ周りはまだほとんど触っていませんが、フロントエンドとバックエンドについてガリガリ書いています。

休みの日は業務以外のプログラミングを楽しんでいます。 最近はRubyメタプログラミングを勉強してるので、今週の日曜日は有名なgemの中身を読んでみようと思ってます。

今後はもっとミドルウェア周りを色々遊んでみたり、サーバーもいじっていきたいです。 あとハードにも興味があるので将来的にはソフトだけではなくて、ハード側も勉強したいと思っています。 ただ今から他の言語に浮気してもうまくいきそうにはないので、まずは我慢してじっくりRubyRailsに慣れていこうと思います。

Cluexではエンジニアの方を募集しています!

エンジニアリングと同時にビジネスマンとして成長したい、スタートアップで働いてみたい、もうレガシーな環境で余計な苦労しをたくないといった方など、 興味がある方はご連絡下さい。 https://www.wantedly.com/projects/60136

規律あるcssを運営するにあたって

こんにちは、エンジニアの井戸田です。

弊社ではmamanokoという子育てママさんのための情報サイトを運営しており、Ruby on Railsで実装しています。 今回はmamanokoで実装されているのcssの構成についてお話ししたいと思います。 mamanokoではSMACSSというcssの設計手法や、BEMというフロントエンド設計手法を弊社なりにカスタマイズして使用しています。

フロント面の開発環境は下記のようになっております。

SCSS
Slim
CoffeeScript

まずは本来のSMACSS、BEMについて説明します。

SMACSS

SMACSSとは、 Scalable and Modular Architecture CSS の略で スマックス と読みます。CSSの設計手法で1つであり、 Base(ベース)Layout(レイアウト)Module(モジュール)State(ステート)Theme(テーマ) の5つのルールに分け、それぞれのルールや記述規約が決められているのが特徴です。

smacssで設計する目的

SMACSSを導入することによって、コードの量が減り、CSSの弱点であった下記のことが解決されます。

  • 保守性を高める
  • メンテナンス性を高める
  • デザインを統一することで、ユーザー体験の一貫性を高める

5つのルール

Base - ベースルール

要素そのもののスタイルを定義します。 セレクタを使ってスタイルを変更する場所であるので、idやclassは使用できません。

body {
  background-color: #fff;
  color: #000;
}

a {
  &:hover {
    color: inherit;
  }
}

Layout - レイアウトルール

ページのエリア分けを定義します。 header , footer などよく id 指定するユニークな要素がLayoutに該当すると思います。 Modulestate との区別のため l- というprefixをつけるのが特徴です。このようにすることで、他のルールと区別ができ、管理がしやすくなります。

.l-header {
  height: 48px;
  background-color: #fff;
}

.l-footer {
  background-color: #fff;
  border-top: 1px solid #000;
}

Module - モジュールルール

その名前の通り再利用可能なスタイルを定義します。 ページデザインのcssを書くにあたって、ほとんどがここに定義されると思います。 どこにおいても再利用できるように独立させておきます。

またクラスの書き方は モジュール名-サブクラス の順番で書きます。

.article
  p.article-title タイトル
. article {
  background-color: #fff;
  border: 1px solid #000;

  &-title {
    color: #000;
  }
}

State - ステートルール

状態によってデザインを上書きする時に定義します。 !important を使用してのデザインの変更は避けましょう。 !important を使用することで醜いコードになってしまいます。 ステートルールはモジュール名を付けてあげると、クラス名で被ることはないと思います。

.is-item-active
.is-item-hidden

Theme - テーマルール

テーマルールを使用する機会は少ないです。 他言語対応時、英語や中国語など言語で標準のフォントサイズでは小さい場合があるので、そのような場合にテーマルールを使用することもあります。 theme- というprefixをつけるのが特徴です。

BEM

BEMとは Block , Element , Modifier の頭文字を取ったもので、 ベム と読みます。 コンポーネント化のためのclss命名規則です。

BEMで設計する目的

BEMを導入することによって、パーツごとにclass名を完全に独立したものとできるので、下記のことが解決されます。

  • 再利用性を高める
  • 拡張性を高める
  • 開発の生産性とメンテナンス性を高める

BEMの規約

  • Block
    • 文字通りブロック(塊)。構成のルートとなる要素です。
  • Element
    • Blockに所属する子要素。必ずBlockの中でのみ存在し、単体では使用してはいけません。
  • Modifier
    • 元となるBlock又はElementから変化した状態を示すものです。

上記の元に下記の規約が定められています。

  • BlockやElementで2つ以上の単語の場合はハイフン1つ(-)
  • Modifierで2つ以上の単語の場合はアンダースコア1つ(_)
  • BockとElementはアンダースコアを2個(__)
  • Block又はElementとModifierはハイフンを2個(--)
.articles-list
  .articles-list__item
    .artiles-list__item__title
      | タイトル

  .articles-list__item--state_small
    ・
    ・
    ・

弊社のSMACSSとBEMの構成

まず弊社ではマルチクラス設計を採用しています。 マルチクラス設計とは、HTMLに複数のclassを書いて、スタイルを当てる設計のことです。 マルチクラスを採用することにより、冗長な記述が減り、保守性の向上につながります。

例) ピンクのボタン(.button-pink)と、緑(.button-green)のボタンを定義

/ good

.button {
  padding: 12px 20px;
  background-color: #fd8280;
  border: 1px solid #fd6360;
  border-radius: 2px;
  color: #fff;
  
  &-pink {
    background-color: #fd8280;
    border: 1px solid #fd6360;
  }
  
  &-green {
    background-color: #3de933;
    border: 1px solid #0ce400;
  }
}

/ bad
.button { 
  &-pink {
    padding: 12px 20px;
    background-color: #fd8280;
    border: 1px solid #fd6360;
    border-radius: 2px;
    color: #fff;
  }
  
  &-green {
    padding: 12px 20px;
    background-color: #3de933;
    border: 1px solid #0ce400;
    border-radius: 2px;
    color: #fff;
  }
}

SMACSS

本来では5つのルールに分けるのですが、弊社では7つのルールに分けて使用しています。
smacssでは、ほとんどのスタイルが module に定義されると思うのですが、 module をもっと細かいルールに分けることによって、fatになってしまいがちの module を防いでいます。

  • Base
    本来のSMACSSと同じで、要素そのもののスタイルを定義します。
    セレクタを使ってスタイルを変更する場所であるので、idやclassは使用できません。
a {
  color: #000;
}
  • Component
    component に書くものなのですが、 見出し 部分や ボタン など、どこのページでも使い回す要素をここで定義します。そのため、グローバルなクラス名を付ける必要があります。
    現在のmamanokoでは見出し部分のクラス名は .heading 、ボタン部分のクラス名は .button と付けています。
.heading {
  border-bottom: 1px solid #000;
  padding: 0 8px;
  margin: 0 0 10px
}
  • Module
    moduleも使い回す要素を定義します。componentとの違いとして、グローバルなclass名をつけない要素を定義するということです。主にグローバルなクラス名ではない要素のほどんどが、ここに定義されます。

ユーザページのアバター画像及び、ユーザーネームを表示するヘッダーのcssを作成する場合

.userHeader {
  padding: 20px;
  background-color: #fff;
  
  &_image {
    width: 80px;
    height: 80px;
  }
  
  &_name {
    font-size: 80%;
  }
}
  • Layout
    本来のSMACSSと同じで、header , footer などよく id 指定するユニークな要素のものを記述し、 l- というprefixを付けて他のルールと区別するようにしています。

  • Patch
    使い回しをしない、特定のcssを定義するルールです。現在ではcontroller名ごとにファイルを分けをしており、ファイル内ではactionごとにcssを変更するようにしています。このようにすることによって、特定のページだけcssが変更することができるのを加えて、componentやmoduleのcssを打ち消すことができます。 但し patchを使いすぎると、醜いコードになってしまうので注意が必要です。

.users_controller {
  .edit_action {
    .heading {
      font-size: 80%;
    }
  }
}
  • Lib
    mixinなどの関数や、colorコードを変数で定義するルールです。
    mixinなどの関数を1ファイルにまとめておくことで、保守性を高め、メンテナンスがしやすくなると思います。 またcolorコードを変数で定義することにより、他のルールでcolorコードを設定する場合に、定義した変数を使うので、保守性、メンテナンス性を高めるのはもちろん、ユーザー体験の一貫性を高める効果があると思います。

現在mamanokoではここに定義されたcolorコードの変数を使い、色を濃くするにはscssの関数である darken や、色を薄くするには独自に関数を作成したものを使用しています。

@mixin rounded-s {
  border-radius: 2px;
}

@mixin rounded-m {
  border-radius: 4px;
}
$base-color: #000;
$primary-color: #ff828c;
  • Vendor
    外部ライブラリのcssを書き換えるルールです。現在mamanokoではBootstrapを使用しているのですが、デフォルトでcssが定義されており、うちのサイトにあったデザインにするためcssを書き換えています。

BEM

本来のBEMでは上記でも書いたようなルールがあります。

- BlockやElementで2つ以上の単語の場合はハイフン1つ(-)
- Modifierで2つ以上の単語の場合はアンダースコア1つ(_)
- BockとElementはアンダースコアを2個(__)
- Block又はElementとModifierはハイフンを2個(--)

BEMの本来の形のまま実行してしまうと、下記のようなデメリットがあります。

  • クラス名が冗長になってしまうので、コーディング量が増える
  • 深くネストした要素はクラス名が複雑になってしまう

このデメリットを防ぐために、弊社では独自のルールを制定しています。

弊社のルール

  • BlockとElement又はModiferとElementの区切りはアンダースコアを1つ( _ )
/ good
.block_element
.block-modifier_element

/ bad
.block__element
.block--modifier__element
  • BlockとModifier又はElementとModifierの区切りはハイフンを1つ( _ )
/ good
.block-modifier
.block_element-modifier

/ bad
.block--modifier
.block__element--modifier
  • BlockやElement, Modifierが2つ以上の単語で表す場合キャメルケースを使用する
/ good
.articlesList
  .articlesList_container

/ bad
.articles-list
  .articles-list__container
  • ElementのElementは記述しなくて良い
/ good
.articlesList
  .articlesList_container
    .articlesList_title
    
/ bad
.articlesList
  .articlesList_container
    .articlesList_container_title

いかがでしょうか? SMACSSのルールをより細分化、BEMを冗長性を防ぐためハイフンや、アンダースコアを省略することで、より使いやすくなると思います。 最初は慣れないと思いますが、慣れてこれば簡単に要素のcssを検索することができるのではないでしょうか。

最後まで読んでいただきありがとうございました。

はじめまして、エンジニアの神山です!!

はじめまして、神山奎吾です。2016年4月から株式会社Cluexでエンジニアとして働いております。よろしくお願いします。

株式会社Cluexで働く前

2016年4月に私は新卒としてCluexにジョインしたのですが、それまでは別の会社で営業をしておりました。そこは大学4年次の4月に内定を頂いたところで、それから大学生ながらも働かせて頂きました。会社規模はCluexとほとんど変わらないスタートアップ企業でした。なぜ営業を選んだのかというと、当時からエンジニアに興味を持っていたのですが、「知識・経験がゼロの状態から始めるのなら営業でいいや」という安易な考えでした。またエンジニアを志した理由もただ興味があるだけで、そもそも将来にやりたいことすら曖昧でした。自分でやりたいと思った事業を創ること、また自分で引っ張る、時に起業できるぐらいの実力者になれたらいいなといった感じでした。

入社して最初の1,2か月はテレアポで新規クライアントの開拓をしてました。毎日200件近く電話をしておりました。しかも最初の2週間くらいはまったくアポが取れずすべて断られ続け、精神的にとても辛く苦しい思いをしました。あまりにもできなかったのが悔しく、休みを利用してテレアポの派遣に登録しテレアポのプロたちの会話術を必死に聞いて真似しておりました。そしてようやくその次の日からアポが取れるようになりました。アポが取れた時に同僚の方々に拍手喝采して頂き、このときの嬉しさは今でも忘れることが出来ません。 今振り返るとこのとき逃げずに挑戦し続けた経験が自分を精神的にかなり強くしてくれたと思います。またダメだった状態から50件に1件ぐらいの割合でアポがとれるようになると仕事がとても好きになり、強い自信を持つことができました。

テレアポの次は実際に広告を運用しており、当月の売り上げを先月の1.25倍にするというノルマのもと働いておりました。この仕事で毎月に一定額以上の利益を出す大変さを学ぶとともに、有限な時間をいかに有効的に使うかを意識して働いておりました。この仕事もハードでしたが、なんとかノルマを達成し続けることができました。このときに努力と根性と少しのクレバーさがあれば人間何でもできると気づきました。

株式会社Cluexに入社したきっかけ

入社して半年ぐらいしたときに、自分は将来何がしたいのか、どのような働き方をしたいのか考えるようになりました。そもそもなぜスタートアップ企業で働いているのかというと、爆速で実力をつけられる一番の環境だったからであり、なぜ実力を付けたいのかというと、実力があれば仕事に困ることもないどころか多種多様な仕事を良い条件でできる、本当の意味での安定が手に入る、また自分の好きな仕事を好きなようにできる、時に旅に出たりなど自由気ままに生きていけると考えていたからでした。 またなぜエンジニアに興味を持ったのかというと、昔から自分はものづくりが好きであり、仕事を楽しみたいとしたらエンジニアが一番なのかなと思ったことでした。欲を言えば自分の代名詞となる世界的なサービスを作りたいと思っておりました。またパソコンやインターネットに興味があり、FacebookAppleに憧れておりました。理想は自分が0の状態からコードを書いたものを、事業として大きくスケールアップさせ、そのサービスが語られるときに自分の名前もセットで語られることです。

そして当時働いていた会社が上場を考えており、もし本当に究極のなりたい自分になるのなら、今エンジニアを目指すべきだと考え、会社をやめることにしました。社会人の基礎や営業のやり方を学ばせて頂き、公私共に大変お世話になっていたので、自己都合で辞めたことは大変申し訳なく感じております。

その後、2月の終盤ごろにCluexに声をかけて頂きました。最初は社歴がとても短く人数もごく少数の会社なので、未経験のエンジニアとして採用されたとして自分の希望通りに働けるのか、未経験を育てるだけの余裕や教育方針があるのか、悪い意味での何でも屋になるのではないかという不安もありました。しかし話を聞いてみると、エンジニアのリーダーが私と同じく営業上がりで未経験ながらもエンジニアに転職した方であったり、そもそもエンジニアとして経験が豊富な方がいらっしゃったりして、当初の不安は感じませんでした。また人と人の距離がとても近く団結力があり、しかも全員がやる気に満ちており凄く魅力的な会社だなと思いました。 それから何回かCluexに足を運んでいたのですが、メンバー全員が毎日朝から晩まで働いていて、しかもそれを当たり前のように笑って仕事をしているという環境がとても羨ましく、自分もここで働きたい、ここなら爆速で実力を付けられると思い、Cluexで働きたいと強く思うようになりました。また社員の皆さんにも歓迎していただけまして、3月中旬にジョインが決まりました。

いまやっていることと将来やりたいこと

現在、Ruby on Railsにて開発しております。4月1日にRoRチュートリアルを始めたので3ヶ月と少し経ちます。当時は何もかもがわからない状態でした。プログラミング自体が初めてで、ターミナルって何?どうやって動いているの?みたいな状態でした。 入社してからはいままで休むことなくパソコンのキーを叩いております。というのも寝てる時間以外常に仕事のことを考えております。仕事が楽しくて楽しくて仕方がない状態です。現在入社して3ヶ月目ですが、出来ることが格段に広がりました(勿論、知識が足りてないところは大いにありますが…)。最近はReactをガシガシ書いており、Reactを書いている時間が日々の楽しみになっております。 また、最近はWebアプリケーションチームとして、少人数のチームが構成され、チームを意識した働き方をしております。この会社に入って個々人で開発していた日々から、本格的にチームとして動くようになり、この先、皆で何かを成し遂げたいと思うようになりました。また会社的にもまず上場をさせるという目標があるので、その時が楽しみでなりません。その場面に立ち会ったときには、自分も会社の重要な軸になっているようにより一層の努力を重ねるつもりです。

将来的には、全世界の人々に楽しんで頂けるようなサービスを作りたいと考えています。年齢や性別、言語や文化関係なく、その時一瞬でも幸せを感じて頂けるサービスを作りたいと考えております。自分は大学時代ずっとマジックをやっており、その時にマジックは全世界の人に平等に驚き、またちょっとした幸せを与えられることを知りました。それをITサービスにできたら、上記の目標に一歩近づけるかなと思います。 私の好きな言葉に、「無駄とユーモアが人を幸せにする」という言葉があります。例えば、カロリーが高いけど、美味しいケーキを食べたとき、またしょうもないジョークにも関わらず、なんか疲れが吹き飛んだ時とか、一見あまり価値のなさそうなことに人は幸せを感じ、それらが積み重なってその人の大きな幸せになっていくのだと強く感じます。もし自分が世界の人々を少しでも幸せにできたら、それだけで自分の生きた意味になるのかなと思います。

Cluexではエンジニアの方を募集しています!

エンジニアリングと同時にビジネスマンとして成長したい、スタートアップで働いてみたい、もうレガシーな環境で余計な苦労しをたくないといった方など、 興味がある方はご連絡下さい。 https://www.wantedly.com/projects/60136

初めまして、エンジニアの高橋です!

はじめまして。 Cluexでエンジニアをしています、高橋佳弥と申します。 今回は自己紹介の記事を書かせてもらいます。

実は昨年の12月にcluexにジョインしたのですが、ブログを書け書けと言われつつ そっぽ向いてひたすらコード書いていたらこのタイミングでの自己紹介となりました。

エンジニアとしての暦は今年で5年目くらいになります。今年で24歳になります。 もともと数学ができないので私文の大学に入ったのですが、それからphpMySQLをメインとして独学で学び、 その後web系の言語を一通り使えるようになってからは、個人でお仕事をいただいたり、スタートアップで仕事をしたりしていました。 学校で習ったり、スクールで学んだりといったことが全くなかったので、常にぶっつけ本番で技術のキャッチアップをしていった感じです。

Cluexに入ったきっかけ

きっかけは、代表の大濱から連絡をもらって、「エンジニア足りないんだけど!」という一言から始まり、 結果としてそのまま入った感じなんですが、もともと代表の大濱と知り合いでして かつては共通の知人と3人で6畳一間に住んでいたこともありました。

でも知り合いだからと言って、おいそれと入社を決めるということってなかなか珍しいかと思います。 日本において創業された会社は、10年間のうちにそのほとんどが消えて無くなると言われてますが、 流れの早いweb業界において新しいサービス作ってそれをしっかりと伸ばしていくことって本当に難しいなと個人的には思っています。 とはいえ、その組織の人間がしっかりとした戦闘能力を身につけていれば、どんな事業でもしっかり伸ばしていくことができると思います。 Cluexはそういう意味では組織として大濱もそれ以外のメンバーにも惹かれるところがあり、そこで入社を決めました。

何をやっているのか

入社してからしばらくはRailsでの開発がメインでした。そもそも初めてRailsを触るのがここに入ってからだったんすが、あれやこれやバグ潰したり新しい機能作ったりしてるうちに気付いたら社内で講義をするようになっていました。個人的にはphpもう触りたくないってくらいにRailsは楽でいいなーと思ってます。

先月はインフラ関連のことをもっぱらやっていました。サーバーのリプレイスから始まり、チューニングしたり、生ログ解析の基盤を作ったりといった感じです。また追ってブログ記事にしようと思います。インフラはインフラでアプリケーションよりもさらに深いところで色々なことができるのでめっちゃ面白いなって思います。ホワイトハッカー目指して引き続き頑張ります。

そして今月からはIOSチームに移りました。 正しくは「立ち上げた」って感じなんですが、いよいよ社内でチームを分けて、それぞれwebとアプリ専業で開発を進めていくようになります。 それに伴い僕もIOSアプリの開発をメインに進めながら、たまにインフラ周りをいじったりしたいなと思います。

目まぐるしく色々とやることが日々変わっていって忙しい感じはありますが、 今まで伸びてるサービスの中で開発に携わることがなかったので、そういう意味では自分が開発してるサービスが伸びているのでめちゃめちゃ楽しいです。

今後やっていきたいこと

チーム全体としてさらに強くなっていきたいなと思ってます。 社内でよく言っているんですが、コードがかけるエンジニアならいくらでもいると思うんですね。 でも世の中にはその上をいく優秀だと言われるエンジニアがいて、そういう人たちと普通のエンジニアとの違いはなんなのかって最近よく考えています。

そもそも何を持って優秀とするかは意見が別れるところだと思うんですが、 技術的な知識が豊富であるとか、綺麗にコードがかけたり、 それを色々なところへアウトプットしていたり、 エンジニアとしてサービスをグロースさせることができたりなどといったことがあげられると思います。

さらに会社で働くエンジニアでいえば社内で用意されている開発環境や開発メンバーもとても影響すると思います。 他にも色々な要素があると思うんですが、エンジニアがエンジニアとして着実に力をつけていける環境づくりをしていきたいなと思っています。 もちろん事業としてもしっかり伸ばしていきます。

簡単ですが自己紹介でした!

Cluexではエンジニアの方を募集しています!

色々な言語を身につけていきたい、スタートアップで働いてみたい、もうレガシーな環境で余計な苦労したくないといった方など、 興味がある方はご連絡くださいませ。 https://www.wantedly.com/projects/60136

AWS Lambdaを使用して、AWS利用料金のお知らせをSlackに届くようにしてみた

AWS Lambdaを使用して、AWS利用料金のお知らせをSlackに届くようにしてみた

エンジニアの志村です。 先日AWS Summit Tokyo2016に2日間行ってきました。

今サミットではLambdaを用いたサーバレスアーキテクチャ、またKinesisを利用したリアルタイムストリーミング解析の話題が中心でした。 Lambdaは少々前から興味があったにも関わらず、実務で使用したことが無かったのでタイトルのようなものを実装してみました。 リクルートさんがやっていたのを真似て、今回は、Lambdaのcloudwatch-alarm-to-slack-pythonというBlueprintを使用し、Pythonにて実装しています。

サーバーワークスさんのブログを参考にさせて頂きました。 blog.serverworks.co.jp

ゴール

下記のような通知を、毎日朝10時にSlackに流すようにします。 f:id:cluex-developers:20160611112826p:plain

やること

下記のような流れで実装を進めていきます。

  1. Slack APIのIncoming Webhooksの設定
  2. IAMのKMSを用いてIncomig WebhooksのURLを暗号化
  3. cloudwatch-alarm-to-slack-pythonというBlueprintを使用し、Lambdaファンクションを作成
  4. Lambdaで作成したロールにCloudwatchに対する権限を付与
  5. Lambdaが起動するトリガーを設定

Slack APIのIncoming Webhooksの設定

ではSlackの設定から行いましょう。

  1. Slackのメニューから「App & Integrations」を選択します。 f:id:cluex-developers:20160611103351p:plain

  2. 下記のように「Incoming Webhooks」を入力し、選択します。 f:id:cluex-developers:20160611114126p:plain

  3. 自分の所属しているチームが表示されているので、該当チームの「Configure」を選択します。 f:id:cluex-developers:20160611103642p:plain

  4. 「Add Configration」を選択します。 f:id:cluex-developers:20160611103804p:plain

  5. 投稿したいチャンネルを選択します。 f:id:cluex-developers:20160611103908p:plain

  6. Webhook URLが必要になるので控えておきましょう。ついでにemojiも設定しちゃいましょう!(僕らはお金のemojiを設定しています) f:id:cluex-developers:20160611104319p:plain

IAMのKMSを用いてWebhook URLを暗号化する

上記で取得したURLですが、そのままLambda Functionに貼り付けて使用するのはセキュリティ上好ましくないですよね。 なのでKMSを用いて、暗号化した状態で使用しましょう。

  1. AWSコンソールからIAMを選択 f:id:cluex-developers:20160611104531p:plain

  2. 左下に「暗号化キー」という項目があるので選択

f:id:cluex-developers:20160611104802p:plain

  1. エイリアスを入力します。 f:id:cluex-developers:20160611104930p:plain

  2. キー管理アクセス許可の定義は環境やルールに従って決めて下さい。

  3. キーポリシーのプレビューで確認をし、「完了」を押す

  4. 下記コマンドを使用し、暗号化を行う(AWS CLIが必要になります。)

$ aws kms encrypt --key-id alias/先ほど設定したエイリアス --plaintext "先ほど取得したWebhook URL"

上記を実行すると、暗号化されたURLとARNというものが表示されます。こちらは Lambda Functionで使用するのでコピーしておきましょう。

cloudwatch-alarm-to-slack-pythonというBlueprintを使用し、Lambdaファンクションを作成

さて、いよいよLambdaの方に入っていきます!

  1. バージニア北部」リージョンを選択 f:id:cluex-developers:20160611105232p:plain

  2. AWSコンソールからLambdaを選択 f:id:cluex-developers:20160611105322p:plain

  3. Create Functionを選択 f:id:cluex-developers:20160611105458p:plain

  4. 「Select blueprint」という画面が出てくるので「cloudwatch-alarm-to-slack-python」というBlueprintを選択 f:id:cluex-developers:20160611105700p:plain

  5. Configure Event Sourcesはスキップ(後で設定します)

  6. Lambda Functionのページに飛びます。ENCRYPTED_URLには先ほど暗号化したURLを、SLACK_CHANNELにはIncoming Webhookで設定したチャンネルを入力しましょう。

最終的なコードは下記のようになりました。

# -*- coding: utf-8 -*-

from __future__ import print_function

import boto3
import json
import logging
import datetime

from base64 import b64decode
from urllib2 import Request, urlopen, URLError, HTTPError


ENCRYPTED_HOOK_URL = '上記で暗号化したURL'
SLACK_CHANNEL = 'Slackのチャンネル'  # Enter the Slack channel to send a message to

HOOK_URL = "https://" + boto3.client('kms').decrypt(CiphertextBlob=b64decode(ENCRYPTED_HOOK_URL))['Plaintext']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

client = boto3.client('cloudwatch')


def lambda_handler(event, context):
    logger.info("Event: " + str(event))

    startDate = datetime.datetime.today() - datetime.timedelta(days = 1)

    response = client.get_metric_statistics (
        MetricName = 'EstimatedCharges',
        Namespace  = 'AWS/Billing',
        Period     = 86400,
        StartTime  = startDate,
        EndTime    = datetime.datetime.today(),
        Statistics = ['Maximum'],
        Dimensions = [
            {
                'Name': 'Currency',
                'Value': 'USD'
            }
        ]
    )

    maximum = response['Datapoints'][0]['Maximum']
    date    = response['Datapoints'][0]['Timestamp'].strftime('%Y年%m月%d日')

    slack_message = {
        'channel': SLACK_CHANNEL,
        'text': "%sまでのAWSの料金は、$%sです。" % (date, maximum)
    }

    req = Request(HOOK_URL, json.dumps(slack_message))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted to %s", slack_message['channel'])
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)

# -*- coding: utf-8 -*-を宣言してあげないと、encodingのエラーが出て、lambdaが正しく動作しないので注意して下さい。

  1. Name, Descriptionを入力 Runtimeは今回Python2.7を選択 f:id:cluex-developers:20160611111009p:plain

  2. Cloudwatchから情報を得る権限が必要なので、Roleから「Basic execution role」を選択 f:id:cluex-developers:20160611111100p:plain

  3. IAMロールは「新しいIAMロールを作成」 好きなロール名を入力し、「許可」を選択 f:id:cluex-developers:20160611110638p:plain

  4. 他の値はそのままで良いので「Next」を選択

  5. 内容を確認し、「Create Function」を押す

Lambdaで作成したロールにCloudwatchに対する権限を付与

先ほどLambda内で作成したロールに対し、Cloudwatchの情報を得ることが出来る権限を付与する必要があります。

  1. AWSコンソールからIAMを選択 f:id:cluex-developers:20160611104531p:plain

  2. 「ロール」を選択

  3. 先ほど作成したロールを選択する f:id:cluex-developers:20160611111430p:plain

  4. 「ポリシーのアタッチ」をクリックし、「CloudWatchReadOnlyAccess」をアタッチ f:id:cluex-developers:20160611111634p:plain

Lambdaが起動するトリガーを設定

さて、いよいよ最後です!

  1. AWSコンソールからLambdaを選択 f:id:cluex-developers:20160611105322p:plain

  2. 先ほど作成したFunctionを選択

  3. 「Event Sources」タブをクリックし、「Add event source」を選択 f:id:cluex-developers:20160611111950p:plain

  4. Event source typeの選択画面が出る 今回は時間指定でSlack通知を行いたいので「CloudWatch Events - Schedule」を選択 f:id:cluex-developers:20160611112212p:plain

  5. 「Rule name」には好きな名前を、「Schedule expression」にcron式を入力 今回「毎日朝10時に通知をする」ので下記のような式になります。 ※ UTCで設定を行う必要があるので注意して下さい。 f:id:cluex-developers:20160611112532p:plain

cron式については下記を参照して下さい。 docs.aws.amazon.com

こんな感じです。あっさりと出来ました! 初めてLambdaを使用しましたが、様々なイベントを感知して、AWSのサービスを自由に動かすことが出来るので工夫次第でもっと大きなことが出来そうです!