CKA를 준비해보자 25일차 - Image Security

0

CKA

목록 보기
25/43

Image Security

pod스펙에서 image: nginx라고 쓰여있다면, 다음과 같이 image 네이밍 부분을 쪼갤 수 있다.

registryuser accountimage/repositorytag
imagedocker.iolibrarynginxlatest

docker에서 image이름 앞에 아무것도 없다면 이는 user account가 명시되지 않아 default값인 library로 설정된다. 따라서 library/nginx가 되는 것이다.

여기서 맨 앞부분인 registry부분이 생략되어있으므로 default값인 docker.io가 되는 것이다. 따라서 docker.io/library/nginx가 된다. 최종적으로 default tag인 latest와 합쳐져 docker.io/library/nginx:latest가 된다.

참고로 docker.io/nginx라고 쓰면 docker.io가 registry 이름이되고, nignx가 이미지 이름이 된다. 즉, user account를 생략하는 것이다. user account에 대해서는 많이들 생략한다.

다른 경우 dnsutils의 경우는 다음의 image이름을 갖게되는 것이다.

gcr.io/kubernetes-e2e-test-images/dnsutils

kubernetes의 경우 대부분의 application image가 private registry인데, docker의 경우 private registry에서 image를 가져오는 경우 다음과 같이 login을 먼저 해야한다. 가령 private-registry.io/apps/internal-app을 가져온다고 해보자.

docker login private-registry.io
...
Login Succeeded

docker run private-registry.io/apps/internal-app

해당 이미지를 사용하면 pod의 정의는 다음과 같다.

  • nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: private-registry.io/apps/internal-app

그런데 kubernetes에서 어떻게 인증하여 credential을 받을 수 있을까??

이를 위해서 private registry에 접근하기 위한 인증 정보를 담은 secret을 만들어야 한다. 각 registry 호스트가 무엇이냐에 따라 secret의 type이 다른데, docker의 경우는 docker-registry이다. 따라서, 다음과 같다.

kubectl create secret docker-registry regcred \
  --docker-server=private-registry.io \
  --docker-username=registry-user \
  --docker-password=registry-password \
  --docker-email=registry-user@org.com

docker-registry라는 type을 가진 regcred secret을 만든 것이다. 그리고, 해당 secret에 들어가는 정보가 private registry에 대한 인증 정보인 username, password들이 들어간다.

이제 해당 secret정보를 imagePullSecrets에 넣어주어야 한다.

  • nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: private-registry.io/apps/internal-app
  imagePullSecrets:
  - name: regcred

docker security

docker container는 VM처럼 host os와 완전히 분리되어있지 않다. host os와 커널을 같이 쓰고, 일부 네트워크, 파일 시스템 볼륨들을 공유할 수 있다.

container는 host와 분리된 namespace에서 process를 구동하는데, 기본 USER자체가 root이다. 따라서 다음과 같이, container안에서 ps aux명령어를 실행하면 USER의 PID가 1이다.

ps aux

USER    PID ...
root      1 ...

root user로 실행하게되면 container에 원치 않은 권한들을 줄 수 있고, 이를 통해 host의 일부 기능들을 컨트롤 할 수 있다. 따라서, 다음과 같이 USER를 dockerfile이나 cli로 넘겨주어 root권한을 주지 못하도록 하는 것이 보안에 좋다.

docker run --user=1000 ubuntu sleep 3600

다음과 같이 dockerfile에 USER를 쓸 수도 있다.

FROM ubuntu

USER 1000

그런데 container는 정말 host의 root유저처럼 동작하는 것인가?? root user처럼 root에 있는 파일들을 지울 수 있고, 네트워크 설정들을 바꿀 수 있는가? 그렇지않다. root로 container를 실행시키면, 일부 capability를 제공하지 않은 채로 container를 동작시키는데, 이는 root user의 일부 권한을 부여하지 않은 것이다. 따라서, host의 root user처럼 동작하지 않는 것이다.

만약 필요하다면 docker container를 실행할 때 --cap-add 옵션을 붙이면 된다.

docker run --cap-add MAC_ADMIN ubuntu

Security Context

이러한 container security 기능을 다음과 같이, kubernetes resource로 제공해줄 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: web-pod
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: ubuntu
    image: ubuntu
    command: ["sleep", "3600"]

위와 같이 pod단위로 user option을 제공할 수도 있고, 다음과 같이 각 container마다의 user option과 capability기능을 추가할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: web-pod
spec:
  containers:
  - name: ubuntu
    image: ubuntu
    command: ["sleep", "3600"]
    securityContext:
      runAsUser: 1000
      capabilities:
        add: ["MAC_ADMIN"]

다음과 같이 pod와 container 둘 다 securityContext를 쓸 수도 있다.

apiVersion: v1
kind: Pod
metadata:
  name: multi-pod
spec:
  securityContext:
    runAsUser: 1001
  containers:
  -  image: ubuntu
     name: web
     command: ["sleep", "5000"]
     securityContext:
      runAsUser: 1002
  -  image: ubuntu
     name: sidecar
     command: ["sleep", "5000"]

web는 1002이고, sidecar는 1001 user id를 가지게 된다.

0개의 댓글