GitHub Actionsを使用してバージョンを指定してパッチを当てたバイナリをビルドするというフローを自動化する

リポジトリはこちらです。

github.com

何をやっているかというと過去にも実施したWazuh agentにパッチを1行当ててビルドするだけです。

k1low.hatenablog.com

ただ、GitHub Actionsの利用方法としてはなかなか面白い使い方かなと思ったのでエントリにしました。

実現しているのは

欲しい「パッチ済みWazuh agentのバージョン」をタグとして git push すると、それをトリガーにGitHub Actionsのワークフローが起動して

  1. 指定のバージョンの wazuh/wazuh のソースをgit clone
  2. パッチを適用
  3. ディストリビューションごとにビルド
  4. ビルドしたバイナリをtarでまとめてReleasesにアップロード

を並列に実行するというものです。

以下YAML全文

name: build

on:
  push:
    tags:
      - v*

jobs:
  debug-build:
    name: Build agent with patch
    strategy:
      matrix:
        platform: [ubuntu-xenial, ubuntu-bionic, centos-6, centos-7]
    runs-on: ubuntu-latest
    steps:
      - name: Install packages
        run: |
          sudo apt-get -qq update
          sudo apt-get install -qq git tar
          curl -sL https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz -o ghr.tar.gz
          sudo sh -c "tar xf ghr.tar.gz -O ghr_v0.13.0_linux_amd64/ghr > /usr/local/bin/ghr"
          sudo chmod +x /usr/local/bin/ghr
      - name: Check out source code
        uses: actions/checkout@v2

      - name: Make directory
        run: |
          mkdir dist
          mkdir pkg
      - name: Build agent with patch
        run: |
          docker build . -f Dockerfile.${{ matrix.platform }} -t wazuh-debug
          docker run --rm -v $(pwd)/dist:/dist --env WAZUH_VERSION=${GITHUB_REF##*/} wazuh-debug
      - name: Release
        run: |
          tar cfz pkg/wazuh-debug-${GITHUB_REF##*/}-${{ matrix.platform }}.tar.gz dist/
          ghr -replace ${GITHUB_REF##*/} pkg/
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

指定のバージョンでビルドする

前提として

  • wazuh/wazuh がバージョンでタグを切っている

というのがあるのですが、それを利用して各DockerfileのCMDに設定している build.sh 内で

git clone https://github.com/wazuh/wazuh.git -b ${WAZUH_VERSION} --depth 1

を実行し、必要なソースコードをgit cloneしてきています。

${WAZUH_VERSION} はどう取得してきているかというと、環境変数 GITHUB_REF${GITHUB_REF##*/} で文字列置換して取得しています。

GITHUB_REFGitHub Actionsにおいてブランチやタグのrefなのですが、on.push.tags: でタグpushのみでジョブが実行されるようにしているので必然的に「タグ=Wazuh agentのバージョン」を取得できる仕組みになっています。

並列実行する

これは strategy.matrix: を利用しています。

よく strategy.matrix: は言語のバージョンや実行環境の種類などが例にあげられますが、別にそれらに限らず並列実行するのに使えるので便利です。

今回はディストリビューション名を与えて、それを使用してDockerfileのファイル名やリリースするアーカイブ名に使っています。

作成したバイナリは ghr でアップロードしています。

ディストリビューションごとにバイナリファイル名をうまく分けて、かつ -replace オプションを使うことで

  • 並列で
  • 何度でも同じタグのReleaseに

アップロードできるので便利です。


以上です。

今年はさらにGitHub Actionsを使い倒すことになりそうです。

2020年の振り返りと2021年の抱負

年が変わるタイミングは寝ていました。起きたら明けてました。

2020年の振り返り

内に籠もった年だったのかなと思います。これには様々な要因があるとは思いますが、あまりアクティブな活動はなかったかなと思います。

(子育てが自分のライフサイクルに深く組み込まれたのは前からですが)在宅勤務になったことと、外でのイベントごとが皆無になったことで発散する場が無くなってしまった感じです(「発散」というと何か鬱憤が溜まっているように聞こえるかもしれませんが、そうではなくて「うまくリズムが取れない」というか)。

自分は生活環境の変化に弱い自覚があるのですが、2020年1年かけてもまだ慣れていないのかも?という感覚です。

2020年の抱負は「視座を上げる」でした。

エンジニアとしては、完全に外的要因なのですが社内の評価制度や周辺の目標(?)設定スキームがさらに洗練されたことがキッカケで中長期的な視点で考える機会が増えました。本当にありがたい。

その結果自分に足りないモノがよく見えるようになってしまい凹むこともありますが、気づかないままでいるより断然マシです。気づきから改善に繋げていきたいです。

ヒトとしての視座は、、、、イマイチ上がらず。です。

OSS活動

いろいろ作りました。

2020年はtblsをより良くすることができた年だったかなと思います。

tbls自体の設計方針と目の前のtblsで解決したい課題にズレがある状態から、tblsの設計を曲げるのではなくtblsにプラグイン機能を追加することで課題解決をする機能を外出しすることにしたのですが、結果としてtblsの可能性を広げることにつながりました。

自分のOSS活動はやっぱり「課題があってこそ」なんだなと実感できました。

そういえば作るOSSも構想や設計に時間がかかるものや機能がポツポツ出てきた気がします(これ、単純にエンジニアとしての能力が衰えたから時間がかかるとかじゃなければいいんですけど。。。)。

2020年に作成したけれどもまだもう少し洗練させたいと思っているツールが1、2個あるので、何か良いアイデアが降ってくるのを待ちつつドッグフーディングをして、良いタイミングで紹介したいと思います。

発表

2020年はほとんど発表していません。ただ、発表しているものは(自分の中では)一貫していて、その点に関しては良かったかなと思っています。

2021年の抱負

今年は「インプットができるようにする」です。読んで字の如くです。

私は解決したい課題ドリブンで技術をインプットする(調査したり習得したりする)ことが多いです。逆に作りたいモノがない状態で何かを1から学ぶというのが苦手です。

このいわゆる「遅延勉強法」はモチベーションがないと何もできない私にはしっくりきているのですが、実は私の場合は大きな欠点があります。

課題を解決してしまうとその技術の習得サイクルが終わってしまい、結果として体系的な知識を獲得できないという点です。「雰囲気でやっている」というやつです。

最近この欠点がじわじわと自分を苦しめているような気がしてならないのです。やっと気づいた。

いきなり遅延勉強法をやめるというのは難しいので、まずはあえて今まで避けてきた「コンフォートゾーンから大きく外れている技術」が必要な課題を解決することを目指したいと思っています。解決までに時間がかかる分、そこまでに得られるインプットも大きいと思っています。

さっそく解決したい課題からかなり遠いチュートリアルからはじめていて新年早々イライラしているのですが、あせらずやっていこうと思います。

あ、あと分割キーボードを手に入れたい。MacBook Proとほぼ同じキー配置で。

今年もよろしくお願いいたします。

フォント周りで作成したGoパッケージの紹介

Goでフォントを扱うことって(おそらく、たぶん、きっと)ほとんどないと思うのですが、私はなぜか文字が入っているpngファイルの生成とかER図とかを出すようなOSSを開発していることから、フォント周りのGoパッケージを作る機会がありました。

どれもパッケージというには小さいコード片で紹介することもないだろうと思っていたのですが、せっかくなので紹介したいと思います*1

fontdir

github.com

フォントを指定するためには大抵フォントファイルのパスを指定しなければいけません。

fontdirは各環境でフォントファイルが格納されているディレクトリを取得できるだけのパッケージです。

これはパッケージを作ったと言うより https://github.com/flopp/go-findfont のフォントディレクトリ取得関数がprivateだったのでその関数だけポーティングしました。

ffff

github.com

何かのOSSとかでフォントを指定してもらうような設定を作った時、「自分でフォントファイルを指定する」ならまだしも「ユーザにフォント名を寸分違わず指定してもらう」って厳しいUXだろうなと思って作りました。

ffffはフォントをFuzzy FindしてくれるGoパッケージです。

今のところTrueTypeとOpenTypeに対応していて、検索するときに 検索結果のフォントがどのTypeかはわからないので両方のOptionを指定しておく というアプローチをとっています。

package main

import (
    "fmt"

    "github.com/beta/freetype/truetype"
    "github.com/k1LoW/ffff"
    "golang.org/x/image/font"
    "golang.org/x/image/font/opentype"
)

func main() {
    fontSize := 12.0
    dpi := 72.0
    to := &truetype.Options{
        Size:              fontSize,
        DPI:               dpi,
        Hinting:           font.HintingNone,
        GlyphCacheEntries: 0,
        SubPixelsX:        0,
        SubPixelsY:        0,
    }
    oo := &opentype.FaceOptions{
        Size:    fontSize,
        DPI:     dpi,
        Hinting: font.HintingNone,
    }

    face, err := ffff.FuzzyFindFace("Arial", to, oo)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%v", face)
}

mplus-fonts

github.com

golang.org/x/image/font/gofont のフォントの組み込みの仕組み面白いですよね。中身は単純にTTFファイルを []byte にして突っ込んでいるだけという。

面白いと思ったのでgofontの []byte 化の仕組みを使って、M+FONTSのTTFファイルをGoパッケージにしてみました。

本当は他のOSSで使おうと思って作ったのですが、他のアプローチでいろいろ解決したのでまだ使っていません。

ちなみに、デザイナーの人からしたら :thinking: な感じかもしれませんが「好きなフォントは何ですか?」と聞かれたら私は「Century GothicかM+FONTS」と答えるくらいにはM+FONTSの印象が強いです。


紹介は以上です。

他にもGoでSVGの扱うときの細かいTipsとかあったりするのですが、GoでSVGを扱うってほとんd(ry

*1:本エントリはGo 5 Advent Calendar 2020 20日目の代理投稿です

Trivy DBを他のデータベースにマイグレーションするツール trivy-db-toを作った

Trivyは言わずと知れた脆弱性スキャナです。

私は最近までコンテナ専用のスキャナだと思っていましたがそんなことはなく、様々な環境に対してOSパッケージやアプリケーションの依存ライブラリの脆弱性を高速に検知してくれる汎用的なスキャナです。

k1low.hatenablog.com

CI環境だろうがサーバ内だろうが脆弱性検知をしてくれるTrivyですが、「その脆弱性データベースはどこにあるんだ?」となると思います。

実は毎回最新の脆弱性データベースを確認し(手元のデータベースより新しいものがあれば)ダウンロードしています。Trivy DBと呼ばれているようです。

Trivy DB

github.com

Trivy DBは通常版とlight版があるのですが、通常版でもダウンロードサイズはわずか19.4MBです *1

(実は、このTrivy DBを作成するまでの仕組みがよくできていたり、Trivy DBの中身についてTrivyの作者である@knqyf263さんに解説いただいたりしたので、それはまた別のエントリとか別のどこかで紹介したいです。)

Trivy DBを利用することでTrivyと同一の脆弱性データベースを得ることができるわけです。

同一のデータベースを得ることで、Trivyで検知した脆弱性情報を確認するWebUIを作ったり、別のツールでTrivyと同一の情報を利用することで情報の信頼性を担保したり、Trivyを中心としたシステムを作りやすくなります。

Trivy DBはbboltというファイルベースのいわゆる組み込み向けKey-Valueデータベースを採用しており、Trivyが効率的に使う想定になっています(それは当然そう)。

bboltのままだと他のシステムからの情報取得がしづらいので*2、Trivy DBを他のデータベースにマイグレーションするツールを作りました。

github.com

trivy-db-to

trivy-db-toはTrivy DBを他のデータベースにマイグレーションをするツールです。

CIやk8sのCronJobなど、データベースに接続できる環境で定期的に実行して脆弱性情報を更新する想定で作成しています。

使い方は以下のようにマイグレーション先のデータベースを指定して実行するだけです。

$ trivy-db-to mysql://root:mypass@127.0.0.1:3306/mydb

内部のフローとしては、

  1. (なければ)最新のTrivy DBを取得
  2. (なければ)マイグレーション先のデータベースに必要なテーブルを作成
  3. Trivy DBから脆弱性情報を吸い出してテーブルに登録
    • INSERT前にTRUNCATEが走ります

となります。更新が必要なデータだけUPDATEすれば良いとは思ったのですが、処理がある程度実時間で完了するので

v0.2.0ではMySQLPostgreSQLに対応しており、要望があれば他のデータベースにも対応したいと思っています。

私もまだプロダクション環境では利用していないのですが、使っていきたいところがあるので近いうちに適用できるように狙っていきます。

ニッチですがぜひどうぞ。

*1:2020/12/7時点のサイズ

*2:Goのツールであれば良いのですが

サーバが抱えているCVEとパッケージアップグレードによって解決するCVEを確認する方法

trivy fs /

これだけ。

Trivy便利!コンテナにしか使ってなかったけど何にでも使っていけそう。

こちらからは以上です。

github.com

データベースドキュメント生成コマンド tbls 更新情報 (テンプレート変更機能/ドキュメント内リンクのbaseUrl変更機能/テーブルリレーション自動検出機能)

無事マージされてさらに便利になったtblsの新機能紹介エントリです。ちなみに、自分が追加した機能は1つもありません。

tblsをさらに便利にしてくれるPull Requestに改めて感謝します。

ドキュメントのテンプレートを変更できるようになりました

https://github.com/k1LoW/tbls#personalized-templates

tblsが生成するドキュメントテンプレート( Goのtext/template ) を差し替えることができるようになりました。

tbls.ymlで、必要な設定だけ以下のように差し替えテンプレートのパスを書くだけです。

templates:
  dot:
    schema: 'templates/schema.dot.tmpl'
    table: 'templates/table.dot.tmpl'
  puml:
    schema: 'templates/schema.puml.tmpl'
    table: 'templates/table.puml.tmpl'
  md:
    index: 'templates/index.md.tmpl'
    table: 'templates/table.md.tmpl'

tbls doc で生成されるドキュメントフォーマットが気に食わない人には朗報ですね。

もう少しフォーマットをいじるためのテンプレート用関数も(デフォルトで使わなくても)追加していこうと思います。

生成するドキュメントのリンクにbaseUrlを設定できるようになりました

github.com

これは、MarkdownファイルをGitHub以外でホスティングしたいということで追加されました。

ただ、今後HTML生成機能が追加されたり、tbls Web UI(?)ができた時にも有効に使えるパラメータになると思っています。

テーブルのリレーションを自動で検出する機能がつきました

https://github.com/k1LoW/tbls/blob/master/sample/detect_relations/README.md

Ruby on Railsなどで採用されている命名規則に沿っていれば外部キー制約がなくても自動でテーブルのリレーションを検出してくれます。

いわゆる [users.id](http://users.id)posts.user_id を自動で関連づけるアレです。

外部キーレスで設計したデータベースも、これを有効にするだけでドキュメントがかなりわかりやすくなるのではないでしょうか?

現在はRoRライクな命名規則だけですが、他にもよく使われる命名規則があれば実装していきたいと思っています。是非教えてください!


というわけで、最近追加された新機能の紹介でした。

GitHubやSTNSと会話して公開鍵を取得・更新するツールkeypを作った

GMOペパボではLinuxユーザ、グループの管理にSTNSやOCTOPASSを利用しています*1

stns.jp

github.com

これらは簡単に言うとそれぞれTOMLファイルやGitHub(GHE)のユーザ情報をデータソースとしてLinuxユーザ、グループ、さらに公開鍵の管理をするものです。

正直何台あるかわからないサーバがSTNSやOCTOPASSを通じてユーザ、グループ管理されていることで「ログインできる必要があるサーバには即ログインできる」ということが実現されており、これらがない環境は正直考えられなくなってきています。ログインユーザ、グループ管理、公開鍵管理に疲弊している方は是非導入を検討して欲しいです。

さて、STNS/OCTOPASSはとても便利なのですが、実際の環境では様々な理由によりSTNS/OCTOPASS配下になっていないユーザも存在しています*2。本当に稀ですがそれらのユーザでログインするための公開鍵の登録作業が発生する事があるのも事実です。

細かいですが、上記のようなシチュエーションでも公開鍵をSTNSサーバ/GitHubと連動するようにするツールkeypを作成しました。

github.com

keyp

keypはGitHub(GHE)やSTNSサーバと会話して公開鍵を取得・更新するツールです。

例えばGitHubのユーザ alicebob と org my-org の team developers に所属しているユーザの公開鍵をまとめて取得したいときは、 keyp collect コマンドで

$ keyp collect -b github -u alice -u bob -t my-org/developers
ssh-rsa C1yc2EAAAADAQABAAACAQCmnCCt6PyH9jLZbPkMijSJYIu14nhxyFVw9M9eAkgcMQ3EsKf86GWlGPDfZcqcDqI+LP7LKQk4kAlmLOQQMavthrhGEURxxdX0Yk2A6pHjG3zrcW7X30ZBMwOzX/a6EWkPXPwPH6LcP3rM9yEIg95f2JntxO3z7l/8QjzJCoPIlqyoX4I7bxHus/rZVnRNh9C0PbUejbg/iWaTTxkNglSeEYpW+ID2k/4Absisa5XyY2zEOMw+6OyfRL9AlfGYv545J0g90qWS11iRSFnMR7A6FNUea/pVESIMmmBI56Ne+S8NmwR724u3d5kNJxuAKpmtThNPdxW/vmtuc5XBZgtPX/rzdAW0TQZvpVoLnoKaqYgfIpcrrkSAoPlcVxfq/NrpVlbIi6c9rZRZR4dcqmAK2eBGuDQZBiYJSESuPbE6i08GGnM8OblD8pshVeGMStztR+NuXywIXbRpyqNF2VNjil8r4qGNW9AB8ZVUB/1s6U8oxvtlbABoxXLrdNlKj3rl2YoPIZyCLAp9QDch8P1SnmQTEZK67YY5KNQrJZ2ql7pblo84JqsRbwuOrexTz6xrbBWFZMHWorFqF8ryX0LOw9TIaHbYqleynhqJ0a8VJHZMmwndYKjw3brtJ3SfCpXU0826LOExWXcjfBqHK65gM+MQ==
ssh-rsa AAADAQABAAACAQC8IEt4MqBed/yXQyjUTCZRdZoCUNhm0bEkOV8Ef5TduQvMIPDpBYyYIvFz7jxJyShPoiTMtIUnkkA2aDF0jhujFzqKmYm9H2tS7Tpf5iNwRJgJJJWv674tGUcu+6+ZadmDBQ//dwo8XWTHxmkWfgaybxs8/o0AlwZQ4pYFcky0q+/qP4cwPAmRW0rGCo0E5BhS/5eGssoLBXu4/Hcaz/93H8AtAe1UQrlCKma0rj0HIA9A9Q9EQtunw/zJTBtTyzE/TvxKcSMNulgdVmFSRmU6l84Ftc6tZPoiaCnxcvQUyjCEeQfy4DbtCWe1tEubyKeBLBTXTnpqWA3Gs9GryQA/bR7Ivan/03FshLFeVVnbvvO11sKNvkAJ8u417Q2/G9bcB1H30Xa9PSRE+2CbQ2maafhPVL17TJVBvkDCM5trmwxfM2tdlKA7R+mTj9nIrSLN4BYrge8IZ1fesC/sKMlMwhNEOrQYQQIZMIx8hfLAS37D8wbUPRodQFJsolrK6cHlNICR/TLcijNhCeHJkD8448EuJn1BCbYKglG7eUYKLbMXcVJAoTPlFTHPU80oaHJhmpLe0vFSxrhWVf/ha81zRefXOiye7Pbn/h+sa2qsKTnAMShpS1m+RP7QmHNmFAbHlPeTlnd0oJI/bt5Mysn5HHjX4vAJdQ==
[...]

というように一気に取得できます。

特定のLinuxユーザの ~/.ssh/authorized_key を更新するためのコマンドも用意しています。

ubuntu ユーザの ~/.ssh/authorized_key をSTNSで管理しているユーザ k1low とグループ some-team-developers に所属しているユーザの公開鍵で更新したいときは、rootユーザで以下のように keyp update-authorized-keys コマンドを実行すれば一気に更新してくれます。

$ keyp update-authorized-keys ubuntu -b stns -u k1low -g some-team-developers

また、CIで利用しているなど登録している公開鍵が上書きされたくない場合は --keep-key オプションで公開鍵の部分文字列を指定しておけば、部分文字列にマッチした鍵を上書きで消すこともありません。

上記 keyp update-authorized-keys コマンドをSystemd TimerやCronに登録しておけば簡易的な公開鍵同期の仕組みの出来上がりです。

まとめ

というわけで、ちょっと(いやかなり)細かいですがこれで公開鍵登録作業などがなくなればいいなと思っています。

他にどんな人に使ってもらえるかなーと思ったのですが、

  • ISUCONのデプロイユーザの公開鍵登録
  • 「ユーザ、グループの管理までしたいわけではないがデプロイユーザだけは公開鍵管理したい」みたいなプロジェクト
  • 公開鍵の登録方法(登録コマンド)を統一したい(プロジェクトAとBとCとDでいちいち新規メンバーの鍵をTerraformやChefやAnsibleなどのプロビジョニングツールに1つ1つ書いて運用するのが面倒)
  • 深遠なる理由でsshdの設定変更までは手が出せない

というところまでで力尽きました。

誰かに刺さればいいなー。

*1:他にも認証方式で採用しているものはあります

*2:例えば、初期構築時のユーザなど