欧州一般データ保護規則(GDPR)についての個人的解釈
いよいよ2018年5月に適用が開始されるGDPR。
GDPRとは
"今回提案されたEUの新たなデータ保護制度はEUデータ保護法の対象範囲を、EU居住者のデータを処理する外国の全ての企業に拡大する。EU全域のデータ保護規制を一致させることで、欧州以外の企業が規則を遵守しやすいようにする。しかし、これを実現する代償として、データ保護を厳格に遵守させる制度、つまり全世界の売上高の4%を上限とする厳しい罰金を導入する。"欧州議会版では過料は5%に増額された。しかし欧州議会、欧州委員会、欧州閣僚理事会の三者の交渉の後、GDPRの文言、および、遵守しなかった場合の金銭的罰則についても全般的な合意がなされた
AWS
AWSの責任範囲については問題なさそう。
aws.amazon.com
欧州含む全世界向けサービス提供者例(ZOZOSUIT)
日本から欧州含む全世界向けにサービスを提供しようとしているZOZOSUITもGDPRが課題になりそうな記事が出ていました。(ZOZOSUITのインフラがどこで動いてるかはわかりませんが)
ZOZOSUITに死角ありーー世界のデータサービス業界の景色を一変させる「GDPR」とは | AMP[アンプ] - ビジネスインスピレーションメディア
いくつか記事から引用。
日本で包括的に世界のユーザー情報を管理する、という設計自体成り立たない。
EU圏内の個人データの処理に関して言えば、子会社をEU圏内に設立するというのが一番現実的なオプションだと思います。
法的な対策をとれば一部のデータ移転の可能性もゼロではありません。しかし、消費者一人ひとりとそのような契約を結ぶのはコスト・時間を考慮すると現実的とは言えないでしょう。
EUのユーザーに関してはEU法人が個別に管理する。
企業は付け焼き刃の対策で既存のモデルをGDPRに対応させようとするのではなく、ユーザーとなる消費者の思いを含め、ビジネスの初期段階から個人データ保護についてしっかりと考えなければいけません。
個人的解釈
消費者一人ひとりと契約を結ぶことは現実的ではないということで、
日本からEU圏内にビジネスを展開する場合は、EU法人を作ってデータを独立させるしか解決策がなさそうに見える。
解釈があってるかどうかは自信なし。
AWS Amplifyを使ってAmazon Cognitoを体験してみる
メインはAmazon Cognitoの理解です。
Amazon Cognitoまわりの作成は簡単にできるのですが、挙動を確認するにはUIがあるほうが理解が進むと思ったので、AWS Amplifyを使って体験してみました。
せっかくだったのでローカルで動かすだけではなく、S3にデプロイして動かしてみたのでメモしておきます。東京リージョンで作業しています。
Amazon Cognitoとは
まずはBlackBeltを読むと良いと思います。
続いてコンソールからアクセスすると以下の説明が出てきます。
Amazon Cognito は、ユーザーのアプリへのサインアップとサインイン、ソーシャル ID プロバイダーからの ID のフェデレーション、AWS のリソースへのセキュアなアクセス、および複数のデバイス、プラットフォーム、アプリケーションにわたるデータの同期を簡単にします。
Amazon Cognitoは「User Pools」と「Federated Identities」に分かれるので、それぞれ個人的な理解を簡潔にメモしておきます。
概要は理解はできたけど、簡潔に説明するとなると難しい。。
※「Cognito Sync」は今回触っていないので説明していません。
User Pools
- マネージドなユーザ管理サービスです。
- サインアップ、サインイン、サインアウトなどの認証を担当しています。
- FacebookやGoogleのような外部認証プロバイダと同列で、独自の認証プロバイダとして動作します。
認証機能だけであればUser Poolsだけでも動作可能です。
Federated Identities
- エンドユーザに一意のIDを割り振るためのサービスです。
- 認証プロバイダに認証されたエンドユーザに対して、AWSリソースへのアクセスを許可することができ認可を担当しています。
- 認可OKの場合はIAM側でSTSから一時アクセスキーが発行されます。
- FacebookやGoogleのような外部認証プロバイダやUser Poolsで認証されたエンドユーザを同一IDとして一元管理できます。
AWSリソースへアクセスするためにはFederated Identitiesが必要です。
User Poolsと統合することで外部認証プロバイダを使用することなくAWSリソースへアクセスすることが可能になります。
Amazon Cognitoをコンソールから構築してみる
実際に構築することで理解を深めていきます。
以下の記事を参考に構築しました。
dev.classmethod.jp
User Pools
Amazon Cognitoの「ユーザプールの管理」からアクセスします。
ユーザープールを作成する
「ユーザープールを作成する」→プール名を埋める→「デフォルトを確認する」→「プールの作成」と進みます。
プールID(ap-northeast-1_XXXXXXXXX)が表示されるのでメモしておきましょう。
アプリクライアントの追加
「アプリクライアント」→「アプリクライアントの追加」→アプリクライアント名を埋めて、「クライアントシークレットを生成」のチェックを外す→「アプリクライアントの作成」と進みます。
アプリクライアントID(XXXXXXXXXXXXXXXXXXXXXXXXX)が表示されるのでメモしておきます。
ユーザーの作成
「全体設定」→「ユーザーとグループ」→「ユーザの作成」→ユーザ名、仮パスワード、Eメールを埋める→「ユーザーの作成」と進みます
ステータスを見ると「FORCE_CHANGE_PASSWORD」と表示されています。
作成と同時に仮パスワードが書かれた以下のメールが飛んできます。
件名 Your temporary password 宛先 no-reply@verificationemail.com
UIがないと動作確認が難しいのでいったんここまで。
Federated Identities
Amazon Cognitoの「フェデレーテッドアイデンティティの管理」からアクセスします。
User Poolsと連携したIDプールを作成する
「新しいIDプールの作成」→IDプール名を埋める→(今回は「認証されていない ID に対してアクセスを有効にする」のチェックはどちらでもよい)→「認証プロバイダー」→さきほどメモした「プールID」と「アプリクライアントID」を埋める→「プールの作成」と進みます。
ID プールの ID(ap-northeast-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX) が表示されるのでメモしておきます。
UIがないと動作確認が難しいのでいったんここまで。
AWS Amplifyを使ってAmazon Cognito体験
以下の記事を参考に構築しました。
dev.classmethod.jp
nodejsのインストール、identityPoolIdの記載、S3へのデプロイ以外はほとんど参考リンク先の記述とほぼ同じになってしまいました。。
AWS Amplifyとは
AWS Amplifyは、2017年11月に公開されたAWSを利用するWebアプリケーション向けのJavaScriptライブラリです。サインアップやサインイン、MFA、追跡またはメトリクスの分析、コンテンツ管理、またはサーバーレスAPI統合などの実装が容易にできるように設計されています。
事前準備
適当にAmazon Linuxインスタンスを起動して試しました。
node jsをインストール
以下のリンクから最新版を確認。
Download | Node.js
$ wget https://nodejs.org/dist/v8.9.4/node-v8.9.4-linux-x64.tar.xz $ xz -dv node-v8.9.4-linux-x64.tar.xz $ tar xfv node-v8.9.4-linux-x64.tar $ ln -s node-v8.9.4-linux-x64 node (.profileを作成) $ cat .profile export NODE_HOME=$HOME/node export NODE_PATH=$NODE_HOME/lib/node_modules export PATH=$NODE_HOME/bin:$PATH $ source .profile $ node --version v8.9.4 $ npm --version 5.6.0
yarnをインストール
$ npm install -g yarn
Reactひな形を作成
(参考リンク先コマンドのyarn global add create-react-app だとcreate-react-appが動かなかったのでnpmで実行)
$ npm install -g create-react-app $ create-react-app amplify-react-sample $ cd amplify-react-sample
Webアプリケーションの実装
まずは、AmplifyライブラリとAmplifyのReact拡張ライブラリのインストールを行います。
$ yarn add aws-amplify aws-amplify-react
src/App.jsの書き換え
続いてsrc/App.jsを以下に書き換えます。
import React, { Component } from 'react'; import Amplify from 'aws-amplify'; import { Authenticator } from 'aws-amplify-react'; Amplify.configure({ Auth: { region: 'ap-northeast-1', // REQUIRED - Amazon Cognito Region userPoolId: 'ap-northeast-1_XXXXXXXXX', //OPTIONAL - Amazon Cognito User Pool ID userPoolWebClientId: 'XXXXXXXXXXXXXXXX', //OPTIONAL - Amazon Cognito Web Client ID identityPoolId: 'ap-northeast-1:XXXXXXXXXXXXXXXXXXXXXXXXXXX', // FederatedID連携後に追加する } }); class AppWithAuth extends Component { render() { return <Authenticator /> } }
参考リンク先のコードにidentityPoolIdの行を追加した形になります。
詳細なシーケンスは意識せずに、裏で紐づけをやってくれるのが非常に便利だと感じました。
各種IDには以下の値を埋めます。
- userPoolID
- プール作成時に作られたプールID
- userPoolWebClientId
- アプリクライアント作成時に作られたアプリクライアントID
- identityPoolId
- フェデレーティッドアイデンティティでIDプール作成時に作られたIDプールのID
S3にデプロイ
パブリックアクセス許可
アクセスコントロールリストから、読み取りのパブリックアクセスを許可をします。
Static website hostingを有効化
「Static website hosting」を有効化して、エンドポイントをメモしておきます。
Amazon GuardDuty 徹底調査!
Amazon GuardDutyについて調べたのでメモしておきます。
非常に簡単に脅威検知が可能になるため、有効化必須のサービスといっても良いと思います。
東京リージョンでも利用可能です。
よくまとまったスライドがあるので一読すると概要が掴めると思います。
目次です。
GuardDutyとは
料金
Amazon GuardDuty の料金表 – アマゾン ウェブ サービス (AWS)
- AWS CloudTrail イベントの数量 (1,000,000 イベントあたり) と分析された VPC フローログと DNS ログデータの量 (GB あたり) に基づいています。
- 以下、Tokyoの例になります。
GuardDutyのセットアップ
Amazon GuardDuty でサポートされるリージョン - Amazon GuardDuty
- コンソールから「GuardDutyの有効化」を実施するだけです。
- リージョンサービスのため、各リージョンで有効化を実施する必要があります。
無料トライアル
- 30日間の無料トライアルがあります。リージョンごとに30日間無料サービスが提供されます。
IAMロールの自動作成
- GuardDutyを有効化すると「AWSServiceRoleForAmazonGuardDuty」というIAMロールが作成されます。
- 作成されたIAMロールはIAMからもGuardDutyからも編集は不可能になります。
- AWSアカウントに対して1つ作成され、リージョンごとにGuardDutyを作成した場合は同じIAMロールを使用することになります。
# アクセス権限(AmazonGuardDutyServiceRolePolicy) { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "ec2:DescribeImages" ], "Resource": "*" } ] }
# 信頼関係 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "guardduty.amazonaws.com" }, "Action": [ "sts:AssumeRole" ] } ] }
各種データソース
- GuardDuty では AWS CloudTrail、VPC フローログ、DNS クエリログのデータソースを分析し結果を生成します。
- ただし、AWS CloudTrail、VPC フローログ、DNS クエリログのイベントやログは提供しないので、AWS CloudTrailやVPCフローログのセットアップが別途必要になります。
- VPCフローログが有効でなくてもGuardDutyの結果は生成されます。
※AWS CloudTrailを有効にする必要があるかは不明であるが、全リージョンで有効にしておくことが無難だと思われる。要確認。
※DNS クエリログはEC2インスタンス上から実行されたクエリログが解析対象のため、有効化は不要と思われる。要確認。
Amazon CloudWatch Events
- GuardDutyのユーザインターフェースはCloudWatchをサポートしていませんので、CloudWatch側から設定する必要があります。
- CloudWatchもリージョンサービスなので、リージョンごとに設定が必要になります。
- イベントタイプにはGuardDuty Findingのみ用意されています。結果の生成をトリガーに通知が送信されます。
ex) ターゲットにSNSトピックを指定することで結果の生成をトリガーにメール通知を行うことができます。
信頼できるIPリストと脅威リスト
停止または無効化
Amazon GuardDuty の停止または無効化 - Amazon GuardDuty
- GuardDuty停止
- AWS 環境のセキュリティは監視されなくなり、新しい結果は生成されません。
- 既存の結果は変更されず、GuardDuty の停止によって影響を受けることはありません。GuardDuty は後で再び有効にすることができます。
- GuardDuty無効
- 既存の結果と GuardDuty の設定は失われ、復旧できなくなります。
- 既存の結果を保存する場合は、GuardDuty を無効にする前にそれらをエクスポートする必要があります。
GuardDutyの結果
- 最近の結果から結果タイプや重要度の一覧を確認できます。
- 以下の結果タイプをサポートしています。
- Amazon GuardDuty の結果タイプ - Amazon GuardDuty
- BackDoor: AWSリソースが攻撃を受けていることを検出
- Behavior: ベースラインとは異なるアクティビティやアクティビティパターンを検出
- Crypto Currency: ビットコインやイーサリアムなどの暗号通過に関連付けられたソフトウェアを検出
- Pentest: 既知のペンテストツールで生成されたアクティビティと類似するアクティビティを検出
- Recon: AWS環境の脆弱性を探そうとしているアクティビティを検出
- Stealth: 攻撃アクションや形跡を隠そうとするアクティビティを検出
- Trojan: トロイの木馬プログラムが攻撃に使用されていることを検知
- Unauthorized Access: 不審なアクティビティまたアクティビティパターンの検出
- 以下に結果タイプの詳細なリストが記載されています。
- 結果タイプの説明横の四角いボタンから詳細なリストへリンクすることができます。
検出されたセキュリティ問題の修復
- 「EC2インスタンスの修正」が必要な場合
- Amazon GuardDuty によって検出されたセキュリティ問題の修復 - Amazon GuardDuty
- 不正なアクティビティの特定停止
- インスタンスの削除
- サポートケースにリクエスト(プレミアムサポートパッケージの受信者限定)
- 「AWS認証情報の修正」が必要な場合
- Amazon GuardDuty によって検出されたセキュリティ問題の修復 - Amazon GuardDuty
- 認証情報の所有者を識別
- 正当な使用か確認
- 不正に利用されていた場合は以下の対応を行います
- Resolve Issues with Potentially-Compromised AWS Accounts
- ルートアカウントとIAMユーザのパスワードを変更する
- AWS IDとアクセスキーを削除またはローテションする
- 不要なリソースを削除する
- AWSサポートセンターから受け取った通知に応答する
検証方法
- 「全般」「結果サンプルの生成」ボタンから、サンプルを生成することができます。
- [例]と書かれた32件のFindingsが作成されました。
- CloudWatch Eventsも送信されるので通知先の動作確認を行うことができます。
※手軽に結果を生成させる方法がわかっていません。要確認。
エクスポート
- JSON形式でエクスポートすることが可能です。
[ { "schemaVersion": "2.0", "accountId": "xxxxxxxxxxxxxxxxxxxxxxxx", "region": "ap-northeast-1", "partition": "aws", "id": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "arn": "arn:aws:guardduty:ap-northeast-1:xxxxxxxxxxxxxxxxxxxxxxxx:detector/xxxxxxxxxxxxxxxxxxxxxxxx/finding/yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "type": "CryptoCurrency:EC2/BitcoinTool.B!DNS", "resource": { "resourceType": "Instance", "instanceDetails": { "iamInstanceProfile": null, "imageId": null, "instanceId": "i-99999999", "instanceState": null, "instanceType": null, "launchTime": null, "networkInterfaces": null, "availabilityZone": null, "platform": null, "productCodes": null, "tags": null } }, "service": { "serviceName": "guardduty", "detectorId": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", "action": { "actionType": "DNS_REQUEST", "dnsRequestAction": { "domain": "example.com", "protocol": "TCP", "blocked": false } }, "resourceRole": "TARGET", "additionalInfo": { "sample": true }, "eventFirstSeen": "2017-12-27T02:21:34Z", "eventLastSeen": "2017-12-27T02:21:34Z", "archived": false, "count": 1 }, "severity": 5, "createdAt": "2017-12-27T02:21:34.592Z", "updatedAt": "2017-12-27T02:21:34.592Z", "title": "Bitcoin-related domain name queried by EC2 instance i-99999999.", "description": "EC2 instance i-99999999 is querying a domain name that is associated with Bitcoin-related activity." } ]
AWS アカウントの管理(マルチアカウント)
- メンバーアカウントで検知した結果をマスターアカウントのGuardDutyで表示および管理することが可能になります。
※メンバーアカウントでも料金が発生するかは要確認。
※マスターアカウントでは、メンバーアカウントの料金も重複して発生するか要確認。
アカウント
- マスターアカウント
- 自分のアカウントおよびすべてのメンバーアカウントの GuardDuty を設定し、さらに GuardDuty の結果を表示および管理できます。GuardDuty に最大 100 のメンバーアカウントを登録できます。
- メンバーアカウント
- 自分のアカウントの GuardDuty を設定し、さらに GuardDuty の結果を表示および管理できます。メンバーアカウントのユーザーは、他のメンバーアカウントの結果を表示または管理することはできません。
※AWS アカウントを同時に GuardDuty のマスターアカウントとメンバーアカウントにすることはできません。
マルチアカウントでの信頼できるIPリスト、脅威リスト
- マスターアカウントで設定した信頼できるIPリスト、脅威リストはメンバーアカウントには反映されません
招待方法
Amazon GuardDuty での AWS アカウントの管理 - Amazon GuardDuty
- ステップ 1 - アカウントを追加する
- ステップ 2: アカウントを招待する
- ステップ 3 - 招待を受け入れる
- メンバーアカウント側で「招待を承諾する」ボタンを押します。
- 事前に有効化していた場合でも、招待を承諾することで各種設定やログが消えることはありません。
- メンバーアカウント側ではアーカイブを実施することができません。
※招待を受け入れた後もメンバーアカウント側で自由に抜けることが可能です。抜けた後はアーカイブを実施することが可能になります。
最後に
組織を超えてAmazon GuardDutyの中央管理を行いたかったので、まとめてみました。
既にメンバーアカウント側でGuardDutyを有効化している場合も検知結果や設定はそのまま、マスターアカウント側で管理することが可能だったので展開自体はしやすいと感じました。
ただし、費用に関してはメンバーアカウント側でも発生して、マスターアカウント側ではメンバーアカウント側の費用も重複して発生しそうなのでコスト的にはデメリットとなるかもしれません。
また、メンバーアカウント側でアーカイブ機能に制限があり、いったんメンバーから離脱しないとメンバーアカウント側では検知されたイベントを消すことができないこともデメリットとなるかもしれません。
マルチアカウントにして、マスターアカウント側で管理することのメリットを明確にしないと、メンバーアカウントのメリットはわかりづらいというのが現時点での感想です。
ex) 社内のCSIRTと連携してインシデントレスポンスを迅速に行う。など
2017/12/14 AWS re:Invent 2017 Security re:Cap 参加レポート
今さらですが、AWS re:Invent 2017 Security re:Capの参加レポートになります。
connpass.com
公式の開催報告ブログがありました。資料もこちらから参照可能です。
aws.typepad.com
AWS Single Sign-On AWSソリューションアーキテクト 辻 義一さん
AWS SSOを実現する3つの方法
— 山野 健太 (@yamano3201) 2017年12月14日
option1: Microsoft ADでオンプレミスのADと信頼関係を結ぶ
option2: AD ConnectorでオンプレミスのADと接続する
option3: AWS Microsoft ADをスタンドアロンで使う #awsjpsec
re:Inventと今後のdwangoでのAWSセキュリティについて 株式会社ドワンゴ 鈴木 栄伍さん
EBC (Executive Briefing Center) #awsjpsec
— 山野 健太 (@yamano3201) 2017年12月14日
re InventのEBCにて、AWSの技術者に直接話を聞けるみたいですね。
https://t.co/357SKJSwaK #awsjpsec
— 山野 健太 (@yamano3201) 2017年12月14日
re Inventの中で登壇者が一番ためになったセッションとのことです。
Amazon GuardDuty AWSパートナーソリューションアーキテクト 酒徳 知明さん
GuardDuty。脅威リスクを検知するサービス。VPC Flow Logs, AWS CloudTrail Event Logs, DNS Logsを利用して分析する。 #awsjpsec
— 山野 健太 (@yamano3201) 2017年12月14日
GuardDutyは簡単に始められる。VPC Flowlogsが有効でなくてもGuardDuty Findingsを生成できる #awsjpsec
— 山野 健太 (@yamano3201) 2017年12月14日
Trusted and Threat IP Lists。IPでホワイトリスト、ブラックリスト登録できる #awsjpsec
— 山野 健太 (@yamano3201) 2017年12月14日
CloudFormationでマルチアカウント、マルチリージョンにばらまくことができる。 #awsjpsec
— 山野 健太 (@yamano3201) 2017年12月14日
ドキュメントだとCloudFormation使わないで下の3ステップで登録してる。使い分けはなんだろう?
— 山野 健太 (@yamano3201) 2017年12月14日
ステップ 1 - アカウントを追加する
ステップ 2: アカウントを招待する
ステップ 3 - 招待を受け入れるhttps://t.co/RG6YJkoawp #awsjpsec
酒徳さんに質問してみた。デモのCloudFormationでは小アカウント側でGuardDutyをオンにしただけなので、データを親アカウント側で表示するにはinvite menbersを実行する必要がありそう。 #awsjpsec pic.twitter.com/Ok1F99Hoh9
— 山野 健太 (@yamano3201) 2017年12月14日
AWS re:Invent 2017振り返り SOCの立場からみたre:Invent 株式会社リクルートテクノロジーズ 安東 美穂さん
EBC(個別ミーティング)の良かったこと
— 山野 健太 (@yamano3201) 2017年12月14日
・製品やロードマップについての質問をダイレクトにできる
・ロードマップにない、製品担当者の思想が聞ける
・製品担当者にユーザの声や要望を伝えることができる #awsjpsec
Security re:Cap 2017 トレンドマイクロ株式会社 姜 貴日 さん
資料のp10にGuardDutyとDeepSecurityの違いが載っています。
検出と遮断というのが大きく異なりますが、ポートスキャン、C&C通信、ブルーとフォースなど対応する脅威は被っているように見えますね。
多層防御なのでどっちかという話ではないかもしれないですが。
GuardDutyとDeepSecurityの連携https://t.co/tTkki6H3pI#awsjpsec
— 山野 健太 (@yamano3201) 2017年12月14日
さっそくGuardDutyとDeepSecurity連携のコードが提供されていました。
AWSのマネージドIDSとDeepSecurityのIPSが連携するとセキュリティを向上させることができそうですね。
2017/10/07 脆弱性診断 初心者ハンズオントレーニング 参加レポート
今さらですが、脆弱性診断 初心者ハンズオントレーニングの参加レポートです。
pentest-web.connpass.com
XSS, SQLインジェクション 脆弱性、アクセス制御系の説明の後にBurpSuiteを使って実際に脆弱性がないかを診断していくハンズオンを実施しました。
OS, ミドルウェアの脆弱性診断は機械的に実施することが多いが、アプリケーションの脆弱性診断はアプリの特性を理解する必要があるので、人がやるのが有効とのことでした。
Webアプリケーション脆弱性診断ガイドラインhttps://t.co/JZoFqn7Vwg #pentestjp
— 山野 健太 (@yamano3201) 2017年10月7日
安全な SQLの 呼び出し方IPAhttps://t.co/arvFvNElGf #pentestjp
— 山野 健太 (@yamano3201) 2017年10月7日
安全なウェブサイトの作り方 IPAhttps://t.co/MBpx6mLDIk #pentestjp
— 山野 健太 (@yamano3201) 2017年10月7日
認可制御の不備は一発アウト #pentestjp
— 山野 健太 (@yamano3201) 2017年10月7日
2017/09/30 TDDBC Tokyo 2017-09 参加レポート
今さらですが、TDDBC Tokyoの参加レポートです。
tddbc.connpass.com
2011年にTDDBCに参加したときはRubyでした。
Lambdaのテストの話はこれですね。https://t.co/kWJtACFKWg #tddbc
— 山野 健太 (@yamano3201) 2017年9月30日
AWS Summit でTestable Lambdaのセッションを聞いてから、なるべくLambdaのテストを書くようにしています。
Lambdaの開発ではPythonを利用しているので、今回はPythonで受けてきました。
ペアプロの成果物は下の2つです。最後にpushしただけなので途中のコミットなどは残していません。
https://github.com/yamano/python_pytest/blob/with_azu1129/acme/semver.py
https://github.com/yamano/python_pytest/blob/with_azu1129/tests/acme/test_semver.py