kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Goss: サーバー設定の検証を自動化しよう

歴史的経緯 (?) によって構成ドリフト状態(Infrastructure as Code 本参照)になっている Amazon EC2 インスタンスが複数台あって,まずはサーバーの期待値を宣言して自動テストもしくは自動検証をする仕組みを作りたいなぁーと考えていた💡

今までの経験としては,2014-2016年頃によく使っていて,過去にブログ記事を書いていたりもする Serverspec を思い出しつつ,Ruby に依存せず実行ファイルをポンッと置いたらすぐに使えるようなツールがあったら良いなぁーと思って探していたところ,Go で実装されていて,サーバーの期待値を YAML で宣言できる「Goss」を発見した👀 実際に試してみたらとても便利で最高だったので,検証ログをまとめて公開することにした❗️

github.com

検証環境

今回は別の検証環境として使っていた Amazon EC2 インスタンス (Amazon Linux 2023 AMI) を使う.

セットアップ

README を参考にセットアップする.現状だと v0.3.23 になった.

$ curl -fsSL https://goss.rocks/install | sh

$ goss --version
goss version v0.3.23

しかし README に Using curl | sh is not recommended for production systems と書いてあるため,本番環境などに使う場合はしっかりと VERSION を固定してインストールする👌 現時点で最新は v0.4.2 で,今回はこちらを使う.

$ VERSION=v0.4.2
$ curl -L "https://github.com/goss-org/goss/releases/download/${VERSION}/goss-linux-amd64" -o /usr/local/bin/goss
$ chmod +rx /usr/local/bin/goss

$ goss --version
goss version v0.4.2

Goss でサポートしてるリソース

Goss では「サーバーの期待値を宣言できる」と書いたけど,実際には以下のように多くのリソースがサポートされている❗️

  • addr: google.com:80 などリモートアドレスに疎通できるか検証する
  • command: go version など任意のコマンドを実行した結果(標準出力・標準エラー・終了ステータスなど)を検証する
  • dns: A レコードや CNAME レコードなど名前解決の結果を検証する
  • file: ファイルやディレクトリの存在や owner / group / mode などの設定を検証する
  • group: Linux グループなどグループの設定を検証する
  • http: HTTP リクエストを送信してレスポンス(ステータスコードなど)を検証する
  • interface: eth0 などネットワークインタフェースの設定を検証する
  • kernel-param: sysctl コマンドで設定できるカーネルパラメータを検証する
  • mount: ファイルシステムのマウントポイントを検証する
  • matching: matcher 構文を使って任意の値を検証する
  • package: httpd などパッケージを検証する
  • port: tcp:22 などポートの状態を検証する
  • process: プロセスの状態を検証する
  • service: サービスの状態を検証する
  • user: Linux ユーザーなどユーザーの設定を検証する

リソースの詳細やリソースごとの構文などは以下のドキュメント(マニュアル)に詳しくに載っている📝

github.com

Goss を試す

さっそく Goss を試していく❗️

🌷 サンプル: sshd

まずは README の Quick start に載っている sshd のサンプルを試す❗️

goss autoadd コマンドを使うと環境に最適な Goss ファイル goss.yaml を自動生成してくれる.

$ goss autoadd sshd

自動生成された goss.yaml は以下のようになっていた.自動的に port / service / user / group / process の検証項目が追加されていた👏

port:
  tcp:22:
    listening: true
    ip:
    - 0.0.0.0
  tcp6:22:
    listening: true
    ip:
    - '::'
service:
  sshd:
    enabled: true
    running: true
user:
  sshd:
    exists: true
    uid: 74
    gid: 74
    groups:
    - sshd
    home: /usr/share/empty.sshd
    shell: /sbin/nologin
group:
  sshd:
    exists: true
    gid: 74
process:
  sshd:
    running: true

Goss で検証を実行する場合は goss validate コマンドを実行する.今回は Failed: 0 となって「すべて成功」していた.

$ goss validate
...............

Total Duration: 0.021s
Count: 15, Failed: 0, Skipped: 0

期待通りの結果にならない状態にするため,意図的に sshd を止めて,もう一度 goss validate コマンドを実行すると,今度は Failed: 4「一部失敗」していた.

$ systemctl stop sshd

$ goss validate
FSFSF.........F

Failures/Skipped:

Port: tcp6:22: listening:
Expected
    false
to equal
    true
Port: tcp6:22: ip: skipped

Port: tcp:22: listening:
Expected
    false
to equal
    true
Port: tcp:22: ip: skipped

Process: sshd: running:
Expected
    false
to equal
    true

Service: sshd: running:
Expected
    false
to equal
    true

Total Duration: 0.022s
Count: 15, Failed: 4, Skipped: 2

🌷 サンプル: amazon-ssm-agent

次は goss add コマンドを使って amazon-ssm-agent の Goss ファイルを生成する❗️

goss add コマンドは goss autoadd コマンドと違って packageservice など具体的なリソースを指定して Goss ファイルを生成する.そして goss コマンドに --gossfile オプションを付けるとデフォルトの goss.yaml 以外のファイルを対象にできる.今回は amazon-ssm-agent の検証に限定するため,ファイル名を変えて Goss ファイル goss-ssm.yaml を生成する.

$ goss --gossfile goss-ssm.yaml add package amazon-ssm-agent
$ goss --gossfile goss-ssm.yaml add service amazon-ssm-agent

自動生成された goss-ssm.yaml は以下のようになっていた.

package:
  amazon-ssm-agent:
    installed: true
    versions:
    - 3.2.1377.0-1.amzn2023
service:
  amazon-ssm-agent:
    enabled: true
    running: true

さらに file リソースも試したかったため,amazon-ssm-agent によって出力されるログファイル /var/log/amazon/ssm/amazon-ssm-agent.log も追加する.

$ goss --gossfile goss-ssm.yaml add file /var/log/amazon/ssm/amazon-ssm-agent.log

最終的に goss-ssm.yaml は以下のようになった.

file:
  /var/log/amazon/ssm/amazon-ssm-agent.log:
    exists: true
    mode: "0644"
    owner: root
    group: root
    filetype: file
    contents: []
package:
  amazon-ssm-agent:
    installed: true
    versions:
    - 3.2.1377.0-1.amzn2023
service:
  amazon-ssm-agent:
    enabled: true
    running: true

🌷 サンプル: openssl

今度は command リソースを試してみたく,openssl version コマンドを実行した結果(標準出力)の一致を検証する Goss ファイルを生成する❗️

$ goss --gossfile goss-openssl.yaml add command 'openssl version'

自動生成された goss-openssl.yaml は以下のようになっていた.

command:
  openssl version:
    exit-status: 0
    stdout:
    - 'OpenSSL 3.0.8 7 Feb 2023 (Library: OpenSSL 3.0.8 7 Feb 2023)'
    stderr: ""
    timeout: 10000

🌷 サンプル: gossfile で Goss ファイルをまとめる

最初に生成した Goss ファイル goss.yamlgoss-sshd.yaml にリネームしてから,新しく以下の YAML で goss.yaml を作る.gossfile リソースでは指定した Goss ファイルをまとめることができるため,Goss ファイルを細かく分割しつつ,対象のサーバーに必要な検証項目をまとめて宣言できる.これは便利❗️

gossfile:
  goss-openssl.yaml: {}
  goss-sshd.yaml: {}
  goss-ssm.yaml: {}

現時点で Goss ファイルは4つある.

$ ls -1
goss-openssl.yaml
goss-sshd.yaml
goss-ssm.yaml
goss.yaml

このまま goss validate コマンドを実行すると Count: 27 と表示されている通り,3つの Goss ファイルの検証項目をすべて実行できている \( 'ω')/ 良いじゃん〜

$ goss validate
...........................

Total Duration: 0.086s
Count: 27, Failed: 0, Skipped: 0

Goss エンドポイントを公開する

Goss で特徴的な(おもしろい)機能の一つとして goss serve コマンドがある.以下のように実行すると Goss の検証結果を取得できる API を起動できる❗️そして検証結果のフォーマットとしては jsonjunitprometheus などを指定できるため,Goss の検証結果を他のサービスと連携できるようになっているのは拡張性がありそう \( 'ω')/

rspecish フォーマット API(デフォルト)

$ goss serve

$ curl http://localhost:8080/healthz
...........................

Total Duration: 0.077s
Count: 27, Failed: 0, Skipped: 0

json フォーマット API

$ goss serve --format json

$ curl -s http://localhost:8080/healthz | jq .summary
{
  "failed-count": 0,
  "skipped-count": 0,
  "summary-line": "Count: 27, Failed: 0, Skipped: 0, Duration: 0.080s",
  "test-count": 27,
  "total-duration": 79993971
}

まとめ

サーバーの期待値を YAML で宣言して自動検証できる「Goss」を試してみた❗️最高〜 \( 'ω')/

現時点で Goss はリモート実行には対応してなく,検証するサーバーに Goss をセットアップして実行する必要があるため,AWS Systems Manager Run Command を使って Goss を実行する仕組みを検証しているところ.また別の記事にまとめようと思う📝