云原生|kubernetes|apparmor的配置和使用

前言:

APPARMor是一个Linux操作系统的内核安全模块,和selinux功能基本是一致的。

AppArmor(Application Armor)是Linux内核的一个安全模块,AppArmor允许系统管理员将每个程序与一个安全配置文件关联,从而限制程序的功能。简单的说,AppArmor是与SELinux类似的一个访问控制系统,通过它你可以指定程序可以读、写或运行哪些文件,是否可以打开网络端口等。作为对传统Unix的自主访问控制模块的补充,AppArmor提供了强制访问控制机制,它已经被整合到2.6版本的Linux内核中。

这里有一点需要特别强调,Ubuntu和openSUSE才有AppAromor,centos,Redhat这些操作系统才是使用SeLinux的。

因此,如果你对centos比较熟悉而对Ubuntu不太熟悉的情况下,可以简单的理解APPArmor为Ubuntu版的SeLinux

一,

APPArmor内核模块的停止和启用

AppArmor可以被禁用,其内核模块可以通过输入以下命令卸载:

sudo service apparmor stop
sudo update-rc.d -f apparmor remove

要重新启用AppArmor,输入:

sudo service apparmor start
sudo update-rc.d apparmor defaults

二,

APPArmor的两种工作模式

像 SELinux 一样,AppArmor 以两种模式运行。在 强制enforce 模式下,应用被赋予它们运行所需要的最小权限,但在 抱怨complain 模式下 AppArmor 允许一个应用执行受限的操作并将操作造成的“抱怨”记录到日志里(/var/log/kern.log/var/log/audit/audit.log,和其它放在 /var/log/apparmor 中的日志)。

这两种模式意思enforce是强制应用,而complain是仅仅记录受限情况,如其名一样,仅仅抱怨一通而已,当然,两种工作模式日志都会记录。

Enforcement(强制模式) :在这种模式下,配置文件里列出的限制条件都会得到执行,并且对于违反这些限制条件的程序会进行日志记录。使用aa-enforce <程序名>开启

Complain(投诉模式):在这种模式下,配置文件里的限制条件不会得到执行,Apparmor只是对程序的行为进行记录。一般用于调试。使用aa-complain <程序名>

Disable: 此时对应配置文件不加载,没有对程序行为进行限制。aa-disable <程序名>

查询APPArmor的状态:

apparmor_status 
apparmor module is loaded.
25 profiles are loaded.
23 profiles are in enforce mode.
   /sbin/dhclient
   /snap/snapd/17950/usr/lib/snapd/snap-confine
   /snap/snapd/17950/usr/lib/snapd/snap-confine//mount-namespace-capture-helper
   /usr/bin/docker
   /usr/bin/lxc-start
   /usr/bin/man
   /usr/lib/NetworkManager/nm-dhcp-client.action
   /usr/lib/NetworkManager/nm-dhcp-helper
   /usr/lib/connman/scripts/dhclient-script
   /usr/lib/snapd/snap-confine
   /usr/lib/snapd/snap-confine//mount-namespace-capture-helper
   /usr/local/bin/etcd
   /usr/sbin/tcpdump
   docker-default
   lxc-container-default
   lxc-container-default-cgns
   lxc-container-default-with-mounting
   lxc-container-default-with-nesting
   man_filter
   man_groff
   snap-update-ns.http
   snap.http.http
   snap.http.man
2 profiles are in complain mode.
   /bin/ping
   /usr/sbin/sshd
27 processes have profiles defined.
24 processes are in enforce mode.
   docker-default (29250) 
   docker-default (29270) 
   docker-default (29278) 
   docker-default (29411) 
   docker-default (29475) 
   docker-default (29554) 
   docker-default (29658) 
   docker-default (29748) 
   docker-default (29788) 
   docker-default (29833) 
   docker-default (29887) 
   docker-default (30114) 
   docker-default (30185) 
   docker-default (30186) 
   docker-default (30187) 
   docker-default (30350) 
   docker-default (30372) 
   docker-default (30569) 
   docker-default (30606) 
   docker-default (30607) 
   docker-default (30609) 
   docker-default (30998) 
   docker-default (31063) 
   docker-default (31064) 
1 processes are in complain mode.
   /usr/sbin/sshd (56029) 
2 processes are unconfined but have a profile defined.
   /usr/sbin/sshd (1409) 
   /usr/sbin/sshd (13170) 

以上表示25个程序是在enforce模式,两个程序是在Complain模式,1个进程在Complain模式下运行,2个sshd进程虽然有配置文件定义过,但没有激活,24个进程都是docker在enforce模式下。

三,

APPArmor的配置文件

我们安装了一个可执行程序,如果想用AppArmor进行访问控制,就需要新建一个配置文件到/etc/apparmor.d/目录下,这个目录下的每个配置文件都是跟可执行程序绑定的,不要随便修改配置文件名或程序路径。

例如,MySQL的APPArmor的配置文件:

# cat usr.sbin.mysqld
# vim:syntax=apparmor
# Last Modified: Tue Jun 19 17:37:30 2007
#include <tunables/global>
/usr/sbin/mysqld {
  #include <abstractions/base>
  #include <abstractions/nameservice>
  #include <abstractions/user-tmp>
  #include <abstractions/mysql>
  #include <abstractions/winbind>
  capability dac_override,
  capability sys_resource,
  capability setgid,
  capability setuid,
  network tcp,
  /etc/hosts.allow r,
  /etc/hosts.deny r,
  /etc/mysql/*.pem r,
  /etc/mysql/conf.d/ r,
  /etc/mysql/conf.d/* r,
  /etc/mysql/*.cnf r,
  /usr/lib/mysql/plugin/ r,
  /usr/lib/mysql/plugin/*.so* mr,
  /usr/sbin/mysqld mr,
  /usr/share/mysql/** r,
  /var/log/mysql.log rw,
  /var/log/mysql.err rw,
  /var/lib/mysql/ r,
  /var/lib/mysql/** rwk,
  /var/log/mysql/ r,
  /var/log/mysql/* rw,
  /var/run/mysqld/mysqld.pid rw,
  /var/run/mysqld/mysqld.sock w,
  /run/mysqld/mysqld.pid rw,
  /run/mysqld/mysqld.sock w,
  /sys/devices/system/cpu/ r,
  # Site-specific additions and overrides. See local/README for details.
  #include <local/usr.sbin.mysqld>
}

在配置文件中,注释总是以# 号开头。#include 行加载文件。

以下是配置文件中使用的不同类型的规则。

  1. 路径条目:这包含有关允许应用程序访问哪些文件的信息。
  2. 能力条目:确定一个受限进程被允许使用的权限。
  3. 网络条目:确定连接类型。例如:tcp。对于数据包分析器网络可以是原始的或数据包等。

在花括号 {} 中,我们还有其他包含语句,还包括访问权限/模式 [read(r)/write (w)/execute (x) (k) 锁(需要 r 或 w,AppArmor 2.1 及更高版本)]各种文件和目录,包括正则表达式,用花括号 {} 对 include 语句进行通配有助于加载 Novell AppArmor 配置文件的组件。

以上配置文件限定了MySQL程序,例如对于 /var/log/mysql这个目录/usr/sbin/mysqld 这个程序只有读权限,但此目录下/usr/sbin/mysqld这个程序具有读写权限,/usr/lib/mysql 这个目录下,/usr/sbin/mysqld 这个程序有读写锁定权限,那么,我们可以判断出这个配置文件是针对yum安装的MySQL服务了。

四,

APPArmor的配置文件示例:

cat >/etc/apparmor.d/k8s-apparmor-example-deny-write<<EOF
profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  file,
  # Deny all file writes.
  deny /** w,
}
EOF

在此示例中,配置文件授予应用程序所有类型的访问权限,但写入整个文件系统除外。它包含两个规则:

  • file: 允许对整个文件系统进行各种访问
  • deny /** w: 拒绝在根目录下写入任何文件/。该表达式/**转换为根目录下的任何文件,以及其子目录下的文件。

五,

将以上配置文件加载进APPArmor模块内:

cat /etc/apparmor.d/k8s-apparmor-example-deny-write |sudo apparmor_parser -a

查看APPArmor的状态:

可以看到新增的配置文件,并且该配置是enforce工作模式

root@k8s-master:~# apparmor_status 
apparmor module is loaded.
25 profiles are loaded.
23 profiles are in enforce mode.
   /sbin/dhclient
   /snap/snapd/17950/usr/lib/snapd/snap-confine
   /snap/snapd/17950/usr/lib/snapd/snap-confine//mount-namespace-capture-helper
   /usr/bin/lxc-start
   /usr/bin/man
   /usr/lib/NetworkManager/nm-dhcp-client.action
   /usr/lib/NetworkManager/nm-dhcp-helper
   /usr/lib/connman/scripts/dhclient-script
   /usr/lib/snapd/snap-confine
   /usr/lib/snapd/snap-confine//mount-namespace-capture-helper
   /usr/local/bin/etcd
   /usr/sbin/tcpdump
   docker-default
   k8s-apparmor-example-deny-write

。。。。。。。。。。。。。。。略略略。。。。。。。。。。。。。。。。。。。。。。

六,

kubernetes的pod引用以上的APPArmor配置文件:

主要是注解container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write

注意,这个localhost前面是有空格的,hello是下面的pod部署文件里的container下的名字,k8s-apparmor-example-deny-write是上面新建的那个apparmor配置文件

apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    # Tell Kubernetes to apply the AppArmor profile "k8s-apparmor-example-deny-write".
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
  containers:
  - name: hello
    image: busybox
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]

运行这个pod,但发现此pod状态是blocked

root@k8s-master:~# kubectl get po
NAME                                      READY   STATUS    RESTARTS         AGE
hello-apparmor                            0/1     Blocked   0                4m48s

查询具体原因为:

kubectl describe pod hello-apparmor
Message:      Cannot enforce AppArmor: profile "k8s-apparmor-example-deny-write" is not loaded


root@k8s-master:~# kubectl describe pod hello-apparmor 
Name:         hello-apparmor
Namespace:    default
Priority:     0
Node:         k8s-node1/192.168.123.151
Start Time:   Thu, 12 Jan 2023 21:34:57 +0800
Labels:       <none>
Annotations:  container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
Status:       Pending
Reason:       AppArmor
Message:      Cannot enforce AppArmor: profile "k8s-apparmor-example-deny-write" is not loaded
IP:           
IPs:          <none>
Containers:
  hello:
    Container ID:  
    Image:         busybox
    Image ID:      
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
      -c
      echo 'Hello AppArmor!' && sleep 1h
    State:          Waiting
      Reason:       Blocked
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-cjw7z (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             False 
  ContainersReady   False 
  PodScheduled      True 
Volumes:
  kube-api-access-cjw7z:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  5m8s  default-scheduler  Successfully assigned default/hello-apparmor to k8s-node1

OK,这个明明在master节点使用aaparmor_status 查看到了的啊,wait,这个pod是运行在node1节点的:

root@k8s-master:/etc/apparmor.d# kubectl get no -owide
NAME         STATUS   ROLES                  AGE    VERSION    INTERNAL-IP       EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
k8s-master   Ready    control-plane,master   400d   v1.22.10   192.168.123.150   <none>        Ubuntu 18.04.5 LTS   4.15.0-200-generic   docker://20.10.0
k8s-node1    Ready    <none>                 30d    v1.22.2    192.168.123.151   <none>        Ubuntu 18.04.5 LTS   4.15.0-200-generic   docker://20.10.0
k8s-node2    Ready    <none>                 30d    v1.22.2    192.168.123.152   <none>        Ubuntu 18.04.5 LTS   4.15.0-200-generic   docker://20.10.0

原因总算清楚了,node1上并没有这个k8s-apparmor-example-deny-write配置文件,OK,拷贝此文件到node1节点并加载到内核中:

root@k8s-master:/etc/apparmor.d# scp k8s-apparmor-example-deny-write k8s-node1:/etc/apparmor.d/
The authenticity of host 'k8s-node1 (192.168.123.151)' can't be established.
ECDSA key fingerprint is SHA256:nG/f/jJzY0mQunN4HWMIaHLaElyZZjOHCG2XCYFA5YI.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'k8s-node1' (ECDSA) to the list of known hosts.
root@k8s-node1's password: 
k8s-apparmor-example-deny-write                                                                                                                      100%  120   106.0KB/s   00:00    

在node1节点上加载k8s-apparmor-example-deny-write这个配置文件:

root@k8s-node1:~# apparmor_parser -a /etc/apparmor.d/k8s-apparmor-example-deny-write

重新app 以上pod,查看状态,可以发现正常的running了:

root@k8s-master:~# kubectl get po hello-apparmor 
NAME             READY   STATUS    RESTARTS   AGE
hello-apparmor   1/1     Running   0          29m

七,

测试APPArmor配置文件效果:

进入容器后,执行创建文件touch命令无法写入,echo重定向也没有权限

root@k8s-master:~# kubectl exec -it hello-apparmor -- /bin/sh
/ # touch aaa
touch: aaa: Permission denied
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # cat etc/
group        hostname     hosts        localtime    mtab         network/     passwd       resolv.conf  shadow
/ # cat etc/hostname 
hello-apparmor
/ # echo "fuck apparmor" >test
/bin/sh: can't create test: Permission denied

OK,此前我们查询到了这个APPArmor配置文件是运行在enforce模式下的,现在登陆node1将它修改成Complain模式:

注意,如果没有aa-complain和aa-enforce命令,那么是需要安装apparmor-utils这个工具包的:

安装apparmor工具包

root@k8s-node1:~# apt-get install apparmor-utils 
Reading package lists... Done
Building dependency tree       
。。。。。。。。。。。略略略。。。。。。。。。

使用aa-complain命令直接指定apparmor的配置文件,切换到complain模式 

root@k8s-node1:~# aa-complain /etc/apparmor.d/k8s-apparmor-example-deny-write 
Setting /etc/apparmor.d/k8s-apparmor-example-deny-write to complain mode.

查看apparmor的状态:

可以看到k8s-apparmor-example-deny-write这个配置文件正确的加载了

root@k8s-node1:~# apparmor_status |grep k8s
   k8s-apparmor-example-deny-write
   k8s-apparmor-example-deny-write (117874) 
root@k8s-node1:~# ps -ef |grep 117874
root     117874 117847  0 22:44 ?        00:00:00 sleep 1h
root     121613  64994  0 22:48 pts/0    00:00:00 grep --color=auto 117874

八,

自动生成apparmor配置文件

root@k8s-master:~# aa-genprof /usr/bin/passwd 
Writing updated profile for /usr/bin/passwd.
Setting /usr/bin/passwd to complain mode.

Before you begin, you may wish to check if a
profile already exists for the application you
wish to confine. See the following wiki page for
more information:
http://wiki.apparmor.net/index.php/Profiles

Profiling: /usr/bin/passwd

Please start the application to be profiled in
another window and exercise its functionality now.

Once completed, select the "Scan" option below in 
order to scan the system logs for AppArmor events. 

For each AppArmor event, you will be given the 
opportunity to choose whether the access should be 
allowed or denied.

[(S)can system log for AppArmor events] / (F)inish

Profiling: /usr/bin/passwd

Please start the application to be profiled in
another window and exercise its functionality now.

Once completed, select the "Scan" option below in 
order to scan the system logs for AppArmor events. 

For each AppArmor event, you will be given the 
opportunity to choose whether the access should be 
allowed or denied.

[(S)can system log for AppArmor events] / (F)inish

多说一句,这个自动生成的配置文件还是在/etc/apparmor.d/目录下,但它是通过读取系统日志文件/var/log/syslog来进行的,因此,上面的提示说要新开一个窗口,运行一下passwd程序,生成相关的日志文件:

/var/log/syslog文件的部分日志

Jan 12 22:51:22 k8s-master kernel: [31693.170585] audit: type=1400 audit(1673535082.352:290): apparmor="STATUS" operation="profile_load" profile="unconfined" name="/sbin/pivot_root" pid=128518 comm="apparmor_parser"
Jan 12 22:51:22 k8s-master root: GenProf: 7da38636a415bfc154b9d7e3303b6226
Jan 12 22:51:29 k8s-master kernel: [31700.804407] audit: type=1400 audit(1673535089.988:291): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/sbin/pivot_root" pid=128629 comm="apparmor_parser"
Jan 12 22:51:46 k8s-master kernel: [31717.547306] audit: type=1400 audit(1673535106.732:292): apparmor="STATUS" operation="profile_load" profile="unconfined" name="/usr/bin/passwd" pid=128922 comm="apparmor_parser"
Jan 12 22:51:46 k8s-master root: GenProf: 4f5800b7e323735332abb0252b0afcf3
Jan 12 22:51:53 k8s-master kernel: [31724.097027] audit: type=1400 audit(1673535113.280:293): apparmor="ALLOWED" operation="open" profile="/usr/bin/passwd" name="/proc/129027/loginuid" pid=129027 comm="passwd" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Jan 12 22:51:53 k8s-master kernel: [31724.097252] audit: type=1400 audit(1673535113.280:294): apparmor="ALLOWED" operation="open" profile="/usr/bin/passwd" name="/etc/nsswitch.conf" pid=129027 comm="passwd" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
Jan 12 22:51:53 k8s-master kernel: [31724.097915] audit: type=1400 audit(1673535113.284:295): apparmor="ALLOWED" operation="open" profile="/usr/bin/passwd" name="/etc/passwd" pid=1290

九,

移除apparmor的配置文件:

apparmor_parser -R /etc/apparmor.d/k8s-apparmor-example-deny-write

再次查询,看不到k8s-apparmor-example-deny-write了:

root@k8s-master:/etc/apparmor# apparmor_status |grep k8s

十,

查询内核是否开启了apparmor:

root@k8s-master:/etc/apparmor# cat /sys/module/apparmor/parameters/enabled 
Y
root@k8s-master:/etc/apparmor# cat /sys/module/apparmor/parameters/debug 
N

查看加载了哪些profile:

root@k8s-master:/etc/apparmor# cat /sys/kernel/security/apparmor/profiles
/usr/bin/passwd (enforce)
/sbin/pivot_root (enforce)

kubernetes查询是否支持apparmor:

kubernetes的版本必须是大于1.14的哦

root@k8s-master:/etc/apparmor#  kubectl get nodes -o=jsonpath=$'{range .items[*]}{@.metadata.name}: {.status.conditions[?(@.reason=="KubeletReady")].message}\n{end}'
k8s-master: kubelet is posting ready status. AppArmor enabled
k8s-node1: kubelet is posting ready status. AppArmor enabled
k8s-node2: kubelet is posting ready status. AppArmor enabled

猜你喜欢

转载自blog.csdn.net/alwaysbefine/article/details/128661805