blog.monophile.net

コンピュータのこととかのメモ。

Takaaki Yamamoto

東京工業大学において計算機科学と応用数学を学び、 情報科学芸術大学院大学[IAMAS] においてメディア表現を専攻し修了。 現在は digitiminimi Inc. において、インフラエンジニアとして生計をたててている。

work

各種システム構築と管理を承ります。

Cloud PlatformOpenstack, GCP, AWS, Azure, ...
Openstackkeystone, glance, cinder, swift, neutron, nova, ...
VirtualizationQEMU+KVM, LXD/LXC, Docker, ...
OSDebian GNU/Linux, Ubuntu, CentOS, ...
NetworksIPSec, L2TP, VXLAN, WirelessAP, ...
WebAppsWordPress, GitLab, Redmine, ...
Configuration ManagementAnsible, Terraform, ...
MonitoringNagios, Munin, ...

posts

PythonでGeoIPを使ってIPアドレスから場所を特定する

概要

IPアドレスから大まかな場所を特定したかったので、 GeoIPをPythonから使ってみた。

GeoIPのインストール

Ubuntuの場合は↓。

$ sudo apt-get install python-geoip

データのダウンロード

次に http://dev.maxmind.com/geoip/geolite からIPアドレスと場所のデータをダウンロードし、展開する。

$ curl http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz | gunzip > GeoLiteCity.dat

↑をするとバイナリデータGeoLiteCity.datができる。

$ file GeoLiteCity.dat
GeoLiteCity.dat: data

IPアドレスから場所を引いてみる

試しにGoogleのIPアドレスを引いてみて、その場所を求めてみる。

$ dig google.com +short
216.58.197.238

google.com のIPは 216.58.197.238 だったので、その場合は↓のようにすればよい。

import pprint
import GeoIP
gip = GeoIP.open("GeoLiteCity.dat", GeoIP.GEOIP_STANDARD)
pprint.pprint(gip.record_by_addr("216.58.197.238"))

↑の出力は↓。

{'area_code': 650,
 'city': 'Mountain View',
 'country_code': 'US',
 'country_code3': 'USA',
 'country_name': 'United States',
 'dma_code': 807,
 'latitude': 37.4192008972168,
 'longitude': -122.05740356445312,
 'metro_code': 807,
 'postal_code': '94043',
 'region': 'CA',
 'region_name': 'California',
 'time_zone': 'America/Los_Angeles'}

(おまけ) IPアドレスを入力し、GoogleMapのURLを生成する

↓をip2gmap.pyとして保存する。

import sys
import GeoIP
gip = GeoIP.open("GeoLiteCity.dat", GeoIP.GEOIP_STANDARD)
google_map_base_url = "https://maps.google.co.jp/maps?ll=%(latitude)s,%(longitude)s"
print(google_map_base_url % gip.record_by_addr(sys.argv[1]))

IPアドレス 216.58.197.238 のGoogleMapのURLは↓を実行すれば得られる。

$ python ip2gmap.py "216.58.197.238"
https://maps.google.co.jp/maps?ll=37.4192008972,-122.057403564

実際に↓をクリックすると上記の経度緯度の場所が表示される。

参考

MacBookPro Retinaの画面の表示解像度を2880x1800で使用する

概要

MacBookPro 15inch Retinaを普段使っていて、 画面の仕様的な解像度は2880x1800だが、 実質的な表示は1920x1200の大きさが最大。 (↓の画像の一番右の設定は画面解像度のdot by dotではない。)

この解像度を最大まで使うにはいくつか方法があるようだが、 今回は無料のツールであるscreenresolutionを使ってみた。

install

Homebrewを使ってscreenresolutionコマンドをインストールする。

$ brew install screenresolution

現在の設定の解像度を取得する

今回、MacBookPro以外に3つの外部モニタを接続しているため、 それらの解像度まで取得されている。 MacBookProのディスプレイは “Display 0”。

$ screenresolution get
2017-11-06 21:40:36.846 screenresolution[60230:1501972] starting screenresolution argv=screenresolution get
2017-11-06 21:40:36.849 screenresolution[60230:1501972] Display 0: 1920x1200x32@0
2017-11-06 21:40:36.850 screenresolution[60230:1501972] Display 1: 1200x1920x32@60
2017-11-06 21:40:36.851 screenresolution[60230:1501972] Display 2: 2560x1440x32@60
2017-11-06 21:40:36.851 screenresolution[60230:1501972] Display 3: 1080x1920x32@60

使える解像度を列挙する

↓のコマンドで使える解像度を列挙する。

$ screenresolution list
2017-11-06 21:39:31.127 screenresolution[60103:1500111] starting screenresolution argv=screenresolution list
Available Modes on Display 0
  2880x1800x32@0       	1440x900x32@0        	2560x1600x32@0
  2048x1280x32@0       	1024x768x32@0        	800x600x32@0
  640x480x32@0         	1680x1050x32@0       	1280x800x32@0
Available Modes on Display 1
  1200x1920x32@60      	1080x1920x32@60      	1080x1920x32@60
  720x1280x32@60       	480x720x32@60        	480x640x32@60
  1080x1920x32@50      	576x720x32@50        	720x1280x32@50
  1080x1920x32@50      	1080x1920x32@24      	600x960x32@60
  640x480x32@60        	600x800x32@60        	800x600x32@60
  500x800x32@60        	768x1024x32@60       	1024x768x32@60
  640x1024x32@60       	960x1280x32@60       	1280x960x32@60
  800x1280x32@60       	1008x1344x32@60      	1344x1008x32@60
  840x1344x32@60       	1200x1600x32@60      	1600x1200x32@60
  1000x1600x32@60
Available Modes on Display 2
  2560x1440x32@60      	1280x1024x32@75      	1024x768x32@75
  1024x768x32@60       	800x600x32@75        	800x600x32@60
  640x480x32@75        	640x480x32@60        	1600x1200x32@60
  1280x1024x32@60      	1920x1080x32@60      	1920x1080x32@60
  1920x1080x32@60      	1280x720x32@60       	720x480x32@60
  640x480x32@60        	720x576x32@50        	1280x720x32@50
  1920x1080x32@50      	1920x1080x32@50      	1920x1080x32@24
  2048x1152x32@60      	1024x576x32@60       	1280x960x32@60
  1344x1008x32@60      	1344x756x32@60       	1600x900x32@60
Available Modes on Display 3
  1080x1920x32@60      	1080x1920x32@60      	720x1280x32@60
  480x720x32@60        	480x640x32@60        	1080x1920x32@50
  1080x1920x32@50      	576x720x32@50        	720x1280x32@50
  640x480x32@60        	600x800x32@60        	800x600x32@60
  768x1024x32@60       	1024x768x32@60       	576x1024x32@60
  960x1280x32@60       	1280x960x32@60       	1008x1344x32@60
  1344x1008x32@60      	756x1344x32@60       	900x1600x32@60

“Display 0”では2880x1800x32@0が最大の解像度。

新しい解像度を指定する

↓のコマンドで解像度を設定する。 解像度は2560x1600にしてみた。 (2800x1800だと文字が小さすぎた。)

$ screenresolution set 2560x1600x32@0 skip skip skip
2017-11-06 21:44:26.646 screenresolution[60555:1507546] starting screenresolution argv=screenresolution set 2560x1600x32@0 skip skip skip
2017-11-06 21:44:26.650 screenresolution[60555:1507546] set mode on display 0 to 2560x1600x32@0
Skipping display 1
Skipping display 2
Skipping display 3

引数はディスプレイの順番どおりの解像度を指定する。 変更しないディスプレイに対しては“skip”を渡せばよい。 コマンドを発行すると画面が暗転して新しい解像度に切り替わる。

元に戻したい場合は、Mac標準のシステム設定からできる。

参考

TOTPのURIを生成し、QRにエンコードとデコードしてから、ワンタイムパスワードを計算する

概要

Pythonのpasslibのドキュメントを読んでいたらバージョン1.7からtotpに対応していたので、 ワンタイムパスワードを計算してみた。

同時に、GoogleAuthenticatorで使うためのQRコードの生成もしてみた。

ツールの準備

必要なツールはQRコードのツールとPythonのpasslib。

↓はMacの場合。

$ brew install zbar
$ brew install qrencode

↓はUbuntuの場合。

$ sudo apt install zbar-tools
$ sudo apt install qrencode

passlibはpipで入れた。

$ pip3 install passlib

TOTPのURIを生成する

TOTPのURIは↓のようになっていて、秘匿部分はBASE32で符号化しておく。

otpauth://totp/{user}@{servicename}?secret={secret_base32}

具体的には↓のようにすればよい。

$ echo "otpauth://totp/user@servicename?secret=$(printf secretsecret00 | base32)"
otpauth://totp/user@servicename?secret=ONSWG4TFORZWKY3SMV2DAMA=

普通は自動生成されて渡されるので、開発者以外は全く必要とされない技術。

TOTPのURIを表すQRコードを作成する

qrencodeコマンドを使って、↓のように標準入力からTOTPのURIを渡せばよいだけ。

$ echo "otpauth://totp/user@servicename?secret=$(printf secretsecret00 | base32)" | qrencode -t png -s 10 -o totp00.png

出来上がったQRコードは↓。

これをGoogleAuthenticatorとかで読み取ればよい。

QRコードからTOTPのURIを読み出す

zbarimgコマンドを使ってQRコードの画像からデコードして、元のURIを取り出す。

$ zbarimg --raw -q  totp00.png | head -n1 | tee -a otp_list.txt
otpauth://totp/user@servicename?secret=ONSWG4TFORZWKY3SMV2DAMA=

元の文字列が取り出せた。

TOTPのURIからpasslibでワンタイムパスワードを計算する

ワンタイムパスワードの計算方法は簡単で↓の4行でできる。

import passlib.totp
totp_uri = "otpauth://totp/user@servicename?secret=ONSWG4TFORZWKY3SMV2DAMA="
totp = passlib.totp.TOTP.from_uri(totp_uri)
print(totp.generate().token)

↑の出力は↓。

503057

メソッドgenerate()の引数はUNIXタイムで、Noneの場合は現在の時間を元に計算される。 [現在, 次, 次の次]の3つのワンタイムパスワードが欲しい場合は↓の様にすればよい。

import passlib.totp
totp_uri = "otpauth://totp/user@servicename?secret=ONSWG4TFORZWKY3SMV2DAMA="
totp = passlib.totp.TOTP.from_uri(totp_uri)
print(totp.generate(totp.now() + 0 * totp.period).token)
print(totp.generate(totp.now() + 1 * totp.period).token)
print(totp.generate(totp.now() + 2 * totp.period).token)

↑の出力は↓。

654409
180466
529287

めでたし、めでたし。

参考

Pythonのpasslibでハッシュ値を指定のソルトで計算する

概要

/etc/shadowに書かれるハッシュ値を自動生成したくなったので、調べてみた。 Ansibleと組み合わせたいので、Pythonのpasslibを使う。

他ブログでも方法は紹介されているが、 passlibで非推奨になっているメソッドが使われていたので、 公式ドキュメントみながら確かめてみた。

例えば、2.0になった段階でencrypt()は廃止されて、hash()のみになるようす。 現時点ではpasslibは1.7だが、1.7では両方とも使用可能。

なお、ハッシュ値の形式は次の様になっているらしい。

${{ algorithm }}${{ option }}${{ salt }}${{ hash }}

各種OSの/etc/shadowで使えるハッシュアルゴリズムの一覧

各種OSで使えるアルゴリズムは下記から得られる。

import passlib.hosts
print("Linux: %s" % ", ".join(passlib.hosts.linux_context.schemes()))
print("FreeBSD: %s" % ", ".join(passlib.hosts.freebsd_context.schemes()))
print("OpenBSD: %s" % ", ".join(passlib.hosts.openbsd_context.schemes()))

↑の出力は↓。

Linux: sha512_crypt, sha256_crypt, md5_crypt, des_crypt, unix_disabled
FreeBSD: bcrypt, md5_crypt, bsd_nthash, des_crypt, unix_disabled
OpenBSD: bcrypt, md5_crypt, bsdi_crypt, des_crypt, unix_disabled

FreeBSDは9.1からsha512に対応しているらしい。 細かく設定したい場合は自分でCryptContextを作る。

ソルトの長さを確認しておく

アルゴリズムによって使えるソルトの長さが異なるので、上記で確認しておく。

  • sha512: [./0-9A-Za-z] 0-16文字
  • sha256: [./0-9A-Za-z] 0-16文字
  • bcrypt: [./0-9A-Za-z] 22文字

平文パスワードからハッシュを計算する (ソルトの指定なし)

ソルトの指定をしないでハッシュを計算する。 この場合は自動でソルトが生成されていることがわかる。

import passlib.hash
secret = "secretsecret"
hashed = passlib.hash.sha512_crypt.hash(secret)
print("sha512('%s') -> '%s'" % (secret, hashed))
print("verify: %s" % passlib.hash.sha512_crypt.verify(secret, hashed))

↑の出力は↓。

sha512('secretsecret') -> '$6$rounds=656000$fcD6caqZ.HTe7aO0$0v1Xsq6tFVQVGnNX4M9A9g/UrvhJYeK53JmFl0xXmqAH5n2Mqt8XbqzX9E5VZrxXltek4CsM1ERgpIfxJRQ/l0'
verify: True

平文パスワードからハッシュを計算する (ソルトの指定あり)

ソルトを指定してハッシュを計算する。

import passlib.hash
secret = "secretsecret"
salt = "saltsalt"
hashed = passlib.hash.sha512_crypt.hash(secret, salt=salt)
print("sha512('%s') -> '%s'" % (secret, hashed))
print("verify: %s" % passlib.hash.sha512_crypt.verify(secret, hashed))

↑の出力は↓。

sha512('secretsecret') -> '$6$rounds=656000$saltsalt$Toz2h6NEmqzQZDpBJHIq3Bp1mExBVFK7rSGitHQ9F90ji1pHLC/yEAi5fbGkPpIhpMoBkY9icINEdTceZe6Ur.'
verify: True

(おまけ)ハッシュ値からアルゴリズムを特定し、内容をパースする

対象とするアルゴリズムを指定して、そのハッシュ内容をパースする場合は↓のようにすればよい。

import passlib.context
ctx = passlib.context.CryptContext(schemes=["sha256_crypt", "sha512_crypt"])
hashed = "$6$rounds=656000$saltsalt$Toz2h6NEmqzQZDpBJHIq3Bp1mExBVFK7rSGitHQ9F90ji1pHLC/yEAi5fbGkPpIhpMoBkY9icINEdTceZe6Ur."
scheme = ctx.identify(hashed)
if scheme:
  for (k, v) in ctx.handler(scheme).parsehash(hashed).items():
    print("%s: %s" % (k, v))
else:
  print("No Match Scheme.")

↑の出力は↓。

checksum: Toz2h6NEmqzQZDpBJHIq3Bp1mExBVFK7rSGitHQ9F90ji1pHLC/yEAi5fbGkPpIhpMoBkY9icINEdTceZe6Ur.
salt: saltsalt
rounds: 656000

備考

上記を見ると rounds=656000 となっているためラウンド数は656000回だが、 glibcの実装のデフォルトは5000回らしい。

保証切れのMacBookPro(15-inch, Mid 2015)のバッテリーをApple Storeで無償交換してもらった

概要

2016/04にMacBook Pro (Retina, 15-inch, Mid 2015)を購入して、 平日10時間ぐらい使用していた。 そして2017/10ごろになり、ボディがグラグラするようになってきたため、 背面を確かめてみたところ、ちょうど底面の中央が盛り上がっていた…。 爆発しても困るため、修理することにした。

以前、別のMacBookProのバッテリー交換を試みて、 サードパーティ製のバッテリーを購入したが、 交換したものがMacBookProに物理的に収まらないということがあったため、 今回はApple正規の修理に持ち込むことにした。

購入してから1年半ほど経っているため保証はない状態で、 Apple Careにも加入していなかったので、有償でのバッテリー交換を覚悟していたが、 無償でバッテリーを交換してもらった。

状況

  • 購入元: apple.com (整備済み製品)
  • 購入日: 2016/04
  • 製品: MacBook Pro (Retina, 15-inch, Mid 2015, Mem 16GB, SSD 512GB)
  • 保証: 製品1年限定保証は期限切れ、 Apple Care 未加入
  • 不具合: 2017/10にバッテリーの膨らみに気づく。水没や落下などによる外傷はなし。
  • バッテリー容量: ほぼ100%
  • バッテリーサイクルカウント: 62

Genius Bar (Apple Store 渋谷) の予約をいれる

上記ページから修理の申込みを行う。 このとき、限定保証が切れていてかつAppleCare未加入だと 「電話」や「チャット」でのサポートは受けられないため「持ち込み修理」を選ぶ。 今回は職場から近かったため 「Apple 渋谷(https://www.apple.com/jp/retail/shibuya/)」で予約を取った。

修理の際にデータが消えてもデータ内容は保証されないので、 修理に持ち込む直前にバックアップを取っておいた。

ちなみにバッテリーの交換費用は公開されていて、今回のMacBookProの場合は1万9800円。

1回目(10/26)のGenius Bar訪問(修理受付)

夕方にGeniusBarへ到着し、「予約をしている」と伝えたらすぐに担当者がついてくれた。 (予約していないと混雑時は相当待ちそうな印象を受けた。)

「バッテリーが膨らんでいるため交換したい」 と伝えたら「バッテリーの修理は1万9800円です」と。 もし、バッテリーが膨らんでおらず、 ケースが外傷によりふくらんでいる場合の外装修理は3万9800円とのこと。

別の部屋で外装を開けて確認するとのことで、一時の待ち時間。 5-10分くらい待ったら担当者が戻ってきて、 「バッテリーが膨らんでました」とiPadで撮った写真を見せられた。 あと「クオリティプラグラムが適用されるので今回の修理費用は無料になります」と。

おぉ〜。無料でいいのか。

クオリティプログラムの適用条件はWebには公開されていないそうだが、 口頭で聞いた限りだと以下の感じ。

  • 購入から2年以内 (1年以内は限定保証で無償で交換してもらえるはず)
  • バッテリーの膨張
  • 水没や落下などによる外傷がない

おそらく、型番などによる条件もあると思う。 クオリティプログラムが適用されるかどうかは実際にGeniusBarへ持ち込むしかない、とのことだった。 Webでは一部の製品は明示的に適用されるケースが書かれているらしい。 おそらくそのページは↓。

肝心の修理は現在パーツの在庫が無いため、一旦持ち帰ることにした。 ここで預けても良いが、次の週に仕事で新サイトのリリースがあったので、後日にしたかった。 パーツが届いてから2週間までは受付なしでいつでも修理持ち込みにきて良いとのことだった。

ちなみに、今回のMacBookProのバッテリー交換はトップケース(キーボードのある面) と一緒に交換しなければならないらしい。 したがって、キーボードも新品になるとのこと。

請求金額0円の見積もり書をゲットして帰る。

2回目(11/01)のGenius Bar訪問(修理持込)

修理受付の翌日10/27に交換パーツ到着の連絡をメールで受けたので、 11/01にGeniusBarにてMacBookProを預けた。 もし、修理の時点で水濡などがある場合は修理の際に連絡がくるらしい。

修理日数は3-5日という説明をうけた。 修理日数は土日祝を挟んでも伸びることはないらしい。

3回目(11/03)のGenius Bar訪問(修理受取)

MacBookProを持ち込んだ当日の11/01の深夜に交換完了のお知らせがメールで来ていた。 気づいたのは11/03だったので、11/03に受け取った。

膨らんでいた底面は交換されず、キーボードの面だけが交換されたらしい。 キーボードが新しくなったせいか、打ち心地が大きく変わっていたので驚いた。

支払いは無料だった。

全体を通して

サポートは全体を通して合理的で理不尽なことは一切なかったのは好印象だった。 ただ、クオリティプログラムに関する情報の不透明感は若干感じた。

上記には書かれていないが、GeniusBarへ持ち込んだ初回にMacBookProをThunderbolt経由で診断してもらったのは技術的に面白かった。 (一般には公開されていないが、Thunderboldを使ってPXEBootをして起動したソフトウェアを用いて診断していた。)

参考