CIサーバのセキュリティを向上する

前回の記事ではnginxによるSSLプロキシを有効にして通信路を暗号化しました。が、今のままでは平文のHTTPでもアクセスできる状態になっていますので、これを閉じます。

例によってdocker-copose.ymlです。

version: '2'

services:
  # リポジトリ
  gitbucket:
    image: takezoe/gitbucket
    container_name: gitbucket
    volumes:
      - ./gitbucket/data:/gitbucket
    environment:
      - TZ=JST-9
    ports:
      - 8080
      - 29418:29418

  # CIサーバ
  jenkins:
    image: jenkins:alpine
    container_name: jenkins
    volumes:
      - ./jenkins/home:/var/jenkins_home
      - ./jenkins/executors.groovy:/usr/share/jenkins/ref/init.groovy.d/executors.groovy
    environment:
      - JAVA_OPTS=-Duser.timezone=Asia/Tokyo
    ports:
      - 8080
      - 50000:50000

  # Rocket.Chatデータ
  db:
    image: mongo:3.0
    container_name: db
    volumes:
      - ./db/data:/data/db
    command: --smallfiles

  # Rocket.Chat本体
  rocket.chat:
    image: rocket.chat:latest
    container_name: rocket.chat
    environment:
      - ROOT_URL=http://localhost:8082
    links:
      - db
    ports:
      - 3000

  # リバースプロキシ
  nginx:
    image: nginx:alpine
    container_name: nginx
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt:ro
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - gitbucket
      - jenkins
      - rocket.chat
    ports:
      # HTTPS
      - 8280:8280
      - 8281:8281
      - 8282:8282

フォントを赤くした3箇所のexposeを解除して、コンテナ間でのみ通信するように変更しました。Jenkinsの50000番ポートについてはJNLPによる接続なので、使わないようであればこれもexposeする必要はないと思います。

GitBucketの29418番ポートは外部とのSSH通信のためのポートですので、このままにしておきます。

nginxでリバースプロキシを立てる

さて、先の記事Let’s Encryptから取得したSSL証明書ですが、使わないと意味がありません。今回はnginxをHTTPSリバースプロキシとしてセットアップしてみます。

まず今回は既存のポートは生かしたまま、8280番ポートにGitBucket, 8281番ポートにJenkins, 8282番ポートにRocket.Chatを、それぞれ割り当てたいと思います。

例によってdocker-compose.ymlです。

version: '2'

services:
  # リポジトリ
  gitbucket:
    image: takezoe/gitbucket
    container_name: gitbucket
    volumes:
      - ./gitbucket/data:/gitbucket
    environment:
      - TZ=JST-9
    ports:
      - 8080:8080
      - 29418:29418

  # CIサーバ
  jenkins:
    image: jenkins:alpine
    container_name: jenkins
    volumes:
      - ./jenkins/home:/var/jenkins_home
      - ./jenkins/executors.groovy:/usr/share/jenkins/ref/init.groovy.d/executors.groovy
    environment:
      - JAVA_OPTS=-Duser.timezone=Asia/Tokyo
    ports:
      - 8081:8080
      - 50000:50000

  # Rocket.Chatデータ
  db:
    image: mongo:3.0
    container_name: db
    volumes:
      - ./db/data:/data/db
    command: --smallfiles

  # Rocket.Chat本体
  rocket.chat:
    image: rocket.chat:latest
    container_name: rocket.chat
    environment:
      - ROOT_URL=http://localhost:8082
    links:
      - db
    ports:
      - 8082:3000

  # リバースプロキシ
  nginx:
    image: nginx:alpine
    container_name: nginx
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt:ro
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - gitbucket
      - jenkins
      - rocket.chat
    ports:
      # HTTPS
      - 8280:8280
      - 8281:8281
      - 8282:8282

既存の設定には手を入れず、末尾のnginxの設定のみを追加してあります。/etc/letsencryptに前回取得したSSL証明書がありますので、これをそのままボリュームとしてマウントします。その他、設定ファイルをいくつか上書きします。

まずはnginxの設定 nginx.conf です。通常、worker_processにはサーバのCPU数を指定します。

# nginx.conf
user  nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;
    include /etc/nginx/conf.d/*.conf;
}

次の各サービスに対するプロキシの設定を、conf.dディレクトリ以下に gitbucket.conf, jenkins.conf, rocketchat.conf として作成します。内容はほぼ同一で、listenするポート番号とproxy_passぐらいしか違いがないので、ここではgitbucket.confのみを例として挙げておきます。

# gitbucket.conf
server {
    # HTTPS
    listen 8280 ssl;
    listen [::]:8280 ssl;
    server_name ドメイン;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/ドメイン/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ドメイン/privkey.pem;

    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDH !aNULL !eNULL !SSLv2 !SSLv3';
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";

    location / {
        proxy_pass http://gitbucket:8080;
        proxy_redirect off;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

proxy_passの値として、jenkins.confでは http://jenkins:8080, rocketchat.confでは http://rocket.chat:3000 をそれぞれ指定します。

また、共通的な部分は reverse_proxy.conf としてくくり出しておきます。

# reverse_proxy.conf
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

ここまで設定が済んだらdocker-compose upでコンテナを起動します。

それではまずGitBucketから見て行きましょう。ブラウザで https://ドメイン:8280/にアクセスし、ログイン前の画面が表示されればOKです。いつも通りログインし、右上のプルダウンから “System administration” を開き、”System settings” のBase URLを https://ドメイン名:8280 に変更して “Apply Changes” で保存します。

次にJenkinsです。ブラウザでhttps://ドメイン:8281/にアクセスします。こちらも同様にログイン画面が開きますのでログインします。「Jenkinsの管理」から「システムの設定」を開き、Jenkins URLを https://ドメイン:8281/ に、末尾のBuild Server URLも https://ドメイン:8281/ にそれぞれ変更して保存します。

続いてジョブの一覧から作成済みのジョブを選択し、「設定」を開いてGitBucketのURL欄を https://ドメイン:8280/〜 に変更します。

最後にRocket.Chatです。https://ドメイン:8282/にアクセスしてログインします。そうするとWarningダイアログが表示されてURLを変更するか確認してきますので「はい」を選択します。

これで、HTTPSで各サービスにアクセスできるようになりました。

2017/02/24追記: 変更を保存し忘れたりすると、サイトにアクセスできなくなります。というかなりました。私の場合、./gitbucket/data/gitbucket.confの base_url の値を直接編集して事無きを得ました。危ない危ない。

Jenkinsを初期設定する

Unlock Jenkins
Unlock Jenkins

先の記事ではJenkinsをDockerイメージとして起動しましたが、当然このままでは使えません。設定に従ってブラウザで8081番ポートにアクセスすると、”Unlock Jenkins”という画面が表示されてパスワードの入力を求められます。

このパスワードは自動生成されて、初回起動時にターミナルに出力されます。docker-composeで起動したログを確認すると、冒頭の方に * で囲まれた32桁のランダム文字列が表示されていると思います。

jenkins        | Jenkins initial setup is required. An admin user has been created and a password generated.
jenkins        | Please use the following password to proceed to installation:
jenkins        | 
jenkins        | ********************************
jenkins        | 
jenkins        | This may also be found at: /var/jenkins_home/secrets/initialAdminPassword

見逃がしていた場合でも、この出力にあるように /var/jenkins_home/secrets/initialAdminPassword に保存されていますのでご安心を。以下のようにdockerコマンドを使ってファイルの中身を確認できます。

$ docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword
********************************

が、イメージ上のボリューム /var/jenkins_homeは ./jenkins/home がマウントされたものですので、直接 ./jenkins/home/secrets/initialAdminPassword を確認することもできます。

$ cat ./jenkins/home/secrets/initialAdminPassword********************************

ここで確認できたパスワードをJenkinsのUnlock Jenkins画面の入力欄にコピペすれば、晴れてJenkinsを使えるようになります。

Unlockできたらカスタマイズ画面 (プラグインの選択)が表示されます。ここでは無難に “Install suggested plugins” を選択しておきましょう。自動で複数のプラグインがインストールされ、管理アカウントの作成画面になります。フィールドを埋めてアカウントを作成したら、いよいよJenkinsが利用可能になります。

JenkinsとRocket.ChatもDockerで動かす

GitBucketをDockerで動かせましたので、続いてJenkinsRocket.ChatもDockerで動かしましょう。いずれも公式のDockerイメージが公開されていますので、それを利用させて頂きましょう。

ここでは、GitBucketは8080番、Jenkinsは8081番、Rocket.Chatは8082番の各ポートで動作するよう設定します。設定ファイル docker-compose.yml はこんな感じになります。

version: '2'

services:
  gitbucket:
    image: takezoe/gitbucket
    container_name: gitbucket
    volumes:
      - ./gitbucket/data:/gitbucket
    environment:
      - TZ=JST-9
    ports:
      - 8080:8080
      - 29418:29418

  jenkins:
    image: jenkins:alpine
    container_name: jenkins
    volumes:
      - ./jenkins/home:/var/jenkins_home
      - ./jenkins/executors.groovy:/usr/share/jenkins/ref/init.groovy.d/executors.groovy
    environment:
      - JAVA_OPTS=-Duser.timezone=Asia/Tokyo
    ports:
      - 8081:8080
      - 50000:50000

  db:
    image: mongo:3.0
    container_name: db
    volumes:
      - ./db/data:/data/db
    command: --smallfiles

  rocket.chat:
    image: rocket.chat:latest
    container_name: rocket.chat
    environment:
      - ROOT_URL=http://localhost:8082
    links:
      - db
    ports:
      - 8082:3000

Rocket.ChatはMongoDBに依存していますので、mongodbの設定もあります。command行で --smallfiles の指定がないと./db/data以下に巨大なファイルが作成されますのでご注意を。また、ROOT_URLの値は環境に合わせて適宜変更して下さい。

設定ファイルを編集・保存し終えたら docker-compose up で各サービスが起動します。ブラウザから各サービスにアクセスしてみましょう。

2016/02/21追記: environmentによるタイムゾーンの設定を追加しました。

GitBucketをDockerで動かす

DockerDocker Composeをインストールしましたので、試しに何か動かしてみましょう。まずは基本のhello worldから。

$ docker run --rm hello-world

Hello from Docker!
...

メッセージが表示されました。問題なく動作するようです。ここでpermission云々言われる場合は、ユーザをdockerグループに追加するかsudoを付けて実行して下さい。

さて、今回はGitサーバの一実装であるGitBucketをDockerで動作させたいと思います。GitBucketは開発者がdocker imageを公開しているので、コマンドライン一発で使えます。ここでは少しオプションを変えて実行します。

$ docker run --rm -p 8080:8080 takezoe/gitbucket
...

これだけで、HTTP 8080番ポートから利用できるようになります。インストール直後のユーザ名/パスワードは root/root です。しかしこのままではコンテナを止めるとファイルも消えてしまいますので、永続化させましょう。ついでにSSHでもアクセスできるようにします。Ctrl+Cで実行中のコンテナを止めて、

$ mkdir gitbucket
$ docker run --rm -p 8080:8080 -p 29418:29418 -v ./gitbucket:/gitbucket takezoe/gitbucket
...

これで、SSHでもアクセスできるようになり、コンテナを止めてもファイルが永続化されるようになります (SSHアクセスのためにはGitBucket側の設定も必要です)。このタイミングでrootユーザのパスワードをデフォルトのものから変更しておきましょう。

“GitBucketをDockerで動かす” の続きを読む

Docker Composeをインストールする

引き続き環境設定です。

Dockerをインストールしたことでサービスをコンテナ上のプロセスとして起動できるようになりましたが、先の記事にも書いたようにマイクロサービスアーキテクチャ的なアプローチでは複数のプロセス(サービス)を立ち上げる必要があります。しかし素のdockerコマンドは複数のコンテナを立ち上げるには少々不便です。そこで複数のコンテナを一つの設定ファイルで管理すべくDocker Composeを導入します。

Debian Stretch (testing)にはdocker-composeパッケージが提供されていますが、バージョンが少々古め(記事執筆時点で1.8.0)です。なるべく新しめのバージョンを利用したいので、ここではgithubからバイナリを取得、インストールしてみたいと思います。これまたインストール方法の詳細はDocker Composeのサイトに記述のある通りですが、インストール先をホームディレクトリの下の ~/bin に変えてみます。

$ mkdir ~/bin
$ curl -L "https://github.com/docker/compose/releases/download/1.10.0/docker-compose-$(uname -s)-$(uname -m)" -o ~/bin/docker-compose
$ chmod +x ~/bin/docker-compose
$ vi ~/.bash_profile
...
export PATH=$PATH:~/bin
...
$ . ~/.bash_profile

既に~/binにPATHが通っている場合は適宜読み飛ばして下さい。また、システム全体でdocker-composeコマンドを使えるようにしたい場合はサイトに記載されている通り、/usr/local/binにインストールするのが無難かも知れません。

なお、多少バージョンが古くてもよい場合は sudo apt-get install docker-compose でもOKです。Docker Composeのサイトには pip install する方法も記載されていますが、個人的にはpipはvirtualenv環境下でしか使いたくないのでスルーしています。

Dockerをインストールする

Intel JouleJetson TX1のようなエッジ機器だけあってもIoTは成り立ちません。エッジから押し上げられるデータを受け止めるサーバ/クラウドサイドの仕組みも必要です。単純には、Linux等でサーバを立ち上げて、その上にエッジからのデータを受け取る機能と保存する機能、保存したデータを利用する機能を実現することになります。

従来のシステム開発ではこれらの機能をモノリシックなウェブサービスとして構築してきましたが、今時は単機能なマイクロサービスを(オープンな)プロトコルで疎に結合するアーキテクチャが流行りです。これを実現するために、Dockerコンテナを利用しましょう。

Dockerコンテナの技術的な説明は他の情報源に譲ります。

“Dockerをインストールする” の続きを読む