オープンソースこねこね

Webプログラミングなどについてあれこれ。

最新のGit-2.14.1のRPMを提供するyumレポジトリを立てた (CVE-2017-1000117対策)

cve-2017-1000117への対応として、GitをアップデートしたいのだけどCentOSの公式の対応がよくわからない。とりいそぎ自前でgit-2.14.1のRPM作ってyumリポジトリgithub pagesで立てた。

github.com

以下のコマンドでcve-2017-1000117の対応がなされたgit-2.14.1が入る。

$ sudo sh -c 'echo "[kohkimakimoto-git-update-el7-x86_64]
name=kohkimakimoto-git-update-el7-x86_64
baseurl=https://kohkimakimoto.github.io/yumrepo-git-update/el7-x86_64
gpgcheck=0
enabled=0
" > /etc/yum.repos.d/kohkimakimoto-git-update-el7-x86_64.repo'
$ sudo yum update --enablerepo=kohkimakimoto-git-update-el7-x86_64 git

RPMの作り方は

CentOSでGit v2系をRPMで作成する | 本日も乙

を参考にさせていただいた。感謝。

Githubのリポジトリをyumリポジトリとして使う

簡単にできた。手順は後ほど。

背景

普段使っているサーバOSはCentOSなので、自作のOSSツールなどをyumコマンドを使ってさくっとインストールしたい。RPMを作るところまではスクリプト化してあって一発でできるようになっている。yumリポジトリをどうするか。今まではbintrayで個人用のyumリポジトリを立てて使っていました。

bintray.com

ところが、先日このbintrayのyumリポジトリから古くてもう使っていないパッケージを削除しようとしたところ「公開後180日たつと削除できない」とエラーになってしまいました。

結局この後、bintrayのサポートに問い合わせてみたら、わりとすぐに削除してくれたのですが、今後パッケージが要らなくなって消そうとするたびに、問い合わせしなくてはならないのかな、、、と思うとちょっとツライです。

そんなわけで代替となるyumリポジトリをホストしてくれるサービスを探していたりしました。packagecloud.ioなどを見てみましたが、いまひとつ使う気になれず。。。

packagecloud.io

そこで昔yumリポジトリを個人サーバに立てたときのことを思い出してみると、結局必要だったのはただのhttpサーバだけだったはずでは。それならGithub Pagesを使えばGithub上にyumリポジトリも立てられるんじゃないかなと思い至り、ちょっとググったら、かなり前にやっぱり同じことをやっている人がいた。

qiita.com

この記事を参考にして、今はgh-pagesのブランチを切る必要もないし、SSLも対応しているので、その辺をふまえて以下のようにやりました。

作業手順

Githubリポジトリを作る

何でもいいですが、ここではリポジトリ名はyumrepo-exampleとします。作成後リポジトリの設定画面からGithub Pagesの設定をします。Sourceをmasterブランチに設定すれば昔のようにgh-pagesのブランチを切る必要もないです。

f:id:kohkimakimoto:20170721194716p:plain

リポジトリをcloneしてRPMファイルを配置する

上記のリポジトリをローカルにcloneし、サポートするディストリビューションアーキテクチャなどで、適当にサブディレクトリを作成してその中にホストしたいRPMファイルをおきます。試しに拙作であるプロビジョニングツールのcofuのRPM

https://github.com/kohkimakimoto/cofu/releases

からダウンロードしてCentOS6とCentOS7用に配置します

$ mkdir el6-x86_64 el7-x86_64
$ curl -L -o el6-x86_64/cofu-0.6.0-1.el6.x86_64.rpm   https://github.com/kohkimakimoto/cofu/releases/download/v0.6.0/cofu-0.6.0-1.el6.x86_64.rpm
$ curl -L -o el7-x86_64/cofu-0.6.0-1.el7.x86_64.rpm   https://github.com/kohkimakimoto/cofu/releases/download/v0.6.0/cofu-0.6.0-1.el7.x86_64.rpm

createrepoコマンドでリポジトリのメタファイルを作る

createrepoコマンドでyumリポジトリに必要なメタファイルを生成します。Macで直接動作するcreaterepoコマンドはないのですが、dockerを使えばこのあたりも簡単にできます。以下のようにしてbashスクリプトcreaterepoとして作成します。

$ echo '#!/usr/bin/env bash
docker run --rm  -u "$(id -u):$(id -g)" -v $PWD:/build -w /build kohkimakimoto/rpmbuild:el7 createrepo "$@"' > createrepo
$ chmod 755 createrepo

そして、RPMを配置したサブディレクトリを指定して実行します。

$ ./createrepo el6-x86_64
Spawning worker 0 with 1 pkgs
Spawning worker 1 with 0 pkgs
Spawning worker 2 with 0 pkgs
Spawning worker 3 with 0 pkgs
Workers Finished
Saving Primary metadata
Saving file lists metadata
Saving other metadata
Generating sqlite DBs
Sqlite DBs complete

$ ./createrepo el7-x86_64
Spawning worker 0 with 1 pkgs
Spawning worker 1 with 0 pkgs
Spawning worker 2 with 0 pkgs
Spawning worker 3 with 0 pkgs
Workers Finished
Saving Primary metadata
Saving file lists metadata
Saving other metadata
Generating sqlite DBs
Sqlite DBs complete

後はコミットしてpushすれば完了です。なおRPMを更新したら都度同じようにcreaterepoスクリプトを実行してメタファイルも更新します。

yumでインストールしてみる

リポジトリができたのでyumでインストールしてみます。以下の例はCentOS7でやった際のもの。リポジトリのパスなどは適宜読み替えてください。

まずリポジトリの設定をして

$ sudo sh -c 'echo "[github-yumrepo-example-el7-x86_64]
name=github-yumrepo-example-el7-x86_64
baseurl=https://kohkimakimoto.github.io/yumrepo-example/el7-x86_64
gpgcheck=0
enabled=1
" > /etc/yum.repos.d/github-yumrepo-example-el7-x86_64.repo'

あとは普通にyum installする

$ sudo yum install cofu

以上です。サンプルのリポジトリは以下においておきます。

github.com

開発環境用に自己署名SSL証明書をつくるスクリプトを書いた

外部からアクセスできるサーバ環境の場合はLet’s Encryptを使うことで手軽にSSL証明書を設定できるようになったのですが、ローカルマシンにVagrantでたてた開発用VMの場合はそうもいかず、この場合は自己署名のSSL証明書をつかっています。

証明書の発行は普通にopensslコマンドを手作業で叩いてやってたんですが、

  • Chromeだと-extfileオプションで追加のパラメータを指定しないと、Macのキーチェーンにインストールした自己署名証明書がエラーになるので、すこし作業の手間が増えた
  • ローカル環境への名前解決をxip.ioなどのワイルドカードDNSを使うようになって、利用するホスト名がmyapp.192.168.0.10.xip.io,myapp2.192.168.0.10.xip.ioのように多くなった
  • xip.ioで名前解決する構成上、サブドメインへの階層が深くなりワイルドカードの証明書が使えなくなったため、ドメイン名ごとに証明書を発行する必要がある

などの理由で、作業がちょっと煩雑になってきたのでスクリプト化しました。

github.com

Bashスクリプト実装です。引数にドメイン名を指定して(複数指定も可)実行すると

$ selfsigned-crt example.com

以下のように秘密鍵CSR、証明書を吐き出します。有効期限は100年。

example.com-selfsigned.key
example.com-selfsigned.csr
example.com-selfsigned.crt

あとはこの生成した証明書をサーバに配置すればいいのですが、自分の場合、開発環境のプロビジョニングツールに、このスクリプトを実行させるようにレシピを書いておいて、証明書の生成もプロビジョニングツールにやらせてます。自作ツールのcofuを使うと以下のような感じ、

execute "generate-self-signed-ssl" {
    command = [=[
        selfsigned-crt -o /etc/nginx/ssl myapp1.192.168.1.10.xip.io
    ]=],
    not_if = "test -f /etc/nginx/ssl/myapp1.192.168.1.10.xip.io-selfsigned.crt",
}

これで開発環境をセットアップするときに、自動的に証明書も作成できるようなりました。

関連外部リンク

Visual Studio CodeからMacの辞書(Dictionary.app)を開くエクステンションをつくった

kohkimakimoto.hatenablog.com

これのvscode版。LL書く用のエディタをvscodeにしたのでIntelliJatomでやってたように、CTRL+CMD+Kで辞書を起動できるようにしてみました。

marketplace:

marketplace.visualstudio.com

github repository:

github.com

表示名Mac Dictionaryでmarketplaceに公開したので、適当に検索してインストールしてください。vscodeだとCTRL+CMD+Dで簡易辞書がひけるのでそれを使ってもいいのだけど、atomIntelliJで上記プラグインを使ってたせいで、直接辞書をひくのに慣れてしまった。vscodeでもそういうのが欲しくなり、やっぱり作ってみました。

f:id:kohkimakimoto:20170511103121g:plain

SSHラッパーコマンドEsshのv1.0.0をリリースしました

Esshは、zsh補完やLuaスクリプトによる動的なコンフィグレーションなど、便利機能を実装した、sshのラッパーコマンドです。

github.com

ドキュメントをまとめたWebサイトも作成しました。

https://essh.sitespread.net/

上記Webサイトのトップにasciinemaで撮ったターミナルオペレーションのデモを乗せましたので、そちらを見ていただければ、どういったものか大体のことはわかると思います。

開発の経緯

2015/11に以下の記事を書き、Zsshというコマンドを作成しました。

kohkimakimoto.hatenablog.com

その後、CentOSリポジトリzsshというパッケージが存在していたため、コマンド名をzsshからesshに変え、自分の利用にあわせて、適宜、機能の追加や変更を続けていました。そして、自分が使う上で必要十分な機能がそろって、大きな変更もなくなってきたので、この度これをv1.0.0としてリリースすることにしました。なお2015/11時点のものとは仕様が大きく様変わりしており、以下に改めて、機能について説明します。

機能

EsshはGoで書かれたシングルバイナリのCLIツールで多機能sshコマンドとして利用できます。実装的には内部でsshコマンドを実行するラッパーコマンドになっています。標準のsshコマンドでは使えない以下のような機能が利用できます。

  • Luaによる設定ファイル。これによりSSH接続サーバの設定(ssh_config)をより動的に構築することができる。
  • サーバ接続、切断時に発火するフックコマンドを設定できる。
  • サーバにタグをつけられ、一覧をターミナルに出力できる。
  • ZshBash用の強力な補完を組み込みで提供。
  • カレントディレクトリごとに設定ファイルの切り替え。
  • Capistranoのような複数サーバに並行してコマンドを実行できるタスクランナー。
  • 設定用のLuaコードはモジュールとしてGitリポジトリ上で共有できる。

一通りの機能に触れるためのチュートリアルを用意したので、利用の際はここから始めるとよいかと思います。

https://essh.sitespread.net/intro/ja/index.html

利用例

上記のチュートリアルには書いていないが、私個人が使っている機能などを中心にいくつか紹介します。

ログイン時にターミナルの色を変える

接続、切断時のフックコマンドhooks_before_connecthooks_after_disconnectでターミナルの色を変えます。私は本番環境につなぐときは赤にしています。

-- ~/.essh/config.lua

-- 事前にmacのターミナル設定でRedとBlackというプロファイルを作っておく
local red_screen_command    = "osascript -e 'tell application \"Terminal\" to set current settings of first window to settings set \"Red\"'"
local black_screen_command  = "osascript -e 'tell application \"Terminal\" to set current settings of first window to settings set \"Black\"'"

host "webserver-01" {
    HostName = "192.168.56.32",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-01",
    hooks_before_connect = { red_screen_command },
    hooks_after_disconnect = { black_screen_command },
    tags = {
        "web",
    },
}

host "webserver-02" {
    HostName = "192.168.56.33",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-02",
    hooks_before_connect = { red_screen_command },
    hooks_after_disconnect = { black_screen_command },
    tags = {
        "web",
    },
}

f:id:kohkimakimoto:20170410083157g:plain

アプリケーションのデプロイ

リモートサーバにコマンドを実行します。以下はただのサンプルです。実環境ではもっと複雑なスクリプトを実行させています。

host "webserver-01" {
    HostName = "192.168.56.32",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-01",
    tags = {
        "web",
    },
}

host "webserver-02" {
    HostName = "192.168.56.33",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-02",
    tags = {
        "web",
    },
}

task "deploy" {
    backend = "remote",
    targets = {"web"},
    prefix = true,
    parallel = true,
    script = [=[
        echo "deploy app to $ESSH_HOSTNAME"
        
        # your deploy commands...

        echo "Done."
    ]=],
}

f:id:kohkimakimoto:20170410104414g:plain

以上です。日々のSSHライフのお供に、どうぞお使いください。m( )m

ReactNativeのiOSアプリをオフラインの実機で動かす

※2017-04-10現在のメモ。

先に結論。以下の#if !(TARGET_IPHONE_SIMULATOR)で囲った部分のコードをios/AppName/AppDelegate.mに仕込むといいです。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

#if !(TARGET_IPHONE_SIMULATOR)
  jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#else
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
#endif

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"AppName"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

経緯

ReactNativeでiOSアプリを作ってみてます。SwiftやObjective-Cで作ったアプリのように、開発中のアプリをUSBケーブルにつないだiPhoneにインストールして、動作確認してみようとしたら、うまくアプリが立ち上がらない。ドキュメントやGithubのissuesをみるとiPhoneが同じwifiにいないと動作しないらしいです。

「react native offline」とかで検索するとstackoverflowやgithubのissuesで、対処法が書いてあるが、今ひとつうまくいかない。というか、ReactNativeの更新が速いせいか、いまでは通用しない古い情報がまじっていてよくわからないです。で、Objective-Cは以前触ったこともあってソースが読めるので、直接ソースを追ってみました。それでわかったこととして

  • 開発中ReactNativeは開発マシン上にhttpサーバを起動し、アプリはこのhttpサーバからJavaScriptコードを取得する。
  • この仕組みのおかげで、開発中再ビルドなしで、変更を反映させられる。実機の場合も同様だが、オフラインだとhttpサーバに到達できず、エラーになる。
  • ビルド時、リリースビルド用にJavaScriptを一つのファイルにまとめてアプリにバンドルするスクリプトが、XCodeのrun scriptで定義されている。
  • リリースビルドの場合のみ、httpサーバからではなくファイルからJavaScriptを読むようにマクロで制御されている。

というわけで冒頭に記載したようにios/AppName/AppDelegate.m内のコードを

#if !(TARGET_IPHONE_SIMULATOR)
  jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif

のようにして「シミュレータ以外のとき」という条件で、JavaScriptのロケーションを設定している変数を、リリースビルドのときと同様に、バンドルされたJavaScriptファイルを読みに行くよう上書きすればOKです。この場合当然、実機上で動かすと、再ビルドなしでコードの変更を反映させることはできなくなるので、それが必要なときは都度この部分を削除やコメントアウトするなりして、対応すればいいかな、と。今のところは。

OmnibusでPHPアプリケーションをRPMにパッケージングする

要約: Omnibusを使うとApachePHPをバンドルしたオールインワンなPHPアプリケーションのRPMパッケージを作れます。

最近CLIツールやAPIサーバなどはGoで書くようになって、PHPをさわる頻度が減ってきているのですが、それでもPHPの実行環境が不必要になることはなく、その個人的な一つの理由にAdminerがあります。これは2012年に一度ブログにも書いたのですが、PHPのシングルファイルをDocumentRootにおくだけで動作する、データベース用のWebUIです。

Adminer - PHPの1ファイルを置くだけで簡単に使えるDB管理ツール - オープンソースこねこね

現在でもメンテは続いているようで、今ではElasticsearchやMongoDBなどにも対応しているそうな。個人的にもずっと使い続けています。

ただ、前述のようにGoの適用範囲が広がってきたので、新しく立ち上げたサーバとかだと、Adminerを使う以外にApachePHPが必要ない環境もあります。後々の運用管理のことも考えてサーバ構築にはプロビジョニングツールを使っていることもあり、これ一つのために、ApachePHPを入れてhttpd.confとphp.iniを書き換えるレシピ書いて。。。てなことがやや面倒くさいです。

Adminerの機能単体でぱっとインストール、アンインストールできればいいかもしれない、と考えたので、omnibusを使ってApachePHPをバンドルしたオールインワンなPHPアプリケーションのRPMを作成してみました。omnibusについては以前ブログに書いたのでそちらを参照してください(CentOS用にSupervisor3.3.0のRPMパッケージを作成した - オープンソースこねこね )。今回の成果物は以下

github.com

Githubのリリースページに生成したRPMをあげたので

$ sudo yum install https://github.com/kohkimakimoto/omnibus-adminer-server/releases/download/v0.3.0/adminer-server-0.3.0-1.el7.x86_64.rpm

でインストールできます。これだけで/opt/adminer-serverApachePHPランタイムを含めたアプリケーションがインストールされます。これらはもちろんシステムワイドにあるApachePHPとは干渉しません。あとは、

$ sudo systemctl start adminer-server

とすればApacheが14200ポートで起動しますのでブラウザでhttp://localhost:14200/adminer.phpにアクセスすると

f:id:kohkimakimoto:20170329115005p:plain

と画面がでます。

補足

  • サービス設定は現状systemdのみの対応なので、このRPMはCentOS7限定です。initスクリプトを書けばCentOS6などにも対応できますが、自分が使わないので書いてません。
  • 設定ファイルは/etc/adminer-server、adminer.phpなどのPHPファイルは/var/lib/adminer-server以下に配置してあります。
  • 今回はやってないがMySQLなどDBもバンドルすれば、いわゆるLAMPアプリケーションがパッケージ配布可能になるんじゃないかと。

あとがき

こんなことしなくても、今はDocker使えばいいかも、とも思いますが、Systemdにサービスを登録するところまでyum installlでできるのでミドルウェア的なものを扱うのなら、RPMも便利ですよと。。。