kakakakakku blog

Weekly Tech Blog: Keep on Learning!

kube-apiserver で「匿名リクエスト」を無効化する : --anonymous-auth=false

Kubernetes の kube-apiserver では,デフォルトで「匿名リクエスト」機能が有効になっている(正確な条件は以下のドキュメントに載っている).今回は「匿名リクエスト」機能の動作確認と kube-apiserver のセキュリティ対策として「匿名リクエスト」機能の無効化を試す.検証環境は Kubernetes v1.20.2 とする.

kubernetes.io

「匿名リクエスト」機能

Kubernetes API にリクエストをする場合,通常は HTTP Header の AuthorizationBearer Token を付ける.以下のドキュメントを参考に Kubernetes API(今回の検証環境では https://172.17.0.12:8443/api)にリクエストをすると,うまく実行できる.

kubernetes.io

$ TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}" | base64 --decode)

$ curl -s -k https://172.17.0.12:8443/api --header "Authorization: Bearer ${TOKEN}"
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "172.17.0.12:8443"
    }
  ]
}

今度は Bearer Token を付けずに Kubernetes API にリクエストをすると HTTP 403 Forbidden となる.これを「匿名リクエスト」と言う.エラーメッセージに載っている通り,RBAC としては「匿名リクエスト」system:anonymous User として権限を確認する.もしくは system:unauthenticated Group として権限を確認する.

$ curl -s -k https://172.17.0.12:8443/api
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/api\"",
  "reason": "Forbidden",
  "details": {
    
  },
  "code": 403
}

$ curl -s -k https://172.17.0.12:8443/api/v1/namespaces
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "namespaces is forbidden: User \"system:anonymous\" cannot list resource \"namespaces\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "namespaces"
  },
  "code": 403
}

system:anonymous User に権限を付与する

「匿名リクエスト」機能の動作確認をするために system:anonymous User に権限を付与する.以下のように Namespace に Read 権限を付与する ClusterRoleClusterRoleBinding を作る.ClusterRoleBindingsystem:anonymous User と紐付けている.

ClusterRole

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: my-cluster-role
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list"]

ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-cluster-role-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: my-cluster-role
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: system:anonymous

もう1度 Bearer Token を付けずに Kubernetes API にリクエストをすると,今度は Namespace を確認できるようになった.ここまでで「匿名リクエスト」機能の動作確認ができた.

$ curl -s -k https://172.17.0.12:8443/api/v1/namespaces | jq .items[].metadata.name
"default"
"kube-node-lease"
"kube-public"
"kube-system"

「匿名リクエスト」機能を無効化する

とは言え,基本的には「匿名リクエスト」に権限を付与する必要はなく,kube-apiserver--anonymous-auth=false オプションを追加すれば「匿名リクエスト」機能を無効化できる.なお「匿名リクエスト」機能の無効化は以下のように CIS Kubernetes V1.20 Benchmark でも推奨されている.

1.2.1 Ensure that the --anonymous-auth argument is set to false (Manual)

さっそく /etc/kubernetes/manifests/kube-apiserver.yaml を修正して --anonymous-auth=false を追加する.すると Static Pod によってすぐに反映される.以下には差分を抜粋して載せておく.

(中略)

spec:
  containers:
  - command:
    - kube-apiserver
+   - --anonymous-auth=false
    - --advertise-address=172.17.0.80
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC

(中略)

なお,kube-apiserver には他にも多くオプションがある.詳しくは以下のドキュメントに載っている.

kubernetes.io

もう1度 Bearer Token を付けずに Kubernetes API にリクエストをすると,今度は HTTP 401 Unauthorized となる.「匿名リクエスト」は拒否されるようになった.なるほど!

$ curl -s -k https://172.17.0.12:8443/api
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

まとめ

Kubernetes の kube-apiserver では,デフォルトで「匿名リクエスト」機能が有効になっている.今回は「匿名リクエスト」機能の動作確認と kube-apiserver のセキュリティ対策として「匿名リクエスト」機能の無効化を試した.kube-apiserver--anonymous-auth オプションは「Docker/Kubernetes開発・運用のためのセキュリティ実践ガイド」「5.3.7 匿名ユーザの取り扱い」にも載っている.

関連記事

kakakakakku.hatenablog.com

Secret の自動マウントをオプトアウトするかどうか : automountServiceAccountToken フィールド

Kubernetes のセキュリティ対策として,Service AccountPod に設定できる automountServiceAccountToken フィールド(Secret の自動マウントをオプトアウトするかどうか)の動作確認をした.とは言え,Service AccountSecret の関係性なども整理する必要があるため,一歩一歩進めていく.今回は Kubernetes v1.20 を検証環境とした.

kubernetes.io

Pod と Service Account

まず,Service Account を指定せずに Pod を作ると default Service Account と紐付く.確認をするために nginx:1.21 イメージを指定した以下のマニフェストを検証用に適用する.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
spec:
  containers:
    - name: my-nginx
      image: nginx:1.21

以下に kubectl get pods my-nginx -o yaml コマンドの結果を抜粋して載せる.serviceAccountName: default になっている通り,デフォルトでは Poddefault Service Account が紐付いている.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: default
spec:
  containers:
  - image: nginx:1.21
    name: my-nginx
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-5qnst
      readOnly: true
  serviceAccountName: default
  volumes:
  - name: default-token-5qnst
    secret:
      defaultMode: 420
      secretName: default-token-5qnst

Service Account と Secret

もう1点重要なのは,上記 YAML の volumesvolumeMounts で,default-token-5qnst SecretPod のボリュームとしてファイルシステムにマウントしている.この default-token-5qnst Secretdefault Service Account に紐付いている.以下に kubectl get serviceaccounts default -o yaml コマンドの結果を抜粋して載せる.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
secrets:
- name: default-token-5qnst

そして kubectl get secrets default-token-5qnst -o yaml コマンドで default-token-5qnst Secret の値を確認すると,Base64 でエンコードされた token を確認できる(以下は xxxxxxxxxx としてマスキングをした).

apiVersion: v1
data:
  token: xxxxxxxxxx
kind: Secret
metadata:
  name: default-token-5qnst
  namespace: default
type: kubernetes.io/service-account-token

Kubernetes API にリクエストを送る

実際に kubectl exec コマンドを使って Pod に接続をすると,ファイルシステムにマウントされた /var/run/secrets/kubernetes.io/serviceaccount/tokenファイルを確認できる.token ファイルは Base64 でデコードされた値となる(以下は yyyyyyyyyy としてマスキングをした).この token を HTTP Header に乗せると Kubernetes API にリクエストを送れるようになる.今回 default Service Account には権限を付与していないため Forbidden になっているのは期待通りと言える.

$ kubectl exec -it my-nginx -- /bin/sh

# cat /var/run/secrets/kubernetes.io/serviceaccount/token
yyyyyyyyyy

# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# curl -s -k https://kubernetes/api/v1/namespaces/default/ --header "Authorization: Bearer ${TOKEN}"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
  },
  "status": "Failure",
  "message": "namespaces \"default\" is forbidden: User \"system:serviceaccount:default:default\" cannot get resource \"namespaces\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "name": "default",
    "kind": "namespaces"
  },
  "code": 403
}

Kubernetes API にリクエストを送る手順は以下のドキュメントを参考にした.

kubernetes.io

Pod : automountServiceAccountToken フィールド

一般的には Pod から Kubernetes API にリクエストを送る場面は少なく,不要なら automountServiceAccountToken フィールドを false にして Secret の自動マウントをオプトアウトする.例えば EKS Best Practices Guides にもベストプラクティスとして載っている.

aws.github.io

まず,PodautomountServiceAccountToken: false を指定すると,Pod レベルで Secret の自動マウントをオプトアウトできる.以下のようにマニフェストに1行追加して,Pod を作り直す.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
spec:
  containers:
    - name: my-nginx
      image: nginx:1.21
  automountServiceAccountToken: false

すると volumesvolumeMounts の設定はなくなり,Secret の自動マウントはされなくなった.

$ kubectl get pod my-nginx -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: default
spec:
  automountServiceAccountToken: false
  containers:
  - image: nginx:1.21
    name: my-nginx
  serviceAccountName: default

念のため /var/run/secrets/kubernetes.io/serviceaccount/token ファイルを確認したところ,存在しなかった.

$ kubectl exec -it my-nginx -- /bin/sh

# cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat: /var/run/secrets/kubernetes.io/serviceaccount/token: No such file or directory

Service Account : automountServiceAccountToken フィールド

Pod レベルではなく,Service Account レベルで automountServiceAccountToken フィールドを false にすることもできる.以下のマニフェストで新しく auto-mount-off Service Account を作る.

$ cat serviceaccount.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: auto-mount-off
automountServiceAccountToken: false

次に serviceAccountName: auto-mount-off を指定した Pod を作る.今回は Pod には automountServiceAccountToken: false を指定していない.

$ cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
spec:
  serviceAccountName: auto-mount-off
  containers:
    - name: my-nginx
      image: nginx:1.21

以下に kubectl get pods my-nginx -o yaml コマンドの結果を抜粋して載せる.同じく volumesvolumeMounts の設定はなくなり,Secret の自動マウントはされなくなった.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: default
spec:
  containers:
  - image: nginx:1.21
    name: my-nginx
  serviceAccountName: auto-mount-off

なお,Service AccountSecret は紐付いているため,以下のように確認できる.

$ kubectl get serviceaccounts 
NAME             SECRETS   AGE
auto-mount-off   1         3m10s
default          1         50m

$ kubectl get secrets
NAME                         TYPE                                  DATA   AGE
auto-mount-off-token-7mhnv   kubernetes.io/service-account-token   3      3m10s
default-token-5qnst          kubernetes.io/service-account-token   3      50m

まとめ

Kubernetes のセキュリティ対策として,Service AccountPod に設定できる automountServiceAccountToken フィールド(Secret の自動マウントをオプトアウトするかどうか)の動作確認をした.CKS (Certified Kubernetes Security Specialist) の出題範囲にも関連するトピックが含まれているし,最近読んでいる「Docker/Kubernetes開発・運用のためのセキュリティ実践ガイド」にも automountServiceAccountToken フィールドの解説が載っていたので,気になっていた!引き続き試していくぞー!

Kubernetes と seccomp を組み合わせてシステムコールを制限する

前回の記事では seccomp (Secure computing mode) に入門するために「Docker と seccomp」を組み合わせて試した.docker run コマンドの --security-opt オプションを使って seccomp プロファイルを指定した.

kakakakakku.hatenablog.com

今回は「Kubernetes と seccomp」を組み合わせて試す.設定や手順などは以下のドキュメントに詳しく載っている.

kubernetes.io

検証環境

まず,kind を使って検証環境を構築する.以下の kind-config.yaml のように,ワーカーノードに seccomp プロファイルを置いたホスト側のディレクトリをマウントする(kubelet に読み込ませる).ドキュメントには ./profiles と書いてあるけど,今回は Docker for Mac を使っていて,マウントできるディレクトリに制限(設定次第)があるため,今回は Mac 側の /tmp/seccomp-profiles に置いた.

apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
nodes:
- role: control-plane
- role: worker
  extraMounts:
  - hostPath: /tmp/seccomp-profiles
    containerPath: /var/lib/kubelet/seccomp/profiles

さっそく kind で Kubernetes クラスターを構築する.準備 OK 💡

$ kind create cluster --config=kind-config.yaml
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.21.1) 🖼
 ✓ Preparing nodes 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜

$ kubectl get nodes
NAME                 STATUS   ROLES                  AGE     VERSION
kind-control-plane   Ready    control-plane,master   2m20s   v1.21.1
kind-worker          Ready    <none>                 113s    v1.21.1

なお,seccompkubelet に設定する.以下のドキュメントを見ると,--seccomp-profile-root オプションに関する記載がある.Kubernetes 1.23 から /seccomp ディレクトリに変更すると書いてあり,覚えておこう!

kubernetes.io

検証 : mkdir システムコールを制限する

前回の記事と同じく Ubuntu で mkdir システムコールを制限して,mkdir コマンドの挙動を確認する.まず,何も指定せず,デフォルト設定のまま Pod で Ubuntu を起動する.

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu
spec:
  containers:
    - name: ubuntu
      image: ubuntu:21.10
      command:
        - sleep
        - infinity

kubectl exec コマンドで Pod に接続をして,問題なく mkdir コマンドを実行できる.

$ kubectl apply -f ubuntu.yaml
pod/ubuntu created

$ kubectl exec -it ubuntu -- /bin/sh

# mkdir sample-dir

次に seccomp プロファイルを指定した Pod で Ubuntu を起動する.前回の記事と同じく Docker (Moby) のデフォルトプロファイルから mkdir システムコールのエントリーを削除した deny-mkdir.json を作る.

$ diff -u default.json deny-mkdir.json
@@ -200,7 +200,6 @@
                "membarrier",
                "memfd_create",
                "mincore",
-               "mkdir",
                "mkdirat",
                "mknod",
                "mknodat",

そして以下のように spec.securityContext.seccompProfileseccomp プロファイルを指定する.プロファイルはワーカーノードにマウントしているため,参照できるようになっている.なお,ドキュメントにも書いてあるけど,Kubernetes v1.19 より前はアノテーションとして annotations.seccomp.security.alpha.kubernetes.io/podseccomp プロファイルを指定していた.今後は spec.securityContext.seccompProfile を使う.

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-deny-mkdir
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/deny-mkdir.json
  containers:
    - name: ubuntu
      image: ubuntu:21.10
      command:
        - sleep
        - infinity

同じく kubectl exec コマンドで Pod に接続をすると,今度は mkdir コマンドを実行できなくなった.実際に Operation not permitted とエラーになった.

$ kubectl apply -f ubuntu-deny-mkdir.yaml
pod/ubuntu-deny-mkdir created

$ kubectl exec -it ubuntu-deny-mkdir -- /bin/sh

# mkdir sample-dir
mkdir: cannot create directory 'sample-dir': Operation not permitted

RuntimeDefault プロファイル

さっきは spec.securityContext.seccompProfile.typeLocalhost を指定したけど,他にも spec.securityContext.seccompProfile.typeRuntimeDefault を指定することができる.これはコンテナランタイム側のデフォルトプロファイルを設定することになる.Docker for Mac なら containerd あたり?

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-runtime-default
spec:
  securityContext:
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: ubuntu
      image: ubuntu:21.10
      command:
        - sleep
        - infinity

kubernetes.io

実際に動作確認をすると問題なく mkdir コマンドを実行することができた.最低限 RuntimeDefault を設定しておくと良さそう.

$ kubectl apply -f ubuntu-runtime-default.yaml
pod/ubuntu-runtime-default created

$ kubectl exec -it ubuntu-runtime-default -- /bin/sh

# mkdir sample-dir

seccomp アクション SCMP_ACT_LOG

ドキュメントを読んでいたら,システムコールを洗い出すための seccomp アクションとして SCMP_ACT_LOG の例が紹介されていた.実際にシステムコールを制限することはせず,syslog にログとして書き出せる.調査目的なら使えそう.

{
    "defaultAction": "SCMP_ACT_LOG"
}

お掃除

検証が終わったら Kubernetes クラスターを削除しておく.

$ kind delete cluster

まとめ

コンテナワークロードのセキュリティ対策として,前回の「Docker と seccomp」の組み合わせに続き「Kubernetes と seccomp」の組み合わせを試した.Podspec.securityContext には他にも多くの設定項目があるため,一歩一歩学んでいくぞ!

Docker と seccomp を組み合わせてシステムコールを制限する

seccomp (Secure computing mode) はプロセスに対してシステムコールを制限する Linux kernel の機能で,今回は「Docker と seccomp」を組み合わせて試す.ドキュメントは以下にある.

docs.docker.com

seccomp デフォルトプロファイル

まず,Docker はデフォルトで seccomp をサポートしている.Docker (Moby) の GitHub リポジトリにデフォルトプロファイル default.json がある.プロファイルは長く感じるけど,比較的簡単に読める.基本的に SCMP_ACT_ERRNO(拒否) となり,ホワイトリストとして列挙したシステムコールを SCMP_ACT_ALLOW(許可) としている.

基本的には制限しすぎず,ホスト側に影響を及ぼすシステムコールなどは禁止されている.ドキュメントにも「It is moderately protective while providing wide application compatibility(幅広いアプリケーション互換性を提供しながら適度に保護する)」と書いてある.

--security-opt オプション

docker run コマンドには --security-opt オプションがあり,--security-opt="seccomp=profile.json" のように seccomp プロファイルを指定できる.また unconfined を指定するとデフォルトプロファイルを無効化できる.

--security-opt="seccomp=unconfined" Turn off seccomp confinement for the container
--security-opt="seccomp=profile.json" White-listed syscalls seccomp Json file to be used as a seccomp filter

docs.docker.com

検証 : mkdir システムコールを制限する

単純な例として,Ubuntu で mkdir システムコールを制限して,mkdir コマンドの挙動を確認する.デフォルトプロファイルでは mkdir システムコールを許可しているため,デフォルトでは以下のように問題なく使える.

$ docker run --rm -d -it ubuntu:21.10
ad3a814faf29ce6407c44723f8a3330c46a425259507dcd4e7673cbaf0f9b2dd

$ docker exec -it ad3a814faf29 /bin/sh

# mkdir sample-dir

次に seccomp を試す.まず,GitHub から default.json を取得して,mkdir のエントリーを削除した deny-mkdir.json を作る.そして docker run コマンドに --security-opt seccomp=deny-mkdir.json オプションを指定すると,mkdir コマンドを実行できなくなった.実際に Operation not permitted とエラーになった.

$ wget https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json

$ diff -u default.json deny-mkdir.json
@@ -200,7 +200,6 @@
                "membarrier",
                "memfd_create",
                "mincore",
-               "mkdir",
                "mkdirat",
                "mknod",
                "mknodat",

$ docker run --rm -d -it --security-opt seccomp=deny-mkdir.json ubuntu:21.10
4e24d4448c43169ef15a928080a6d82fc7732ee3b782234dfb9b00b0564689c8

$ docker exec -it 4e24d4448c43 /bin/sh

# mkdir sample-dir
mkdir: cannot create directory 'sample-dir': Operation not permitted

検証 : strace システムコールを制限する

関連する例として,今度は Ubuntu で ptrace システムコールを制限して,strace コマンドの挙動を確認する.デフォルトプロファイルでは,以下のように Linux Kernel 4.8ptrace を許可している.

{
        "names": [
                "process_vm_readv",
                "process_vm_writev",
                "ptrace"
        ],
        "action": "SCMP_ACT_ALLOW",
        "args": null,
        "comment": "",
        "includes": {
                "minKernel": "4.8"
        },
        "excludes": {}
},

よって,デフォルトでは以下のように strace コマンドを問題なく使える.

$ docker run --rm -d -it ubuntu:21.10
14c033b88ab4fc361561c37d271dc9d9a3cb98833d570c7c5e86e8cd94db2281

$ docker exec -it 14c033b88ab4 /bin/sh

# uname -r
5.10.25-linuxkit

# apt-get -y update
# apt-get -y install strace

# strace ls
execve("/usr/bin/ls", ["ls"], 0x7ffc3ae41460 /* 5 vars */) = 0
brk(NULL)                               = 0x561158e2e000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd05d87490) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=5826, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 5826, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe3dd13d000
close(3)

(中略)

# strace -cw ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 17.22    0.002433        2432         1           munmap
 16.71    0.002361          90        26           mmap
 12.49    0.001765         176        10           close
  8.47    0.001197         132         9           newfstatat
  6.58    0.000930         116         8           openat
  6.29    0.000889         127         7           read
  4.33    0.000612         101         6           pread64
  4.19    0.000592          84         7           mprotect
  3.72    0.000526         262         2           getdents64
  2.99    0.000423         423         1           execve
  2.10    0.000297         297         1           set_tid_address
  2.08    0.000294         146         2           ioctl
  1.99    0.000281         280         1           set_robust_list
  1.87    0.000264         132         2           rt_sigaction
  1.78    0.000252         126         2         2 statfs
  1.72    0.000243          81         3           brk
  1.36    0.000193         192         1           write
  1.32    0.000186          92         2         1 arch_prctl
  1.22    0.000172          86         2         2 access
  0.81    0.000114         113         1           prlimit64
  0.78    0.000110         109         1           rt_sigprocmask
------ ----------- ----------- --------- --------- ----------------
100.00    0.014131         148        95         5 total

次に ptrace のエントリーを削除した deny-ptrace.json を作る.そして docker run コマンドに --security-opt seccomp=deny-ptrace.json オプションを指定すると,strace コマンドを実行できなくなった.

$ diff -u default.json deny-ptrace.json
@@ -402,8 +402,7 @@
        {
            "names": [
                "process_vm_readv",
-               "process_vm_writev",
-               "ptrace"
+               "process_vm_writev"
            ],
            "action": "SCMP_ACT_ALLOW",
            "args": null,

$ docker run --rm -d -it --security-opt seccomp=deny-ptrace.json ubuntu:21.10
b78b00e8ce7b02b7635a19478fa089caeff1ebc85446ea4013e07696331ca9c6

$ docker exec -it b78b00e8ce7b /bin/sh

# uname -r
5.10.25-linuxkit

# apt-get -y update
# apt-get -y install strace

# strace ls
strace: test_ptrace_get_syscall_info: PTRACE_TRACEME: Operation not permitted
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace: PTRACE_SETOPTIONS: Operation not permitted
strace: detach: waitpid(262): No child processes
strace: Process 262 detached

# strace -cw ls
strace: test_ptrace_get_syscall_info: PTRACE_TRACEME: Operation not permitted
strace: ptrace(PTRACE_TRACEME, ...): Operation not permitted
strace: PTRACE_SETOPTIONS: Operation not permitted
strace: detach: waitpid(266): No child processes
strace: Process 266 detached

seccomp と DockerSlim (docker-slim)

実際にプロファイルを作るときに必要最低限なシステムコールを洗い出すのは難しく,strace コマンドを使うこともできるけど,「DockerSlim (docker-slim)」を使うとお手軽にプロファイルを作ることができる.

github.com

「Docker/Kubernetes開発・運用のためのセキュリティ実践ガイド」を読んでいたら載っていた.「DockerSlim」は今度試す予定!

参考資料 : Introduction to Seccomp

seccomp を詳しく解説したスライドがあってとてもわかりやすかった💡後半は僕には難しかったけど...!

まとめ

コンテナワークロードのセキュリティ対策として,今回は「Docker と seccomp」を組み合わせて試した.プロファイルを編集してシステムコールを制限することができた.次は「Kubernetes と seccomp」を組み合わせて試すぞー!

GitHub Actions で Re:VIEW プロジェクトをビルドする

最近 Pandoc 以外の選択肢として Re:VIEW を試している.ビルドを自動化するために GitHub ActionsRe:VIEW プロジェクトをビルドできるように設定してみた.今回は vvakame/review Docker Image を使うことにした.検証日時点で最新となる Re:VIEW v5.1 もサポートされていて良かった💡その前に GitHub MarketplaceRe:VIEW 専用の Action も探してみたけど,あまり良さそうなものはなかった.

github.com

Re:VIEW プロジェクトを作る

サンプルとして review-init コマンドを使って Re:VIEW プロジェクトを作る.今回は sandbox-github-actions-review という名前にした.以下のように rake pdf コマンドで簡単に PDF 化できる.

$ review-init sandbox-github-actions-review --without-doc --without-config-comment
$ cd sandbox-github-actions-review
$ rake pdf

GitHub Actions

さっそく GitHub Actions ワークフロー用に YAML を書く.今回はmain ブランチに対するプルリクエスト」main ブランチに対するプッシュ(マージなど)」をトリガーするように on を設定した.そして jobsvvakame/review Docker Image を使うように設定した.なお actions/upload-artifact を使うとビルド結果などのアーティファクトを簡単にファイルをアップロードできる.便利!

name: Build

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    name: Re:VIEW Workflow
    runs-on: ubuntu-latest
    container: docker://vvakame/review:5.1
    steps:
      - uses: actions/checkout@v2
      - name: Build PDF
        run: rake pdf
      - uses: actions/upload-artifact@v2
        with:
          name: Artifacts
          path: book.pdf

YAML 構文はドキュメントに載っている.

https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actionsdocs.github.com

ビルド結果

GitHub Actions でうまくビルドできた👌

関連記事

もし GitHub Actions 入門するなら以下の記事で紹介している「GitHub Learning Lab」を使うと便利!

kakakakakku.hatenablog.com

Re:VIEW「節単位」に分割したファイルをビルドする Tips は以下にまとめてある!

kakakakakku.hatenablog.com