- 投稿日:2020-03-01T23:34:51+09:00
[整理用]Python開発環境
整理がてら自分のPython開発環境のメモ
用途
- Deep learning / 画像処理の開発
- リモートセンシング
方針
- なるべく楽に導入
- 管理は最低限出来れば良い
- コードを楽に書ける
- 補完やフォーマットなど
OS: Ubuntu 18.04
- ライブラリなどの導入がWindowsに比べて楽
- 最近はそうでもないけど、Ubuntuじゃないと動かないものもあったので
環境管理: Anaconda
- 機械学習で必要なのが一通りインストールされる + 最低限の環境管理が出来るため
- 主なライブラリは以下
- numpyとかAnacondaについてくるのは省く
- geopandasはないとやってられない
# Deep learning用 pytorch torchvision tensorboard # 画像処理用 opencv # リモートセンシング用 gdal qgis geopandasエディタ: VSCode
- Extensionの充実
- Remote Development
- リモートサーバ上(GPUサーバ)で開発しているため
- Extensionがリモート先でも使えるのがよい
- ms-python
- Microsoft公式Extension
- イライラしない程度に補完してくれる
- Bracket Pair Colorizer
- 対応する括弧の色付け
- LinterやFormatterの設定が楽
- notebookファイル編集が出来る
Linter: Flake8
- 厳しすぎず緩すぎずといった感じ
max-line-length = 120, max-complexity = 10で使用Formatter: yapf
- Format後の感じが一番しっくり来たので
- Flake8と同じく
column_limit = 120で使用おわりに
- albumentationsとか便利系も今後は入れていきたい
- 他に良さげなものがあれば教えていただけるとありがたいです
- 投稿日:2020-03-01T20:18:17+09:00
シェルスクリプト で Amazon S3 ライクな RESTful WEB サーバーを作ってみた
タイトルが少しおかしいですが、シェルスクリプトが好きすぎて、シェルスクリプトで RESTful なつもりの CMS 兼 WEB サーバーを作成してみました。ここでは、このシェルスクリプト製 WEB サーバー兼 CMS について紹介させていただきます。API 操作のデザインについて Amazon S3 に多分に影響を受けています。
作ったモノと特徴
GitHub - ellbrid/xsk: CMS and Web Server written in ShellScript
基本的には Markdown の供給をメインとする CMS 兼 WEB サーバーとなっています。
Markdown ファイルを配置しておくと HTML に変換して表示してくれます。
また、PATHとクエリに応じてシェルスクリプトを実行することもできる CGI の側面も持ちます。
コンテンツを操作するために公開している API と認証機構について Amazon S3 から大きな影響を受けていて、HTTP メソッドを使用した操作に加えて、 AWS バージョン4署名プロセスに似た方法による認証に対応します。次の 4 つの基本的な設計原則に従うように作成したつもりです。
- 明示的に
GET/PUT/POST/PATCH/DELETE/OPTIONS/HEADなどのメソッドを使う。- ステートレスにする
- ディレクトリ構造ににた URI を公開する
- HTTP/HTTPS に対応する
以下、上記と他の特徴について簡単に説明します。
明示的に HTTP メソッドを使う
REST では明示的に HTTP メソッドを使う必要があるとされていますが、XSK でもできるだけ、HTTP プロトコルをその定義どおりの方法で使うように心がけました
XSK では、各種メソッドを下記リストに示すような方法で使います。
GET: リソースの表現を取得しますPUT: 指定された URI に新規リソースを作成するか、既存 URI の上書き更新(常に新規)POST: 既存の URI へ従属するリソースの新規作成、追記。MIMEマルチパートデータを利用PATCH: 既存リソースに対して diff データを元にパッチを当てて更新しますDELETE: 既存リソースの削除を行いますOPTIONS: 許可されているメソッド一覧を返しますHEAD: ここでは、単にリソースの存在有無を確かめ、ステータスコードでその結果を返します。上記の API のいくつかは Amazon S3 の API に影響を受けています。
ステートレス
一応ステートレスです。状態を保持せず、クライアントは、パラメーターやコンテキスト、データを、リクエストの HTTP ヘッダーとボディーの中に含めることとしています。
ディレクトリ構造に似た URI を公開する
URI の構造は単純かつ予測可能で、容易に理解できるものであるべきとされています。この観点から、XSK では UNIX のディレクトリ構造をそのまま URI の階層構造に適用します。指定した WEB サーバーのルート用ディレクトリからヴァーチャルパスを構成し、1 つのパス上にルートがあり、そのパスから、サービスの各領域を公開するサブパスが分岐しています。なお、これはディレクトリでありファイルです。とても直感的だと思います。
UI もそれに準じた直感的なものであるべきだろうと思ったので、インデックスページは、単純にtreeコマンドの実行結果のような index ページを返すようにしました。実装には find コマンドを使用しています。ファイル / ディレクトリを公開するか否かは完全に UNIX のファイルのパーミションの機能によって決定されます。
HTTP/HTTPS 対応
HTTPとHTTPS の両方に対応しています。証明書を設置すれば、HTTPS でコンテンツを公開できます。
例えば、Google Domainsで dev ドメインを取得した場合には必須の機能でしょう。AWS 署名バージョン 4 署名プロセス
XSK はもともと
GET,PUT,POST,PATCH,DELETE,OPTIONS,HEADなどの HTTP メソッドに対応していますが、GET以外はAWS 署名バージョン 4 署名プロセスにたプロセスで署名したAuthorizationヘッダを持たない限りアクセスできません。
XSK では、この署名の計算と検証もシェルスクリプトで行います。markdown 対応
拡張子が
.mdで終わるファイルをGETする場合には、サーバーサイドのシェルスクリプトでHTML に変換してから供給します。ちなみに、生成される HTML は twitter で共有された時にサムネを表示する twitter card に対応しています。HTML のテンプレートをセットするだけなので特徴でもなんでもないですが。 twitter card に対応しています。シェルスクリプトを実行可能
公開ディレクトリ内部に配置されているファイルが
.sh拡張子であり、かつ実行可能な場合には、そのスクリプトファイルをGETすると、そのスクリプトが実行された結果が返されます。例えばシェルスクリプトにはパラメータとして、?以降のクエリを渡すことができます。もちろんどのように処理するかは実行スクリプト側の責任で自由です。互換性
実は微塵もテストはしていないのですが、互換性に注意して書いたつもりです。
内部で使用しているコマンドは POSIX の Shell & Utilities: Table of Contents に定められているコマンド以外はsocatとopenssl,curl/wgetのみです。
なお、便利スクリプトとして、秘密結社シェルショッカー 日本支部 · GitHubの方々が作成されたいくつかのコマンドに加えて、シェルスクリプトで作成された独自のスクリプトも使用しています。ありがとう!シェルショッカー。さて、以上の特徴を持つ XSK を使用して、実際に WEB サービスを作成してあるので、実際に動作する例とともにどのように動くのかもう少し説明したいと思います。
動作例と使い方
XSK で作成した実際の web サービスは以下になります。ただの個人ブログですが、気になる方はのぞいてみてください。
上記のサイトは、完全にシェルスクリプトとpure JavaScriptのみで作成されています。
なお、XSK のディレクトリ構成は下記のようになっています。XSK (EXOSKELETON) の構成と起動
$ tree -a . . ├── .log # <============== アクセスログ格納ディレクトリ ├── .xsk # <============== 認証情報とかのディレクトリ │ ├── credentials # <=== 認証情報(アクセスキー, シークレットアクセスキー) │ ├── fullchain.pem # <= 証明書と中間証明書を連結したファイル(HTTPS用) │ └── privkey.pem # <=== 秘密鍵(HTTPS用) ├── exo # <=============== 公開用ルートディレクトリ │ ├── .attachments # <== css/js/cgi用シェルスクリプト置き場 │ │ ├── css │ │ ├── img │ │ ├── js │ │ └── sh │ └── ... # <=========== (ここにmarkdownファイルを配置していく) | ├── pilot # <============= クライアントコマンド ├── skeleton | └── ... # <=========== 共通のコアスクリプト/ツール ├── srvcs # <============= WEB サーバー起動サービス設定ファイル(upstart用) └── xsk # <=============== サーバー起動コマンドXSK を使用する場合、サーバー用コマンドの
xskと、クライアントコマンドのpilotを主に使用します。XSK起動:
XSK の起動は以下のようにします。HTTPS を利用する例は、証明書の用意が必要になると思うので、ここでは一旦 80 番ポートで起動する例を試してみてください。
-pオプションにポート番号を、-uにツイッターユーザー名、-hにホスト名を指定します。-sオプションを付加した場合に、予め設置しておいた証明書を使用して HTTPS で稼働します。HTTP: 80 番ポートで稼働する例:
$ pwd /path/to/xsk $ sudo ./xsk -p 80 -u "@ツイッターユーザー名" -h "ホスト名"HTTPS: 443 番ポートで稼働する例:
$ pwd /path/to/xsk $ sudo ./xsk -p 443 -u "@ツイッターユーザー名" -h "ホスト名" -sなお、
GET以外のメソッドに対する署名の認証用に、下記のような credentials ファイルを用意しておいてください。
key_idとsecretはお好みのそこそこ長いランダム文字列を使用するのをお勧めします。$ pwd /path/to/xsk $ cat credentials key_id: abcd secret: efghijklmnopqrstuvwxyzこれに対して、WEBブラウザと、クライアントコマンド
pilotを使用して実際にリクエストを作成しアクセスすることになります。PILOT:
pilotはクライアントコマンドで、署名済みの HTTP リクエストを生成して XSK にアクセスします。
なお、このコマンドは自身の勉強のためにも作成しています。できるだけ生に近い HTTP リクエストを手作りして、REST を体験したいという思いがあり、このコマンドもその思想のもとに作成されました。
基本的に、生の HTTP リクエストもどきをテキスト形式で作成しておいて、これを標準入力から食べさせることで動作します。そのため、
pilotコマンドの使い勝手は良くないですけど(むしろ悪い)、比較的面白い試みなのではないかなと個人的には思っています。ただ、流石に HTTP リクエスト形式を忠実に再現しすぎるのは使いにくすぎるので、いくらか簡単化して、実際に使用する形式は、以下のようなHTTP リクエストもどきの型として定めました:
METHOD URI Key Value key Value Key Value Host: hostname Content-Type: application/hoghoge X-Xsk-moge: hogehogehoge body (JSON, XML. Binary)クエリは、
METHOD URIの下に key value 形式で分解して指定します。
これをファイルに保存して第一引数に指定するか、標準入力からパイプを通して流し込みます:$ ./pilot RESTful_API_file $ cat RESTful_API_file | ./pilotただ、上記のコマンドはボディにバイナリを持たせたい時に問題があります。
そこで、そのような問題に対応するためにもいくつかのオプションがあります。
-f file: HTTP リクエストのボディ部を別ファイルに指定できます。-q: リクエスト後、レスポンスボディのみを受け取りたい場合には、このオプションを指定することでレスポンスヘッダを省略できます。-v: curlを使用している場合にはリクエスト状況を詳細表示します。-s: HTTPS でリクエストを行います。多少細かく紹介しましたが、実際にみてみる方が早いと思うので、以下で各メソッドの使い方と動作例を記載していきます。
GET(ブラウザから)
GET については、ブラウザからのみアクセスすることを想定していますので、ここでもブラウザでの例を取り上げます。XSK はディレクトリ構造をそのまま URI の階層構造に適用しているので、その思想をそのまま視覚に取り入れています。そのため、インデックスページでの各コンテンツへのリンクは、tree 型のリンクとして表示されます。
下段のiframeに選択した記事のタイトルと説明が抜粋されます。
1クリックで説明の表示、ダブルクリックか、「go」と書かれたリンクを押下することで、その記事へと飛びます。マークダウンも問題なく表示されています:
パンくずリストも表示するようになっています。PUT
PUT では、新規リソースを作成するか、既存 URI へ上書き更新するのでした。
以下の Markdownファイル(aiu.md)を新規に追加してみましょう。# POSTテスト これはテストです。 !-- ## アイウエオ かきくけこ
# 見出し1が記事のタイトルになり、通常一記事に対して一つのみ持つことができます。なお、!--マークまでの部分が記事の説明として抜き出されます。ここでは、HTTP リクエストを手作りし続けるのは流石に疲れるので、テンプレを用意しておき、それをmojihameコマンドとともに使用することにします[1]。[1] Open-usp-Tukubai/mojihame at master · ShellShoccar-jpn/Open-usp-Tukubai · GitHub
実際に
mojihameコマンドとテンプレートを使用してリクエストを作っていきます:$ pwd /path/to/xsk $ cat skeleton/template/PUT PUT %1 Host: localhost $ echo /aiu.md | ./skeleton/mojihame skeleton/template/PUT - PUT /aiu.md Host: localhost上記を
pilotコマンドに食べさせると、勝手に他のリクエストヘッダを補完し、署名を行なってリクエストを発行します:$ echo /aiu.md | ./skeleton/mojihame skeleton/template/PUT - | ./pilot -vf aiu.md 2>&1 | grep -E '^(<|>)' > PUT /aiu.md HTTP/1.1 > Host: localhost > User-Agent: curl/7.64.1 > Accept: */* > Authorization: XSK4-HMAC-SHA256 Credential=/20200229/localhost/localhost/xsk4_request, SignedHeaders=content-length;content-md5;host;x-xsk-content-sha256;x-xsk-date, Signature=f86cff356542b90f30f161a2adc7a81ece065c061178b95a010c892ac8e7070d > X-Xsk-Date: 20200229T114555Z > X-Xsk-Content-Sha256: c42a41d73efd1cf620c04912d24b337d2c5f748abde0950c830a2ea28e4c79c4 > Content-MD5: TJC4NBcOm6Ts9zWoUtoVDw== > Content-Length: 84 > Content-Type: application/x-www-form-urlencoded > < HTTP/1.1 200 OK < Date: Sat, 29 Feb 2020 20:45:56 JST < Expires: Sat, 29 Feb 2020 20:45:56 JST < Server: exoskeleton < Location: exo/aiu.md < Content-Type: ; charset="UTF-8" <上記は Authorization ヘッダの存在をわかりやすくするために、
pilotコマンドに-vオプションを渡した(curlに--verboseオプションを付加した)結果になっています。主にデバッグ用で、本来は下記のようにレスポンスのみ表示します。上記ではわかりやすさのために、--verboseオプションを付けて表示しましたが、以降は付加しません。もちろん、Authorization ヘッダのSignature=が正しくなければ、リクエストは 403 エラーで終了します。$ echo /aiu.md | ./skeleton/mojihame skeleton/template/PUT - | ./pilot -f aiu.md HTTP/1.1 200 OK Date: Sat, 29 Feb 2020 20:47:58 JST Expires: Sat, 29 Feb 2020 20:47:58 JST Server: exoskeleton Location: exo/aiu.md Content-Type: ; charset="UTF-8"webアクセスしてみると、しっかりと反映されていることがわかります。
200 が返ってきていたので当たりまえですね。ディレクトリに対して PUT すれば、ディレクトリが作成されます。ちなみに、XSKでは URI が
/で終わっている場合には、これをディレクトリに対するリクエストとして処理します。以下例です。
ボディ部には、ディレクトリの説明をmarkdown形式で記載すると、webアクセス時にディレクトリの説明が表示されます。以下ではmojihameコマンドは使用しないで手で書いてみました。$ cat <<END | ./pilot > PUT /testdir/ > Host: localhost > > # ディレクトリ > これはディレクトリです。 > END HTTP/1.1 200 OK Date: Sat, 29 Feb 2020 20:52:47 JST Expires: Sat, 29 Feb 2020 20:52:47 JST Server: exoskeleton Location: exo/testdir/ Content-Type: ; charset="UTF-8"無事にディレクトリが追加されていますね。
POST
POST は既存の URI へ従属するリソースの新規作成か追記を担います。追記の場合には、全体の上書き更新ではない点に注意してください。既存のファイルの末尾にボディ部を書き加えることになります。例えば、先ほど作成した
/aiu.mdにさしすせその文を追記してみましょう。なお、POSTは MIMEマルチパート形式で作成しなくてはいけません。ここではmime-makeコマンドを使用します[2]。[2] misc-tools/mime-make at master · ShellShoccar-jpn/misc-tools · GitHub
なお、POST 形式の HTTP リクエストを作成するのは大変なので、
mime-makeをラップして POST 形式の HTTP リクエストを作成してくれるpostmakerというコマンドも作りました。ここではこれを使用します。
第一引数にファイル名、第二引数に親ノードを指定します。$ echo さしすせそ > aiu.md $ cat aiu.md さしすせそ $ ./skeleton/postmaker aiu.md / POST / Host: localhost Content-Type: multipart/form-data --MimeBoundary-OaRaWP0PjrD5SdLjor1rodr14UUEfCJjK9XT3O90k1Sip38Hx2cOovEqiOIWZ Content-Disposition: form-data; name="DATA"; filename="aiu.md" Content-Type: application/octet-stream さしすせそ --MimeBoundary-OaRaWP0PjrD5SdLjor1rodr14UUEfCJjK9XT3O90k1Sip38Hx2cOovEqiOIWZ Content-Disposition: form-data; name="PATH" / --MimeBoundary-OaRaWP0PjrD5SdLjor1rodr14UUEfCJjK9XT3O90k1Sip38Hx2cOovEqiOIWZ--上記を
pilotコマンドに食べさせます:$ ./skeleton/postmaker aiu.md / | ./pilot HTTP/1.1 200 OK Date: Sat, 29 Feb 2020 21:33:44 JST Expires: Sat, 29 Feb 2020 21:33:44 JST Server: exoskeleton Location: /aiu.md Content-Type: ; charset="UTF-8"レスポンスを見ていただけますとわかりますが、
Locationヘッダにリソースの URI が表示されてますね。
WEBページを見ると以下のようになっていました。こちらもしっかり反映されてます:PATCH
PATCHメソッドはそのまま内部では
patchコマンドとして動作します。
bodyにはdiffデータを指定すればいいことになります。ここでは、aiu.mdから、「かきくけこ」の文字を削除してみることにします。「かきくけこ」の文字を消したファイルを作成して:
$ cat aiu2.md # POSTテスト これはテストです。 !-- ## アイウエオ さしすせそ
diffを取ります:$ diff -u aiu.md aiu2.md --- aiu.md 2020-02-29 22:13:05.000000000 +0900 +++ aiu2.md 2020-02-29 22:13:22.000000000 +0900 @@ -3,5 +3,4 @@ !-- ## アイウエオ -かきくけこ さしすせそリクエストを生成して
pilotコマンドでアクセスします:$ echo /aiu.md | ./skeleton/mojihame skeleton/template/PATCH - | ./pilot -f aiu-diff.md patching file exo/aiu.md HTTP/1.1 200 OK Date: Sat, 29 Feb 2020 22:15:11 JST Expires: Sat, 29 Feb 2020 22:15:11 JST Server: exoskeleton Location: exo/aiu.md Content-Type: ; charset="UTF-8"無事反映されました:
DELETE
DELETE はその名の通りリソースの削除です。
今まで作成したaiu.mdを削除しましょう。以下のようになります:$ cat<<END | ./pilot > DELETE /aiu.md > Host: localhost > END HTTP/1.1 204 NoContent Date: Sun, 01 Mar 2020 00:33:17 JST Expires: Sun, 01 Mar 2020 00:33:17 JST Server: exoskeleton Content-Type: ; charset="UTF-8"削除するとコンテンツがなくなるわけなので、
204が返却されています。
削除した後にアクセスすると404が返ってきます。$ curl -include localhost/aiu.md HTTP/1.1 404 Not Found Date: Sun, 01 Mar 2020 00:37:17 JST Expires: Sun, 01 Mar 2020 00:37:17 JST Server: exoskeleton Content-Type: ; charset="UTF-8" <html><body><h1>404 Not Found</h1></body></html>OPTIONS
許可されているメソッド一覧を返します。
ただ、これは今現在手抜き実装となっており、404か下記の通りの全てのメソッドが載った Allow ヘッダしか返しません。本来はファイルのパーミションなどに応じたメソッドを返すべきでしょう。$ cat<<END | ./pilot > OPTIONS / > Host: localhost > END HTTP/1.1 200 OK Date: Sun, 01 Mar 2020 00:43:39 JST Expires: Sun, 01 Mar 2020 00:43:39 JST Server: exoskeleton Allow: DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT Content-Type: ; charset="UTF-8"HEAD
ここでは、単にリソースの存在有無を確かめ、ステータスコードでその結果を返します。
$ cat<<END | ./pilot > HEAD /aiu.md > Host: localhost > END HTTP/1.1 404 Not Found Date: Sun, 01 Mar 2020 00:45:47 JST Expires: Sun, 01 Mar 2020 00:45:47 JST Server: exoskeleton Content-Type: ; charset="UTF-8"$ cat<<END | ./pilot > HEAD / > Host: localhost > END HTTP/1.1 200 OK Date: Sun, 01 Mar 2020 00:46:16 JST Expires: Sun, 01 Mar 2020 00:46:16 JST Server: exoskeleton Content-Type: ; charset="UTF-8"どんな実装なのか少し覗いてみることにします。
実装
以降は大雑把に、XSK のコア機能がどのように実装されているのかみていくことにしましょう。
xsk
サーバーの本体は
xskスクリプトですが、実はこれ自体はかなり単純な作りとなっています。
socatのラッパーに過ぎず、リクエストを fork して処理するように書いてあります。# === Run as a web server ======================================= if [ -n "${CMD_SOCAT:-}" ]; then # socat ${LISTEN}:${PORT},pktinfo,reuseaddr,fork${SSL:-} \ EXEC:"skeleton/core.sk \ ${DOPT:-} ${ROPT:-}" 2>/dev/null # else # while : # do # cat "$PIPE" | # ${CMD_NC:-}${CMD_NETCAT:-} -l ${PORT} | # skeleton/core.sk 1>"$PIPE" # # [ $? != 0 ] && break # # done # fi #みるからに単純です。実は互換性確保のために、
ncによる無限ループでも起動できるようにしてはいるのですが、まぁダメです。特に HTTPS では機能できません。
オプションの細かい意味は説明しなくてもお分かりいただけると思います。
比較的重要なのがpktinfoですね。IP 情報などアクセス元の情報を取得します。ログ情報として使用する他、将来的には IP に応じたアクセス制限機能を実装したいと思います。上記の中で他に特に注目していただきたいのが、
EXECに指定してあるskeleton/core.skです。skeleton/core
文字通り、これがこの WEB サーバー兼CMSのコア部分です。
基本的には、case文を使用して HTTP メソッドに応じて処理を振り分けます:case "${REQUEST_METHOD}" in GET ) # --- GET METHOD ------------------------------------- # target=$(chamber -m GET \ -u "${REQUEST_URI:-N}" \ -q "${QUERY_STRING:-N}" ) if [ ! -n "${target:-}" ]; then GET.sk -q "${QUERY_STRING:-N}" "${URL_PATH}" "${target:-}" else response 500 <<-END <html><body><h1>500 Internal server error/h1></body></html> END fi ;; PUT ) # --- PUT METHOD ------------------------------------- # target=$(chamber -m PUT -v \ -u "${REQUEST_URI:-N}" \ -q "${QUERY_STRING:-N}" ) if [ -n "${target:-}" ]; then PUT.sk -q "${QUERY_STRING:-N}" "${URL_PATH}" "${target:-}" else response 403 <<-END <html><body><h1>403 Forbidden</h1></body></html> END fi ...なお、このコードの目玉は、内部で使用している
chamberコマンドと各メソッドの名前のコマンド(例:PUT.sk)と、responseコマンドです。skeleton/chamber
chamberコマンドは、Authorizationヘッダのバリデーションとログ記録を担当しています。
オプションはそれぞれ下記の意味です。
-m: 検証するメソッドの指定-u: 検証する URI の指定-q: 検証する クエリの指定-t: 検証しない URI を正規表現で指定このコマンドは比較的長めのシェルスクリプトであり、いい感じに抜粋できる処理部分もないので、ここでは実装詳細は省略します。
以下の処理を行います。
- 標準入力を読み取る
- ログを記録する
- リクエストのボディをContent-Lengthヘッダに指定されたバイト数分だけ、一時ファイルに溜め込む
- Authorizationヘッダ&日時情報以外のリクエストヘッダを使用して、溜め込んだペイロードをもとに署名計算を行う
- 計算した署名が、Authorizationヘッダの内容と一致するか確認する
- 計算結果が一致した場合にはリクエストボディを格納した一時ファイルへのパスを返す
標準入力を読み取って、そこから処理が進んでいく様は、これぞ本質という感じがして、個人的には気に入っています。
なお、署名の計算は下記のようになっています。まさに AWS の sig4 ですね。# === XSK version 4 Signature 5 Sign Step ==================== # SSTEP0=$(printf "$MESSAGEDATE_A" | openssl sha256 -hmac "XSK4${XSK_SECRET_KEY}" -binary | od -v -tx1 -An | tr -dc '[a-z0-9]' ) SSTEP1=$(printf "$REGION" | openssl sha256 -mac HMAC -macopt hexkey:"$SSTEP0" -binary | od -v -tx1 -An | tr -dc '[a-z0-9]' ) SSTEP2=$(printf "${SERVICE}" | openssl sha256 -mac HMAC -macopt hexkey:"$SSTEP1" -binary | od -v -tx1 -An | tr -dc '[a-z0-9]' ) SSTEP3=$(printf "xsk4_request" | openssl sha256 -mac HMAC -macopt hexkey:"$SSTEP2" -binary | od -v -tx1 -An | tr -dc '[a-z0-9]' ) SIGNATURE=$(printf "$STRING_TO_SIGN" | openssl sha256 -mac HMAC -macopt hexkey:"$SSTEP3" -binary | od -v -tx1 -An | tr -dc '[a-z0-9]' )openssl のオプションについては、 @grethlen さんに教えていただきました。?♂️
skeleton/[METHOD].sk
メソッド名が名前になっているコマンドです。
chamberによるバリデーションの結果、payloadを格納する一時ファイルへのPATHが返ってきた場合には、このメソッド名が名前になっている各コマンドで、そのメソッドごとの処理を行います。
例えば、skeleton/PUT.skは以下のようになっています。################################################################# # PUT FILE OR DIRECTORY # ################################################################# # # # === 1 PUT if directory ====================================== # if echo $URL_PATH | grep '/$' 2>&1 >/dev/null ; # then # DIR_PATH="${URL_PATH%/}" # [ -d ${DIR_PATH%/*} -a \ -x ${DIR_PATH%/*} ] && { # mkdir -p ${DIR_PATH} # cat $UPTARGET > "${DIR_PATH}/.memo.md" # response -H "Location: ${URL_PATH}" 200 <<-RESPONSE RESPONSE rm $UPTARGET 2>/dev/null # exit 0; } || { # response 403 <<-RESPONSE RESPONSE rm $UPTARGET 2>/dev/null # exit 0; # } # # === 2 PUT if file =========================================== # else # DIR_PATH="${URL_PATH%/*}" # [ -d ${DIR_PATH} -a \ -x ${DIR_PATH} ] && { # cat $UPTARGET > "${URL_PATH}" # response -H "Location: ${URL_PATH}" 200 <<-RESPONSE RESPONSE rm $UPTARGET 2>/dev/null # exit 0; } || { # response 403 <<-RESPONSE RESPONSE rm $UPTARGET 2>/dev/null # exit 0; # } # fi # # # # === 4. Error ================================================ # response 500 <<-RESPONSE RESPONSE exit 0 #こちらも実態はかなり単純であることがわかります。
ファイルかディレクトリであるかをURIの形式から判定して、ファイルであればファイルを、ディレクトリであればディレクトリを作成します。ちなみに、XSK では、URIの末尾が/で終わっている場合には、これをディレクトリとみなすことにしています。他のコマンドも全く同じような実装になっています。
違うのはリソースに作用する時の内部コマンドだけで、例えば PATCH メソッドにはpatchコマンドを使用します。skeleton/response
クライアントに返す HTTP レスポンスを生成するのは、
responseコマンドの仕事です。
responseコマンドには以下の特徴とオプションがあります。
- 第一引数にステータスコードを指定する(例:
200、403など)- レスポンスボディを標準入力から受け取るか、レスポンスを生成する/もとになるファイルを第2引数に指定
-Hオプションで追加のレスポンスヘッダを何個でも付与できる(-H ヘッダ1、-H ヘッダ2)-tオプションでレスポンスのコンテントタイプを指定できる- レスポンスの第二引数に指定されたのが
*.shのシェルスクリプトであった場合にはこれを実行する- その際、
-qオプションに実行するシェルスクリプトに渡すクエリー(引数)を指定できる。このクエリは HTTP リクエストのクエリを横流しするだけなので、このクエリを解釈するのはシェルスクリプトの仕事である。- レスポンスの第二引数に指定されたのが
*.mdである場合には、skeleton/skmdコマンドを実行して、markdownは HTML に変換する- レスポンスの第二引数に指定されたのが
*/で終わるディレクトリの場合には、skeleton/sktreeを実行して tree 形式のインデックスページを生成するこちらも以下のような単純な実装になっています。
cat<<-__RESPONSEHEADER | $(printf '%s\n' "$STATUS" | awk ' # BEGIN{ # res[200]="OK"; # res[204]="NoContent"; # res[400]="Bad Request"; # res[403]="Forbidden"; # res[404]="Not Found"; # res[405]="Method Not Allowed"; # res[500]="Internal Server Error"; # } # { printf "HTTP/1.1 %s ", $0; # print ($0 in res) ? res[$0] : Error; # }') Date: $DATE Expires: $DATE Server: ${SNAME:-exoskeleton} ${HEADERS:-} ${CONTENTTYPE:-Content-Type: text/html} __RESPONSEHEADER grep -v '^$' | sed '/Content-Type/ s/$/; charset="UTF-8"/' # echo if [ $(($FLAG & 1)) != 0 ]; then # [ -x "$FILE" ] && { $FILE ${QUERY_STRING:-}; } || { echo "<html><body><h1>403 Forbidden</h1></body></html>"; } elif [ $(($FLAG & 2)) != 0 ]; then # [ -x $(which skmd) ] && { skmd ${FILE} ; } || { echo "<html><body><h1>403 Forbiddon</h1></body></html>"; } elif [ $(($FLAG & 4)) != 0 ]; then # [ -x $(which sktree) ] && { sktree $FILE ; } || { echo "<html><body><h1>403 Forbidden</h1></body></html>"; } else cat $FILE fiskeleton/skmd
このコマンドは markdown を HTML に変換するコマンドです。もちろんシェルスクリプトです。
skeleton/sktree
このコマンドは find コマンドを使用して、tree 形式の表示を作り出します。
コメント機能
コメント機能は、POST メソッドをしようします。なお、
skeleton/POST.skは以下のように.comment-*の文字列を含む URI については、認証をスルーするようにしています。このおかげでコメント機能を使えます。
ただし、実際に対象のファイルが存在しない場合には書き込みを行うことはできません。
コメントを記述していくファイルは、必ず.comments-コメント先のファイル名.mdです。例えば、aiu.mdにコメントをつけれるようにするためには、同じディレクトリに.comments-aiu.mdを作成する必要があります。PUT で作成してみてください。POST ) # --- POST METHOD ------------------------------------ # target=$(chamber -m POST -v -t '\.comments-.*\.md$' \ -p "${URL_PATH}" \ -u "${REQUEST_URI:-N}" \ -q "${QUERY_STRING:-N}" ) if [ -n "${target:-}" ]; then POST.sk -q "${QUERY_STRING:-N}" "${URL_PATH}" "${target:-}" else response 403 <<-END <html><body><h1>403 Forbidden</h1></body></html> END fi以下のような感じになります。
ランキング表示
簡便なものですが、アクセスログからランキングを表示するコマンドがあります。
xskのトップディレクトリ配下に.logディレクトリがあり、この記録から最もアクセスされている記事をアクセス数とともに表示します。$ pwd path/to/xsk $ ./skeleton/ranking .log/* 17 /aiu.md 5 /test.md 4 /posttest.md 2 /test/test1.md詳細が気になる方は、GitHub を覗いてください。
最後にいくつか発展的なトピックを扱って終わりにしたいと思います。シェルスクリプトを実行する
XSK はシェルスクリプトを実行できるのでした。
その例をいくつか扱いたいと思います。記事の説明部分を抜き出してみる
公開ディレクトリ
exo/.attachments/sh/の下にstub.shというシェルスクリプトが存在します。このシェルスクリプトは、記事タイトルと記事の説明を抜き出すものです。具体的には markdown 中の# 見出し1から!--までを抜き出します。
なお、抜き出す記事の選択はpathクエリの値として、WEBサーバーのルートディレクトリからの絶対パスとして指定します。以下のような感じです。
GET /.attachments/sh/stub.sh?path=/aiu.mdこのスクリプトを使用して、インデックスページの iframeに記事の切り抜きを表示しています。
もちろん、他にも実行したいスクリプトがあるのなら、それを簡単に実装することもできます。grep検索を実装してみよう!
exo/.attachments/sh/配下に、以下のようなxgrep.shというスクリプトを作成してみてください。
XSK に簡単に簡易検索機能を実装することができます。$ cat exo/.attachments/sh/xgrep.sh #!/bin/sh LF=$(printf '\\\n_') LF=${LF%_} this=$(dirname $0) CGINAME="$this/../../../skeleton/cgi-name" NAMEREAD="$this/../../../skeleton/nameread" QUERY="$(printf "%s" $1 | ${CGINAME} | ${NAMEREAD} q -)" cd $this/../../../exo grep $QUERY ./* | sed 's/$/<br\/>/' exit 0以下は実行してみた例です
pilotコマンドでも同じような結果を取得できます。
クエリはkey-value形式に分解しなければいけません。$ cat <<END | ./pilot > GET /.attachments/sh/xgrep.sh > q リスト > Host: localhost > END HTTP/1.1 200 OK Date: Sun, 01 Mar 2020 16:27:09 JST Expires: Sun, 01 Mar 2020 16:27:09 JST Server: exoskeleton Content-Type: text/html; charset="UTF-8" ./test.md:## リスト<br/> ./test.md:リストは1階層ぶんだけ表示できます。 <br/> ./test.md:- リストは入れ子にできません。 <br/> ./test.md:- リストの開始の際には空行で前後をはさみます<br/>簡単ですね。
このようにシェルスクリプトでの拡張性も十分?だと思います。[注]ここで使用している
cgi-nameとnamereadのコマンドは例によって秘密結社様のものです。
便利ですね!素晴らしいコマンドをありがとうございますmmHTTPS に対応しよう!
XSK は証明書を配置すれば HTTPS に対応できるのでした。
ここでは、 Certificates for localhost - Let's Encrypt - Free SSL/TLS Certificates を参考にして、HTTPS 対応してみましょう。$ cd path/to/xsk/.xsk $ openssl req -x509 -out fullchain.pem -keyout privkey.pem \ -newkey rsa:2048 -nodes -sha256 \ -subj '/CN=localhost' -extensions EXT -config <( \ printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth") $ pwd path/to/xsk/.xsk $ ls credentials fullchain.pem privkey.pem以下 macOS での例です:
1: 上記で生成した
fullchain.pemをキーチェーンに追加します。
キーチェーンアクセスを開いてサイドバーの「システム」にファイルをドロップします。以下のように「localhost」の証明書が追加されます。2: 証明書をダブルクリックして、「常に信頼」を設定します。
以上で設定は完了です。
HTTPS モードで起動してみましょう。$ pwd /path/to/exoskeleton $ sudo ./xsk -p 443 -u "@ツイッターユーザー名" -h "ホスト名" -sもちろん
pilotコマンドは-sオプションで動作します。$ echo /aiu.md | ./skeleton/mojihame skeleton/template/PUT - | ./pilot -vsf aiu.md * Trying ::1... * TCP_NODELAY set * Connection failed * connect to ::1 port 443 failed: Connection refused * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/cert.pem CApath: none * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS change cipher, Change cipher spec (1): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server did not agree to a protocol * Server certificate: * subject: CN=localhost * start date: Feb 29 17:38:47 2020 GMT * expire date: Mar 30 17:38:47 2020 GMT * subjectAltName: host "localhost" matched cert's "localhost" * issuer: CN=localhost * SSL certificate verify ok. > PUT /aiu.md HTTP/1.1 > Host: localhost > User-Agent: curl/7.64.1 > Accept: */* > Authorization: XSK4-HMAC-SHA256 Credential=/20200229/localhost/localhost/xsk4_request, SignedHeaders=content-length;content-md5;host;x-xsk-content-sha256;x-xsk-date, Signature=897421a8696ba0ffd73b2401de57d50a0fcd3b8005700294dedc7bca7d135faa > X-Xsk-Date: 20200229T175659Z > X-Xsk-Content-Sha256: c42a41d73efd1cf620c04912d24b337d2c5f748abde0950c830a2ea28e4c79c4 > Content-MD5: TJC4NBcOm6Ts9zWoUtoVDw== > Content-Length: 84 > Content-Type: application/x-www-form-urlencoded > * upload completely sent off: 84 out of 84 bytes < HTTP/1.1 200 OK HTTP/1.1 200 OK < Date: Sun, 01 Mar 2020 02:56:59 JST Date: Sun, 01 Mar 2020 02:56:59 JST < Expires: Sun, 01 Mar 2020 02:56:59 JST Expires: Sun, 01 Mar 2020 02:56:59 JST < Server: exoskeleton Server: exoskeleton < Location: exo/aiu.md Location: exo/aiu.md < Content-Type: ; charset="UTF-8" Content-Type: ; charset="UTF-8" * no chunk, no close, no size. Assume close to signal end < * TLSv1.2 (IN), TLS alert, close notify (256): * Closing connection 0 * TLSv1.2 (OUT), TLS alert, close notify (256):あとがき
いかがでしたでしょうか?
シェルスクリプトでもそこそこ?ほんのちょっとはまともな? HTTP/HTTPS 対応の CMS 兼 WEB サーバーが作れていると言えませんでしょうか。。(え、言えない??) ただ、S3 ライクな HTTP メソッドでの操作とディレクトリ構造そのままの URI の構成は、シンプルで明快であり、お遊びとお勉強にはちょうどいいと思います。拡張もそこそこしやすいように作ったつもりです。もし興味のある方がいれば、拡張用のモジュールの追加や機能のリクエスト、修正などは大歓迎です。githubのissueやプルリクを投げていただければと思います。
注:もちろん、なんでもシェルスクリプトでやるという考え方には賛成はしているわけではありません。個人の趣味の範囲なら問題ないでしょう。
- 投稿日:2020-03-01T18:07:28+09:00
例で学ぶC言語開発・解析ツール
C言語を用いたソフトウェアを開発・解析するためのツール群を、サンプルコードを実行するだけで習得しよう、という趣旨の連載です。
方針
なるべく文章を書かない
代わりに図を見てサンプルコードを実行するだけで技術を習得できるように心がけます。
TODO: 理由同じCのサンプルコードを使い回す
サンプルコード自体の学習時間を最小にするため。
普段Cを書かない方も対象とする
直接Cの開発をしていなくてもCの知識が役立つことが多々あるため。
例:ssh先で
sleep 9999しているときにsshをkillすると、ssh先のsleep 9999は生き残る?(答:SIGHUPで死ぬ)
TODO: disownしとくとプロセスグループから外れて死なない nohupしといても死なない他にもCで書かれているPythonやRuby、Cのツールを流用できるRust等でも役立ちます。(例 1 2 3 4)
Caveats
筆者はWindowsプログラミングが全くできないのでUNIXプログラミングに偏ってしまっています。(そのうち勉強したいとは思っていますが…)
目次
- autoconf
- CERT secure coding standards
- clang-format
- clangd
- CLion
- cmake
- cross debugging
- cscope
- ctags
- ELF
- gcc
- gcov
- gdb
- gdbgui
- gdbserver
- gef
- ghidra
- Hex Rays IDA
- Linux kernel のデバッグ
- LLVM compilation database (compile_commands.json)
- llvmいろいろ
- ltrace, strace, SystemTap
- Mozilla rr
- PANDA record/replay
- peda
- POSIX C programming
- pwndbg
- Pythonによるgdb拡張
- QEMU gdbserver
- QEMU record/replay
- valgrind
- vim
各記事の依存関係
実践:必須
破線:関連TODO
- 書きまくる
- 上のリスト以外にもあるはず
- 安定してきたら dev.to に英語版も書く "C development tools for dummies"(しばらくは頻繁に更新すると思われるので日本語のみで)
- 投稿日:2020-03-01T16:49:12+09:00
CentOsで、一般ユーザーを作成して、sudoコマンドを実行できるようになるまでの手順(備忘録)
前語り
出来るエンジニアさんからすれば取るに足らない知識でも、まずは発信していく癖を身に着けていこうと思います。
前提
・MacOS Mojave 10.14
・仮想マシン[Virtual Box]に建てた、LinuxOS
・CentOs7sudoコマンド
root権限でコマンドを使用できるようになるコマンド1 ユーザーの作成
まずはrootユーザーでログインし、useraddコマンドでユーザーを作成していきます。
制作したユーザーでログインできるようになるには、パスワードの設定が不可欠です。
パスワードを設定するコマンド 「passwd」で、testUser01にパスワードを設定します。これで、一般ユーザー(=testUser01)でログインするための準備が整いました。
2 wheelグループに一般ユーザーを追加し、visudoコマンドでオプションを有効にする。
さて、試しに一般ユーザーでログインし、ユーザー権限では見られないログファイルをsudoコマンドで閲覧してみましょう。
cat var/log/messages
何やらパスワードを聞かれ、言われるがまま打ち込んだら、「ユーザーはsudoers fileにありません」と言われてしまいました。
これは言われた通り、sudoersファイルに、一般ユーザーの設定がsudoersファイルに記述されていないことが原因です。
これを解消するには、以下の手順をこなします。
・一般ユーザーをwheelグループに所属させる。
CentOS7では「wheelグループ」に全てのコマンドの実行が許可されています。一度rootユーザーでログインし、
ユーザー定義を変更する「usermod」コマンドで、一般ユーザーをwheelグループに追加しましょう。
wheelグループにユーザーが追加できたかどうかは、vigrコマンドで確認できます。
・sudoersファイルを設定する[visudo]コマンドで、一般ユーザーの設定を追加する。
続いて、visudoコマンドで、wheelグループの定義を有効にしましょう。%wheel ALL=(ALL) ALL にあるコメントアウトを外し、オプションを有効にします。
esc→:wqで保存し、再びsudoコマンドを試してみましょう。
画面上に見えないのでコマンド記述
cat /vat/log/messages
sudoコマンドが使用可能となり、ログファイルの中身を参照できました!
- 投稿日:2020-03-01T15:14:55+09:00
Linux 簡単ノート
- 投稿日:2020-03-01T15:14:55+09:00
Linux コマンド 16
概要
Progateを参考に今の自分にとって必要なコマンドをノートしました。
派生も含めて16選びました。コマンド
mkdir ディレクトリ作成
cd ディレクトリの移動
pwd カレントディレクトリを確認する
ls ディレクトリの中身を確認する
ls -l 詳細な情報が確認できる
ls -a 隠れているファイルも表示される
less テキストファイルを1画面ずつ表示する
Enter 改ページ
/ 前から検索
? 後ろから検索
Shift + > ファイルの最後
Shift + < ファイルの先頭
cp コピーする
chmod 権限を変える 例)chmod 777
tail -f ttt.dat 追記を監視する
ps aux プロセスを表示する
- 投稿日:2020-03-01T14:59:06+09:00
knoppixを焼いてvmwareからブートをしてみた
knoppixとは
CD-ROMまたはDVD-ROMから起動することが可能なDebianベースのLinuxディストリビューション(Wikipedia参照)
ということでこれをDVD-Rに焼いていきます。
実行した環境
メインPC:Windows 10 64ビット
DVDドライブ:EX-DVD04W by i-o data
knoppix latest version 8.6.1必要なもの
- DVD-R
- メインPC
- DVDドライブ(今回はI・O DATAが出しているEX-DVD04Wを使用)
- knoppixのイメージ(knoppixの公式サイトからダウンロード)
手順1 (osイメージのダウンロード)
ノーマルダウンロード方法
knoppixのイメージをダウンロードします。ダウンロードページから好きなミラーサイトを選び、リンクをクリックすると利用規約のページに飛びます。Akzeptieren(同意する)を選び、
なんかよくわからんURLがいっぱい貼ってあるサイトに行きます。
ここではknoppix8.6.1の.iso形式のファイルをダウンロードするため、KNOPPIX_V8.6.1-2019-10-14-EN.iso.md5と書いてあるリンクをクリックします。ブラウザーの設定にもよりますが、自動でダウンロードが開始するのではないかと思います。
およそ4.3GBのファイルがダウンロードされますのでちょっと待ちます。Torrentダウンロード方法
こっちのほうがダウンロード速度が速いです。
Torrentクライアント(qbittorrentなど)を事前にダウンロードし、Torrentのダウンロードサイトから.torrent形式のファイルをダウンロードします。そして、Torrentクライアントの説明に沿ってknoppixイメージが入ったフォルダをダウンロードします。手順2 (DVD-Rにイメージをフラッシュ)
DVD-RにOSを焼きます。
.iso形式ものを選択し、右クリックしたときに出るメニューからディスクへの書き込みを選んでください。
またちょっと待ちます。
また、ディスク書き込み中は.isoファイル、親フォルダの名前を変えたり、削除をしないでください。ディスクが破損する可能性があります。手順3 (vmwareの設定)
vmware workstationのセッティングをします。
vmware workstation playerのダウンロード
vmware workstationはvmwareの公式サイトからダウンロードしてください。
vmware workstation playerの起動
vmwareが起動したら最初に目に入ってくるメニューから新規仮想マシンの作成(N)を選択し、新規仮想マシン作成ウィザードを開きます。
仮想マシンの設定
- 一番下の後でOSをインストールを選択し、次へ(N)を押します
- ゲストOSのバージョンを構成するメニューが出るため、Linuxを選択し、バージョンはDebian 10.x 64ビットを選択して次へ(N)を押します。
仮想マシン名ははっきり言ってどうでもいいですが、ここではknoppix8.6.1という名前にしました。場所も確認して次へ(N)を押します。
ディスク最大サイズを選ぶメニューが出てくるので、割り当てたいディスク容量を設定してください。ここではデフォルトの20GBにします。
仮想ディスクを分けるかまとめるかのチェックボックスがありますが、ここは仮想ディスクを単一ファイルに格納する設定にします。ほかのPCに仮想環境を移す予定がない場合は、単一にまとめてしまって大丈夫ですが、移植したい場合は複数のファイルに分割する設定にしてください。次へ(N)を押して先に進みます。完了を押して仮想マシンの設定を終了させます。
手順4 (ブート)
DVD-RドライブをパソコンにさしてC:\Users\ ユーザー名 \Documents\Virtual Machines\knoppix8.6.1\knoppix8.6.1.vmxをダブルクリックして実行します。
- 投稿日:2020-03-01T11:21:26+09:00
Another app is currently holding the yum lock; waiting for it to exit...のエラー
EC2のサーバーで
sudo yum install パッケージ名でパッケージインストール中に、別の作業をしていたら、タイムオーバーにより、正常にインストールが終了しないままEC2サーバーと切断(broken pipe)してしまいました。
Is this ok [y/d/N]: packet_write_wait: Connection to [パブリックIP] port 22: Broken pipeその後、再度EC2サーバーへログインして、
sudo yum install パッケージ名を実行したところ
ロックファイル /var/run/yum.pid が存在します: PID 18488 として別に実行されています。 Another app is currently holding the yum lock; waiting for it to exit... 他のアプリケーション: yum メモリー: 143 M RSS (541 MB VSZ) 開始 : Sun Mar 1 00:55:34 2020 - 1:03:58 秒経過 状態 : スリープ中、PID: 18488というコメントが続いて、インストールが始まらない状態が続きました。
sudo rm -f /var/run/yum.pid
を実行した後、再度同じインストールコマンドを実行したところ、正常にインストールが実行されました。追記)
https://pentan.info/server/linux/yum_lock.html
を参考に、ps aux | grep yum #該当するPTDの番号確認 kill [PIDの番号]により、二重処理を解消できます。


















