SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

リモートワークでの開発環境を改善しよう

DockerとSSHでセキュリティと手軽さを両立したリモート開発環境を構築する

リモートワークでの開発環境を改善しよう 第1回

  • X ポスト
  • このエントリーをはてなブックマークに追加

DockerとSSHを用いた、柔軟性と管理性を両立する開発環境

 そして、現在の開発環境は図3のようになりました。

図3:最終的なリモート開発環境の構成
図3:最終的なリモート開発環境の構成

 ありきたりの構成になってしまったとは思いますが、やはりSSHというソリューションが最も汎用的で、かつ自由度が高いことを実感しました。ただし、SSHをインターネット上に開放することはセキュリティ的にあまり好ましくはありません。それらの問題の改善も後述します。

Portainerの環境構築

 今やDockerは開発者にとって必要不可欠になりつつあり、サーバソリューションを含む開発であれば必須のツールです。

 しかし、Dockerの基本的な操作方法がわかることと、実際に環境を構築することには差があります。誰もが同じDocker環境を作れるように定義するのは難しく、またその事に注力することは開発をする上で本質的な作業とも言えません。

 そこで、Docker環境を社内で使い回せるように複数の同じDockerコンテナを作成します。そして、各開発者はWebインターフェースを通じて各Dockerコンテナにアクセスできるようにすれば、開発業務として十分なはずです。そのような要件を実現するのがPortainerというツールです。

 Portainer自体がDocker上で動作するので、Dockerがすでにインストールされていればリスト1のようにインストールは簡単です。

[リスト1]Portainerのインストール例
#docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer-ce

 そして、以下のようなURLですぐに利用できます。

[リスト2]Portainerへの利用URL
http://<host>:9000/

 各コンテナは図4のように表示されます。ローカル上のコンテナであれば特別な設定はいりません。

図4:Dockerにインストールされているコンテナ一覧
図4:Dockerにインストールされているコンテナ一覧

 コンテナの停止や起動などもできますが、各コンテナのコンソールにも図5のように簡単にアクセスできます。

図5:コンテナのコンソール利用例
図5:コンテナのコンソール利用例

 Dockerコマンドを使うのは面倒だなと思う方も多く、筆者もコーディング中であればDockerを意識した作業は面倒にすら感じます。そのときに、Docker DesktopのようにUIを用いて簡単に操作したいという方も多いと思います。Portainerを使えば、そのような非常に使いやすい環境がWebシステムとして構築できます。

 また、今回は詳しくは説明しませんが、Portainerはローカル上のDockerだけではなくリモートにあるDockerコンテナも管理できます。従って、Dockerコンテナの数が必要になっても拡張しやすいはずです。

SSHでのセキュリティ対策

 SSHポートをインターネットに公開することはあまり好ましくありません。開発者がリモート作業用に固定のグローバルアドレスを持っていればアクセス制限は簡単ですが、自宅からアクセスする場合には事前にどのIPからアクセスしてくるかは大概わかりません。

 そこで、図6のようなシステムで、許可された人だけがSSH接続できるようなシステムにしています。実際に利用しているシステムはもう少し複雑ですが、今回は概要と主となる部分のみについて説明します。

図6:SSHアクセスを申請ごとに許可するシステム例
図6:SSHアクセスを申請ごとに許可するシステム例

 (1)Web画面を通じて、メールアドレスを入力し利用申請をします。この際、あらかじめ許可されたメールアドレスやドメインのみしか許さないようなシステムを作っておけば、誰でも利用できるようなことにはなりません。

 (2)Webシステムを通じて取得できるグローバルアドレスを取得します。

 (3)利用されていないDockerコンテナとホストで共有しているディレクトリを開発者ごとの$HOME(※)にシンボリックリンクなどを使って切り替えます。

 ここでのディレクトリ構造は実際に作成するシステムによって異なると思いますので詳細は割愛しますが、Dockerコンテナ側から見えるディレクトリをプロジェクトごとに決まった構成でユーザごとに割り当てたディレクトリにします。Dockerのコンテナと利用者を固定にするのであれば、このような仕組みは必要ありません。

 (4)(2)で取得したグローバルアドレスからのみ接続できるSSHポートをポートフォーワードにより新たに作成します。

 (5)取得したポート番号を申請者にメールで伝えます。

 (6)開発者はそのメールに記載されているポート番号を使ってSSHでアクセスします。

※ここでは厳密な$HOMEではなく、開発者ごとに利用するディレクトリ程度の意味です。

 このような流れで開発が開始できます。

 リスト3は、(4)で利用する指定されたアドレスに一定時間、SSH接続を許可するシェルのサンプルです。

[リスト3]SSHポートを動的に取得したポートから転送する設定と自動停止
#!/bin/sh
# $1 : HOST

min=${2:-2}
sec=`expr $min '*' 60  - 30`;
# (1) 動的に空いているポート番号を取得する
port=`php -r '$s = socket_create_listen(0); socket_getsockname($s,$addr,$port); print($port);'`;
# (2) redirを使って22番ポートへのポートフォーワード指定を行う
redir -n -t $sec :$port 127.0.0.1:22 &
# (3) プロセスIDの取得
PID=$!
# (4) ファイアーウォール(ufw)でアクセスを許可する
ufw allow from $1 to 0.0.0.0/0 port $port
# (5) 時間が来たら切断する
echo "/usr/local/bin/close.sh $1 $port $PID" | at now + $min min
echo $port

 (1)では、PHPを使って現在利用可能なポート番号を取得しています。PHP以外でも空いているポート番号を調べる方法は他にもありますので、PHPがインストールされていない場合にはこの部分を書き換えてください。

 また、(2)ではredirというポートフォーワードするコマンドを使っています。このコマンドは特定のポートで受信したパケットを他のポートへ簡単に転送することができます。「-t」オプションで指定しているのは最大接続秒数です。これでつないだままになるという状態をなくしています。

 そして、今回の例ではローカルホストに転送していますが、他のサーバに転送することもできます。従って、クライアントからSSHで接続するサーバとして踏み台サーバを用意するような場合でも対応可能です。

 (4)はufwを使ってファイアーウォールで指定されたポート番号への接続を許可するようにしています。0.0.0.0/0を指定しているのはIPv4のみを対象とするためです。ufwはデフォルトでIPv4とv6の設定の2つの指定が行われるので、意図的にv4のみにして、削除する指定も1つにして処理を単純にしています。

 そして、最後に利用期限の時間が来たら(5)のようにatコマンドを使って切断する処理を自動で実行するようにしています。

 このシェルプログラムはリスト4のようにして利用する想定です。ただし、前提としてはUbuntu上で動作することを想定しています。

[リスト4]Portainerへの利用URL
./open.sh <グローバルアドレス> 60

 このような処理を利用申請ごとに実行するようにしています。ただし、実行にはRoot権限が必要なため、Webサーバ上で実行することは避けたいものです。そのため、例えば申請データをDBに保存しておき、一定間隔のcronなどで実行するような方法もあります。

 また、先ほどの(5)で実行する切断処理スクリプトはリスト5のようになります。

[リスト5]SSHポートを動的に取得したポートから転送する設定と自動停止
#!/bin/sh
# (1) 許可した指定の行を取得する
LINE=`ufw status numbered | grep ALLOW | grep $1 | grep $2`

if [ "x$LINE" = "x" ]; then
    echo "not found"
else
    # (2)
    N=`echo $LINE | cut -f2 -d[ | cut -f1 -d]`
    if [ "x$N" != "x" ]; then
        # (3) ファイアーウォールの指定削除
        ufw --force delete $N
        # (4) redirのプロセス停止
	    kill $3
    fi
fi

 (1)は接続時に登録したファイアーウォールでの対象の行を取得しています。(2)ではその指定の行番号を取得します。そして(3)で対象のファイアーウォールでの指定を削除したら、(4)で起動時に立ち上げたredirのプロセスを停止しています。

 このように少々面倒な方法を採るのはSSHへの接続制限という面もありますが、セキュリティ面にも理由があります。リモート開発環境では実際に誰がどの程度使っているのかがわからなくなりやすいということがよくあります。そのため、プロジェクトを抜けたエンジニアや退職した社員などが接続できる状態のままになってしまうという状態になりやすい傾向があります。そこで、SSHの接続履歴をシステムのログから見るだけではなく、明確な申請ベースにすることで、リモート環境を誰がいつから作業をしたのか履歴が作れるようにしています。

 また、このような管理はSSHだけではなく、他のサービスであっても同様の手法で許可された時間に許可された人だけという仕組みが作りやすいはずです。

 特に開発時には、厳しい制限をしてしまうと実用性が下がってしまいます。そのため、できることの制限は緩くし、一方で時間と利用者の制限を厳格にすることでこの両立を図っていくようにしています。

さいごに

 現在の開発環境には管理と柔軟性が求められています。これらの点に関して、オフィスのような環境ではある程度実現ができつつあります。しかしまだ十分とは言えません。

 そんな状況のなかリモート開発での環境作りとなると、まだまだ手探りという現場も多いのではないでしょうか。そのとき、既存のサービスやシステムを組み合わせつつ、足りない部分をほんの少しだけ開発者が補えば、それぞれのプロジェクトによって良い環境ができるのではないかと思っています。

 そこで今回は、オフィスで使っている、もしくは使える開発環境をできるだけそのままリモート開発環境にも適用できるように考え、構築した事例を紹介してみました。

 次回は、VPN環境があることを前提にこのリモート開発環境を補足していった事例を紹介します。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
リモートワークでの開発環境を改善しよう連載記事一覧
この記事の著者

WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/16975 2022/12/28 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング