bash

2015年03月27日

このエントリーをはてなブックマークに追加

個人作業環境を一撃セットアップ

身近なメンバーに説明する手頃な材料が欲しかったので、本エントリを書いた。

作業ノードを転々としていると、各所に作業環境を構築する回数が多くなる。作業対象が使い捨てである事が増えれば増える程、その傾向が強くなった。構築回数が多いからこそ、整理する機会に恵まれていたとも言う。その結果、今ではログイン後に下記コマンド1行を実行するだけだ。

$ curl -fSkL https://raw.githubusercontent.com/hansode/env-bootstrap/master/build-personal-env.sh | bash

build-personal-env.shの処理を掘り下げて行く。

build-personal-env.sh 処理概要

  1. mkdir -p ${HOME}/repos/git/github.com
  2. hansode/dotfiles${HOME}/repos/git/github.com/に配置
    1. cd ${HOME}/repos/git/github.com/ && make
    2. make 実行により、自分好みのdotfilesが配置・セットアップされる

dotfilesdotfileだけを管理し、env-bootstrapdotfilesをセットアップする。dotfilesにセットアップスクリプトを置いても良いが、役割分担を明確にする為にも別プロジェクトで管理している。今(2015/03/27現在)は、たまたまdotfilesしか扱ってないが、他のプロジェクトを扱いたい場合は、今の様に役割分担してる方が柔軟な管理が可能。かつては別プロジェクトを扱っていた経緯がある。

dotfilesが環境を判定

作業対象は複数あり、プラットフォーム毎に必要なdotfileが異なって来る。

  • Cygwin 1.7 / Windows 8.1
  • MacOS 10.10 (Yosemite)
  • Raspbian 7.6 (Wheezy)
  • Fedora release 20 (Heisenbug)
  • CentOS-6.6/6.5/6.4
  • CentOS-7.0.1406

例えばCygwin環境にはDropboxをインストールしているので、Cygwinのホームディレクトリからアクセスしやすいようにしている。また、CygwinとMacOSでは、Vagrantを扱う事があるので、Vagrant環境を整理している。

自身の${HOME}構造を標準化

build-personal-env.shが配置するdotfileswork/repos/git/github.com/dotfiles配下。これは自分だけのルールであり、第三者に強制されるものではない。長年の経験から程よく管理しやすい構造として辿り着いた構造は、下記の通り。

まとめ

自分が実施して来た手順を整理すると、こうなる。

  1. dotfilesを整理する
  2. dotfilesをセットアップするスクリプトを管理
  3. ${HOME}構造を標準化する
  4. 1〜3を繰り返す

自分は上記順序で整理して来たが、1〜3の順序に根拠や強制力は無い。整理出来そうな所から整理すれば良い。重要なのは、整理する事だ。

関連成果物




編集
@hansode at 19:30|PermalinkComments(0)TrackBack(0)

2015年01月30日

このエントリーをはてなブックマークに追加

CVE-2015-0235: GHOST Buster シェルスクリプト #CentOS-6 | blog.hansode.orgのCentOS-7編。

CVE-2015-0235関連

RHEL-7 / CentOS-7 関連

glibcを入れ替える必要がある。

2015/01/30時点の対応

検証用にbuildワーカーを複数環境扱ってるので、それぞれ対応する必要があった。CentOS-7系に関しては7.0.1406のみで良いので対応は大分軽度だ。手順と動作を確認し、上手く行ったのが下記シェルスクリプト。

 ghost-buster-rhel7.sh

バージョン判定には、

  • /etc/yum/vars/releaseverが存在する場合は使用し、
  • 存在しない場合は/etc/centos-releaseから情報取得
#!/bin/bash
#
# requires:
#  bash
#
set -e
set -o pipefail
set -x

if [[ -f /etc/yum/vars/releasever ]]; then
  releasever=$(< /etc/yum/vars/releasever)
else
  releasever=$(
   sed \
    -e '/^CentOS /!d' \
    -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' \
    < /etc/centos-release
  )
fi

arch=$(arch)
case "${arch}" in
  i*86)   basearch=i386 arch=i686 ;;
  x86_64) basearch=${arch} ;;
esac

case "${releasever}" in
  7.0.1406)
    yum update  -y \
      glibc \
      glibc-common \
      glibc-devel \
      glibc-headers
    ;;
esac

対象環境で実行するとglibcが入れ替わる。

あとがき

手元に都合の良く手頃な検証環境があるのは幸せな事なのだろう。




編集
@hansode at 18:00|PermalinkComments(0)TrackBack(0)

2015年01月29日

このエントリーをはてなブックマークに追加

CVE-2015-0235関連

RHEL-6 / CentOS-6 関連

glibcを入れ替える必要がある。

2015/01/29時点の対応

検証用にbuildワーカーを複数環境扱ってるので、それぞれ対応する必要があった。updatesが反映されているのは6.6のみ。それ以下を放置する訳にもいかない。それゆえ、6.66.[0-5]と言う場合分けが必要だ。手順と動作を確認し、上手く行ったのが下記シェルスクリプト。

 ghost-buster-rhel6.sh

バージョン判定には、

  • /etc/yum/vars/releaseverが存在する場合は使用し、
  • 存在しない場合は/etc/centos-releaseから情報取得
#!/bin/bash
#
# requires:
#  bash
#
set -e
set -o pipefail
set -x

if [[ -f /etc/yum/vars/releasever ]]; then
  releasever=$(< /etc/yum/vars/releasever)
else
  releasever=$(
   sed \
    -e '/^CentOS /!d' \
    -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' \
    < /etc/centos-release
  )
fi

arch=$(arch)
case "${arch}" in
  i*86)   basearch=i386 arch=i686 ;;
  x86_64) basearch=${arch} ;;
esac

case "${releasever}" in
  6.6)
    yum update  -y \
      glibc \
      glibc-common \
      glibc-devel \
      glibc-headers
    ;;
  6.[0-5])
    expected_version=2.12-1.149.el6_6.5
    case "$(rpm -qa --qf '%{Version}-%{Release}\n' glibc)" in
      ${expected_version})
        ;;
      *)
        base_uri=http://ftp.jaist.ac.jp/pub/Linux/CentOS/6.6/updates/${basearch}/Packages
        yum install -y \
          ${base_uri}/glibc-${expected_version}.${arch}.rpm \
          ${base_uri}/glibc-common-${expected_version}.${arch}.rpm \
          ${base_uri}/glibc-devel-${expected_version}.${arch}.rpm \
          ${base_uri}/glibc-headers-${expected_version}.${arch}.rpm
        ;;
    esac
    ;;
esac

対象環境で実行するとglibcが入れ替わる。

あとがき

手元に都合の良く手頃な検証環境があるのは幸せな事か。

続きを読む


編集
@hansode at 21:30|PermalinkComments(0)TrackBack(0)

2015年01月26日

このエントリーをはてなブックマークに追加

UI経由ではバージョン指定できない(2015/01/26現在)

Jenkinsは、恐らく手頃なCIツールだ。個人的には2年半近く使っている。他のツールと同様に、ある程度使い続けていると、どこか使い辛さを感じ始める。Jenkinsにおける使い辛さの1つは、狙ったバージョンのJenkinsプラグインをインストール出来ない事。通常のJenkinsプラグインインストール手順は、こうだ。

  1. プラグインマネージャhttp://jenkins/pluginManager/からプラグインを検索
  2. 欲しいプラグインを選択
  3. プラグインをダウンロード・インストール
  4. Jenkinsを再起動

この手順には、プラグインのバージョンが登場しない。暗黙のルールとして、最新バージョン(latest)が指定され、プラグインがインストールされて行く。しかも、管理者の意図とは関係のない所で、だ。これは問題を含んでいる。とある日に構築したシステム構築を再現する様な場合は、要件を満たせない。そこをどうにかしたくて、手順を確立させた。こんな手順、本当は無くなって欲しい。

プラグインインストールの裏側を理解する

Plugins - Jenkins - Jenkins Wiki より、

By hand Download Site

Save the downloaded *.hpi/*.jpi file into the $JENKINS_HOME/plugins directory. You will then need to restart Jenkins (many containers let you do this without restarting the container)

http://updates.jenkins-ci.org/download/plugins/からJenkinsプラグインをダウンロード出来る事が分かる。プラグインリポジトリを注意深く調査していると、過去のバージョンもダウンロード出来る事が分かる。wikiの内容と調査結果をまとめると、こうなる。

最新版:

  • http://updates.jenkins-ci.org/latest/${name}.hpi

特定バージョン:

  • http://updates.jenkins-ci.org/download/plugins/${name}/${version}/${name}.hpi

保存先:

  • ${JENKINS_HOME}/plugins/${name}.hpi

反映方法:

  • Jenkins再起動

サービス起動後、プラグインマネージャを確認すると、狙ったバージョンのプラグインがインストールされている事を確認できるはずだ。少なくとも、自分が検証した限りでは、期待通りの結果となっている。

おまけ:プラグイン名とバージョンのペアを管理する

パッケージを1つ1つインストールするのは、実に面倒臭い。プラグインによっては最新版が良い場合もある。それを一括管理する為、自分は下記のようなスクリプトによりシステム構築している。

jenkins-plugin.sh:

#!/bin/bash
#
# requires:
#  bash
#
set -e
set -o pipefail

JENKINS_HOME=${JENKINS_HOME:-/var/lib/jenkins}
base_url=http://updates.jenkins-ci.org

function plugin_list() {
  cat <<_EOS_ | egrep -v '^$|^#'
PrioritySorter 1.3
config-autorefresh-plugin
configurationslicing
config-file-provider
cron_column
downstream-buildview
git        1.4.0
git-client 1.1.1
hipchat 0.1.5
greenballs
managed-scripts 1.1
nested-view
next-executions
parameterized-trigger 2.18
rebuild 1.20
timestamper 1.5.6
token-macro
urltrigger
view-job-filters
_EOS_
}

while read line; do
  set ${line}
  name=${1} version=${2}
  if [[ -z "${version}" ]]; then
    version=latest
  else
    version=download/plugins/${name}/${version}
  fi
  curl -fSkL ${base_url}/${version}/${name}.hpi -o ${JENKINS_HOME}/plugins/${name}.hpi
done < <(plugin_list)

chown -R jenkins:jenkins ${JENKINS_HOME}/plugins

ヒアドキュメント部分が、プラグイン名とバージョンを指定のペアを管理。

  • プラグイン名のみの場合は、最新版(latest)をインストール
  • プラグイン名とバージョン指定している場合は、そのバージョンをインストール

上記ヒアドキュメント部分を説明すると、こうなる。

  • バージョン固定:
    • PrioritySorter 1.3
    • git 1.4.0
    • git-client 1.1.1
    • hipchat 0.1.5
    • managed-scripts 1.1
    • parameterized-trigger 2.18
    • rebuild 1.20
    • timestamper 1.5.6
  • 最新版:
    • config-autorefresh-plugin
    • configurationslicing
    • config-file-provider
    • cron_column
    • downstream-buildview
    • greenballs
    • nested-view
    • next-executions
    • token-macro
    • urltrigger
    • view-job-filters

なお、これは自分が今までに使って来た厳選プラグインでもある。

あとがき

使い辛い所を分かった上で使い続けるJenkinsさんとの生活。もう少し続きそう。




編集
@hansode at 10:30|PermalinkComments(0)TrackBack(0)

2015年01月25日

このエントリーをはてなブックマークに追加

if文を省略可

GitHub API v3をcurlコマンド経由でAPI呼び出しをしていると、Token有無による場合分けが、しばしば必要となる。普段は当たり前の様にbashスクリプトを活用してcurlコマンドオプションを組み立てている。そして、-n-zによる文字列テストによる場合分けをしていた。そう、今までは。

[[ -n "${GITHUB_TOKEN}" ]] && { echo "-H \"Authorization: token ${GITHUB_TOKEN}\""; }

ある日、時々シェルスクリプトを書く@unakatsuoが言う。

文字列展開のうち、+の使い方・使い処が分かった。 echo ${var:+"foo ${var}"} みたいに書くと良い。

存在は知っていたが、使わずに来た。

未開拓のパラメタ展開

man bash(1)より。

   Parameter Expansion

       ${parameter:+word}
              Use Alternate Value.  If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
  • parameterがnullかunsetの場合は置き換えない
  • そうでない場合は置き換える

これを上手く使うと、if文を省略して書けると言う。先述のコードを、+によるパラメタ展開を使い書き直すと、

before:

[[ -n "${GITHUB_TOKEN}" ]] && { echo "-H \"Authorization: token ${GITHUB_TOKEN}\""; }

after:

echo ${GITHUB_TOKEN:+"-H \"Authorization: token ${GITHUB_TOKEN}\""}

コードが短くなった。

おまけ:test-github-issues.sh

普段使いのスクリプトから一部を抜粋。

#!/bin/bash
#
#
set -e
set -o pipefail

function curl() {
  echo curl "${@}"
}

function github_issues() {
  curl \
   -fsSkL \
   $([[ -n "${GITHUB_TOKEN}" ]] && { echo "-H \"Authorization: token ${GITHUB_TOKEN}\""; } ) \
   "https://api.github.com/repos/${owner}/${repo}/issues"
}

function github_issues2() {
  curl \
   -fsSkL \
   ${GITHUB_TOKEN:+"-H \"Authorization: token ${GITHUB_TOKEN}\""} \
   "https://api.github.com/repos/${owner}/${repo}/issues"
}

owner=github
repo=github

GITHUB_TOKEN=     github_issues
GITHUB_TOKEN=     github_issues2

GITHUB_TOKEN=asdf github_issues
GITHUB_TOKEN=asdf github_issues2

[[ "$(GITHUB_TOKEN=     github_issues)" == "$(GITHUB_TOKEN=     github_issues2)" ]]
echo ${?}
[[ "$(GITHUB_TOKEN=asdf github_issues)" == "$(GITHUB_TOKEN=asdf github_issues2)" ]]
echo ${?}

上記スクリプトを実行すると、こうなる。

$ chmod +x ./test-github-issues.sh
$ ./test-github-issues.sh
curl -fsSkL https://api.github.com/repos/github/github/issues
curl -fsSkL https://api.github.com/repos/github/github/issues
curl -fsSkL -H "Authorization: token asdf" https://api.github.com/repos/github/github/issues
curl -fsSkL -H "Authorization: token asdf" https://api.github.com/repos/github/github/issues
0
0
  • GITHUB_TOKEN あり / なし
  • github_issuesgithub_issues2 による生成文字列

生成された文字列が改良前後で同一かどうかをテストし、同一である事を確認。

あとがき

多用し過ぎると可読性が低下しそうなので、ご利用は計画的に。




編集
@hansode at 17:00|PermalinkComments(0)TrackBack(0)

2015年01月06日

このエントリーをはてなブックマークに追加

快適なOpenSSH作業環境を手に入れるまでの道のり

OpenSSHによるリモートログイン対象が多く、整理せざるを得ない状況に追い込まれた所から始まり、改善を重ね、今の所は快適な環境を手に入れた。これまでの記録を、ここにまとめておく。

主な特徴

  • keychainのような機能
    • ssh-agent多重起動防止
    • ssh-addによる複数の秘密鍵登録管理
    • SSH_AUTH_SOCKパス固定化(screen/tmux detach/reattach連携対応用途)
  • keychainよりも軽量
    • 軽量な理由は、keychainよりも処理してる内容が少ないから

背景・経緯

  1. 作業対象が数台だった頃、各サーバに秘密鍵を配置する運用でしのいで来た
    1. ログイン対象数が増え始める
    2. 管理対象キーペア数が増え始め、鍵管理が面倒臭くなる
    3. SSHクライアントとなる作業用ラップトップ数が増え始める
    4. ~/.ssh/をバージョン管理し、ssh_configと秘密鍵を管理
    5. ~/.ssh/をデプロイする所までは解決 (本エントリにおいては省略)
  2. ログイン時のパスフレーズ入力が面倒になり、ssh-agentについて調査・検証を開始
    1. ssh-agentが自動起動するように~/.bashrcを改良
    2. ssh-agent多重起動問題に遭遇
    3. mitchellh謹製bashrc と出会い、コードを拝借
    4. ssh-agent多重起動防止を実装
  3. 複数の秘密鍵を扱いたいが、mitchellh版ではssh-addのデフォルトパスしか扱ってなかった
    1. 複数の秘密鍵を扱えるように改良
  4. リモートログイン先にて新たなssh-agentが起動して来てしまう問題に遭遇
    1. 大元のSSHクライアント環境においては、単一ssh-agentプロセスのみ起動していて問題は無い
    2. しかし、リモートログインすると、リモートログイン先で新たにssh-agentが起動してしまう問題に遭遇
    3. リモートログイン状態かどうかを判定する仕組みを追加
  5. screenでdetachした後、reattachすると、UNIXソケットパスが切り替わってしまう問題に遭遇
    1. ssh-agentscreenの中から使う方法を発見・拝借し、SSH_AUTH_SOCKを固定化
    2. 更にtmux内からもSSH_AUTH_SOCKが固定化されてる事を確認
  6. リモートログイン先がMacOSの場合、SSH_AUTH_SOCK が固定されない問題に遭遇
    1. Yosemiteでの外部からログインしてssh-agentを正しく使う方法を発見・拝借し、SSH_AUTH_SOCK を固定化
  7. 快適なssh生活を手に入れた
  8. 実は、ここまでまとめ来た内容と似たツールとしてkeychainの存在を知る・・・
    1. keychainモードへの切り替えを試みる
    2. プロトタイプ版を作り、使ってみると、起動するまでが遅かった
    3. keychainモードへの切り替えを保留
    4. keychainの機能のうち、ホームディレクトリがNFSマウントされた環境を考慮した仕組みだけは拝借し、機能追加
  9. 2015/01/06現在、似非keychainで快適生活を過ごしている
  10. YosemiteへのSSHログイン後の鍵が期待通りではない事に気付く
    1. 手元の環境に置いてはYosemiteでの外部からログインしてssh-agentを正しく使う方法が不要だったので、修正・削除
  11. 2015/01/08現在、似非keychainで快適生活を過ごしている

対象環境

  • ログインシェルをbashに設定しているUNIXアカウント
  • ログインシェルをzshに設定しているUNIXアカウント

対象者?(自分が置かれた環境とも言う)

  • ログイン元環境が複数台存在する
  • ログイン先環境が複数台存在する
  • 1日にSSH接続する回数が恐らく多い方だ
  • keychainが遅いと感じている

動作確認済み環境

$ bash --version
$ ssh -V
  • Cygwin 1.7 / Windows 8.1
    • GNU bash, version 4.1.17(9)-release (i686-pc-cygwin)
    • OpenSSH_6.7p1, OpenSSL 1.0.1j 15 Oct 2014
  • MacOS 10.10 (Yosemite)
    • GNU bash, version 3.2.53(1)-release (x86_64-apple-darwin14)
    • OpenSSH_6.2p2, OSSLShim 0.9.8r 8 Dec 2011
  • Raspbian 7.6 (Wheezy)
    • GNU bash, version 4.2.37(1)-release (arm-unknown-linux-gnueabihf)
    • OpenSSH_6.0p1 Debian-4+deb7u2, OpenSSL 1.0.1e 11 Feb 2013
  • Fedora release 20 (Heisenbug)
    • GNU bash, version 4.2.53(1)-release
    • OpenSSH_6.4p1, OpenSSL 1.0.1e-fips 11 Feb 2013
  • CentOS-6.6
    • GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
    • OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013

使い方

  1. 【設定】 後述する作業対象ファイルを作成・修正・保存
  2. 【準備】 新規bashプロセスを作成し、ssh-agentssh-addを自動起動させる
  3. 【実行】 ssh -A <remotehost> にてリモートホストにSSH接続

作業対象ファイル

  1. ~/.bashrc or ~/.zshrc - 必須
    • 追記。無い場合は新規作成。
    • SSHクライアントおよび全SSH接続対象に反映
  2. ~/.ssh/agent_keys - 必要に応じて
    • 新規作成 +SSHクライアント環境のみ
~/.bashrc or ~/.zshrc- 必須

~/.zshrcにおいても動作を確認。bash前提で話を進めて行くので、zshの場合はbashzshで読み替えて頂きたい。

SSHクライアント環境および全SSH接続対象の~/.bashrcに追記・反映する必要がある。下記内容を、~/.bashrcに追記。追記場所は行末などで良い。

#-------------------------------------------------------------------------------
# SSH Agent
# + based on https://github.com/mitchellh/dotfiles/blob/master/bashrc#L181-L203
#-------------------------------------------------------------------------------

ssh_env=${HOME}/.ssh/environment.${HOSTNAME}

function start_ssh_agent() {
  # remote?
  [[ -z "${SSH_CLIENT}" ]] || return 0

  ssh-agent | sed 's/^echo/#echo/' > ${ssh_env}
  chmod 0600 ${ssh_env}
  . ${ssh_env} > /dev/null

  local ssh_agent_keys=${HOME}/.ssh/agent_keys

  if [[ -f "${ssh_agent_keys}" ]]; then
    local privkey=
    while read privkey; do
      # expand a file path using "~" or "${HOME}"
      eval privkey=${privkey}
      [[ -f "${privkey}" ]] || continue
      ssh-add ${privkey}
    done < ${ssh_agent_keys}
  else
    ssh-add
  fi
}

# Source SSH agent settings if it is already running, otherwise start
# up the agent proprely.

if [[ -f "${ssh_env}" ]]; then
  . ${ssh_env} > /dev/null
  ps -p ${SSH_AGENT_PID} > /dev/null || {
    start_ssh_agent
  }
else
  start_ssh_agent
fi

# static ssh agent sock path

ssh_agent_sock=${HOME}/.ssh/agent.sock.${HOSTNAME}

# based on http://www.gcd.org/blog/2006/09/100/
if ! [[ -L "${SSH_AUTH_SOCK}" ]] && [[ -S "${SSH_AUTH_SOCK}" ]]; then
  ln -fs ${SSH_AUTH_SOCK} ${ssh_agent_sock}
  export SSH_AUTH_SOCK=${ssh_agent_sock}
fi
~/.ssh/agent_keys - 必要に応じて作成

このファイルは作成しなくても良い。もしも使う場合、記述例は下記の通り。

~/.ssh/keys/github/hansode
~/.ssh/keys/wakame/deploy

この仕組みが威力を発揮する主な状況は、

  • 扱う秘密鍵が複数存在する時
  • 扱う秘密鍵は単数だが、ssh-addコマンドで追加させたい秘密鍵パスがデフォルト検索パスではなく、別の場所か複数指定したい時

なお、デフォルト検索パスはssh-addmanで述べられている。

man ssh-add より:

 DESCRIPTION
     ssh-add adds private key identities to the authentication agent,
    ssh-agent(1).  When run without arguments, it adds the files
    ~/.ssh/id_rsa, ~/.ssh/id_dsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ed25519 and
    ~/.ssh/identity.  After loading a private key, ssh-add will try to load
    corresponding certificate information from the filename obtained by
    appending -cert.pub to the name of the private key file.  Alternative
    file names can be given on the command line.

manに述べられている通り、引数無しssh-addコマンド実行の場合は、下記ファイルが追加対象となる。

  • ~/.ssh/id_rsa
  • ~/.ssh/id_dsa
  • ~/.ssh/id_ecdsa
  • ~/.ssh/id_ed25519
  • ~/.ssh/identity

これらに該当しないファイルパス、または複数ファイルを扱いたい場合、~/.ssh/agent_keysに記述しておくと、秘密鍵を容易に扱えるようになる。例え扱う秘密鍵が単一であり、しかもデフォルト検索対象であったとしても、明示的な宣言により、何を利用しているのかが見えやすいと言うメリットがある。それゆえ、個人的には積極的に利用している。

動作確認: ローカル環境編

前提条件
  • ローカル環境の~/.bashrc 修正が完了している事
確認項目
  1. ssh-agentが自動起動する
  2. ssh-addにより秘密鍵が登録される事
  3. ssh-agentが多重起動しない事
  4. SSH_AUTH_SOCKが固定化される事
確認作業: ssh-agent自動起動、ssh-addによる鍵登録、多重起動防止

bashを起動する。設定が正しければ、ssh-agent起動とssh-addによる複数鍵登録に成功し、下記のような出力が得られる。キーペアにパスフレーズが設定されている場合は、パスフレーズ入力待ちとなる。

localhost$ bash
Identity added: /home/hansode/.ssh/keys/github/hansode (dsa w/o comment)
Identity added: /home/hansode/.ssh/keys/wakame/deploy (rsa w/o comment)

次に、ssh-agentが多重起動しない事を確認する。新たなbash起動前のPIDを確認。

localhost$ echo $$
6796

PID6796である事が分かった。更にbashを起動。

localhost$ bash
localhost$ echo $$
2388

bash起動後、今度は何も出力されていない。そしてPID2388。これにより、別PIDである事が分かる。更にもう1つbashを起動してみる。

localhost$ bash
localhost$ echo $$
3916

PIDssh-agentの起動状況をまとめると、

  1. PID=6796 - ssh-agentが起動
  2. PID=2388 - ssh-agentは起動しない
  3. PID=3916 - ssh-agentは起動しない

整理すると、

  • ssh-agnetが起動してない場合は、ssh-agentを起動し、ssh-addで秘密鍵を登録
  • ssh-agentが起動している場合は、何もしない
  • ssh-agent多重起動を防止
確認作業: SSH_AUTH_SOCK固定化

SSHに関する環境変数を確認。

localhost$ env | sort | egrep ^SSH_
SSH_AGENT_PID=192
SSH_AUTH_SOCK=/home/hansode/.ssh/agent.sock.localhost

この結果から、SSH_AUTH_SOCK~/.ssh/agent.sock.${HOSTNAME}である事が分かる。ファイルパスに${HOSTNAME}を含めている理由は、ホームディレクトリがNFSマウントされた環境においても動作させる為。

~/.ssh/agent.sock.${HOSTNAME}のファイル情報を確認してみると、

localhost$ ls -l ~/.ssh/agent.sock.localhost
lrwxrwxrwx 1 hansode なし 32 Jan  3 21:39 /home/hansode/.ssh/agent.sock.localhost -> /tmp/ssh-3RRm1KL1FZ1A/agent.6700=

SSH_AUTH_SOCKに指定されているファイルは、シンボリックリンクである事が分かる。

シンボリックリンク先は/tmp/ssh-3RRm1KL1FZ1A/agent.6700である事も分かる。/tmp/ssh-3RRm1KL1FZ1A/agent.6700のファイル情報を確認してみると、実態となるUNIXソケットである事が分かる。

localhost$ ls -l /tmp/ssh-3RRm1KL1FZ1A/agent.6700
srw------- 1 hansode なし 0 Jan  3 21:29 /tmp/ssh-3RRm1KL1FZ1A/agent.6700=

整理すると、

  1. SSH_AUTH_SOCK~/.ssh/agent.sock.${HOSTNAME}
  2. ~/.ssh/agent.sock.${HOSTNAME}は、シンボリックリンク
  3. リンク先は/tmp/ssh-3RRm1KL1FZ1A/agent.6700

ssh-agentが改めて起動する場合は、シンボリックリンク情報だけが張り替わり、SSH_AUTH_SOCK=~/.ssh/agent.sock.${HOSTNAME}は固定化・定数化される。

動作確認: リモート環境編

前提条件
  • ローカル環境の~/.bashrc 修正が完了している事
    • ssh-agentが起動してる事
    • リモート環境用キーペアの秘密鍵がssh-addによる登録されてる事
  • リモート環境の~/.bashrc 修正が完了している事
    • リモート環境用キーペアの公開鍵が~/.ssh/authorized_keysに登録されてる事
確認項目
  1. SSH_AUTH_SOCKが固定化される事
確認作業: SSH_AUTH_SOCK固定化

参考までに、ForwardAgent noの場合は、SSH_AUTH_SOCKが存在しない。

localhost$ ssh remotehost
remotehost$ env | sort | egrep ^SSH_
SSH_CLIENT=192.0.2.68 39922 22
SSH_CONNECTION=192.0.2.68 39922 192.0.2.101 22
SSH_TTY=/dev/pts/0

ForwardAgent yes(-Aオプション付き)の場合は、SSH_AUTH_SOCKが設定される。そして、リモート環境においても~/.ssh/agent.sock.remotehostが設定されている事も分かる。

localhost$ ssh -A remotehost
remotehost$ env | sort | egrep ^SSH_
SSH_AUTH_SOCK=/home/hansode/.ssh/agent.sock.remotehost
SSH_CLIENT=121.114.159.68 39934 22
SSH_CONNECTION=192.0.2.68 39922 192.0.2.101 22
SSH_TTY=/dev/pts/0

ローカル環境と同じ様に、リモート環境も~/.ssh/agent.sock.remotehostは、実態ソケットファイルを参照するシンボリックリンクとなっている事が分かる。

remotehost$ ls -l ~/.ssh/agent.sock.remotehost
lrwxrwxrwx 1 hansode hansode 31 Jan  3 22:33 /home/hansode/.ssh/agent.sock.remotehost -> /tmp/ssh-2cwxr7qAo7/agent.30716

動作確認: リモート環境screen連携編

前提条件
  • ローカル環境の~/.bashrc 修正が完了している事
  • リモート環境の~/.bashrc 修正が完了している事
確認項目
  1. SSH_AUTH_SOCK
    1. screenの外から固定化されている事
    2. screenの中から固定化されている事
    3. detach/reattachしたscreenの中からも固定化されている事
確認作業: SSH_AUTH_SOCK固定化

ForwardAgent yes(-Aオプション付き)で対象サーバにSSHログイン。

localhost$ ssh -A remotehost

screen起動前のPIDと環境変数SSH_AUTH_SOCKを確認。

remotehost$ echo $$
27973
remotehost$ printenv SSH_AUTH_SOCK
/home/hansode/.ssh/agent.sock.remotehost

期待通り~/.ssh/agent.sock.${HOSTNAME}が指定されている。

この時のファイルパスが、ローカル環境におけるファイルパスとは違う事に注目したい。リモート環境はリモート環境特有ファイルパスが生成されいてる。

  • localhost: ~/.ssh/agent.sock.localhost
  • remotehost: ~/.ssh/agent.sock.remotehost

これはローカル環境と同様にNFSマウントされたホームディレクトリ環境を考慮した仕組みである。

次にscreenを起動。

remotehost$ screen

screen内のプロセスIDと環境変数SSH_AUTH_SOCKを確認。

remotehost:screen$ echo $$
28303
remotehost:screen$ printenv SSH_AUTH_SOCK
/home/hansode/.ssh/agent.sock.remotehost

期待通り~/.ssh/agent.sock.${HOSTNAME}が指定されている。

次に、detachしreattachする。

remotehost$ screen -r

再度screen内のプロセスIDと環境変数SSH_AUTH_SOCKを確認。

remotehost:screen$ echo $$
28303
remotehost:screen$ printenv SSH_AUTH_SOCK
/home/hansode/.ssh/agent.sock.remotehost

期待通り~/.ssh/agent.sockが指定されている。これにより、detachしたscreen内の作業を後日行う事が可能だ。

余談:Keychain化を試みたが・・・

ある程度の仕組みをまとめ終えてからkeychainの存在を知り、一度はkeychainに乗り換え作業をしてみた。結果は、keychainを使わない事を選択した。理由は、keychain起動速度が遅い事。多機能ゆえに速度が犠牲になっているのだろう。秘密鍵を常時5つ登録している状況においては、特に遅く感じられた。独自実装だけでも機能要件を満たしていたので、敢えてkeychain化を中断した。

個人利用の限りでは、似非keychainで問題は無い。仮に多人数を対象にした鍵管理フローを構築したい場合は、keychain仕様が良いのかも知れない。少なくとも誰か1人が不満を言うまでは。

現バージョンの課題

  • ssh-agent再起動を考慮してない
    • 解決策はssh-agent -kを手動実行し、bashを起動

あとがき

本エントリを書くきっかけとなったのは、身近なメンバーに対して自分のssh-agent・秘密鍵管理術の説明資料が無かった事。彼らに作業改善案を口頭で説明・提案するにしては、伝える量が多く、手頃な説明資料が欲しくなった。そして、今回の末年始休みを活用し、まとめ終えた。

エントリ初版は数時間で書き終えたが、どうしても関連するコードが気になり、整理を開始。そして再検証作業、文書修正・・・。最終的に書き終えるまでにかかったのは、合計6日間。関連物も含め、整理する良い期間となった。

とにかく、手頃な説明資料を手に入れた!喜ばしい。

関連成果物

参考文献

続きを読む


編集
@hansode at 10:30|PermalinkComments(0)TrackBack(0)

2013年07月01日

このエントリーをはてなブックマークに追加

世間では余り使われてないのか

test(1)コマンドによる変数の中身やファイル・ディレクトリの存在有無を確認する方法は、良く知られている気がする。しかし、functionに関して触れらてるエントリを見かけないので、ここに書いておく。

declareかtypesetの出番

declareとtypeset、どちらでも良い。同じ意味。自分は何となくな理由で declare を使っている。

declare -f [name]

サンプルコード

#!/bin/bash

function defined_func() { :; }

declare -f defined_func
echo $? # should be zero

declare -f undefined_func
echo $? # should be non-zero

実行結果は、こうなる。

defined_func ()
{
    :
}
0
1
結果まとめ
項目declare -f 実行結果終了ステータス
定義済み関数の内容0
未定義無し1

もしも関数の内容を表示させたくない場合は /dev/null へリダイレクトすれば良い。

declare -f [name] >/dev/null
使い所は?

declare -f [name] を上手く使うと、bashでDSLが可能になって来る。bashでDSLのネタを書く為の前提エントリとして書いたのが、本エントリ。次回、bash-DSLエントリをお楽しみに?

続きを読む


編集
@hansode at 13:00|PermalinkComments(0)TrackBack(0)

2013年06月30日

このエントリーをはてなブックマークに追加

個人見解: 大半は "$@" にしておけば、期待する動作となる

幾つかスクリプトを書いて来て、気を付けておいた方が良かったことの1つが、$@の扱い。$@ と "$@" は、内部処理では大きく違う。サンプルコードと結果を、まとめてみた。

dump-vers.sh

#!/bin/bash
#
# $ dump-vers.sh [args]
#

function dump_vers() {
  while [[ "$1" ]]; do
    echo "'$1'"
    shift
  done
}

echo '#1'; dump_vers $@
echo
echo '#2'; dump_vers "$@"

実行例1: " なし

$ ./dump-vers.sh a b c d
#1
'a'
'b'
'c'
'd'

#2
'a'
'b'
'c'
'd'
  • #1 引数4つ
  • #2 引数2つ

実行例2: " あり

$ ./dump-vers.sh "a b" "c d"
#1
'a'
'b'
'c'
'd'

#2
'a b'
'c d'
  • #1 引数4つ
  • #2 引数2つ
まとめ
引数#1#2
実行例1a b c d44
実行例2"a b" "c d"42

大半は "$@" にしておけば、期待する動作となる。

続きを読む


編集
@hansode at 15:00|PermalinkComments(0)TrackBack(0)

2013年01月14日

このエントリーをはてなブックマークに追加

Bash + Vagrant ⇒ Bagrant

これまでに何度も仮想マシンイメージを作って来て、どこか面倒臭さを感じていた。そんな中、Vagrantに出会い、ソレが欲しかったモノの1つであると感じ、調査・検証・評価。そして、bash実装する事にした。

Bagrantの機能
  • Bagrantfileで仮想マシンを定義
  • bagrantコマンドで仮想マシンをビルド・起動・停止
  • ハイパーバイザーはKVM
利用ライブラリ

自作したvmbuilderのみ。

使い方

詳細はhansode/bagrantを参照の事で、起動までは、下記手順で行える。

  1. Bagrantfileを作成 $ bagrant init
  2. 仮想マシンイメージをビルド $ bagrant build
  3. 仮想マシンを起動 $ bagrant up

また、hansode/bagrant-boxがBagrantfileを含んだテンプレート集となっているので、Bagrantfileを自作したい方の参考になるはず。

開発期間

移植作業だったので、結構あっさりと実装出来た気がする。

  • 設計: 3日(主にVagrantの調査)
  • 実装: 2日
今後の予定

開発した動機がそうであるように、何か機能追加される事があったら、自分自身が欲しいと思ったタイミング。

あとがき

これも、まだ、自動化したかった事の1つでしかない。

フィードバックなど

もしBagrantに興味がある方がいらっしゃいましたら、ご一報下さい。

続きを読む


編集
@hansode at 10:00|PermalinkComments(0)TrackBack(0)

2012年11月01日

このエントリーをはてなブックマークに追加

複数ディストリビューション対応へ向けて

ギーク達はfedora対応を希望

前回のエントリに反応してくれたギーク達は、CentOSよりもfedora対応を希望していた。『fedora対応…!』と思ったけども、fedoraを使ってないので良くわかってない。それよりは、複数ディストリビューション対応への準備体操として、CentOS 5対応に着手してみた。

検証環境

インストール
$ git clone git://github.com/hansode/vmbuilder.git
実行方法
$ cd vmbuilder/kvm/rhel/6/
$ sudo ./vmbuilder.sh --distro-name=**centos** --distro-ver=**5**

実行してから約10分程度で実行したディレクトリ直下に仮想マシンイメージが作成される。

2012/11/01現在の対応ディストリビューション

distro-name と distro-ver に指定可能な組み合わせは下記の通り。

  • centos 5 (5.x)
  • centos 6 (6.x)
  • sl 6 (6.x)

新規追加されるタイミングは、今の所、自分が欲しいと思う時か、聞こえて来る周りからの声。

問題点

rhel6環境でrhel5を構築すると、仮想マシンイメージを作成可能ではあるが、rhel5環境としては不完全だ。

  • 今のvmbuilder.shの仕様では、ホスト環境のyumコマンドでゲスト環境を構築する
  • ゲスト環境におけるrpmdbのフォーマットは、ホスト環境に依存する

個人的に困ってないので、しばらくの間は保留扱い。

改善案

検証すべき項目

  • ホスト環境とゲスト環境を一致させる
    • 例) rhel5用仮想マシンイメージを構築する場合は、rhel5環境で行う
  1. post installフェーズで、rpmdbを変換するなどの対応をする
  2. yumコマンドかyum.confにrpmdbのバージョン指定する方法があれば、指定してみる

上記のうち、 1 が良さそうなので、対応手段は、構築フェーズを分ける事だろうか。

  1. ゴールとなる仮想マシンをビルドする為のビルド環境(1次chroot環境) を構築
  2. 構築されたビルド環境(1次chroot環境)にて、ゲスト環境(2次chroot環境)を構築
  3. ホスト環境にてマシンイメージ化

これで対応出来そうな気がするので、あとは検証・実装するだけの事。この辺をしっかり実装しておかないと、fedora対応も大変そう。

あとがき

環境差異が発生するとは面白い事。

フィードバックなど

希望するネタ等、フィードバック、大歓迎です。

続きを読む


編集
@hansode at 13:30|PermalinkComments(0)TrackBack(0)