ExpressVPNがWebサーバーにパッチを適用して安全に保つ方法

[ware_item id=33][/ware_item]

ExpressVPNサーバーは灰から上昇します。


この記事では、ExpressVPNのアプローチについて説明します ExpressVPN Webサイトを実行するインフラストラクチャのセキュリティパッチ管理 (VPNサーバーではありません)。一般的に、セキュリティへのアプローチは次のとおりです。

  1. システムを非常に作る ハッキングが難しい.
  2. 潜在的な損傷を最小限に抑える システムが仮にハッキングされ、一部のシステムを完全に安全にできないという事実を認めた場合。通常、これはアプリケーションのアクセスを最小限に抑えるアーキテクチャ設計段階から始まります.
  3. 時間を最小限に抑える システムが危険にさらされる可能性があること.
  4. 検証 これらのポイントは、内部と外部の両方の定期的なペンテストで.

セキュリティは私たちの文化に根付いており、私たちのすべての仕事を導く主な関心事です。セキュリティソフトウェア開発プラクティス、アプリケーションセキュリティ、従業員のプロセスとトレーニングなど、他にも多くのトピックがありますが、これらはこの投稿の範囲外です。.

ここでは、以下を達成する方法について説明します。

  1. すべてのサーバーに完全なパッチが適用されていることを確認してください CVEの公開から24時間以内に遅れることはありません.
  2. サーバーが24時間以上使用されないようにします, したがって、攻撃者が持続できる時間に上限を設ける.

私たちは両方の目標を達成します OSおよびすべての最新のパッチから開始してサーバーを再構築し、24時間ごとに少なくとも1回破棄する自動システム.

この記事の目的は、同様の課題に直面している他の開発者に役立ち、ExpressVPNの運用の透明性をお客様とメディアに提供することです。.

AnsibleプレイブックとCloudformationの使用方法

ExpressVPNのウェブインフラストラクチャはAWSでホストされ(専用ハードウェアで実行されるVPNサーバーとは対照的に)、再構築を可能にするためにその機能を多用しています.

Webインフラストラクチャ全体がCloudformationでプロビジョニングされており、できるだけ多くのプロセスを自動化しようとしています。ただし、繰り返しの必要性、全体的な可読性の低さ、JSONまたはYAML構文の制約のために、未加工のCloudformationテンプレートでの作業は非常に不快であることがわかりました。.

これを軽減するために、cloudformation-ruby-dslというDSLを使用して、Rubyでテンプレート定義を記述し、JSONでCloudformationテンプレートをエクスポートできるようにします.

特に、DSLを使用すると、ユーザーデータスクリプトを、JSONに自動的に変換される通常のスクリプトとして記述することができます(スクリプトの各行を有効なJSON文字列にする苦痛なプロセスを経ることはありません).

cloudformation-infrastructureと呼ばれる一般的なAnsibleロールは、実際のテンプレートを一時ファイルにレンダリングし、cloudformation Ansibleモジュールで使用されます。

- 名前: '{{コンポーネント}}スタックcloudformation jsonをレンダリングします'
シェル:「ルビー "{{template_name | default(component)}}。rb" expand --stack-name {{stack}} --region {{aws_region}} > {{tempfile_path}} '
引数:
chdir:../cloudformation/templates
changed_when:false

-名前:「{{コンポーネント}}スタックを作成/更新」
雲形成:
stack_name: '{{stack}}-{{xv_env_name}}-{{component}}'
状態:現在
地域: '{{aws_region}}'
テンプレート: '{{tempfile_path}}'
template_parameters: '{{template_parameters |デフォルト({}) }}'
stack_policy: '{{stack_policy}}'
登録:cf_result

プレイブックでは、異なるコンポーネント変数を使用してcloudformation-infrastructureロールを数回呼び出して、複数のCloudformationスタックを作成します。たとえば、VPCと関連リソースを定義するネットワークスタックと、Auto Scalingグループ、起動構成、ライフサイクルフックなどを定義するアプリスタックがあります。.

次に、ややlyいですが便利なトリックを使用して、cloudformationモジュールの出力を後続のロールのAnsible変数に変換します。 Ansibleでは動的な名前の変数を作成できないため、このアプローチを使用する必要があります。

- インクルード:_tempfile.yml
-コピー:
内容: '{{コンポーネント| regex_replace("-", "_")}} _ stack:{{cf_result.stack_outputs | to_json}} '
dest: '{{tempfile_path}}。json'
no_log:true
changed_when:false

-include_vars: '{{tempfile_path}}。json'

EC2 Auto Scalingグループの更新

ExpressVPN Webサイトは、Application Load Balancerの背後にあるAuto Scalingグループの複数のEC2インスタンスでホストされ、インスタンスが終了する前にロードバランサーが既存の接続を排出できるため、ダウンタイムなしでサーバーを破棄できます.

Cloudformationは再構築全体を調整し、AWS :: AutoScaling :: AutoScalingGroupリソースのAutoScalingRollingUpdate UpdatePolicy属性を使用して、24時間ごとに上記のAnsibleプレイブックをトリガーしてすべてのインスタンスを再構築します.

変更せずに繰り返しトリガーする場合、UpdatePolicy属性は使用されません。ドキュメントに記載されている特別な状況でのみ呼び出されます。そのような状況の1つは、Auto Scaling起動構成(Auto ScalingグループがEC2インスタンスを起動するために使用するテンプレート)の更新です。これには、新しいインスタンスの作成時に実行されるEC2ユーザーデータスクリプトが含まれます。

リソース 'AppLaunchConfiguration'、タイプ: 'AWS :: AutoScaling :: LaunchConfiguration'、
プロパティ: {
KeyName:param( 'AppServerKey')、
ImageId:param( 'AppServerAMI')、
InstanceType:param( 'AppServerInstanceType')、
SecurityGroups:[
param( 'SecurityGroupApp')、
]、
IamInstanceProfile:param( 'RebuildIamInstanceProfile')、
InstanceMonitoring:true、
BlockDeviceMappings:[
{
DeviceName: '/ dev / sda1'、#ルートボリューム
Ebs:{
VolumeSize:param( 'AppServerStorageSize')、
VolumeType:param( 'AppServerStorageType')、
DeleteOnTermination:true、
}、
}、
]、
UserData:base64(interpolate(file( 'scripts / app_user_data.sh')))、
}

ユーザーデータスクリプトを更新した場合、コメントも含めて、起動構成は変更されたと見なされ、CloudformationはAuto Scalingグループ内のすべてのインスタンスを更新して新しい起動構成に準拠します.

cloudformation-ruby-dslとその補間ユーティリティ関数のおかげで、app_user_data.shスクリプトでCloudformation参照を使用できます。

readonly rebuild_timestamp ="{{param( 'RebuildTimestamp')}}"

この手順により、再構築がトリガーされるたびに起動構成が新しくなります。.

ライフサイクルフック

Auto Scalingライフサイクルフックを使用して、インスタンスが完全にプロビジョニングされ、稼働する前に必要なヘルスチェックに合格するようにします.

ライフサイクルフックを使用すると、Cloudformationで更新をトリガーするときと自動スケーリングイベントが発生するとき(たとえば、インスタンスがEC2ヘルスチェックに失敗して終了するとき)に同じインスタンスライフサイクルを持つことができます。 Cloudformationが更新をトリガーしたときにのみ適用されるため、cfn-signalおよびWaitOnResourceSignals自動スケーリング更新ポリシーは使用しません。.

自動スケーリンググループが新しいインスタンスを作成すると、EC2_INSTANCE_LAUNCHINGライフサイクルフックがトリガーされ、インスタンスが自動的にPending:Wait状態になります.

インスタンスが完全に構成された後、ユーザーデータスクリプトからのcurlを使用して、独自のヘルスチェックエンドポイントへのヒットを開始します。ヘルスチェックによりアプリケーションが正常であると報告されたら、このライフサイクルフックに対してCONTINUEアクションを発行します。これにより、インスタンスはロードバランサーに接続され、トラフィックの提供を開始します.

ヘルスチェックが失敗した場合、障害のあるインスタンスを終了するABANDONアクションを発行し、自動スケーリンググループが別のインスタンスを起動します.

ヘルスチェックに合格しないことに加えて、ユーザーデータスクリプトは他のポイントで失敗する場合があります。たとえば、一時的な接続の問題によりソフトウェアのインストールが妨げられる場合などです。.

新しいインスタンスの作成は、決して正常にならないことに気付いたらすぐに失敗するようにします。それを実現するために、ユーザーデータスクリプトにERRトラップをset -o errtraceとともに設定し、ABANDONライフサイクルアクションを送信する関数を呼び出して、障害のあるインスタンスができるだけ早く終了できるようにします.

ユーザーデータスクリプト

ユーザーデータスクリプトは、インスタンスに必要なすべてのソフトウェアをインストールする役割を果たします。 Ansibleを使用してインスタンスをプロビジョニングし、Capistranoを使用してアプリケーションを長期間デプロイすることに成功したため、ここでもそれらを使用して、通常のデプロイと再構築の最小限の違いを考慮しました.

ユーザーデータスクリプトはGithubからアプリケーションリポジトリをチェックアウトします。これにはAnsibleプロビジョニングスクリプトが含まれており、ローカルホストを指すAnsibleとCapistranoを実行します.

コードをチェックアウトするときは、現在デプロイされているバージョンのアプリケーションが再構築中にデプロイされていることを確認する必要があります。 Capistrano展開スクリプトには、現在展開されているコミットSHAを保存するS3のファイルを更新するタスクが含まれています。再構築が行われると、システムはそのファイルからデプロイされるはずのコミットを取得します.

ソフトウェア更新は、無人アップグレード-dコマンドを使用してフォアグラウンドで無人アップグレードを実行することにより適用されます。完了すると、インスタンスは再起動し、ヘルスチェックを開始します.

秘密に対処する

サーバーは、EC2パラメーターストアから取得したシークレット(Ansible vaultパスワードなど)に一時的にアクセスする必要があります。サーバーは、再構築中に短時間だけシークレットにアクセスできます。取得した後、すぐに初期インスタンスプロファイルを、アプリケーションの実行に必要なリソースにのみアクセスできる別のプロファイルに置き換えます.

インスタンスの永続メモリにシークレットを保存しないようにします。ディスクに保存する唯一の秘密はGithub SSHキーですが、パスフレーズではありません。 Ansible vaultのパスワードも保存しません.

ただし、これらのパスフレーズをそれぞれSSHとAnsibleに渡す必要があります。パスフレーズがコマンドの一部である場合は、正当な理由により、対話モードでのみ可能です(つまり、ユーティリティはパスフレーズを手動で入力するようユーザーに要求します)シェル履歴に保存され、psを実行するとシステム内のすべてのユーザーに表示されます。 expectユーティリティを使用して、これらのツールとの対話を自動化します。

期待する << EOF
cd $ {repo_dir}
spawn make ansible_local env = $ {deploy_env} stack = $ {stack} hostname = $ {server_hostname}
タイムアウト2を設定
「Vaultパスワード」が必要です
送る "$ {vault_password} \ r"
タイムアウト900を設定
{
"unreachable = 0 failed = 0" {
出口0
}
eof {
1番出口
}
タイムアウト {
1番出口
}
}
EOF

再構築のトリガー

インフラストラクチャの作成/更新に使用されるのと同じCloudformationスクリプトを実行して再構築をトリガーするため、再構築中に更新されるはずのないインフラストラクチャの一部を誤って更新しないようにする必要があります.

再構築に必要なリソースのみが更新されるように、Cloudformationスタックに制限的なスタックポリシーを設定することでこれを実現します。

{
"ステートメント" :[
{
"効果" : "許可する",
"アクション" : "更新:変更",
"主要な": "*",
"資源" :[
"LogicalResourceId / * AutoScalingGroup"
]
}、
{
"効果" : "許可する",
"アクション" : "更新:交換",
"主要な": "*",
"資源" :[
"LogicalResourceId / * LaunchConfiguration"
]
}
]
}

実際のインフラストラクチャ更新を行う必要がある場合、スタックポリシーを手動で更新して、これらのリソースの明示的な更新を許可する必要があります.

サーバーのホスト名とIPは毎日変わるため、ローカルのAnsibleインベントリとSSH構成を更新するスクリプトがあります。タグを使用してAWS APIを介してインスタンスを検出し、ERBテンプレートからインベントリおよび構成ファイルをレンダリングし、新しいIPをSSH known_hostsに追加します.

ExpressVPNは最高のセキュリティ標準に準拠しています

サーバーを再構築すると、特定の脅威(カーネル/ソフトウェアの脆弱性を介してサーバーにアクセスする攻撃者)から保護されます。.

ただし、これはインフラストラクチャを安全に保つための多くの方法の1つにすぎません。これには、定期的なセキュリティ監査の実施や重要なシステムへのインターネットからのアクセスの制限が含まれます。.

さらに、すべてのコードと内部プロセスが最高のセキュリティ標準に従っていることを確認します.

ExpressVPNがWebサーバーにパッチを適用して安全に保つ方法
admin Author
Sorry! The Author has not filled his profile.