Snapy−Debugの利用方法

この記事ではSnappy-debugを利用して、必要なインターフェースを探す方法を紹介したいと思います。

#SNAPパッケージとUbuntu Core
###Snapパッケージ
アプリケーション・ソフトウェアとその依存パッケージをバンドルしたパッケージになります。SNAPパッケージは[SnapStore](https://snapcraft.io/store)を通して公開されており、様々な既存アプリケーションのスナップパッケージが登録されています。

###Snapパッケージのメリット
SnapパッケージはSnapストアからインストールされ、トランザクショナルな自動アップデート(OTA)や、ロールバック機能を提供します。ですので、Debパッケージと違いアップデートに失敗した場合でもSnapパッケージが完全に使用不可能になるリスクを抑えることが出来ます。またSnapパッケージはインターフェースという機能を利用してCgroup、SecCompを利用したコンファインメント(必要のないリソースへのアクセスをブロック)を提供します。

###Ubuntu Core
Ubuntu CoreはIoT向けのUbuntuでセキュリティーにフォーカスをおいたUbuntuになります。高い安全性を担保するために、Debパッケージを直接使用することはできず、すべてがSnapパッケージで構成されます。(Aptを利用してDebパッケージをインストール出来ない)そのため、Debパッケージを利用したい場合は、DebパッケージをSnapパッケージ化して利用する必要があります。

#環境
Ubuntu 20.04がインストールされたラップトップでテストをしています。デフォルトでインストールされていない場合は、[リファレンス](https://snapcraft.io/docs/installing-snapd)にSNAPをサポートするDistroと各Distroでのスナップの使い方が書いてあります。


#SNAPパッケージ作成の流れ
ここでは簡単にSnapパッケージを作成する一連の流れをご説明します。
直接DebパッケージをSnap化していきたいとおもいます。まずDebパッケージをSnap化する場合には大まかに以下の流れをたどる形になります。
* snapcraft.yamlの作成(Snapパッケージ化するDebパッケージを指定)
* DevモードでSnapパッケージを作成
* 動作確認
* Strictモードに変更し再度パッケージを作成
* Interfaceの追加
* 動作確認

###DevモードとStrictモードの違い
SnapパッケージにはDevモードとStrictモードの2つのモードがあります。Devモードは通常のDebパッケージと同様にシステムのすべてのリソースへのアクセスを許可するモードになります｡(アクセスバイオレーションはログされますが、アクセス自体は許可されます)｡一方Strictモードはインターフェースで許可されているリソースにのみアクセスが許可されます。高い安全性を担保するために、UbuntuCoreではStrictモードのSnapのみの利用が許可されます(一部開発中にDevモードを許可することも出来ますが、製品出荷時にはStrictモードのみを許可するというやり方になります)


#Snapcraftを使用してSNAPパッケージを作成する
ここではSnappy-debugのデモ用にiptableDebパッケージをスナップ化してみます。Snapパッケージの作成方法はご存じで、どうやって必要なインターフェースを調べるかを知りたい方はSnappy-Debugの使い方までスキップしてください。

SNAPパッケージの作成にはsnapcraftというSNAPを使用しするため、まずはsnapcraft
SNAPを以下のコマンドでインストールします。またスナップパッケージを作成する際に
デフォルトでMultipass（VM）を使用するのでこちらもインストールします。

```snapcraftをインストール
snap　install snapcraft　--classic
sudo snap install multipass
```
####snapcraft.yamlを作成する
snapcraftはsnapcrafy.yamlに記載された情報をもとにパッケージを作成するので、
snapcrafy.yamlを作成、編集していきます。

```snapcraft.yamlの作成
snapcraft init

Created snap/snapcraft.yaml.
Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more information about the snapcraft.yaml format.
```
snapcraft initを実行したパスの直下にsnapというフォルダにsnapcraft.yamlが作成されます。

```フォルダ構成
tree -d
.
├── DebパッケージSnapデモ
　  ├── snap
　  └── src
```



####snapcraft.yamlの編集
作成されたYAMLファイルを編集します。

```フォルダ構成
name: debpackagesnap # you probably want to 'snapcraft register <name>'
base: core20 # the base snap is the execution environment for this snap
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Demo Snap based on iptables deb package # 79 char long summary
description: |
  This is iptables snap.

grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode # use 'strict' once you have the right plugs and slots

```

name: 
SNAPパッケージの名前。SnapStoreに登録する場合は、ストア内でSNAPパッケージを一意に設定する必要があります。同じ名前のパッケージは複数存在できません。例えばhello-worldというパッケージはすでにストアに
存在するので、hello-worldというSNAPパッケージを作ることはできません。ここではdebpackagesnapという名前のSnapパッケージを作成します｡

base:
SNAPパッケージに実行環境を提供するベースsnap。ここではCore20を指定します。

version: 
作成するSANPパッケージのバージョン

summary: 
ストアに表示されるSNAPパッケージのサマリ

description: |
SNAPパッケージの詳細


```フォルダ構成
apps:
  general:
    command: iptable-legacy.sh
    plugs: [firewall-control]
    
parts:
  my-part:
    # See 'snapcraft plugins'
    plugin: dump
    source: src
    stage-packages: [iptables]
```
次にappsスタンザを記述し、Partsスタンザを編集します。

appsはこのSnapパッケージが提供する実行コマンドを記述します。Commandに記載されたgeneralがSnapが公開するコマンド､iptable-legacy.shがgeneralを実行した際に実際に実行される実体になります。今回はShellスクリプトファイルを指定し､Snapパッケージ内のiptables-legacyを実行するようにしています。

partsは実際の実行ファイルを提供するために必要なアセットやバイナリ､Debパッケージを記述します｡今回はiptables DebパッケージをSnap化するので、stage-packagesキーワードでDebパッケージを指定し実際に実行されるiptable-legacy.sh(Srcフォルダ内に作成)をsourceとして指定し、dumpプラグインを利用して、SrcフォルダないのファイルをすべてSnapパッケージ化します。





<details><summary>snapcraft.yaml</summary><div>

```
name: debpackagesnap # you probably want to 'snapcraft register <name>'
base: core20 # the base snap is the execution environment for this snap
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Demo Snap based on iptables deb package # 79 char long summary
description: |
  This is iptables snap.

grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode # use 'strict' once you have the right plugs and slots

apps:
  general:
    command: iptable-legacy.sh    
parts:
  my-part:
    # See 'snapcraft plugins'
    plugin: dump
    source: src
    stage-packages: [iptables]
```
</div></details>

<details><summary>iptable-legacy.sh</summary><div>

```
#! /bin/bash
$SNAP/usr/sbin/iptables-legacy $1 $2 $3 $4 $5 $6
```
</div></details>

####パッケージ化を実行する
パッケージを作成するにはClientあるいはServerフォルダ内で、snapcraftコマンドを実行します。

```
GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snapcraft 
Launching a VM.
Launched: snapcraft-debpackagesnap                                              
Get:1 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]       
Get:2 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB]
Get:3 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [1,069 kB]
Get:4 http://archive.ubuntu.com/ubuntu focal-updates InRelease [114 kB]
Get:5 http://archive.ubuntu.com/ubuntu focal/main amd64 Packages [970 kB]
Get:6 http://archive.ubuntu.com/ubuntu focal/main Translation-en [506 kB]
Get:7 http://security.ubuntu.com/ubuntu focal-security/main Translation-en [197 kB]  
Get:8 http://security.ubuntu.com/ubuntu focal-security/multiverse amd64 Packages [21.9 kB]          
Get:9 http://security.ubuntu.com/ubuntu focal-security/multiverse Translation-en [4,948 B]      
Get:10 http://security.ubuntu.com/ubuntu focal-security/restricted amd64 Packages [566 kB]            
Get:11 http://security.ubuntu.com/ubuntu focal-security/restricted Translation-en [80.9 kB]
Get:12 http://archive.ubuntu.com/ubuntu focal/multiverse amd64 Packages [144 kB]                      
Get:13 http://security.ubuntu.com/ubuntu focal-security/universe amd64 Packages [668 kB]          
Get:14 http://archive.ubuntu.com/ubuntu focal/multiverse Translation-en [104 kB]                
Get:15 http://archive.ubuntu.com/ubuntu focal/restricted amd64 Packages [22.0 kB]                     
Get:16 http://archive.ubuntu.com/ubuntu focal/restricted Translation-en [6,212 B]             
Get:17 http://archive.ubuntu.com/ubuntu focal/universe amd64 Packages [8,628 kB]                        
Get:18 http://security.ubuntu.com/ubuntu focal-security/universe Translation-en [112 kB]          
Get:19 http://archive.ubuntu.com/ubuntu focal/universe Translation-en [5,124 kB]                        
Get:20 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages [1,400 kB]
Get:21 http://archive.ubuntu.com/ubuntu focal-updates/main Translation-en [283 kB]
Get:22 http://archive.ubuntu.com/ubuntu focal-updates/multiverse amd64 Packages [24.8 kB]
Get:23 http://archive.ubuntu.com/ubuntu focal-updates/multiverse Translation-en [6,928 B]
Get:24 http://archive.ubuntu.com/ubuntu focal-updates/restricted amd64 Packages [616 kB]
Get:25 http://archive.ubuntu.com/ubuntu focal-updates/restricted Translation-en [88.1 kB]
Get:26 http://archive.ubuntu.com/ubuntu focal-updates/universe amd64 Packages [884 kB]
Get:27 http://archive.ubuntu.com/ubuntu focal-updates/universe Translation-en [193 kB]
Fetched 22.2 MB in 7s (3,240 kB/s)                                                                                                                                      
Reading package lists... Done
Reading package lists... Done
                     
（略）
Unpacking apt-transport-https (2.0.6) ...
Setting up apt-transport-https (2.0.6) ...
2022-01-02T11:26:30+09:00 INFO Waiting for automatic snapd restart...
snapd 2.54.1+git8.g16ded42 from Canonical✓ installed
"snapd" switched to the "latest/edge" channel

core20 20211129 from Canonical✓ installed
"core20" switched to the "latest/stable" channel

snapcraft 6.0 from Canonical✓ installed
"snapcraft" switched to the "latest/stable" channel

snapd is not logged in, snap install commands will use sudo                     
snap "core20" has no updates available
Get:1 iptables_1.8.4-3ubuntu2_amd64.deb [390 kB]                                                                                                                        
Fetched 390 kB in 0s (0 B/s)                                                                                                                                            
Pulling my-part 
+ snapcraftctl pull
Building my-part 
+ snapcraftctl build
+ cp --archive --link --no-dereference . /root/parts/my-part/install
Staging my-part 
+ snapcraftctl stage
Priming my-part 
+ snapcraftctl prime
Snapping |                                                                                                                                                    
Snapped debpackagesnap_0.1_amd64.snap

```

これで SNAPパッケージ（debpackagesnap_0.1_amd64.snap）が作成されました。

#作成したSNAPパッケージをインストールして実行する
ローカル上で作成されたSNAPパッケージをインストールするには"--dangerous"オプションが必要になります。またこのSnapはDevモードで作成されたため、"--devmode"を追加してインストールします。

```
GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snap install debpackagesnap_0.1_amd64.snap --dangerous --devmode
debpackagesnap 0.1 installed
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ 

```
Snapパッケージがインストールされたので、ICMPパケット(宛先IPが192.168.0.1)を送信したらログに記録する、というルールを追加してみます。きちんとログされているのが確認出来ます。

```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ sudo debpackagesnap.general  -L OUTPUT 
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
cali-OUTPUT  all  --  anywhere             anywhere             /* cali:tVnHkvAo15HuiPy0 */
KUBE-SERVICES  all  --  anywhere             anywhere             ctstate NEW /* kubernetes service portals */
KUBE-FIREWALL  all  --  anywhere             anywhere            
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ sudo debpackagesnap.general  -A OUTPUT -p icmp -d 192.168.50.1 -j LOG
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ sudo debpackagesnap.general  -L OUTPUT 
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
cali-OUTPUT  all  --  anywhere             anywhere             /* cali:tVnHkvAo15HuiPy0 */
KUBE-SERVICES  all  --  anywhere             anywhere             ctstate NEW /* kubernetes service portals */
KUBE-FIREWALL  all  --  anywhere             anywhere            
           icmp --  anywhere             TUF-AX3000-6560     
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ ping -c 1 192.168.50.1
PING 192.168.50.1 (192.168.50.1) 56(84) bytes of data.
64 bytes from 192.168.50.1: icmp_seq=1 ttl=64 time=0.382 ms

--- 192.168.50.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.382/0.382/0.382/0.000 ms
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ sudo debpackagesnap.general  -nvxl OUTPUT --line-numbers
iptables v1.8.4 (legacy): unknown option "-nvxl"
Try `iptables -h' or 'iptables --help' for more information.
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ sudo debpackagesnap.general  -nvxL OUTPUT --line-numbers
Chain OUTPUT (policy ACCEPT 10714 packets, 11421142 bytes)
num      pkts      bytes target     prot opt in     out     source               destination         
1     1184217 1338458116 cali-OUTPUT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:tVnHkvAo15HuiPy0 */
2       23999  1965263 KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes service portals */
3     1196811 1350343159 KUBE-FIREWALL  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
4           1       84            icmp --  *      *       0.0.0.0/0            192.168.50.1       
```

#Strictモードに変更する
再度snapcraft.yamlファイルを開き、ConfinmentをStrictモードに変更してセーブします。

```
confinement: strict # use 'strict' once you have the right plugs and slots
```

再度snapcraftコマンドを実行して、Snapパッケージを作成しインストールします。

```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snapcraft
Launching a VM.
snapd is not logged in, snap install commands will use sudo
snap "core20" has no updates available
Skipping pull my-part (already ran)
Skipping build my-part (already ran)
Skipping stage my-part (already ran)
Skipping prime my-part (already ran)
The requested action has already been taken. Consider
specifying parts, or clean the steps you want to run again.
Snapping |                                                                                                                                                                                         
Snapped debpackagesnap_0.1_amd64.snap
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snap install debpackagesnap_0.1_amd64.snap --dangerous
debpackagesnap 0.1 installed
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ 
```
正常にインストール出来たので、Pingを送り再度パケット数を確認してみます。
先ほどとは違い、Confinmentが有効となっているので、コマンドを実行してもエラーが発生します。

```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ ping -c 1 192.168.50.1
PING 192.168.50.1 (192.168.50.1) 56(84) bytes of data.
64 bytes from 192.168.50.1: icmp_seq=1 ttl=64 time=0.380 ms

--- 192.168.50.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.380/0.380/0.380/0.000 ms
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ sudo debpackagesnap.general  -nvxL OUTPUT --line-numbers
Fatal: can't open lock file /run/xtables.lock: Permission denied
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ 
```

#Snappy-Debugの使い方
ここで必要なインターフェースの探し方として、Snappy-Debugを使用します。まずはSnappy-Debugをインストールします

```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snap install snappy-debug
snappy-debug 0.36-snapd2.49.1 from Canonical✓ installed

```
正常にインストール出来たので、Snap-Debugコマンドを実行します。

``` 
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snappy-debug
INFO: Following '/var/log/syslog'. If have dropped messages, use:
INFO: $ sudo journalctl --output=short --follow --all | sudo snappy-debug
``` 

この状態で新しいターミナルを開き、debpackagesnap.generalコマンドを実行します。
再度Snappy-Debugを実行しているターミナルを確認すると、以下のような出力が見つかると思います。
これはApparmorのディナイアルのため/run/xtables.lockにWriteアクセスが出来なかったという内容になります。この場合解決策としてSuggestionに幾つかの方法が提案されています。一番最後に"add 'firewall-control' to 'plugs'"という提案が見つかります。これが必要だと考えられるインターフェースになります。

```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snappy-debug
INFO: Following '/var/log/syslog'. If have dropped messages, use:
INFO: $ sudo journalctl --output=short --follow --all | sudo snappy-debug
= AppArmor =
Time: Jan  2 11:51:19
Log: apparmor="DENIED" operation="open" profile="snap.debpackagesnap.general" name="/run/xtables.lock" pid=356855 comm="iptables-legacy" requested_mask="rc" denied_mask="rc" fsuid=0 ouid=0
File: /run/xtables.lock (write)
Suggestions:
* adjust program to use $SNAP_DATA
* adjust program to use /run/shm/snap.$SNAP_NAME.*
* adjust program to use /run/snap.$SNAP_NAME.*
* adjust snap to use snap layouts (https://forum.snapcraft.io/t/snap-layouts/7207)
* add 'firewall-control' to 'plugs'
```

#####インターフェースの仕組み
ここでインターフェースを追加する前に、簡単にインターフェースについて説明をしたいと思います。StrictモードのSnapパッケージ内のアプリケーションはApparmor等でパッケージ外部のリソースへのアクセスが制限されているため、インターフェースを利用してリソースへのアクセスを許可する必要があります。このインターフェースはプラグとスロットという2つの概念で構成されています。アクセスに必要なリソースを提供するSnapはスロットを提供し、リソースを使用するスナップはプラグを作成します。このプラグをスロットに接続することで、リソースへのアクセスが可能となります。たとえば、特殊なSnapがインターネットへのアクセス用のスロットを提供している場合、インターネットにアクセスしたいSnapはネットワークアクセス用のプラグを作成し、このプラグをスロットに接続することでネットワークへのアクセスを行います。

#インターフェースの追加
それでは必要なインターフェースが判明したので、早速snapcraft.yamlを編集します。ここではプラグが必要となるので、Plugsスタンザを利用して必要なPlugを記載していきます。今回はgeneralコマンドの実行にfirewall-controlプラグが必要なので、以下のようにsnapcraft.yamlを編集します。

```
apps:
  general:
    command: iptable-legacy.sh
    plugs: [firewall-control]　#Plugを追加
```

変更を保存し再度パッケージを作成し、インストールします。

###プラグとスロットの接続
インターフェースの仕組みで説明したように、プラグとスロットは接続される必要があります。まずは正しくプラグが作成されたかを確認します。これにはsnap connectionsコマンドを利用します。実行すると以下のように表示され、プラグが作成されたもののどのスロットにも接続されていないことが確認できます。

```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snap connections debpackagesnap
Interface         Plug                             Slot  Notes
firewall-control  debpackagesnap:firewall-control  -     -
```
プラグを接続するために、snap connectコマンドを利用します。(snap connect <Snapパッケージ名:プラグ＞）再度snap connectionsコマンドを実行すると正しくプラグが接続されていることが確認できます。


```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snap connect debpackagesnap:firewall-control 
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ snap connections debpackagesnap
Interface         Plug                             Slot               Notes
firewall-control  debpackagesnap:firewall-control  :firewall-control  manual
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ 
```

では再度ICMPパッケット数を表示してみます。

```
masahiro@masahiro-GS65-Stealth-9SE:~/02.Study/04.Snap/06.demo$ sudo debpackagesnap.general  -nvxL OUTPUT --line-numbers
Chain OUTPUT (policy ACCEPT 674038 packets, 837870996 bytes)
num      pkts      bytes target     prot opt in     out     source               destination         
1     1847541 2164907970 cali-OUTPUT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:tVnHkvAo15HuiPy0 */
2       37420  2907929 KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes service portals */
3     1860135 2176793013 KUBE-FIREWALL  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
4           4      384            icmp --  *      *       0.0.0.0/0            192.168.50.1      
```

正しくコマンドが実行出来ていることが確認できました。
このようにSnappy-Debugを活用することで必要なインターフェースを調べることが出来ます。


中川　雅裕　Canonical Japan (Iot Field Engineer)
Ubuntu Blog: https://jp.ubuntu.com/blog
Canonical Japan: https://jp.ubuntu.com/
Email: masahiro.nakagawa@canonical.com
Email:info-jp@canonical.com
Phone: 03-6205-3075