プログラマ行進曲第二章

主にソフトウェア関連の技術をネタにした記事を執筆するためのブログ

2020年の抱負

明けましておめでとうございます。

今年の抱負を書こうとしていたら、あっという間に正月休みを越えてしまいました。

もっと計画的に動かないといけないですね、と思った年初です。

今まで書いてきた抱負

今年の抱負 - プログラマ行進曲第二章

2014年の振り返りと今後やっていきたいこと - プログラマ行進曲第二章

2016年の抱負 - プログラマ行進曲第二章

2017年の抱負 - プログラマ行進曲第二章

2018年の抱負 - プログラマ行進曲第二章

2019年の抱負 - プログラマ行進曲第二章

2020年の抱負

IT系技能関連の目標

達成度を計測する目標としては今年も設定しないことにしました。

最初はここにも目標書いていたんですが、書いてみて見直したところ、多分達成できそうにないものだったので「達成度は計測しないが、やっていきたいこと」に移しました。

日和りました。

ITはあまり関係ない目標

1年で4冊は本を読み、感想ブログを書く

2019年も同じ目標を立ててやってみたところ、何とか達成できたので、今年も同じ量でやっていきたいと思います。

昨年挑戦してみて思ったのは、本を読むことよりも、読んだ本についてブログ記事を書く方が大分手間取るということが分かったので、4冊というのは冊数にすれば少ないですが、今の私の習慣からするとちょうどいい量なのかなとも思います。

まあ、今年もやっていきます。できるならもっと多くの本、それも技術書以外を読んでいきたいです。

1週間に1度は瞑想をする

実はこれはもう既に今年に入ってからやっていて、記録も取りつつ習慣化できつつあるので、記録残しとして1週間か2週間に1度ブログ記事にして証明するようにしようかなと思っています。

やっている内容としては以下の書籍、『サーチ・インサイド・ユアセルフ』の最初の簡単なやつ*1をやる感じですね。

以下に記載する事項を達成するためのキーとなる目標なので、習慣化して続けるようにしたいと思ってます。

達成度は計測しないが、やっていきたいこと

アルゴリズムとデータ構造の勉強を定期的に行う

これに関しては多くは語らないですが、自分の足りないところなので埋めていく感じでやっていきたいです。

達成度は計測しないですが、量の目安を記載すると、最低限以下の書籍を終わらせるところまではやるつもりです。量的には大分ハードルが低いですが。

オンラインジャッジではじめるC/C++プログラミング入門

オンラインジャッジではじめるC/C++プログラミング入門

  • 作者:渡部 有隆
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2014/06/28
  • メディア: Kindle版

勿論、早く終わったら以下の書籍とか他のもの*2もやる予定です。

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造

  • 作者:渡部 有隆
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2015/01/30
  • メディア: Kindle版

まとめ

潰れない程度に頑張っていきます。

一つでもいい習慣が身につけることができるならいいのかな、なんて考えてます。

*1:2分間だけやる瞑想のこと

*2:LeetCodeや競技プログラミング的なもの

2019年の振り返り

はじめに

2020年になる前に振り返ることができればよかったのですが、「体力ない〜。疲れた〜」なんて思いながら横になっている内に年が明けてしまいました。

で、このまま放置しているとせっかく立てた目標の達成度合いや新年の計画も台無しになってしまいそうなので、年明けて時間が経っても気にせず、2019年の振り返りを少しでもいいからしてみようと思います。

関連エントリ

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

「振り返」という単語で単純に検索に引っかかった昔の記事を引用しただけなので、もしかしたらもっと前から振り返りエントリは書いていたかもしれません。

久し振りに昔の記事をこうやって振り返ってみると、「まがいなりにも2013年から振り返り記事を書いていたんだな」と思いました。

成果確認

takuan-osho.hatenablog.com

IT系技能関連の目標

これは目標を立ててなかったので、特になし。

1年で4冊は本を読み、感想ブログを書く

こちらに関しては、達成できました!

実際に感想をブログ記事にしてまとめたのが以下の記事たちです。

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

takuan-osho.hatenablog.com

9月以降、読むペースが落ちたのと、読み終わっても感想をまとめきらずに記事に出来てなくて4冊分にしかなってないのが残念ですが、ちょうど目標と同数の達成度合いだったので、いい塩梅の目標だったのかなと思います。

達成度は計測しないが、やっていきたいこと

こちらに関しては以下の2点を書いていたようです。

  • 瞑想を定期的に行う習慣をつける
    • Beingの改善
  • 技術ネタでブログ記事を書いていく習慣をつける

1点目に関しては最初はブログ記事として1ヶ月は続けていたのですが、2019年後半からは中々習慣的に続けられなかったので、次はこの瞑想の習慣を身につけるのを今年の目標にしてもいいのかなと思いました。

2点目に関してですが、理想的な状態からは遠いとはいえ、全く目標を立ててなかったときよりは小さい技術ネタを含めて少しは更新していくことができたかなと思います。

特に以下のArgoCDの調査中に気づいたことをまとめた記事は内容・気づき共にクオリティが高いものを手にすることができたと思ったので、こういう系統の記事をもっとアウトプットとして出していけたらいいなと考える次第です。

takuan-osho.hatenablog.com

2020年に向けて

2020年の目標は別途書くことにします。

2019年の目標と同じく、頑張れば達成可能な(=非現実的ではない)目標を立てて、年末の振り返りの時に「今回も達成できました!」と報告できるようにしたいと思ってます。

34のおっさん(当時)が痔ろう手術するハメになって健康大切だと実感した思い出話(前編)

こちら、pyspa Advent Calendar 2019の22日目の記事です。前日の記事は@takabowさんのふるさと納税の上限計算2019でした。

pyspaに参加したのは今年が初めてで、pyspa Advent Calendarに参加するのも初めてなのですが、昨年経験して書こうと思っていながらまだ書けていなかった記事をこのAdvent Calendarを利用して書いて、昨年の経験を供養したいと思います。

言いたいことまとめ

多分、文章が長くなるので、言いたいことを最初にまとめておきます。

  • 痔ろうになると、(程度にもよるが)生活するのが大変辛くなる
  • お尻は普段からいたわるべき
  • 問題が起こったときにお世話になる肛門科の医局・クリニックを可能なら事前に見つけておく
  • 痔など肛門周りの疾患を経験した人は男女問わず結構周りにいるので、恥ずかしがる必要は無い
  • 仕事がリモート可な職場で助かった
  • 座イスを用意しておくとその後の生活がスムーズに過ごせる

34のおっさん、突如痔ろうになる!

あれは去年(2018年)の7月でした。

普段ソフトウェアエンジニアとして働いているのでデスクワークが多いのですが、7月6日(金)の仕事終わりに近づいた18時頃、微妙にお尻が痛いというか痒い状態になっていることに気づきました。

その時は症状も大したことが無かったので特に気にもせず椅子に座りながらPCのキーボードをカタカタ打ちながら仕事をしてました。

その後迎えた週末、私は趣味の散歩をしていたのですが、2018年の7月は猛暑だったため、ひどく汗をかきながら散歩をしていました。

当時のTwitterを振り返ってみると、「暑い、暑い」と書いているくらいで、相当暑かったんだろうと思います*1

そんな猛暑の状況だったため、この散歩をしている状況の時にも少しお尻というか肛門周りの状況が悪化していたのですが、「汗疹でもできたかな?」とあまり気にも留めていませんでした。

しかし週明けの7/10(火)、詳細な記憶は忘れているのですが、とうとう痛みが無視できなくなってデスクワークに支障が出ると思ったのでしょう、出勤前に肛門科のクリニックに診察してもらいに行こうとしている様子がTwitterの記録に残っています。

実は今回書こうとしている痔ろうの話よりもずっと前、下血したり便秘由来の切れ痔の影響で肛門科のクリニックに大分お世話になっており*2、その経験があってクリニック選びに迷わず、「肛門科のクリニックに行って診察してもらうなんて恥ずかしい」なんてことを思わずに早めに診察にいけたことが結果的によかったんだろうと思います。

これで診察に行くのが遅れていたら、単に痛みに耐える日数が長くなるだけで辛い仕事時間をずっと過ごす羽目になったと思うので、早めの診察は大切だなと個人的には今も思っています。

ただ、これは今から当時を振り返って思った感想で、診察に行った当時は「まあ多分汗疹とかそこら辺だろうから、少し見てもらって処置してもらったら、少し遅れるだろうけど会社に行こう」とのんきに考えていました。

が、クリニックの院長のT先生に診察してもらったところ、「これは痔ろうで、手術が必要な状態ですね。早めに手術することをお勧めします。手術は日帰りのものが2回は必要です」という趣旨のことを言われ、「おっとぉ…、これは予想してなかった面倒事だなあ」と思いつつ、どうしようか思案しました。

今の自分がどういう状態にあるかの説明、以下の書類を示されながらT先生から説明を受けました。

肛門の病気の説明

肛門の病気には大きく分けて3つの種類があるそうで、「イボ痔」「切れ痔」「痔ろう」の内、今回の私は痔ろうでした。

上の書類の最下部はT先生が病状の説明中に描いてくれた痔ろうの図で、書類に〇が付けられている箇所から分かるように、私の病状は切開して膿を出さないといけない、つまり手術が必要な状態です。しかも2回も。

下血などで複数回大腸内視鏡をこのクリニックでお世話になっている経験もあり、「肛門の手術が必要です」と言われても普通の人よりかは動揺は少なかったはずですが、手術したら仕事の関係とか諸々面倒なことがあるだろうなあと思い、どうしようかと多少逡巡したはずです*3

ですが少しここで思い直しました。というのも、今までの人生経験上、痛みとか病気とか疾患に対する処置を後回しにすると結果的に無駄な時間とお金がかかる割合が多いことを理解していたので、「今回も早めに処置した方がいいんだろうな」と思い、即日手術を受けることに。

最初の手術に向かう

そう決めたらT先生にまず最初の手術を受ける旨を伝えて、詳しい説明や今後の流れの説明を受けました。

その後、家族に軽く状況を伝えた後、職場のチームリーダーにSlackのDMで「手術のため本日(7/10)休むこと」「詳しい状況は手術後にまた連絡すること」と報告して手術をする準備に向かいました*4

実際に手術に向かう細かい流れは失念してしまいましたが、手術前により詳しい説明と同意を取るために以下の案内と説明書を渡され、記入して提出しました。

肛門日帰り手術のご案内

肛門手術説明書7月

今読み返してみても微妙に怖いことが書いてありますね。

手術にあたって予想外だったのは「麻酔(仙骨硬膜外麻酔)がムチャクチャ痛かったこと」と「手術後麻酔が覚めるまで排尿を何度もしなくてはならないのに中々排尿できなかったこと」でした。

仙骨硬膜外麻酔は詳しいことは分かりませんが、下半身だけ麻酔をかけるように仙骨の辺りに注射して麻酔を打つ方式のようで、うつ伏せになった状態で注射針を打たれるのですが、脊椎とか仙骨の内部まで針は入ってないはずなのにむっちゃ痛かったんですよ。骨の内部まで打ってないのにこんなに痛いなら、骨髄バンクに登録して骨髄を提供するドナーの方はどれだけ聖人なのかと思います。

手術は麻酔が効いているので麻酔ほどは辛くなく*5、そこまで時間はかからずに終わったのですが、その後麻酔が覚めるまでの状態が面倒でした。

麻酔が覚めたときに下の世話を自力でできる状態で帰すようにするためか、ちゃんと排尿できる状態になるまでずっと横になりながらトイレによって排尿に挑戦するのですが、麻酔が効いているのとそもそも私は排尿に使える力が弱いのか、大分時間がかかって、3時間は排尿が正常な調子になるまで戻らず、帰るのが遅くなっていました。

手術は9時頃始めたはずで、以下のツイートによると16:45にはツイートできる状態までになっているので、動けるようになるまではおそらく16時までかかったと思います。

お世話になったクリニックは自宅最寄り駅から3駅のところにあり、手術後はあまりに痛くて、電車を使ってもその距離を歩いて帰るのは無理だったので、家族に迎えに来てもらってその後タクシーで自宅に帰り、後は安静に横になっていました。

以前に大腸内視鏡経験が3回あるとはいえ、肛門の手術は初経験でした。そのためか、こんなことを当時Twitterに残していますね。

今となっては痛みも無いので当時の痛みを思い出すのも難しくなってきていますが、こんなことを残すくらいの余裕はありつつ、相当辛かったんだろうと思います。

やたらと長くなったので、手術後の面倒な生活の過ごし方や2回目の手術に関しては後日公開します。

明日は@shiumachiさんです。

*1:もはや猛暑だった記憶が飛んでいます

*2:この手術前、34歳の段階で大腸内視鏡経験が3回ありましたが、同年代の平均を超えているのではないかと思います

*3:重ねて言うと、細かいことは忘れてしまったので当時の記憶は予想込みです

*4:SlackのDMを見返すとこの時は9時頃でした

*5:しかし久し振りの肛門周りの処置だったためか、手術器具が肛門から内部に入っていくときの感覚はちょっと「ウッ…」ってきてしまいました

Argo CDのSSO設定をしてLDAPでログインできるようにしてみた

仕事でNomad環境からkubernetes環境への移行を進めていて、その流れでkubernetesのエコシステムを調べており、今はCI/CD周りを重点的に調べていて、その関係でArgo CDの調査をしています。

個人的にはkubernetesクラスタにデプロイしやすく、kustomizeとも連携してデプロイできたり、Web UIだけでなくCLIもちゃんとついていたりと、kubernetes用のCDシステムとして生まれただけあって、筋のいいソフトウェアだなと思いながら調査しています。

実運用にはいるときには認証・認可が問題になるので、最近はその辺りを調べているのですが、そこで中々パッとは分からなかったことがあるので覚え書きをメモして残そうと思い、記事を書きました。

Argo CDのログインをLDAPでやりたいとなったときにすること

私の職場ではあらゆる場面でLDAPを利用するようになっているので、Argo CDのSSOもLDAPでできるならやりたいところなので調べました。

Argo CD does not have any local users other than the built-in admin user. All other users are expected to login via SSO. There are two ways that SSO can be configured:

  • Bundled Dex OIDC provider - (以下略)

  • Existing OIDC provider - (以下略)

Argo CDの公式ドキュメントに書かれているように、Argo CD自体はadmin以外のユーザー管理をせず、他のユーザーを使いたい場合は組み込みのdexを使用するか、OktaやAuth0やOneLoginなど外部のOIDC Providerを使うかの2通りのやり方があります。

私のケースの場合、既にLDAPサーバーはあり、そのデータを使って管理をしたいので、組み込みのdexと連携させる必要があったのですが、Argo CDの方はgithub連携の例しか書いておらず、LDAPと連携したい場合は下記のdexのLDAP連携の記事と合わせて理解する必要があって、どうやっていいのかよく分かりませんでした。

結局最終的にどうやればいいのかよく分からなかったのですが、github連携の例を自分で試した後、「github連携の例で kubectl edit configmap argocd-cm -n argocd で変更しているConfigMapの値を、dexのLDAP connectorに記載されたフォーマットでLDAPの情報を書いてやればいいんだろうな」と分かったので試したところ、上手くいきました。

どんな値を入れたかというと、以下のような値です。LDAPサーバーのホストが yourldaphost 、LDAPサーバーが使用するportが 37685 、Argo CDのURLが https://localhost:8080 の時、以下のように書けば私のニーズは満たせました。

data:
  url: https://localhost:8080
  dex.config: |
    connectors:
    - type: ldap
      # Required field for connector id.
      id: ldap
      # Required field for connector name.
      name: LDAP
      config:
        host: yourldaphost.com:37685
        bindDN: cn=viewer,dc=yourldap,dc=com
        bindPW: <masked>
        usernamePrompt: LDAP Username
        userSearch:
          baseDN: dc=yourldap,dc=com
          username: uid
          idAttr: uid
          emailAttr: mail
          nameAttr: displayName

DexのLDAP connectorのドキュメントを見る限り、グループとかの設定も含めて細かく設定する場合は、LDAPの知識も必要になりそうな感じですが、単純なLDAP連携でいいならこんな感じでやればよさそうです。

LDAP連携だけだと権限はまだ設定していないので、,LDAPのユーザーやグループ毎に違う権限を設定したい場合はArgo CDのRBACの設定がおそらく必要なのでしょう。

引き続きArgo CDの使い方を調べていきたいと思います。

『エンタングル:ガール』を読み終えました

最近本を読めているので、この調子で書評記事を書いていきます。

今回扱うのは『エンタングル:ガール』です。

エンタングル:ガール (創元日本SF叢書)

エンタングル:ガール (創元日本SF叢書)

『エンタングル:ガール』は2006年に放送されたアニメ『ゼーガペイン』のスピンオフ小説で、舞浜南高校に通う映画監督志望の高校一年生、カミナギ・リョーコが主人公の青春小説です。

原作であるアニメ『ゼーガペイン』はロボットやSF要素が強いながらも切ない感情を呼び起こすストーリーを持ち、未だにコアなファン人気を誇る作品ですが、この『エンタングル:ガール』はアニメ『ゼーガペイン』が持っていた青春群像劇の要素を強調して押し出した作品となっています。

『エンタングル:ガール』の大まかなストーリーは、映画コンテストに出す映画を撮るため、主人公のカミナギ・リョーコが監督となり、舞浜南高校に伝わる七不思議を軸にした映画を舞浜南高校映研のメンバー+出演者で作るというもので、その中で徐々に明らかになる人間関係や世界の謎に触れるという流れになってます。

読んでみての感想

最近小説を読まなくなっている身としては、久し振りの真っ当な青春小説だったので、読後感は爽やかな気持ちになってよかったですね。

私が熱狂的な『ゼーガペイン』ファンということもあり、大分評価が甘い可能性はあるのですが、SF要素にあたる話や『ゼーガペイン』の要素を前提にした話は最後の方にしかないので、アニメ『ゼーガペイン』を知らない人でも読める作品になっているのでは?と思います。

個人的によかったポイントは、非常に読みやすい日本語だったという点です。

『三体』を読んだときの記事にも書いたのですが、小説に限らず、本を読んでいるときに読みにくい日本語で書かれていると、相当の本好きでも無い限り、限られた時間を使って読んでいるので、読み通すのに障害になってしまうので、この読みやすさはよかったです。

この読みやすさは著者の高島雄哉氏の持ち味なのかどうなのか、私は分からないですが、これだけ読みやすいならば氏の他の著作(『ランドスケープと夏の定理』など)を読んでみようかなという気になりました。

余談

この『エンタングル:ガール』の出版を記念したトークショー&サイン会が8/31に開かれたのですが、当初予定が無いから行こうかなとTwitterで呟いていたら、ありがたいことに著者からアドバイスをもらいました。

ただ、ここで残念だったのが、確認不足で8/31には用事があってそちらを優先したかったので、このトークショー&サイン会には行けず、上記ツイートの返信もしないまま今日まで来てしまいました。

放置したままなのもどうかと思いつつ、だからといって返信をするには随分日が経ってしまったので、返信の代わりに読書感想の記事をこうやって書いたわけです。

少しでも著者の手元に何か届けば幸いです。

Xcode 11が正式にリリースされたのでXcode 11のベータを消したらgit周りでエラーが出たため対処した

タイトルの通りで、Xoce 11が正式にリリースされたので、今まで入れていたXcode 11のベータを消した後、gitのコマンドを実行しようとしたら以下のようなエラーが出ました。

xcrun: error: active developer path ("/Applications/Xcode11-beta.app/Contents/Developer") does not exist, use xcode-select to change

出たエラーメッセージをちゃんと記録してなかったので細かい文言は違うと思いますが、こんな感じのエラーでした。

which git した結果は /usr/bin/git だったので、Xcode付属のgitを使っていて、Xcode11 ベータを消したことでだめになっている模様。

普段Xcodeをヘビーには使わないのでどうやって解決するのがいいのかとググったところ、以下の記事を見つけて同様の対処をすれば解決しました。

atsumo.hatenablog.com

根本原因を理解せずにやっているのはあまりよろしくないとは思いつつ、当面の問題を回避するのによさそうだったので実行しました。

上記記事と同じことの繰り返しになりますが、やったことは以下の通りです。

まず、 xcode-select --print-path を実行して、現在のxcode-selectのパスを確認。

xcode-select --print-path
/Applications/Xcode11-beta.app/Contents/Developer

実際に使いたいのは Xcode.app なので、それを戻す。

sudo xcode-select --reset

再度パスを確認する。

$ xcode-select --print-path
/Applications/Xcode.app/Contents/Developer

確認した後、gitコマンドを使ってみたらエラーが出なくなったのでこれでひとまず解決。

別のことを調べていたらGo言語のx/crypto/sshパッケージがOpenSSH形式のパスフレーズ付き秘密鍵にまだ対応できてないことに気づいたという話

業務中に調べていたら発見したことなのですが、発見した後調べてみたら既にクラスメソッドさんの記事でも触れられていたことだったのでまとめる意味あるかなと思いつつ、自分で調べて突き止めたことなので記録に残しておいた方が自分の実力の証明になるかと思い、久々に技術系の記事を書いてみます。

dev.classmethod.jp

dev.classmethod.jp

TL;DR

この問題に気づいたきっかけ

もともとgolangのアプリを書いて気づいたわけではなく、KubernetesのCI/CD周りのエコシステムを調べている中でArgoCDの調査をしており、ArgoCDにSSH private keyを使ってgithub等のprivate repositoryを登録するのを試していたところ、ArgoCDのCLIがエラーを出したので、まずArgoCDのrepositoryにissue登録をしました。

github.com

issueにも書きましたが、出たエラーはこんな感じです。

$ argocd repo add git@github.com:<my private repository> --ssh-private-key-path ~/.ssh/id_rsa
FATA[0000] ssh: cannot decode encrypted private keys

そしてissue登録をした後にdelveでデバッグをしながらArgoCDのソースコードを見たところ、まずこの箇所で引っかかっていることが分かり、step inしながら調べたところ、ssh.ParsePrivateKey関数にしか対応していないことが分かったので、単にArgoCDの問題かと思って一旦放置していました。

ただ、少し経った後「別に技術調査中なんだから、雑にこの箇所をssh.ParsePrivateKeyWithPassphrase関数を使うように書き換えたCLIをビルドして試すのはありなんじゃないか?」と思ったので、雑に書き直した後ビルドして試したところ、またエラーが出てきて「あれ?」と問題に気づいたという流れです。

x/crypto/sshパッケージ自体に問題があると確信するためにした検証の仕方

上記の流れでArgoCDのCLIの挙動をdelveを使って追ったり、x/crypto/sshパッケージのソースコードを読んだりして、細かい検証をする前にx/crypto/sshパッケージ自体に問題がありそうだと思ってはいたのですが、VSCode経由でArgoCDのCLIをdelveでデバッグしていたときの変数の中身の表示がおかしかったりして、本当にx/crypto/sshパッケージの問題なのか他者に証明しづらかったので、ssh.ParsePrivateKeyWithPassphrase関数の挙動がおかしいことを示すコードをgistに残し、挙動を確かめたところ、やはりssh.ParsePrivateKeyWithPassphrase関数自体が手元のssh private key(パスフレーズ付き)をパース出来ないことが分かりました。

確認するのに使ったコードなどは上記のgistに全て書いているのですが、リンク先に飛ぶのも面倒なので以下に同内容のコードを説明付きで書いていきます。

使用したパスフレーズ付き秘密鍵を生成したOpenSSHのバージョン、秘密鍵の状態、Go言語のバージョンは以下の通りです。

$ ssh -V
OpenSSH_7.9p1, LibreSSL 2.7.3

$ ssh-keygen -l -f ~/.ssh/id_rsa
2048 SHA256:/<masked> taku@altair.local (RSA)

$ go version
go version go1.12.6 darwin/amd64

この環境で以下のGoのコードを実行します。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"

    "golang.org/x/crypto/ssh"
)

func main() {
    sshPrivateKeyPath := os.Args[1]
    keyData, err := ioutil.ReadFile(sshPrivateKeyPath)
    if err != nil {
        log.Fatal(err)
    }
    signer, err := ssh.ParsePrivateKey([]byte(keyData))
    if err == nil {
        fmt.Println(signer)
        os.Exit(0)
    }

    passPhrase := os.Getenv("PASSPHRASE")
    if passPhrase == "" {
        fmt.Println("Please configure `PASSPHRASE` environment variable for your passphrase")
        os.Exit(1)
    }

    signer, err = ssh.ParsePrivateKeyWithPassphrase(keyData, []byte(passPhrase))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Parsed private key with passphrase!!")
    fmt.Println(signer)
}

雑なコードですが、PASSPHRASE環境変数が正しく設定されていて、実行時にパスフレーズ付き秘密鍵のパスを指定していればssh.ParsePrivateKeyWithPassphrase関数を実行するようになっていて、signerが表示されるのが期待する動作というコードです。

これで意図通りに動くのか以下のように確かめました。

# ~/.ssh/id_rsa がパスフレーズが無い秘密鍵の時
$ go run main.go ~/.ssh/id_rsa
&{0xc0000ac120 0xc0000ac120}
# 事前にファイルを使って export PASSPHRASE="<~/.ssh/id_rsa のパスフレーズ>" と下準備して、パスフレーズ付き秘密鍵を使用する時
$ go run main.go ~/.ssh/id_rsa
2019/07/11 23:25:03 ssh: cannot decode encrypted private keys
exit status 1

上記のように、パスフレーズ付き秘密鍵の時にはエラーとなり、期待通りに動いていないことが分かりました。

このエラーが「SSH private key側の問題」なのか、「ssh.ParsePrivateKeyWithPassphrase関数の問題」なのかを調べるため、delveを使って上記Goのソースコード実行時の状態を見たところ、以下の箇所でエラーが出ているところまで突き止めました。

詳しくはgistに残したコメントを参照して欲しいのですが、ここまで調べた段階で個人的には「エラーハンドリングが甘い(=ssh.ParsePrivateKeyWithPassphrase関数の問題)のでは?」とほぼ確信はしていたのですが、普段扱っているわけではないgolangの話だったので、社内Slackの #golang チャンネルにgistのコードを添付して質問して他の人にもチェックしてもらったところ、やはりssh.ParsePrivateKeyWithPassphrase関数の問題だと言われました。

自分で調べているときにも見つけてはいましたが、上記Slackチャンネルでも「以下issueに関係ある話なのでは?」と指摘され、ほぼ間違いなくx/crypto/sshパッケージ自体の問題だと判明。

github.com

何が問題だったかというと、OpenSSH 7.8からssh-keygenで生成される秘密鍵のフォーマットがOpenSSH形式になったのに、x/crypto/sshパッケージではまだそのOpenSSH形式のフォーマットのパスフレーズ付き秘密鍵をサポートできていなかったことがこのエラーを引き起こしていたのです。

自分自身ではこのOpenSSHのバージョン違いによる秘密鍵のフォーマットの差には気がつかなかったので、教えてもらえたことで最終的な原因に気がつけてよかったです。

教えてもらったときに参考に貼ってもらった記事は以下のものがありました。

qiita.com

検証後に気づいたことと感想

こういう風に色々調べきった後に改めて調べ直していたら、本記事の頭に書いたように既にクラスメソッドさんの記事でこの問題に言及されていたことに気づきました。

最初の方で見つけられていれば原因特定が早くなったと思いますが、今回自力で大半の原因を特定でき、分からない所は分かりそうな人に質問して特定するといったフローを実際に実行でき、経験が積めたので、今回はこれでよかったのかなと思います。

余談になりますが、このOpenSSH形式の秘密鍵をgolangで扱いたい場合は、x/crypto/sshに関するissueでコメントされているように、ScaleFT/sshkeysという3rd party製パッケージを使えば出来るみたいです。試したことはないので保証は出来ませんが。

golangの場合、delve(+VSCode)によるデバッグが非常に強力だというのも実感しました。またgolangを使ってデバッグをすることになったら今回の経験を活かしたいと思います。

*1:使ったことがないので憶測です