GitLab CI/CDでSSHキーを使う

GitLabは現在、ビルド環境(GitLab Runnerが実行される環境)でのSSHキーの管理をビルトインでサポートしていません。

SSHキーは次のような場合に役立ちます:

  1. 内部サブモジュールをチェックアウトしたい場合
  2. パッケージマネージャー(Bundlerなど)を使って非公開パッケージをダウンロードしたい場合
  3. アプリケーションを自分のサーバーや、例えばHerokuにデプロイしたい場合
  4. ビルド環境からリモートサーバーにSSHコマンドを実行したい場合
  5. ビルド環境からリモートサーバーにファイルをrsyncしたい場合

上記に心当たりのある方は、SSHキーが必要な可能性が高いです。

最も広くサポートされている方法は、.gitlab-ci.ymlを拡張することで、SSHキーをビルド環境に注入することです。これは、どのタイプのexecutor(Docker、shellなど)でも動作するソリューションです。

どのように動作するか

  1. でローカルに新しいSSHキーペアを作成します。ssh-keygen
  2. プロジェクトに秘密鍵を変数として追加します。
  3. ssh-agent をジョブ中に実行し、秘密鍵をロードします。
  4. 公開鍵をアクセスしたいサーバーにコピーするか(通常は~/.ssh/authorized_keys)、非公開の GitLab リポジトリにアクセスする場合はデプロイ鍵として追加します。
注意:デバッグログを有効にしない限り、秘密鍵はジョブログに表示されません。 パイプラインの可視性を確認することもできます。

Docker executor使用時のSSHキー

CI/CDジョブがDockerコンテナ内で実行され(つまり環境が封じ込められている)、コードを非公開サーバにデプロイしたい場合、それにアクセスする方法が必要です。 そこでSSHキーペアが役に立ちます。

  1. まず、SSH キーペアを作成する必要があります。詳しくは、SSH キーを生成する手順に従ってください。 SSH キーにパスフレーズを追加しないでください。さもないと、before_script がパスフレーズの入力を要求します。

  2. 新しい変数を作成します。Keyとして SSH_PRIVATE_KEY と入力し、Valueフィールドに先ほど作成した秘密_鍵の_内容を貼り付けます。

  3. before_script.gitlab-ci.yml を変更します。以下の例では Debian ベースのイメージを想定しています。 必要に応じて編集してください:

    before_script:
      ##
      ## Install ssh-agent if not already installed, it is required by Docker.
      ## (change apt-get to yum if you use an RPM-based image)
      ##
      - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    
      ##
      ## Run ssh-agent (inside the build environment)
      ##
      - eval $(ssh-agent -s)
    
      ##
      ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
      ## We're using tr to fix line endings which makes ed25519 keys work
      ## without extra base64 encoding.
      ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
      ##
      - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    
      ##
      ## Create the SSH directory and give it the right permissions
      ##
      - mkdir -p ~/.ssh
      - chmod 700 ~/.ssh
    
      ##
      ## Optionally, if you will be using any Git commands, set the user name and
      ## and email.
      ##
      #- git config --global user.email "user@example.com"
      #- git config --global user.name "User name"
    
    [before_scriptbefore_scriptはグローバルまたはジョブごとに設定できます。
  4. 非公開サーバーのSSHホスト鍵が検証されていることを確認してください。

  5. 最後のステップとして、最初のステップで作成した_公開_鍵を、ビルド環境内からアクセスしたいサービスに追加します。 GitLabの非公開リポジトリにアクセスする場合は、デプロイ鍵として追加する必要があります。

これでビルド環境で非公開サーバーやリポジトリにアクセスできるようになりました。

SSHキー(executor使用時

DockerではなくShell executorを使う場合は、SSHキーを設定する方が簡単です。

GitLab RunnerがインストールされているマシンからSSHキーを生成し、このマシンで実行されるすべてのプロジェクトにそのキーを使うことができます。

  1. まず、ジョブを実行するサーバーにログインしてください。

  2. 次に、ターミナルからgitlab-runner ユーザーとしてログインします:

    sudo su - gitlab-runner
    
  3. SSHキーを生成する手順の説明に従って、SSH キーペアを生成します。 SSH キーにパスフレーズを追加しないでください。さもないと、before_script がパスフレーズを要求します。

  4. 最後のステップとして、先ほど作成した_公開_鍵をビルド環境からアクセスしたいサービスに追加します。 GitLabの非公開リポジトリにアクセスする場合は、デプロイ鍵として追加する必要があります。

完了したら、フィンガープリントを受け入れるためにリモートサーバーへのログインを試みます:

ssh example.com

GitLab.com のリポジトリにアクセスするには、git@gitlab.comを使います。

SSHホストキーの検証

中間者攻撃に狙われていないことを確認するために、非公開サーバー自身の公開鍵をチェックするのは良い習慣です。 不審なことが起きた場合、ジョブが失敗する(公開鍵が一致しないとSSH接続が失敗する)ので、それに気づくでしょう。

サーバーのホスト鍵を調べるには、信頼できるネットワークから(理想的には非公開サーバー自体から)ssh-keyscan コマンドを実行します:

## Use the domain name
ssh-keyscan example.com

## Or use an IP
ssh-keyscan 1.2.3.4

新しい変数を作成し、SSH_KNOWN_HOSTS を “キー “とし、ssh-keyscanの出力を “値 “として追加します。

注:複数のサーバーに接続する必要がある場合、すべてのサーバーのホスト・キーを、1行に1つずつ、変数の値に集める必要があります。
ヒント:ssh-keyscan の内部で直接変数を使用する代わりにssh-keyscanssh-keyscan変数を使用することで、ホストドメイン名が何らかの理由で変更された場合にssh-keyscan変更する必要がないという利点が ssh-keyscanあります。 また、値はあらかじめ定義されています。つまり、ホストキーが突然変更された場合、CI/CDジョブは失敗し、サーバーまたはネットワークに何か問題があることがわかります。

変数SSH_KNOWN_HOSTS が作成されたので、上記の.gitlab-ci.yml](#ssh-keys-when-using-the-docker-executor)の[の内容に加えて、さらに追加する必要があるのは以下の通りです:

before_script:
  ##
  ## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the
  ## following two lines.
  ##
  - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
  - chmod 644 ~/.ssh/known_hosts

  ##
  ## Alternatively, use ssh-keyscan to scan the keys of your private server.
  ## Replace example.com with your private server's domain name. Repeat that
  ## command if you have more than one server to connect to.
  ##
  #- ssh-keyscan example.com >> ~/.ssh/known_hosts
  #- chmod 644 ~/.ssh/known_hosts

  ##
  ## You can optionally disable host key checking. Be aware that by adding that
  ## you are susceptible to man-in-the-middle attacks.
  ## WARNING: Use this only with the Docker executor, if you use it with shell
  ## you will overwrite your user's SSH config.
  ##
  #- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config'

プロジェクト例

GitLab.comで公開されている共有ランナーを使ったSSHプロジェクトの例を用意しました。

ハックしたいのであれば、フォークしてコミットし、変更をプッシュするだけです。 数瞬のうちに公開ランナーによって変更が選択され、ジョブが開始されます。