Quantcast
Browsing Latest Articles All 16 Live
Mark channel Not-Safe-For-Work? (0 votes)
Are you the publisher? or about this channel.
No ratings yet.
Articles:

Markdown再入門

どうも若松です。

最近、過去のメモを掘り返して見ることがあるんですが、書いた時期によって書式がバラバラで、自分で書いたのにやたら見にくいことが多いです。

ならば書式を統一しようじゃないか!
いっそのことMarkdownで書いちゃえば他に転用できていいんじゃね?!

などと思い立ち、改めてMarkdown記法を調べたのでまとめます。

Markdown記法早見表

いきなり早見表かよ!というツッコミは受け付けておりません。
なんで早見表が一番上なのかというと、書いた本人が記法を忘れて見に来たときに一覧がほしいからです。
(ぶっちゃけこれが欲しくてこの記事を書きました)

記法名 書式 機能
段落 (行間に空行) 文章に段落をつけます。
改行 (行末に半角スペース2つ) 行に改行をつけます。
見出し # 見出し 行を見出しにします。
の数で見出しレベルが変わります。
引用 > 引用文 引用文にします。
>を重ねると多重引用になります。
斜体 *斜体* テキストを斜体にします。
強調 **強調** テキストを強調します。
斜体&強調 ***斜体&強調*** テキストを斜体にし、さらに強調します。
打ち消し線 ~~打ち消し線~~ テキストに打ち消し線をつけます。
コード ```コード``` テキストをコード表示します。
空白や特殊文字もそのまま表示されます。
インラインコード `コード` テキストの一部をコード表示します。
空白や特殊文字もそのまま表示されます。
リスト - リスト1
- リスト2
- リスト3
* or + でも可
テキストをリスト表示にします。
インデントにはタブを用います。
番号付きリスト 1. リスト1
1. リスト2
1. リスト3
テキストを番号付きリスト表示にします。
インデントにはタブを用います。
水平線 ---
___
水平線を表示します。
テーブル | 項目1 | 項目2 | 項目3 |
| ---- | --- | --- |
| 値1 | 値2 | 値3 |
テーブルを表示します。
-は3つ以上であれば数は任意で決めることができます。
両端の|は省略可能です。
:を用いることで値の位置を変更することができます。
自動リンク <URL> URLのリンクを貼ります。
メールアドレスもリンク可能です。
インラインリンク [テキスト](URL) テキストにURLのリンクを貼ります。
メールアドレスもリンク可能です。
外部参照リンク [テキスト][参照名]
[参照名]:URL
テキストに参照名を介してURLのリンクを貼ります。
メールアドレスもリンク可能です。
画像 ![alt属性](画像のパス) 画像を表示します。
エスケープ \ 特殊文字をエスケープします。

Markdown記法詳解

Markdown記法を例を交えて解説します。

段落

行間に空行を入れることで段落になります。

Markdown:

段落1
(空行)
段落2

表示:

段落1

段落2

改行

文の最後に半角スペースを2つ以上入れると改行になります。

Markdown:

文1(半角スペース2つ)
文2

表示:

文1
文2

見出し

#と半角スペースを文頭に置くことで見出しになります。
#が増える毎に見出しレベルが変わります。

Markdown:

# 見出し1
## 見出し2
### 見出し3
#### 見出し4
##### 見出し5
###### 見出し6

表示:

見出し1

見出し2

見出し3

見出し4

見出し5
見出し6

引用

>と半角スペースを文頭に置くことで引用になります。
>を2つ以上置くと多重引用になります。

Markdown:

> 引用1
>> 引用2
>>> 引用3

表示:

引用1

引用2

引用3

装飾

記号で文(or 文の一部)を囲むことでテキストを装飾できます。
*の代わりに_を使用することも可能です。

Markdown:

これは*イタリック(斜体)*です。
これは**ボールド(強調)**です。
これは***イタリック(斜体)&ボールド(強調)***です。
これは~~打消し線~~です。

表示:

これはイタリック(斜体)です。
これはボールド(強調)です。
これはイタリック&ボールドです。
これは打消し線です。

コード

```または~~~で文を囲うとコード表示になります。
コード表示内ではMarkdownは効かず、空白やタブなどもそのまま表示されます。

Markdown

```
echo "This is a code1"
echo "This is a code2"
```

表示:

echo "This is a code1"
echo "This is a code2"

インラインコード

`で囲うと行内の一部テキストをコード表示にできます。

Markdown

以下の`echo`を実行してください。

表示:

以下のechoを実行してください。

リスト(箇条書き)

* or + or - と半角スペースを文頭に置くことでリスト表示にできます。
ネストはタブで表現します。

Markdown

- リスト1
- リスト2
  - リスト3
    - リスト4

表示:

  • リスト1
  • リスト2
    • リスト3
    • リスト4

番号付きリスト

数字とドット(.)を文頭に置くことで番号付きリスト表示にできます。
ネストはタブで表現します。

Markdown

1. リスト1
1. リスト2
  1. リスト3
    1. リスト4

表示:

  1. リスト1
  2. リスト2
    1. リスト3
      1. リスト4

水平線(罫線)

- or _を3つ以上並べることで水平線(罫線)を表示できます。

Markdown

---
文1
______________
文2

表示:


文1


文2

テーブル(表)

-を組み合わせることでテーブル表示になります。
-は3つ以上であれば数は任意で決めることができます。
両端の|は省略可能です。
※テーブルは元々のMarkdownにはない仕様のため、動作しない環境もあります。

Markdown

| 項目1 | 項目2 | 項目3 |
| ---- | --- | --- |
| 値1 | 値2 | 値3 |

表示:

項目1 項目2 項目3
値1 値2 値3

また、:を用いると値の位置を変更することができます。

Markdown

| 左寄せ | 中央寄せ | 右寄せ |
| :---- | :---: | ---: |
| 値1 | 値2 | 値3 |

表示:

左寄せ 中央寄せ 右寄せ
値1 値2 値3

自動リンク

URLを<>で囲むことでリンクを貼ることが出来ます。
メールアドレスもリンク可能です。

Markdown

<http://google.com>
<xxxxx@gmail.com>

表示:

http://google.com
xxxxx@gmail.com

インラインリンク

[]でテキストを囲み、その後()でURLを囲むとテキストにURLのリンクを貼ることができます。
メールアドレスもリンク可能です。

Markdown

[Google](http://google.com)
[xxxxxへメール](xxxxx@gmail.com)

表示:

Google
xxxxxへメール

外部参照リンク

[]でテキストを囲み、その後さらに[]でテキストを囲むと後ろのテキストを参照名(変数)として扱う事ができます。
実際のURLは[]で参照名テキストを囲み、その後に:とURLを書くと参照名にURLを紐付きます。
メールアドレスもリンク可能です。

Markdown

[Google][G]
[xxxxxへメール][xxxxx]



表示:

Google
xxxxxへメール

画像

リンクの記法の頭に!を付けると画像表示になります。
[]内のテキストは画像のalt属性、()内は画像のパスです。

Markdown

![サンプル](sample.png)

表示:

サンプル

エスケープ

\を特殊文字(ここまで出てきた表示を変える文字)をエスケープ(無効化)することが出来ます。

Markdown

#### 見出し   
\#\#\#\# 見出しと書くと見出し表示になります。

表示:

見出し

#### 見出しと書くと見出し表示になります。

インラインHTML

Markdownは元々HTMLを簡単に書く目的で考案されたため、素のHTMLも扱うことが出来ます。
言い換えれば、Markdown記法で表現できない表示形式はHTMLタグで補えるということです。
例として上付き文字装飾を上げます。

Markdown

この文に上付き文字をつけます。<sup>上付き文字</sup>

表示:

この文に上付き文字をつけます。上付き文字

まとめ

全部書いたら結構長くなってしまいました。
本記事の内容はあくまで私が今回調べた成果ですので、抜け等はあると思います。
ですので、これ足りてないよーとかこれ間違ってんじゃん!というのがありましたら、教えていただけるとありがたいです。

Markdown記法はご覧のとおり、簡単に記述することができます。
各種ブログやBacklog,GitHubなど幅広く使えますので、これを気にMarkdownを使ってみてはいかがでしょうか。
(各々のToolには方言・拡張機能が存在しますが、本記事では割愛します)

参考

Markdown原文
はしくれエンジニアもどきのメモ
Markdown記法 チートシート

CLIでEC2をローンチする際にエフェメラルディスクをつけない方法

Webコンソール以外でEC2をローンチする際にエフェメラルディスク(インスタンスストア)が付いてしまう仕様があるようです。
少なくともCLIとTerraformではこの現象が起きます。

この現象の解消方法は現在のところBlockDeviceMappingのエフェメラルディスクに片っ端からNoDeviceを指定する他ありません。

以下にCLIとTerraformでの例を記載します。

CLI

BlockDeviceMapping用のjsonファイルを以下のように記述します。

block_device_mappings.json
[
    {
        "DeviceName": "/dev/sda1",
        "Ebs": {
            "VolumeSize": 50,
            "DeleteOnTermination": true,
            "VolumeType": "gp2"
        }
    },
    {"DeviceName": "xvdca", "NoDevice": ""},
    {"DeviceName": "xvdcb", "NoDevice": ""},
    {"DeviceName": "xvdcc", "NoDevice": ""},
    {"DeviceName": "xvdcd", "NoDevice": ""},
    {"DeviceName": "xvdce", "NoDevice": ""},
    {"DeviceName": "xvdcf", "NoDevice": ""},
    {"DeviceName": "xvdcg", "NoDevice": ""},
    {"DeviceName": "xvdch", "NoDevice": ""},
    {"DeviceName": "xvdci", "NoDevice": ""},
    {"DeviceName": "xvdcj", "NoDevice": ""},
    {"DeviceName": "xvdck", "NoDevice": ""},
    {"DeviceName": "xvdcl", "NoDevice": ""},
    {"DeviceName": "xvdcm", "NoDevice": ""},
    {"DeviceName": "xvdcn", "NoDevice": ""},
    {"DeviceName": "xvdco", "NoDevice": ""},
    {"DeviceName": "xvdcp", "NoDevice": ""},
    {"DeviceName": "xvdcq", "NoDevice": ""},
    {"DeviceName": "xvdcr", "NoDevice": ""},
    {"DeviceName": "xvdcs", "NoDevice": ""},
    {"DeviceName": "xvdct", "NoDevice": ""},
    {"DeviceName": "xvdcu", "NoDevice": ""},
    {"DeviceName": "xvdcv", "NoDevice": ""},
    {"DeviceName": "xvdcw", "NoDevice": ""},
    {"DeviceName": "xvdcx", "NoDevice": ""},
    {"DeviceName": "xvdcy", "NoDevice": ""},
    {"DeviceName": "xvdcz", "NoDevice": ""}
]

そして、以下のようにCLIを実行します。

ec2_run-instances.sh
aws ec2 run-instances \
    --image-id ami-12345678 \
    --key-name example \
    --instance-type c3.large \
    --subnet-id subnet-12345678 \
    --iam-instance-profile Arn=arn:aws:iam::123456789123:role/example-role \
    --security-group-ids sg-12345678 \
    --block-device-mappings file://block_device_mappings.json \
    --desable-api-termination

Terraform

TerraformもVer0.8からNoDeviceをサポートしています。
tfファイルには以下のように記述します。

aws_instance.tf
resource "aws_instance" "example-instance" {
    ami = "ami-12345678"
    instance_type = "c3.large"
    key_name = "example"
    subnet_id = "${aws_subnet.example.id}"
    security_groups = ["${aws_security_group.example.id}"]
    disable_api_termination = true
    tags {
        Name = "example-instance"
    }
    iam_instance_profile = "example-role"
    root_block_device {
        volume_type = "gp2"
        volume_size = 50
    }
    ephemeral_block_device { device_name = "xvdca" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcb" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcc" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcd" no_device = "true" }
    ephemeral_block_device { device_name = "xvdce" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcf" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcg" no_device = "true" }
    ephemeral_block_device { device_name = "xvdch" no_device = "true" }
    ephemeral_block_device { device_name = "xvdci" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcj" no_device = "true" }
    ephemeral_block_device { device_name = "xvdck" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcl" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcm" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcn" no_device = "true" }
    ephemeral_block_device { device_name = "xvdco" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcp" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcq" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcr" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcs" no_device = "true" }
    ephemeral_block_device { device_name = "xvdct" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcu" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcv" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcw" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcx" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcy" no_device = "true" }
    ephemeral_block_device { device_name = "xvdcz" no_device = "true" }
}

まとめ

書式としてはあまりスマートとは言えませんが、現状はこうするしかないようです。
SDKでは検証していませんが、おそらく同じかと思います。
もっとよい書き方があったら教えてください。

SSMを用いたCloudWatchLogsへのイベントログ出力

WindowsServer2012のEC2Config Ver4.x以降とWindowsServer2016からはCloudWatchLogsへのログ出力設定方法が変わりました。

今回は東京リージョンにイベントログを出力する手順に絞って記載します。
※いままでEC2Config経由でログ出力設定をしていた方であれば、任意ログの設定方法もわかると思います。

EC2でWindowsインスタンス作成

  • 割愛

EC2インスタンスにアタッチしたIAMRoleの設定

  • AmazonEC2RoleforSSM を対象インスタンスのIAMRoleにアタッチする

ドキュメント作成

  1. 以下の画面で[Create Document]をクリック
    https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home#Documents:Owner=MeOrAmazon;sort=Name
  2. 以下を入力
    Name: [ドキュメント名]
    Content: 以下のjsonファイルの内容をコピペ
cloudwatchlogs_conf.json
{
    "schemaVersion": "1.2",
    "description": "Example CloudWatch Logs tasks",
    "runtimeConfig": {
        "aws:cloudWatch": {
            "settings": {
                "startType": "Enabled"
            },
            "properties": {
                "EngineConfiguration": {
                    "PollInterval": "00:00:15",
                    "Components": [
                        {
                            "Id": "ApplicationEventLog",
                            "FullName": "AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                                "LogName": "Application",
                                "Levels": "1"
                            }
                        },
                        {
                            "Id": "SystemEventLog",
                            "FullName": "AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                                "LogName": "System",
                                "Levels": "7"
                            }
                        },
                        {
                            "Id": "SecurityEventLog",
                            "FullName": "AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                            "LogName": "Security",
                            "Levels": "7"
                            }
                        },
                        {
                            "Id": "ETW",
                            "FullName": "AWS.EC2.Windows.CloudWatch.EventLog.EventLogInputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                                "LogName": "Microsoft-Windows-WinINet/Analytic",
                                "Levels": "7"
                            }
                        },
                        {
                            "Id": "IISLog",
                            "FullName": "AWS.EC2.Windows.CloudWatch.IisLog.IisLogInputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                                "LogDirectoryPath": "C:\\inetpub\\logs\\LogFiles\\W3SVC1"
                            }
                        },
                        {
                            "Id": "CustomLogs",
                            "FullName": "AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                                "LogDirectoryPath": "C:\\CustomLogs\\",
                                "TimestampFormat": "MM/dd/yyyy HH:mm:ss",
                                "Encoding": "UTF-8",
                                "Filter": "",
                                "CultureName": "en-US",
                                "TimeZoneKind": "Local"
                            }
                        },
                        {
                            "Id": "PerformanceCounter",
                            "FullName": "AWS.EC2.Windows.CloudWatch.PerformanceCounterComponent.PerformanceCounterInputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                                "CategoryName": "Memory",
                                "CounterName": "Available MBytes",
                                "InstanceName": "",
                                "MetricName": "Memory",
                                "Unit": "Megabytes",
                                "DimensionName": "",
                                "DimensionValue": ""
                            }
                        },
                        {
                            "Id": "CloudWatchLogs",
                            "FullName": "AWS.EC2.Windows.CloudWatch.CloudWatchLogsOutput,AWS.EC2.Windows.CloudWatch",
                            "Parameters": {
                                "AccessKey": "",
                                "SecretKey": "",
                                "Region": "ap-northeast-1",
                                "LogGroup": "Default-Log-Group",
                                "LogStream": "{instance_id}"
                            }
                        },
                        {
                            "Id": "CloudWatch",
                            "FullName": "AWS.EC2.Windows.CloudWatch.CloudWatch.CloudWatchOutputComponent,AWS.EC2.Windows.CloudWatch",
                            "Parameters": 
                            {
                                "AccessKey": "",
                                "SecretKey": "",
                                "Region": "ap-northeast-1",
                                "NameSpace": "Windows/Default"
                            }
                        }
                    ],
                    "Flows": {
                        "Flows": 
                        [
                            "(ApplicationEventLog,SystemEventLog,SecurityEventLog),CloudWatchLogs"
                        ]
                    }
                } 
            }
        }
    }
}

3.[Create Document]をクリック

ドキュメント紐付け

  1. 以下の画面で[Create Association]をクリック
    https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home#ManagedInstances:sort=InstanceId
  2. Command documentで作成したドキュメントを選択
  3. Select Targets byでManually Selecting Instancesを選択後、対象インスタンスを選択
    ※インスタンス作成直後だと、表示されないことがあります。待ちましょう。
  4. [Create Association]をクリック

まとめ

ここまでの手順からわかるとおり、ログ出力設定はドキュメントとして外出しする形になりました。
インスタンス構築の度に同じログ出力設定を書く手間がなくなり、かつログインせずにログ出力設定ができるようになりました。
複数台に同じ設定を適用する場面ではうれしい機能ですね。

boto3でec2をdescribeして値を取り出す

どうもLambda(Python)初心者の若松です。

唐突ですが、みなさんはLambda使っていますか?

サーバが無くてもコードが動く。
魅力的ですよねぇ。

やっぱり時代はServerlessだと。
インフラでも言語の一つくらい使えるようになれと。

そんな流れに逆らわないゆとり世代ど真ん中の私は、とうとうLambdaに踏み込んだわけです。

前置きが長くなりましたが、インフラ屋が四苦八苦しながら戯言言ってんなぁぐらいの気持ちで生暖かく見ていただければと思います。

やりたいこと

特定のIPを持っているインスタンスのインスタンスIDを取得したい。

環境

AWS Lambda
Python 3.6

第一形態

コード

とりあえずインスタンス情報を全部取り出してみようかと思い、以下のコードを書きました。

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances()

    return responce

結果

returnで以下のエラーが返って失敗

{
  "errorMessage": "An error occurred during JSON serialization of response",
  "errorType": "datetime.datetime(2017, 5, 11, 5, 15, 59, tzinfo=tzlocal()) is not JSON serializable"
}

結果そのままではJSONとして扱えないみたいです。

調査

boto3のリファレンスを呼んでみると、どうやら describe_instances の返り値は dict(辞書)型 というもののようです。
とりあえずprintで出力したほうがよさげなので、コードを変更することにしました。

第二形態

コード

とりあえず出力をプリント文に変更

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances()

    print(responce)

    return

結果

ログに以下が出力されました。
AWSCLIの結果でよく見るあれですね。

{
    "Reservations": [
        {
            "OwnerId": "xxxxxxxxxxxx", 
            "ReservationId": "r-xxxxxxxxxxxxxxxxx", 
            "Groups": [], 
            "Instances": [
                {
                    "Monitoring": {
                        "State": "disabled"
                    }, 
                    "PublicDnsName": "ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com", 
                    "Platform": "xxxxxxx", 
                    "State": {
                        "Code": 80, 
                        "Name": "stopped"
                    }, 
                    "EbsOptimized": false, 
                    "LaunchTime": "xxxx-xx-xxxxx:xx:xx.xxxx", 
                    "PublicIpAddress": "xxx.xxx.xxx.xxx", 
                    "PrivateIpAddress": "xxx.xxx.xxx.xxx", 
                    "ProductCodes": [], 
                    "VpcId": "vpc-xxxxxxx", 
                    "StateTransitionReason": "", 
                    "InstanceId": "i-xxxxxxxxxxxxxxxxx", 
                    "EnaSupport": true, 
                    "ImageId": "ami-xxxxxxxx", 
以下略

調査

次はIPで絞りたいと思います。
AWSCLIでいうところの--filterはboto3にもあるらしいのでそれを使うことにしました。

第三形態

コード

特定のIPを持っているインスタンスに絞るため、Filtersを追加

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances(
        Filters=[{'Name':'network-interface.addresses.private-ip-address','Values':["xxx.xxx.xxx.xxx"]}]
    )

    print(responce)

    return

結果

指定したIPアドレスを持つインスタンスに絞れた。

調査

次は返り値をインスタンスIDに絞ります。
イメージとしてはAWSCLIでいうところの--queryだが、boto3にはないようです。
ここでJSONに変換するだのなんだのと、ネットの情報に踊らされてかなり苦戦しました。
結果的には、dict型はオブジェクトの後に ["hoge"]["fuga"] と書くことで値を取り出せるらしいことがわかりました。

最終形態

コード

import boto3

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    responce = ec2.describe_instances(
        Filters=[{'Name':'network-interface.addresses.private-ip-address','Values':["xxx.xxx.xxx.xxx"]}]
    )["Reservations"][0]["Instances"][0]["InstanceId"]

    print(responce)

    return

結果

めでたくインスタンスIDの取得に成功しました。
リストある際には[0]と明示的に0を書く必要があるというところに気をつけたいところです。
※AWSCLIの--queryでは[]と省略して書けるため

まとめ

いかがでしたでしょうか。
やってる内容は非常に初歩的な内容ですが、いくつかハマった箇所があったので備忘録的にまとめました。

意外と簡単に動かせるので、是非Tryしてみてください。
Lambda怖くない。

SORACOM Buttonから渡されるイベントを確認してみた

どうも若松です。

SORACOM LTE-M Button がリリースされたので、買ってみました。
https://soracom.jp/products/gadgets/aws_button/

どこでもLambda叩けるのは便利ですよねー
いろいろ可能性を感じます。

セットアップ

AWS IoT 自体が触るの初めてだったので、いろいろと仕様を確認しながら触ってみました。
セットアップはこの辺りを見ながらやりました。(割愛)
https://dev.soracom.io/jp/start/aws_button_slack/

イベント確認用Lambda

とりあえず、Lambdaに渡されるイベントの内容を確認しないとコードが書けないので、確認用のLambdaを用意しました。
CloudwatchLogsに吐かれたイベントのJSONを確認するだけなので、超シンプルです。
言語はPython3.6です(一応)

get-event
import json

def lambda_handler(event, context):

    print(event)

    return

確認結果

以下のJSONがイベントとしてLambdaに渡されます。

event
{
  "deviceInfo": {
    "deviceId": "XXXXXXXXXXXXXXXX",
    "type": "button",
    "remainingLife": 99.49749,
    "attributes": {
      "projectRegion": "ap-northeast-1",
      "projectName": "soracom",
      "placementName": "soracom-button",
      "deviceTemplateName": "get-event"
    }
  },
  "deviceEvent": {
    "buttonClicked": {
      "clickType": "SINGLE",
      "reportedTime": "yyyy-mm-ddThh:mm:ss.xxxZ"
    }
  },
  "placementInfo": {
    "projectName": "soracom",
    "placementName": "soracom-button",
    "attributes": {},
    "devices": {
      "get-event": "XXXXXXXXXXXXXXXX"
    }
  }
}

clickTypeの値はボタンの押し方で以下のように変わります。

押し方
シングルクリック SINGLE
ダブルクリック DOUBLE
長押し LONG

押し方で機能変えたいときは、この値で判別すればよいようです。

気になること

以下のドキュメントを見ると、実際にイベントとしてLambdaに渡されたJSONと異なる形式のJSONの記載がありました。
しれっと修正されてました。(11/8追記)
https://dev.soracom.io/jp/aws_button/how-it-works/

SORACOMコンソールで確認できるJSONと同じ形式のようなので、AWS IoTに送られる生のJSONがこの形式なのかもしれません。

まとめ

簡単にセットアップできてサクサク試せるので、とても楽しいです。
ボタン自体の機能はシンプルですが、Lambdaにつながるのでアイディア次第ではいろいろできそうですね。

SORACOM Button でバスターコールを発令してみた

どうも若松です。

SORACOM LTE-M Button で何作ろうかと悩むのは楽しいですね。
せっかくなので押しがいがあるのを作りたいといろいろ考えていたときに、あるマンガの場面を思い出しました。

バスターコール

  
ワンピースに出てくる、全てを破壊する命令ですね。
このバスターコールを発令するきっかけがボタンなのです。

AWS式バスターコール

(プロレス技みたいですね)

せっかくLambdaをキックできるので、AWSリソースを破壊してみようと思います。
流石にAWSサービスは多すぎるので、EC2インスタンスの範囲に限定することにしました。

SORACOM Button → AWS IoT → Lambda → EC2Terminate

という構成です。

Lambda

コードは以下となります。
言語はPython3.6です。

BusterCall
import boto3
import jmespath

def lambda_handler(event, context):

    ec2 = boto3.client('ec2')

    instanceids = jmespath.search(
        'Reservations[].Instances[?State.Name!=`terminated`][].InstanceId',
        ec2.describe_instances()
    )

    for instanceid in instanceids:
        ec2.modify_instance_attribute(
            InstanceId=instanceid,
            DisableApiTermination={
                'Value': False
            }
        )

    ec2.terminate_instances(
        InstanceIds=instanceids
    )

    print(instanceids)

    return

AWS IoT

以下のように設定を済ませます。
スクリーンショット 2018-11-05 22.38.11.png
スクリーンショット 2018-11-05 22.38.24.png

EC2

流石に東京リージョンは怖いので、オレゴンで行います。
インスタンスは何でもいいのでt2.nanoで2台起動しました。
ENIES LOBBY には世界政府の旗代わりにTerminateProtectionを設定しておきます。
スクリーンショット_2018-11-05_22_41_15.png

バスターコール発令

全てを焼き尽くします。
TerminateProtectionとて効きません。
無慈悲です。
スクリーンショット_2018-11-05_22_45_00.png
スクリーンショット_2018-11-05_22_45_18.png

まとめ

ボタンひとつでインスタンスが消し飛んでいくのは意外と爽快です。

本当はAMIやEIPとかも削除するようにしようかと思ってましたが、キリが無くなりそうなのでやめました。
検証環境とかで全てのインスタンスを燃やし尽くしたいときとか便利ですね(私は怖いので使いません。

SORACOM ButtonでLambdaを使わずにSlack連携してみた

どうも若松です。

みなさん、SORACOM Button活用してますか?
Slackへの連携も公式に出ているので、簡単に実装できますね。
https://dev.soracom.io/jp/start/aws_button_slack/

これを今から帰るよメッセージに使ってる方もいるようです。

しかしながら、以下のような方もいらっしゃるかと思います。
- そもそもLambdaって何者よ
- 例え公式から出ていようともコードなんて書きたくない

そこで、Lambdaを使わずに(意識せずに)Slack連携する方法を考えてみました。

メール送信設定

Slack連携方法としてメール送信を利用します。
メール送信設定方法は以下などに記載があるので、割愛します。
https://blog.soracom.jp/blog/2018/10/25/are-you-ready-for-lte-m-button/

取り急ぎ、帰るよメッセージ送信を想定して、以下のように設定しました。
スクリーンショット_2018-11-08_0_48_55.png

SlackのEmailインテグレーションを使う

SlackにはEmailインテグレーションが標準で備わっています。
Mailインテグレーションでメールアドレスを発行し、そこにメールを送るとSlackに通知する仕組みです。
なので、AWS IoTからメールを送る機能と組み合わせることで、Slackへ連携できます。
連携後の通知は以下のようになります。
スクリーンショット_2018-11-08_0_54_41.png
ただ、このEmailインテグレーションはSlackの有償プランじゃないと使えないので、ややハードルが高いです。
なので、無償でできそうな仕組みも考えてみます。

Microsoft Flow でメールから連携する

Office365を利用していると、Microsoft Flowというサービスを使うことができます。
https://japan.flow.microsoft.com/ja-jp/

簡単に言うと様々なサービスの間を取り持って連携させてくれるサービスです。
Microsoft Flow for Office 365はフリープランがあるので、無償で始めることができます。
https://japan.flow.microsoft.com/ja-jp/pricing/

今回はGmailをトリガーにしてSlackへ投稿します。
設定は以下の通りです。
スクリーンショット 2018-11-10 23.31.43.png
Gmailのトリガーは最初に使うときにアカウント連携させる必要があります。(詳細は割愛)
簡単のために件名のみで引っ掛けてますが、他の項目も設定してトリガーするメールを絞り込みことが可能です。

実際にMicrosoft Flowで連携して投稿すると以下のようになります。
スクリーンショット_2018-11-10_23_38_31.png

上記ではメールの本文をそのまま投稿していましたが、以下のようにメールとは関係ないメッセージに変えることも可能です。
スクリーンショット 2018-11-10 23.32.00.png

これを投稿すると以下のようになります。
スクリーンショット 2018-11-10 23.36.48.png

まとめ

そもそもメール送る時点で自動生成されるLambdaは使ってるので、タイトルの表現が合ってるかは微妙ですが、コードを見なくていいという点ではアレルギーの少ない構成かと思います。

Microsoft Flowの連携はメールが到着してから5分程度のタイムラグが出るので、即時性を求める場合は難しいですが、Slackの有償プランを使わなくてもいいのは嬉しいですね。

soracom-cliでButtonの情報を取得してみた

どうも若松です。

SORACOM API では、エンドポイントの /gadgets にリクエストを送ることで Button の情報を取得することができます。
APIの一覧は以下となります。
https://dev.soracom.io/jp/docs/api/#/Gadget

しかし、インフラエンジニアの私は生のエンドポイントを叩くことに慣れていません。
やはり馴染みのあるCLIで叩きたいわけです。

そこで何の気なしに soracom-cli の github を覗いたところ、gadgets サブコマンドがリリースされていたので、使ってみました。

soracom-cli

soracom-cli は SORACOM から公式で提供されているコマンドラインツールで、コマンドラインからAPIを叩くことができます。

インストール手順は github のREADMEに記載されています。
https://github.com/soracom/soracom-cli/blob/master/README_ja.md

soracom-cli の利用には(というかAPIを叩くには)、SAMの AuthKeyIdAuthKey が必要ですが、取得方法等は割愛します。
SAM については以下にまとまっています。
https://dev.soracom.io/jp/start/sam/

Button の情報を取得する

Button 単体の情報を取得するには、以下のコマンドを実行します。

soracom gadgets get --product-id button --serial-number XXXXXXXXXXXXXXXX

--product-id には button を指定します。
(おそらく Button 以外の gadgets がリリースされれば他の値と使い分けることになると思います)
--serial-number には製造番号(DSN)を指定します。

レスポンスは 以下のような Button 情報の JSON となります。

{
    "attributes": {
        "contractEndingTime": "yyyy-mm-ddThh:mm:ss.xxxZ",
        "firstClickTimestamp": "yyyy-mm-ddThh:mm:ss.xxxZ",
        "remainingCount": 1400
    },
    "createdTime": XXXXXXXXXXXXX,
    "id": "button/XXXXXXXXXXXXXXXX",
    "lastModifiedTime": XXXXXXXXXXXXX,
    "lastSeen": {
        "batteryLevel": 1,
        "clickEventPropagated": true,
        "clickTime": "yyyy-mm-ddThh:mm:ss.xxxZ",
        "clickType": "SINGLE"
    },
    "operatorId": "OPXXXXXXXXXX",
    "productId": "button",
    "serialNumber": "XXXXXXXXXXXXXXXX",
    "status": "active",
    "tags": {},
    "terminatedTime": null,
    "terminationEnabled": false
}

まとめ

soracom-cli は AWSCLI と使用感が非常に似ているので、AWSCLIユーザであれば慣れると思います。
スクリプトに組み込む際に、活用してみてはいかがでしょうか。

東京リージョンのマネージメントコンソールからInstanceConnectでSSHする際のIPレンジを調べるワンライナー

どうも、若松です。

先日ローンチされたInstanceConnect、すこぶる便利ですね。
純粋なSSHなので、SessionManagerのような環境変数が読み込まれない等がなくて快適です。
ただし、コマンド履歴のログは取れないのでトレードオフですね。

コンソールからSSHする際に躓くIPレンジの許可

InstanceConnectは純粋なSSHのため、SGでIPレンジを許可してあげる必要があります。
許可するべきIPレンジは以下にありますが、探すのが面倒です。
https://ip-ranges.amazonaws.com/ip-ranges.json

そこで以下のBashワンライナーを駆使することで、許可すべきIPレンジを一発で出すことができます。(curlとjqを実行できることが前提です。)

curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq '.prefixes[]' | jq 'select(.service == "EC2_INSTANCE_CONNECT" and .region == "ap-northeast-1").ip_prefix'

ワンライナーの解説

curl

curlコマンドを用いてWeb上のjsonをダウンロードしています。
このjsonはAWSが公開している、AWSが使用しているIPレンジの一覧です。
-s はダウンロード時間などの無駄な情報を出力しないようにするオプションです。

1個目のjq

1個目のjqでprefixesの中身を抜き出しています。
これを入れている理由は、次に行うselectで、配列を渡してしまうとjsonのトップレベルが帰ってしまうからです。

2個目のjq

ここで絞り込みを行っています。
絞り込み条件は以下です。

項目
service EC2_INSTANCE_CONNECT
region ap-northeast-1

つまり、東京リージョンのInstanceConnectで使用されるIPレンジに絞っています。
さらに出力をip_prefixのみ指定することでIPレンジのみが出力されます。

2019/6/30時点では以下が出力されるはずです。

"3.112.23.0/29"

まとめ

最初は公開されているIPレンジを全て許可しなくてはいけないのかと絶望しましたが、絞ってみると案外少なくてホッとしました。
ただ、このIPレンジは追加/変更される可能性があるので、本気でマネージメントコンソールからのSSHを運用に組み込みたい場合は、定期的にjsonの変更を監視し、SGの変更を自動化しておく必要があるかと思います。

AmazonLinux2でLaravelを起動する

どうも、若松です。

ひょんなことからLaravelを触ることになったので、AmazonLinux2で一から動かしてみます。

コマンド一覧

# PHPインストール
amazon-linux-extras -y install php7.3
yum install -y php-pecl-zip php-mbstring php-dom

# Composerインストール
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
mv composer.phar /usr/local/bin/composer

# 環境変数設定
export COMPOSER_ALLOW_SUPERUSER=1
export COMPOSER_HOME="/composer"
export PATH="$PATH:/composer/vendor/bin"

# Laravelインストール
composer global require "laravel/installer"

# Laravelプロジェクト作成
laravel new

# Laravelサーバー起動
php artisan serve --host 0.0.0.0

コマンド詳細

PHPインストール

amazon-linux-extras -y install php7.3
yum install -y php-pecl-zip php-mbstring php-dom

PHP7.3はextrasリポジトリに格納されています。
AmazonLinux2では通常のyumリポジトリの他にextrasリポジトリを利用することができます。
extrasリポジトリは専用の amazon-linux-extras コマンドで利用できます。

Composerインストール

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
mv composer.phar /usr/local/bin/composer

Composerのインストールは公式のコマンドをそのまま使用します。
https://getcomposer.org/download/

環境変数設定

export COMPOSER_ALLOW_SUPERUSER=1
export COMPOSER_HOME="/composer"
export PATH="$PATH:/composer/vendor/bin"

意味は以下のとおり

変数名 意味
COMPOSER_ALLOW_SUPERUSER 1 rootでのインストールを許可
COMPOSER_HOME /composer Composerのインストール先
PATH $PATH:/composer/vendor/bin vender配下のbinにPATHを通す

Laravelインストール

composer global require "laravel/installer"

ComposerでLaravelをインストールします。
vender配下にlaravelコマンドが配置されます。

Laravelプロジェクト作成

laravel new

Laravelのプロジェクトを作成します。
プロジェクトを作成すると以下のようなファイル群が配置されます。

# ll
total 396
drwxr-xr-x  6 root root     84 Jul  6 05:12 app
-rw-r--r--  1 root root   1686 Jul  6 05:17 artisan
drwxr-xr-x  3 root root     34 Jul  6 05:12 bootstrap
-rw-r--r--  1 root root   1550 Jul  6 05:17 composer.json
-rw-r--r--  1 root root 167312 Jul  6 05:17 composer.lock
drwxr-xr-x  2 root root    247 Jul  6 05:12 config
drwxr-xr-x  5 root root     72 Jul  6 05:12 database
-rw-r--r--  1 root root   1125 Jul  6 05:17 package.json
-rw-r--r--  1 root root   1156 Jul  6 05:17 phpunit.xml
drwxr-xr-x  4 root root     98 Jul  6 05:12 public
drwxr-xr-x  6 root root     53 Jul  6 05:12 resources
drwxr-xr-x  2 root root     75 Jul  6 05:12 routes
-rw-r--r--  1 root root    563 Jul  6 05:17 server.php
drwxr-xr-x  5 root root     46 Jul  6 05:12 storage
drwxr-xr-x  4 root root     83 Jul  6 05:12 tests
drwxr-xr-x 40 root root   4096 Jul  6 05:18 vendor
-rw-r--r--  1 root root    538 Jul  6 05:17 webpack.mix.js
-rw-r--r--  1 root root 207745 Jul  6 05:17 yarn.lock

Laravelサーバー起動

php artisan serve --host 0.0.0.0

artisanサブコマンドを用いてサーバーを起動します。
hostオプションでListenアドレスを指定しない場合は localhost でListenされます。

ブラウザでサンプルを表示

http://[IPアドレス]:8000 にアクセスすることで以下のサンプルを表示します。
スクリーンショット 2019-07-06 22.57.48.png

まとめ

AmazonLinux2でLaravelのサンプルを表示するまでをまとめました。
次はDockerで同様のサンプル表示までまとめていきたいと思います。

DockerでLaravelを起動する

どうも。若松です。

前回の記事でLaravelの起動までをまとめました。
https://qiita.com/t_wkm2/items/29b150e52a7c08f0e976

今回DockerでLaravelの起動までをまとめます。

Dockerfile

FROM amazonlinux:2

# PHPインストール
RUN amazon-linux-extras install -y php7.3
RUN yum install -y php-pecl-zip php-mbstring php-dom

# Composerインストール
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

# 環境変数設定
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME "/opt/composer"
ENV PATH "$PATH:/opt/composer/vendor/bin"

# Laravelインストール
RUN composer global require "laravel/installer"

# Laravelプロジェクト作成
WORKDIR /var/www
RUN composer create-project laravel/laravel laravel

# ポートを公開
EXPOSE 8000

WORKDIR /var/www/laravel
CMD ["php","artisan","serve","--host","0.0.0.0"]

Dockerfile詳細

イメージ

FROM amazonlinux:2

前回AmazonLinux2を使用していたこともあり、今回のDockerでもAmazonLinux2をベースにDockerイメージを構築します。

PHPインストール

RUN amazon-linux-extras install -y php7.3
RUN yum install -y php-pecl-zip php-mbstring php-dom

前回同様、extrasリポジトリからインストールします。

Composerインストール

RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

こちらも前回同様に、Composerのインストールは公式のコマンドをそのまま使用します。
https://getcomposer.org/download/

環境変数設定

ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME "/opt/composer"
ENV PATH "$PATH:/opt/composer/vendor/bin"

環境変数は RUN ではなく ENV を使用します。
RUN export で環境変数を定義するとそのレイヤーのコンテナ内部のみに適用されてしまうため、他のレイヤーで環境変数が効かなくなってしまうためです。

Laravelインストール

RUN composer global require "laravel/installer"

こちらも前回同様、Composerを使用してLaravelをインストールします。

Laravelプロジェクト作成

WORKDIR /var/www
RUN composer create-project laravel/laravel laravel

前回は laravel new を使用してLaravelプロジェクトを作成しましたが、 Laravel new では対話型のターミナルが前提になっているようで、Dockerfileからビルドするとエラーになってしまいます。
そのためComposerを用いてLaravelプロジェクトを作成します。
※なぜComposerを用いるのか、なぜComposerでも作成できるかは深く追求していません。

ポートを公開

EXPOSE 8000

LaravelではデフォルトでTCP8000をListenするため、8000番を公開します。

Laravelサーバー起動

WORKDIR /var/www/laravel
CMD ["php","artisan","serve","--host","0.0.0.0"]

CMDで記述することで、コンテナ起動時にこのコマンドが実行され、サーバーが起動します。

コンテナビルド

docker build -t laravel .

Dockerfileに従ってコンテナをビルドします。
Dockerfileはカレントディレクトリに配置されている想定です。
成功すると laravel:latest という名前のコンテナイメージが作成されます。

コンテナ実行

docker run -p 8000:8000 laravel

作成したコンテナイメージを実行します。
-p 8000:8000 オプションでローカルの8000番とコンテナの8000番を繋ぎ、ローカル8000番でコンテナへアクセスできるようにします。

ブラウザでサンプルを表示

http://localhost:8000 にアクセスすることで以下のサンプルを表示します。
スクリーンショット 2019-07-06 22.57.48.png

まとめ

DockerでLaravelのサンプルを表示するまでをまとめました。
次回はマルチステージビルドでコンテナサイズのスリム化について書きたいと思います。

作成したLaravelコンテナを軽量化する

どうも、若松です。

前回はDockerでLaravelを起動するまでをまとめました。
https://qiita.com/t_wkm2/items/9b2011af9569627fee40

しかしながら、現在のコンテナイメージはお世辞にも軽量とは言えません。
コンテナイメージサイズはコンテナ起動時間に直結するため、できるだけ軽量化していきたいと思います。

Dockerfile

FROM amazonlinux:2 as vender

# PHPインストール
RUN amazon-linux-extras install -y php7.3
RUN yum install -y php-pecl-zip php-mbstring php-dom

# Composerインストール
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

# 環境変数設定
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME "/opt/composer"
ENV PATH "$PATH:/opt/composer/vendor/bin"

# Laravelインストール
RUN composer global require "laravel/installer"

# Laravelプロジェクト作成
WORKDIR /var/www
RUN composer create-project laravel/laravel laravel

FROM php:7.3-alpine

# ビルド用コンテナから必要なコンテンツをコピー
COPY --from=vender /var/www/ /var/www/

# ポートを公開
EXPOSE 8000

# Laravelサーバーを実行
WORKDIR /var/www/laravel
CMD ["php","artisan","serve","--host","0.0.0.0"]

軽量化に際して覚えておくこと

マルチステージビルド

マルチステージビルドは、一般的にビルドに必要なコンテンツの生成フェーズと実行に必要なコンテンツに絞ってコンテナを固めるフェーズに分けてコンテナをビルドすることを指します。
ビルド時に必要だが実行時にには必要ない(一般的にDeveloperKitのような)ものを実行するコンテナから除外できるため、コンテナイメージの軽量化が期待できます。

squashオプション

Dockerfileでビルドする際、Step毎にコンテナのレイヤーが生成され、最終的なコンテナはそのレイヤーを含むためにサイズが肥大化しがちです。
ビルド時にsquashオプションを用いると、最終的にレイヤー1つに集約してくれるため、コンテナイメージの軽量化が期待できます。

Alpine Linux

詳細は割愛しますが、軽量OSとしてコンテナ界隈では有名です。
https://ja.wikipedia.org/wiki/Alpine_Linux

Alpine Linuxをベースとすることで、その他のイメージをベースとするよりも、コンテナイメージの軽量化が期待できます。

Dockerfile詳細

ビルド用イメージ

FROM amazonlinux:2 as vender

前回同様、AmazonLinux2をベースとしていますが、後段でしようするためにエイリアスとして as vender を付けています。

省略

# PHPインストール
# Composerインストール
# 環境変数設定
# Laravelインストール
# Laravelプロジェクト作成
# ポートを公開
# Laravelサーバーを実行

上記は前回と同じなため、解説を省略します。

実行用イメージ

FROM php:7.3-alpine

PHP公式から提供されているAlpineLinuxのイメージを使用します。
これによってAlpineLinux且つPHP7.3の環境を構築できます。

ビルド用コンテナから必要なコンテンツをコピー

COPY --from=vender /opt/composer/vendor/ /opt/vender/
COPY --from=vender /var/www/ /var/www/

前段のビルド用コンテナから必要なコンテンツをコピーします。
ここで前段で用いた、 as vender 効力を発揮します。

コンテナビルド

docker build -t laravel . --squash

ビルド時に --squash オプションを付加します。
これによってレイヤーを1つに集約し、計量化を図ります。

軽量化前と軽量化後の比較

軽量化前

docker images laravel
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
laravel         latest              xxxxxxxxxxxx         xx seconds ago        748MB

軽量化後

docker images laravel
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
laravel         latest              xxxxxxxxxxxx         xx seconds ago        117MB

まとめ

少しの工夫でコンテナが軽量化できることがわかっていただけたと思います。
最初にも述べたように、コンテナイメージサイズはコンテナ起動時間に直結するため、積極的に軽量化を図っていきたいですね。

コンテナにログインして内部を調査できるワンライナー

どうも、若松です。

コンテナイメージを作成するときはDocekerfileを作成すると思いますが、その前にベースイメージ内部を確認したいことが多いと思います。

そこで、ベースイメージをpullした状態からコンテナへログインするワンライナーをご紹介します。

AmazonLinux2コンテナへログインするワンライナー

docker run -it amazonlinux:2 /bin/bash

AlpineLinuxコンテナへログインするワンライナー

docker run -it alpine /bin/sh

まとめ

ベースイメージにログインできるとDockerfileを作成するための材料が揃えやすいので、このワンライナーは有用かと思います。

dockerコマンドの補完を設定する

どうも、若松です。

dockerコマンドを使っていると、わかってるけど単語のスペルが出てこないなどで、サブコマンドやオプションをhelpで調べたり、ググる場面が多いかと思います。
そんなときにdockerにはコマンドを補完してくれる機能あるので、紹介したいと思います。

補完設定

実は公式にドキュメントも用意されています。
http://docs.docker.jp/compose/completion.html

私の環境がMac且つbashのため、以下はそれに合わせたコマンドとなります。

brew install bash-completion
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose > /usr/local/etc/bash_completion.d/docker-compose

補完実行

補完を試すのは簡単です。

サブコマンド

例えば、dockerのサブコマンドがわからなくなった場合、 docker と打ってtabボタンを押すだけで以下のような結果が得られます。

$ docker
attach      deploy      images      logs        push        secret      tag
build       diff        import      network     rename      service     top
checkpoint  events      info        node        restart     stack       trust
commit      exec        inspect     pause       rm          start       unpause
config      export      kill        plugin      rmi         stats       update
container   help        load        port        run         stop        version
cp          history     login       ps          save        swarm       volume
create      image       logout      pull        search      system      wait

オプション

例えば、 docker images のオプションがわからなくなった場合、 docker images -- と打ってtabボタンを押すだけで以下のような結果が得られます。

$ docker images --
--all       --digests   --filter    --format    --help      --no-trunc  --quiet

まとめ

dockerコマンドはサブコマンドやオプションが充実している分、覚えるのは難儀です。
ですので、補完をうまく使って、快適なdockerライフを送りましょう。

Nginxとphp-fpmを用いてLaravalを表示する

どうも、若松です。

前回はLaravelをDockerで起動し、イメージを軽量化するところまで行いました。
https://qiita.com/t_wkm2/items/245288e42083ac7e4057

今回は、 php artisan でのサーバ起動ではなく、Nginx+php-fpmでLaravelを表示するところまでを行います。

設定

ディレクトリ構造

docker/
├─ docker-compose.yml
├─ nginx/
|  ├─ Dockerfile
|  └─ default.conf
└─ laravel/
   └─  Dockerfile

docker-compose.yml

version: '2'
services:
  nginx:
    image: nginx
    ports:
      - "80:80"
  laravel:
    image: laravel

Dockerfile(nginx)

FROM nginx:1.17-alpine

# ローカルから設定ファイルをコピー
COPY  default.conf /etc/nginx/conf.d/default.conf

default.conf(nginx)

server {
    listen       80;
    server_name  localhost;

    location / {
        # ドキュメントルート設定
        root   /var/www/laravel/public;

        fastcgi_split_path_info  ^(.+\.(?:php|phar))(/.*)$;   
        fastcgi_intercept_errors on;

        fastcgi_index  index.php;
        include        fastcgi_params;
        # FastCGIの向き先をLaravelコンテナに設定
        fastcgi_pass   laravel:9000;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO $fastcgi_path_info;
        fastcgi_param  PATH_TRANSLATED $document_root$fastcgi_path_info;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Dockerfile(laravel)

FROM amazonlinux:2 as vender

# PHPインストール
RUN amazon-linux-extras install -y php7.3
RUN yum install -y php-pecl-zip php-mbstring php-dom

# Composerインストール
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

# 環境変数設定
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME "/opt/composer"
ENV PATH "$PATH:/opt/composer/vendor/bin"

# Laravelインストール
RUN composer global require "laravel/installer"

# Laravelプロジェクト作成
WORKDIR /var/www
RUN composer create-project laravel/laravel laravel

FROM php:7.3-fpm-alpine

# ビルド用コンテナから必要なコンテンツをコピー
COPY --from=vender --chown=www-data:www-data /var/www/ /var/www/

操作

コマンドは全て最上位ディレクトリの docker から行う想定です。

Nginxコンテナビルド

docker build nginx/. -t nginx --squash

Laravelコンテナビルド

docker build laravel/. -t laravel --squash

docker-composeで起動

docker-compose up

ブラウザで表示を確認

http://localhost:8000 にアクセスすることで以下のサンプルを表示します。
スクリーンショット 2019-07-06 22.57.48.png

解説

Nginxコンテナ

ベースイメージ

FROM nginx:1.17-alpine

ベースイメージにはNginx公式リポジトリにあるnginx:1.17-alpineを使用しました。
2019/7/14現在のNginxの最新が1.17であり、軽量化を目的にAlpineLinux版を使いたかったためです。
80番ポートの開放やNginxの起動についてはベースイメージ内で既に設定されているため、今回のDokcerfileには記述していません。

default.conf

COPY  default.conf /etc/nginx/conf.d/default.conf

設定はローカルに用意したdefault.confをイメージにコピーして配置します。
FastCGI設定のほとんどは一般的な設定のため、特徴的なものだけ解説します。

root

root   /var/www/laravel/public;

ドキュメントルートはLaravelコンテナのアプリケーションが配置されているディレクトリを指定します。

fastcgi_pass

fastcgi_pass   laravel:9000;

UnixソケットかTCPを指定できますが、Unixソケットではコンテナを越えられないため、TCPで設定します。
アドレスの指定にはDockerのNamespaceを利用します。

Laravelコンテナ

前回のDockerfileからの差異のみ解説します。

ベースイメージ

FROM php:7.3-fpm-alpine

実行用イメージのベースを php:7.3-alpine から php:7.3-fpm-alpine に変更しました。
これによってデフォルトでphp-fpmがインストールされた状態から設定を行えばよくなります。
9000番ポートの開放やphp-fpmの起動についてはベースイメージ内で既に設定されているため、今回のDokcerfileには記述していません。

Laravelコンテンツのオーナー変更

COPY --from=vender --chown=www-data:www-data /var/www/ /var/www/

php:7.3-fpm-alpineのphp-fpm初期設定では、php-fpmのワーカ起動ユーザはwww-dataになっています。
COPYコマンドで配置したLaravelコンテンツはrootがオーナーになってしまうため、そのままだと権限エラーとなります。
そこで、--chownオプションを使用し、オーナーをwww-dataへ変更しています。
--chownオプションはBashでいうところのchown -Rとなるため、ディレクトリがあっても再帰的に処理してくれます。

docker-compose

今回からコンテナが2つになったため、操作簡略のためにdocker-composeを導入しました。
docker-compose には起動時のオプション設定や、複数コンテナのビルド、依存関係制御など様々な機能がありますが、ここではコンテナ起動/停止とポートオプションのみ使用しています。

複数コンテナのビルドを使用しない理由

本当であれば使用したかったのが本音です。
しかしながら2019/7/14現在、BuildKitやsquashオプションに対応していないため、あえてdockerコマンドでビルドを行っています。

Tips

コンテナイメージ内にあるファイルをローカルにコピーする

設定ファイルを作成する際に、デフォルトの設定をローカルにコピーし、それを改変して作成していくことはよくあると思います。
コンテナではSCPが使えないため、代わりにdocker cpコマンドを使用します。
今回のdefault.confの場合は、以下のようにしてコピーしました。

docker run -d --name nginx nginx:1.17-alpine
docker cp $(docker ps --filter name=nginx -aq):/etc/nginx/conf.d/default.conf .

コンテナイメージの履歴を確認する

FROMで使用するベースイメージには予めポートの開放やデーモンの起動が設定されている場合があります。
今回でいうところのNginxやphp-fpmですね。
それを確認するにはdocker historyコマンドを使用します。
例としてphp-fpmのhistoryを確認してみます。

docker history --format {{.CreatedBy}} php:7.3-fpm-alpine
/bin/sh -c #(nop)  CMD ["php-fpm"]
/bin/sh -c #(nop)  EXPOSE 9000
/bin/sh -c #(nop)  STOPSIGNAL SIGQUIT
/bin/sh -c set -eux;  cd /usr/local/etc;  if…
/bin/sh -c #(nop) WORKDIR /var/www/html
/bin/sh -c #(nop)  ENTRYPOINT ["docker-php-e…
/bin/sh -c docker-php-ext-enable sodium
...

このようにズラズラとコマンドが表示されるかと思います。
これは実行日次のtimestampが新しい順で上から並んでいます。
これを見るとベースイメージの最後に、9000番ポートの開放とphp-fpmの実行が行われているため、今回のDockerfileではポートの開放とデーモンの起動が不要なことがわかります。

まとめ

Nginx+php-fpmに加えて、docker-composeも導入してみました。
そんなに特殊な設定を行ったわけではありませんが、Dockerfileの書き方やコンテナ特有の設定等はお伝えできたかと思います。

コンテナイメージのセキュリティチェック&脆弱性診断を簡易的に実施する

どうも、若松です。

最近、コンテナイメージをビルドしまくっており、オレオレなコンテナイメージが増殖しています。
そんなおり、以下の記事が目に止まりました。

DockerHubで公開されているコンテナが安全か確かめてみた結果【人気のコンテナ上位800個】

そりゃあ脆弱性あるわなぁと思いつつ、じゃあオレのコンテナイメージは大丈夫か?と思いました。
というわけで上記の記事で使用されていたdockleTrivyを使って、オレオレコンテナイメージをチェックしていきたいと思います。

使用ツール

dockle

CISベンチマークやDockerベストプラクティスに準拠したコンテナイメージかをチェックできます。
WARNやFATALの項目には、どのように改善すればよいかのヒントが添えられていて小憎いです

それぞれの基準は以下の通り
CISベンチマーク
Dockerベストプラクティス

Trivy

OSやアプリケーションに潜む脆弱性をチェックできます。
CVE番号と共に脆弱性の詳細を表示してくれるので、どのように改善するかの計画が立てやすいです。

対象OSとアプリケーションは以下の通り
https://github.com/knqyf263/trivy#vulnerability-detection

インストール

今回はMacのローカルにコンテナイメージがあるため、brewでインストールしました。
手順はそれぞれのREADMEに記載があるため、省略します。

実行結果

前回の記事で作成したLaravelコンテナを対象にしました。
https://qiita.com/t_wkm2/items/910b4d7079b8413895a6

dockle

$ dockle laravel:latest
WARN    - CIS-DI-0001: Create a user for the container
    * Last user should not be root
PASS    - CIS-DI-0005: Enable Content trust for Docker
WARN    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
    * not found HEALTHCHECK statement
PASS    - CIS-DI-0007: Do not use update instructions alone in the Dockerfile
PASS    - CIS-DI-0008: Remove setuid and setgid permissions in the images
PASS    - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
PASS    - CIS-DI-0010: Do not store secrets in ENVIRONMENT variables
PASS    - CIS-DI-0010: Do not store secret files
PASS    - DKL-DI-0001: Avoid sudo command
PASS    - DKL-DI-0002: Avoid sensitive directory mounting
PASS    - DKL-DI-0003: Avoid apt-get/apk/dist-upgrade
PASS    - DKL-DI-0004: Use apk add with --no-cache
PASS    - DKL-DI-0005: Clear apt-get caches
WARN    - DKL-DI-0006: Avoid latest tag
    * Avoid 'latest' tag
PASS    - DKL-LI-0001: Avoid empty password
PASS    - DKL-LI-0002: Be unique UID
PASS    - DKL-LI-0002: Be unique GROUP

大きな問題はありませんでしたが、WARNが3件出て、改善に向けたアドバイスが表示されていることがわかります。

Trivy

$ trivy laravel:latest
2019-07-16T00:26:44.376+0900    INFO    Updating vulnerability database...
2019-07-16T00:26:46.203+0900    WARN    You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed
2019-07-16T00:26:48.159+0900    INFO    Detecting Alpine vulnerabilities...
2019-07-16T00:26:48.171+0900    INFO    Updating composer Security DB...
2019-07-16T00:26:51.928+0900    INFO    Detecting composer vulnerabilities...
2019-07-16T00:26:51.928+0900    INFO    Updating composer Security DB...
2019-07-16T00:26:53.211+0900    INFO    Detecting composer vulnerabilities...
2019-07-16T00:26:53.214+0900    INFO    Updating composer Security DB...
2019-07-16T00:26:54.422+0900    INFO    Detecting composer vulnerabilities...
2019-07-16T00:26:54.422+0900    INFO    Updating composer Security DB...
2019-07-16T00:26:56.052+0900    INFO    Detecting composer vulnerabilities...

var/www/laravel/vendor/psy/psysh/vendor-bin/box/composer.lock
=============================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


var/www/laravel/vendor/hamcrest/hamcrest-php/composer.lock
==========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


laravel:latest (alpine 3.10.1)
==============================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


var/www/laravel/vendor/phar-io/manifest/composer.lock
=====================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


var/www/laravel/composer.lock
=============================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

脆弱性は検出されませんでしたが、AlpineLinuxに加えてComposerも対象に診断を行ってくれることがわかります。

引っかかる場合の表示

上記でチェックしたコンテナイメージは最新のベースイメージであったため、引っかかる項目が少ない結果となりました。
しかしながら、これではチェックに引っかかった場合の表示がわからないため、ずいぶん前(docker imagesで見ると9ヶ月前)にpullして手元にあったhttpd:2.4-alpineをチェックしてみます。

dockle

$ dockle httpd:2.4-alpine
WARN    - CIS-DI-0001: Create a user for the container
    * Last user should not be root
PASS    - CIS-DI-0005: Enable Content trust for Docker
WARN    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
    * not found HEALTHCHECK statement
PASS    - CIS-DI-0007: Do not use update instructions alone in the Dockerfile
PASS    - CIS-DI-0008: Remove setuid and setgid permissions in the images
PASS    - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
PASS    - CIS-DI-0010: Do not store secrets in ENVIRONMENT variables
PASS    - CIS-DI-0010: Do not store secret files
PASS    - DKL-DI-0001: Avoid sudo command
PASS    - DKL-DI-0002: Avoid sensitive directory mounting
PASS    - DKL-DI-0003: Avoid apt-get/apk/dist-upgrade
FATAL   - DKL-DI-0004: Use apk add with --no-cache
    * Use --no-cache option if use 'apk add': /bin/sh -c set -eux;  runDeps='       apr-dev         apr-util-dev        apr-util-ldap       perl    ';  apk add --no-cache --virtual .build-deps        $runDeps        ca-certificates     coreutils       dpkg-dev dpkg       gcc         gnupg   libc-dev        libressl        libressl-dev        libxml2-dev         lua-dev         make        nghttp2-dev         pcre-dev        tar         zlib-dev    ;       ddist() {       local f="$1"; shift;        local distFile="$1"; shift;         local success=;         local distUrl=;         for distUrl in $APACHE_DIST_URLS; do        if wget -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then        success=1;              break;          fi;     done;       [ -n "$success" ];  };      ddist 'httpd.tar.bz2' "httpd/httpd-$HTTPD_VERSION.tar.bz2";     echo "$HTTPD_SHA256 *httpd.tar.bz2" | sha256sum -c -;       ddist 'httpd.tar.bz2.asc' "httpd/httpd-$HTTPD_VERSION.tar.bz2.asc";     export GNUPGHOME="$(mktemp -d)"; for key in         A93D62ECC3C8EA12DB220EC934EA76E6791485A8    B9E8213AEFB861AF35A41F2C995E35221AD84DFF    ; do        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key";  done;   gpg --batch --verify httpd.tar.bz2.asc httpd.tar.bz2;   command -v gpgconf && gpgconf --kill all || :;  rm -rf "$GNUPGHOME" httpd.tar.bz2.asc;      mkdir -p src;   tar -xf httpd.tar.bz2 -C src --strip-components=1;  rm httpd.tar.bz2;   cd src;         patches() {         while [ "$#" -gt 0 ]; do            local patchFile="$1"; shift;            local patchSha256="$1"; shift;          ddist "$patchFile" "httpd/patches/apply_to_$HTTPD_VERSION/$patchFile";  echo "$patchSha256 *$patchFile" | sha256sum -c -;           patch -p0 < "$patchFile";           rm -f "$patchFile";     done;   };  patches $HTTPD_PATCHES;         gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)";  ./configure         --build="$gnuArch"      --prefix="$HTTPD_PREFIX"        --enable-mods-shared=reallyall      --enable-mpms-shared=all    ;   make -j "$(nproc)";     make install;       cd ..;  rm -r src man manual;       sed -ri         -e 's!^(\s*CustomLog)\s+\S+!\1 /proc/self/fd/1!g'       -e 's!^(\s*ErrorLog)\s+\S+!\1 /proc/self/fd/2!g'        "$HTTPD_PREFIX/conf/httpd.conf";    runDeps="$runDeps $(        scanelf --needed --nobanner --format '%n#p' --recursive /usr/local          | tr ',' '\n'       | sort -u           | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }'     )";     apk add --virtual .httpd-rundeps $runDeps;  apk del .build-deps
PASS    - DKL-DI-0005: Clear apt-get caches
PASS    - DKL-DI-0006: Avoid latest tag
FATAL   - DKL-LI-0001: Avoid empty password
    * No password user found! username : root
PASS    - DKL-LI-0002: Be unique UID
PASS    - DKL-LI-0002: Be unique GROUP

FATALが出ているのが確認できます。
最新のAlpineLinuxでは出ないものなので、定期的にチェックは必要だなと思います。

Trivy

$ trivy httpd:2.4-alpine
2019-07-16T00:48:09.514+0900    INFO    Updating vulnerability database...
2019-07-16T00:48:13.096+0900    INFO    Detecting Alpine vulnerabilities...

httpd:2.4-alpine (alpine 3.7.1)
===============================
Total: 13 (UNKNOWN: 0, LOW: 2, MEDIUM: 5, HIGH: 5, CRITICAL: 1)

+------------+------------------+----------+-------------------+---------------+--------------------------------+
|  LIBRARY   | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |             TITLE              |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| bzip2      | CVE-2019-12900   | HIGH     | 1.0.6-r6          | 1.0.6-r7      | bzip2: out-of-bounds write in  |
|            |                  |          |                   |               | function BZ2_decompress        |
+------------+------------------+          +-------------------+---------------+--------------------------------+
| expat      | CVE-2018-20843   |          | 2.2.5-r0          | 2.2.7-r0      | expat: large number of colons  |
|            |                  |          |                   |               | in input makes parser consume  |
|            |                  |          |                   |               | high amount...                 |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| libxml2    | CVE-2018-14404   | MEDIUM   | 2.9.7-r0          | 2.9.8-r1      | libxml2: NULL pointer          |
|            |                  |          |                   |               | dereference in                 |
|            |                  |          |                   |               | xpath.c:xmlXPathCompOpEval()   |
|            |                  |          |                   |               | can allow attackers to cause   |
|            |                  |          |                   |               | a...                           |
+            +------------------+          +                   +               +--------------------------------+
|            | CVE-2018-14567   |          |                   |               | libxml2: Infinite loop when    |
|            |                  |          |                   |               | --with-lzma is used allows for |
|            |                  |          |                   |               | denial of service...           |
+            +------------------+----------+                   +               +--------------------------------+
|            | CVE-2018-9251    | LOW      |                   |               | libxml2: infinite loop in      |
|            |                  |          |                   |               | xz_decomp function in xzlib.c  |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| perl       | CVE-2018-18311   | HIGH     | 5.26.2-r1         | 5.26.3-r0     | perl: Integer overflow         |
|            |                  |          |                   |               | leading to buffer overflow in  |
|            |                  |          |                   |               | Perl_my_setenv()               |
+            +------------------+          +                   +               +--------------------------------+
|            | CVE-2018-18314   |          |                   |               | perl: Heap-based buffer        |
|            |                  |          |                   |               | overflow in S_regatom()        |
+            +------------------+          +                   +               +--------------------------------+
|            | CVE-2018-18312   |          |                   |               | perl: Heap-based               |
|            |                  |          |                   |               | buffer overflow in             |
|            |                  |          |                   |               | S_handle_regex_sets()          |
+            +------------------+----------+                   +               +--------------------------------+
|            | CVE-2018-18313   | MEDIUM   |                   |               | perl: Heap-based buffer read   |
|            |                  |          |                   |               | overflow in S_grok_bslash_N()  |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| postgresql | CVE-2019-10164   | CRITICAL | 10.5-r0           | 10.9-r0       | PostgreSQL: stack-based        |
|            |                  |          |                   |               | buffer overflow via setting a  |
|            |                  |          |                   |               | password                       |
+            +------------------+----------+                   +---------------+--------------------------------+
|            | CVE-2019-10129   | MEDIUM   |                   | 10.8-r0       | postgresql: Memory disclosure  |
|            |                  |          |                   |               | in partition routing           |
+            +------------------+----------+                   +               +--------------------------------+
|            | CVE-2019-10130   | LOW      |                   |               | postgresql: Selectivity        |
|            |                  |          |                   |               | estimators bypass row security |
|            |                  |          |                   |               | policies                       |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| sqlite     | CVE-2018-20346   | MEDIUM   | 3.21.0-r1         | 3.25.3-r0     | CVE-2018-20505 CVE-2018-20506  |
|            |                  |          |                   |               | sqlite: Multiple flaws in      |
|            |                  |          |                   |               | sqlite which can be triggered  |
|            |                  |          |                   |               | via...                         |
+------------+------------------+----------+-------------------+---------------+--------------------------------+

ライブラリ毎に表示されるので見やすいですね。
CRITICALが出ているので、ここは最低でも塞いで起きたいところです。

まとめ

簡単にイメージスキャンができることがわかっていただけたと思います。
今回は素のままのコマンド実行でしたが、表示形式の変更やファイルへの出力などのオプションもあるので、うまく使って様々な箇所に応用していきましょう。

Browsing Latest Articles All 16 Live