エンジニャーリング

技術ときどきネコ

owasp zapをEC2にたててみる

脆弱性確認などを行うにあたっていくつかある検証ツールのうち、owasp zapを使ってみることにした。
複数人で作業できるように、ローカル環境ではなく、クラウドに立てる。

まず、EC2インスタンスを設置(セッションマネージャーを使えるようにしておく)

taiyakikuroann.hatenablog.com

EC2インスタンスにセッションマネージャーで接続する。

最新にする

$ sudo yum update -y

dockerをインストール

$ sudo amazon-linux-extras install docker

dockerをスタートさせる

$ sudo service docker start

dockerをec2-userでも使えるように

$ sudo usermod -a -G docker ec2-user

owasp zapを起動する。このときイメージに「owasp/zap2docker-stable」を、コマンドに「zap-webswing.sh」を指定

$ docker run -u zap -p 8080:8080 -p 8090:8090 -i owasp/zap2docker-stable zap-webswing.sh

立ち上がったかを確認

$ docker ps

なんか出てくればOK
再起動が必要な場合はこちら。

docker restart [docker_name]

URLにアクセスして、このような画面が立ち上がればOK! http://[EC2のIPアドレス]:8080/zap
f:id:taiyakikuroann:20210811181637p:plain

テスト対象はローカル環境や自分が管理するシステムにすることに注意してください。クラウドの場合はその管理会社にあらかじめ申請が必要な場合もあるので確認が必要。
管理外のものに対して実行してしまうと不正アクセスで起訴されちゃう可能性もある怖いツールなので取り扱いは要注意。

AmazonLinux2にPHP8.0とphpredisをインストール

PHP7.4のセキュリティサポート期限が来年の11月末、アクティブサポートは今年の11月と聞いたので、新しく提供するサービスのPHPバージョンを上げることにした。

PHP8.0のインストール自体はPHP7.4だったものをPHP8.0に変えるだけの簡単なもの。しかし、必要なPHP関連のモジュールのうち php-pecl-redis だけyum使っても入らなかった。

$ sudo yum -y install php-pecl-redis --enablerepo=amzn2extra-php8.0
    :
Error: Package: php-pecl-redis-2.2.8-1.el7.x86_64 (epel)
    :

どうやらまだamzn2extraの方で対応されていない模様。

今回はRedisを使うため、代わりのモジュールを探して検証する手間ももったいないから、ソースを落としてビルドすることにした。

前提

  • php8.0がインストール済みであること
  • phpizeを使うために、php-develが必要。あらかじめyumでインストールしておく
  • makeするためにgccもインストールしておくこと

phpredisのインストール

githubから最新版のphpredisを直接gitでclone

$ cd /usr/local/src
$ sudo git clone https://github.com/phpredis/phpredis.git

makeする

$ cd /usr/local/src/phpredis
$ sudo phpize
$ sudo ./configure
$ sudo make && sudo make install

/etc/php.ini に追加

extension=redis.so

/etc/httpd/conf.d/php.conf
書き換える

    php_value session.save_handler "files"
    php_value session.save_path    "/var/lib/php/session"

  ↓
    php_value session.save_handler "redis"
    php_value session.save_path    "tcp://[redisのエンドポイント]:6379"

Apacheを再起動する

$ sudo systemctl stop httpd
$ sudo systemctl start httpd

確認

$ php -m | grep redis

redisって出たらOK

ansibleで書いたらこんな感じ

- hosts: all
  become: true
  gather_facts: no
  tasks:
    - name: Enable PHP8.0
      shell: "amazon-linux-extras enable php8.0"
      changed_when: False
    - name: Install PHP
      yum: name={{ items }} state=present enablerepo=amzn2extra-php8.0
      vars:
        items:
          - php
          - php-fpm
#          - php-pecl-redis # エラーになるので、ここでインストールができない
          - php-devel

    - name: Install packages
      yum: name={{ packages }} state=present
      vars:
        packages:
          - redis
          - git
          - gcc*

    - name: git clone for phpredis
      ansible.builtin.git:
        repo: https://github.com/phpredis/phpredis.git
        dest: /usr/local/src/phpredis
    - name: Install phpredis
      command: chdir=/usr/local/src/phpredis {{ item }}
      with_items:
        - phpize
        - ./configure
        - make
        - make install
    - name: lineinfile add extension redis.so in php.ini
      lineinfile:
        path: /etc/php.ini
        line: 'extension=redis.so'
        insertafter: EOF

yumでインストールできれば必要がない、gitやらgccを入れないといけないところが、ちょっと気持ちが悪いとことですが、仕方ない。 githubには、「Fix PHP 8.1 tests」と書かれていたので、8.1くらいまでは問題なく動くようです。

terrafrom の backend の設定に 変数を渡してみた(失敗談)

複数人で触れるように terraform で backend を S3 に設定することがある
この時に develop とか staging とか、分けたくなっちゃうことがある
CodeBuild 上で実行するため workspace は使えない

そんな時は backend に出力する tfstate を分けたらなんとかなるはず!
ということで、早速やってみた。

terraform {
  required_version = ">= 0.12.6"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "3.31.0"
    }
  }

  backend "s3" {
    bucket         = "sample-tfstate"
    key            = "sample-${var.XXXX}/main.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "terraform_state_lock_sample"
  }
}

このように、書いたとしても var.XXXX のパラメータを渡せなくて init 時にエラーになる。 こんな時は、どうすればいいかというと、init時に引数で渡せばいけるらしい。

こんな感じ

  • tfの設定
  backend "s3" {}
  • init を流す
terraform init \
    -backend-config="key=sample-dev/main.tfstate" \
    -backend-config="backet=sample-tfstate" \
    -backend-config="region=ap-northeast-1" \
    -backend-config="dynamodb_table=terraform_state_lock_sample"

公式にも書いてある
https://www.terraform.io/docs/language/settings/backends/configuration.html

うまくいく・・・はずだった。
devは問題なく立ったんだが、stgを立てるときにdevと混在したような形でcloud watchなどの一部の設定が入り混じる。例えば、XXXX-dev の名前のものが、XXXX-stg に変わってしまったり、同じ tfstate を参照していそうな、そんな感じの動きをしているように見える。

S3の出力先が間違ったのかなどチェックしてみるも、実行時間の通りに tfstate が出力されるし、この tfstate の中身も入り混じってはいない。

正しそうなだけに、謎は深まるばかり・・・

結局、解決には至らず、その部分を sed を使った置換を行うことで対処することにしました。
今回のように terraform を CI/CD っぽく流す時に、この辺の管理はどうするのがベストプラクティスってなんだろう?

terraform へ渡すパラメータを環境変数渡しにした時にハマったこと

ローカル環境などで terraform を実行する場合、パラメータを.tfvarsファイルにセットして渡しているのですが、gitでは管理したくないので CodeBuild で terraform を使う時に引数渡しをどうするか悩みどころ。

terraform plan -var 'env=dev'

のように、-var オプションを使って渡せば良いのだが、list渡しをどうすればいいのか分からなかった。
調べてみたところ、このように意外と簡単に設定できることがわかった。

terraform plan -var 'security_group_ids=[ "sg-012345678", "sg-876543210" ]'

ふむふむ、なるほど、と、環境変数を使う形に変えてみた。

export SECURITY_GROUP_IDS='[ "sg-012345678", "sg-876543210" ]'
terraform plan -var 'security_group_ids=$SECURITY_GROUP_IDS'

これを実行すると terraform でパラメータが変だというエラーで怒られる。
きちんとexportできていないのか心配になって、echo で出力させてみたところ、設定はできている模様。
パラメータとして渡っているはずなのになぜエラーなのか・・・
設定のやり方を少しずつ変えて色々なパターンを試してみるも、対策は全滅。

迷走を極める中、ヒントを発見。
qiita.com

この記事によれば、シングルクォートはただの文字列として扱われるとのこと!
もしかして?と思いダブルクォートに変えてみました。

terraform plan -var "security_group_ids=$SECURITY_GROUP_IDS"

通った!!!

'" は、似て非なるもの・・・改めて学びました。

AWSのACMで作ったプライベート認証局から取得したプライベートキーからパスフレーズを抜く

通常の ACM (AWS Certificate Manager) で取得した Public な SSL/TLS証明書は、Private Key のダウンロードができません。

そこで、AWSACM (AWS Certificate Manager) で PrivateCA をたてて、Private な SSL/TLS証明書を発行し、この PrivateCA で発行した証明書から Private Key のダウンロードをすることにした。

作った証明書
f:id:taiyakikuroann:20210718144013p:plain

エクスポートを選択
f:id:taiyakikuroann:20210718144026p:plain

ここでパスフレーズを設定
f:id:taiyakikuroann:20210718144034p:plain

ダウンロードする
f:id:taiyakikuroann:20210718144046p:plain

証明書をクラインと証明書として使いたい場合にプログラムに組み込むとき、このダウンロードした Private Key はパスフレーズで保護されていて、使い勝手が悪い。
curlの場合はオプションでパスフレーズを渡せばそのまま使えるが、そのままcurlを叩くのもちょっと違う。

そこで、opensslコマンドを使って、このパスフレーズを抜いた形で保存し直す。

openssl rsa -in private.key -out private_no_pass.key -passin pass:[指定したパスフレーズ]

private_no_pass.key というパスフレーズが解除されたプライベートキーが作成できた。

ECSなどを使う場合、CodeBuildなどでビルド中に非公開のS3バケットから取得してパスフレーズを解除して取り込むなどの処理を追加すれば、よりセキュアな形でのPrivate Keyの管理ができて良いかと思います。

API Gateway + Lambda で403エラーが出て困った話

API Gateway を private に配置して、VPCエンドポイントからのアクセスを試みた。

タイムアウトした。
うむうむ、エンドポイントに設置したSGだなとインバウンドをセットして接続したところ、今度は403エラーに。

これは権限周りの設定のエラーになるので、resouce policy もセットして接続を試みた。

うん、403エラー。何故だ?!
調べても調べても resouce policy にセットしたら OK だと書かれてる。
以前設置したAPI Gatewayの設定を見ても同じような設定になっていて何ら問題はなさそう。

はて・・・どこの設定に抜けがあるのか??

とうとう英語のページにも手を出して調査を続ける。
そこにはこんな記載が。

if you update the resource policy after the API is created, you’ll need to deploy the API to propagate the changes after you’ve attached the updated policy.

APIの作成後にリソースポリシーを更新する場合は、更新されたポリシーを添付した後で変更を反映するためにAPIデプロイする必要があります

なん・・・だ・・・と・・・?!デプロイ?!!!

はい、デプロイ忘れてました。。。
API Gateway あまり使わないので思いつきもしなかった。
公開系に反映するのにはデプロイが必要なのです。

サクッとデプロイしたら難なく通りましたとさ。
2hくらい無駄にしてしまった。
Terraform で自動更新できるようにセットしたからもう忘れない(はず)