이쿠의 슬기로운 개발생활

함께 성장하기 위한 보안 개발자 EverNote 내용 공유

클라우드/Kubernetes

95. Portieris (v0.12.2)

이쿠우우 2022. 2. 23. 16:13
반응형

 

 

 
 
 

Portieris

 
 

kubernetes image인증 - Portieris2


 

목표

Portieris가 update되면서 설치 과정이 간편해짐.
kubernetes에서 인증된 image 만 cluster에 배포되도록하는 Portieris addon을 설치, 적용해봄.
 
설치에 사용한 porieris version : v0.12.2
 
 

 


 

Portieris란?

 
IBM에서 개발한 Kubernetes notary 적용 Addon.
2018년도에 개발된 Addon으로
아직 많은 자료는 없지만 IBM이기에 믿고 사용해보기로 함.
Portieris 는 Mutating Addmission Controller Webhook 기반으로 동작하여
kube-apiserver를 통해 오는 요청을 Portieris webhook으로 보내서 
request를 변경한 후 respone을 주는 형시긍로
Portieris Webhook이 DCT역할을 수행해주어
Kubernetes 에서 images 인증을 수행할 수 있게 해주는 Addon임.
현재는 IBM cloud 전용 addon으로 사용되고 있다고함.
 
[참고 Site]
 
 
 

 

Test 환경

 
Master Node server
OS = CentOS 7
리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64
containerD version : 1.4.12
 
Worker Node server 1
OS = CentOS 7
리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64
containerD version : 1.4.12
 
Kubernetes version
1.23.1

 

CNI : Calico
Container runtime : containerd 1.4.12

 


 
 

Test에 사용한 Private Registry 정보

 

[ Private Registry 주소 ]
iksoon.registry.com
IP : 10.0.2.5

 

[ K8s에서 Private Registry 접속 설정완료. ]
/root/certs 경로에 crt인증서가 있음.
ca.crt, server.crt, server.key = iksoon.registry.com https 접근 용 인증서

 

[ Private Registry Image 정보 ]
iksoon.registry.com/testproject/tomcat:1.0.0 = 인증 안됨.
iksoon.registry.com/testproject/tomcat:1.0.1 = 인증 됨.

 

 

 

 

 

Portieris 설치

 
 

1. Portieris v0.12.2 Download

tar xvzf portieris-v0.12.2.tgz

 

 

2. Portieris에 사용되는 인증키를 생성

 
K8s와 Portieris webhook server 간 통신에 필요한 인증키를 생성하는 작업.
 
[명령어]
cd portieris
chmod 755 gencerts
./gencerts
 
[결과]
portieris/certs directory로 이동해보면 키가 생성된 것을 확인할 수 있음.

 

 

 

3. ibm-system namespace 생성

 
Portieris Helm에서 Job 동작 시 ibm-system namespace가 필요한데
helm 에 해당 namespace생성 과정이 포함되어있지 않아서
오류가 발생함.
해당 오류 해결을 위해 직접 namespace생성함.
 
[명령어]
kubectl create ns ibm-system

 

 

 

4. Portieris 설치

 
[명령어]
cd ..
helm install portieris --create-namespace --namespace portieris ./portieris

설치 완료.

 

 


 

Portieris Notary 연동 Test

 

Portieris 정책 생성

 
정책으로는 ImagePolicy와 ClusterImagePolicy 가 있음.
 
ImagePolicy = 특정 namespace에만 적용되는 정책
ClusterImagePolicy = kubernetes cluster 전체에 적용되는 정책
 
[예제 ImagePolicy.yaml]
apiVersion: portieris.cloud.ibm.com/v1
kind: ImagePolicy
metadata:
  name: iksoon-custom
  namespace: iksoon-ns
spec:
   repositories:
    - name: "iksoon.registry.com/*"
      policy:
        trust:
          enabled: true
          trustServer: "https://notary-server:4443"
 
 

정책 생성 후 Pod 생성

 
image는 
iksoon.docker.com:5000/domain_iksoon_tomcat:1.0.0
을 지정함.
 
[정책에서 정해지지 않은 Registry에서 image pull 시도할 경우 ]
 
예) docker hub의 peksoon Registry 에서 image pull 시도.
 
Error from server: error when creating "2_deployment.yaml": admission webhook "trust.hooks.securityenforcement.admission.cloud.ibm.com" denied the request:
Deny "peksoon/iksoon_tomcat:1.0.6", no matching repositories in the ImagePolicies

 

 

첫번째 ERROR 발생.

 
설정된 Registry에서 image pull 시도 했는데 trustServer(notary-server)를 바라보지 못하는 오류 발생 
 
[오류명]
Error from server: error when creating "1_deployment.yaml": admission webhook "trust.hooks.securityenforcement.admission.cloud.ibm.com" denied the request:
trust: policy denied the request: Deny "iksoon.docker.com:5000/domain_iksoon_tomcat:1.0.0", failed to get content trust information: dial tcp: lookup notary-server on 10.96.0.10:53: no such host

 
[원인]
Kubernetes의 CoreDNS가 Host Name을 확인할 수 없기 때문. (상위 예제 환경에서의 hostname = notary-server)
Notary Server가 TLS 인증서에서 해당 Host Name을 사용하므로 Host Name을 IP 주소로 바꿀 수 없음. 
따라서 Kuberntes에서 이 Host Name을 확인하도록해야함.
이어서 이 오류의 해결 방법을 찾아봄.

 

 


 

 

첫번째 ERROR 해결법

Kubernetes 에서는 자체 DNS Server 로 CoreDNS를 사용 중.
notary-server no such host 오류가 발생하는 이유는 CoreDNS에서 
해당 Domain을 찾기 못하기 때문.
이러한 문제를 해결하기 위해
notary-server 정보를 가지고 있는 DNS를 추가해야함.
CoreDNS에서 host를 찾기 못하면 
다른 DNS에서 찾을 수 있도록
unbound DNS 를 구축하고
CoerDNS에서 StubDomain설정으로 unbound DNS를 설정하는 작업을 진행함.
 
[참고]
K8s version 1.12 이전는 kube-dns 를 사용했었으나
이후 version부터는 CoreDNS를 사용함.
 

Unbound DNS 설치

 

1. Unbound DNS를 다운받기 위해 helm repository 추가

helm repo add stable https://charts.helm.sh/stable
 
 

2. Helm으로 Unbound DNS 실행

helm install  unbound --create-namespace --namespace=unbound --set-string localRecords[0].name=notary-server,localRecords[0].ip=10.0.2.5,allowedIpRanges[0]=10.0.2.0/24,allowedIpRanges[0]=172.16.0.0/16 stable/unbound  
 
추가 하고자 하는 domaim = notary-server : 10.0.2.5
 
CNI가 Flannel인 경우 : 10.244.0.0/16 
CNI가 Calico인 경우 : 192.168.0.0/16
만약 설치 시 cidr 을 설정 하지 않았다면 : 172.16.0.0/16
 
[CNI network 대역 모르는 경우 확인 명령어]
kubectl cluster-info dump | grep -m 1 cluster-cidr
 
 
[설치 결과]

 


 

CoreDNS에 StubDomain으로 Unbound DNS 설정

 

1. Unbound DNS 의 Service Object Cluster IP 확인

 
[명령어]
kubectl -n unbound get svc | grep ClusterIP | awk '{print $3}'
 
[결과]
service : 10.105.253.52

 

 

2. CoreDNS 설정 변경

 
[명령어]
kubectl edit cm coredns -n kube-system
 
[설정 추가]
apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf 
        cache 30
        loop
        reload
        loadbalance
    }
    notary-server:53 {
        errors
        cache 30
        forward . 10.107.116.10 # Unbound DNS의 Service object cluster ip.
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2020-08-27T00:14:37Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data: {}
    manager: kubeadm
    operation: Update
    time: "2020-08-27T00:14:37Z"
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        f:Corefile: {}
    manager: kubectl
    operation: Update
    time: "2020-09-09T00:02:49Z"
  name: coredns
  namespace: kube-system
  resourceVersion: "2574871"
  selfLink: /api/v1/namespaces/kube-system/configmaps/coredns
  uid: 0f3f31b3-2799-47ab-977d-d43baa8620ab
 
 
 
[ 옵션 설명 ]
errors:
오류가 표준 출력(stdout)에 기록된다.
 
health:
CoreDNS의 상태(healthy)가 http://localhost:8080/health 에 기록된다.
이 확장 구문에서 lameduck 은 프로세스를 비정상 상태(unhealthy)로 만들고, 프로세스가 종료되기 전에 5초 동안 기다린다.
 
ready:
8181 포트의 HTTP 엔드포인트가, 모든 플러그인이 준비되었다는 신호를 보내면 200 OK 를 반환한다.
 
kubernetes:
CoreDNS가 쿠버네티스의 서비스 및 파드의 IP를 기반으로 DNS 쿼리에 대해 응답한다. 
해당 플러그인에 대한 세부 사항은 CoreDNS 웹사이트에서 확인할 수 있다.
ttl 을 사용하면 응답에 대한 사용자 정의 TTL 을 지정할 수 있으며, 기본값은 5초이다.
허용되는 최소 TTL은 0초이며, 최대값은 3600초이다. 레코드가 캐싱되지 않도록 할 경우, 
TTL을 0으로 설정한다. pods insecure 옵션은 kube-dns 와의 하위 호환성을 위해 제공된다. 
pods verified 옵션을 사용하여, 일치하는 IP의 동일 네임스페이스(Namespace)에 파드가 존재하는 경우에만
A 레코드를 반환하게 할 수 있다. pods disabled 옵션은 파드 레코드를 사용하지 않을 경우 사용된다.

 

prometheus:
CoreDNS의 메트릭은 프로메테우스 형식(OpenMetrics 라고도 알려진)의 http://localhost:9153/metrics 에서 사용 가능하다.
 
forward:
쿠버네티스 클러스터 도메인에 없는 쿼리들은 모두 사전에 정의된 리졸버(/etc/resolv.conf)로 전달된다.
 
cache:
프론트 엔드 캐시를 활성화한다.
 
loop:
간단한 전달 루프(loop)를 감지하고, 루프가 발견되면 CoreDNS 프로세스를 중단(halt)한다.
 
reload:
변경된 Corefile을 자동으로 다시 로드하도록 한다.
컨피그맵 설정을 변경한 후에 변경 사항이 적용되기 위하여 약 2분정도 소요된다.
 
loadbalance:
응답에 대하여 A, AAAA, MX 레코드의 순서를 무작위로 선정하는 라운드-로빈 DNS 로드밸런서이다.
 
 

3. CoreDNS 설정 적용

 
[명령어]
kubectl delete pod --namespace kube-system --selector k8s-app=kube-dns
 
CoreDNS는 reload 설정이 되어있어서 
Corefile이 변경되면 약 2분 후 자동으로 다시 로드하지만 2분도 꽤 긴시간으로 느껴지니 바로 적용하기 위해
상위 명령으로 pod를 삭제하고 상위에서 설정한 정보로 자동으로 재생성되도록 함.
 
 

결과 

 

상위 설정 후 Pod 생성 결과 : 다른 에러 발생

 
상위 설정을 완료하니 
이제 정상적으로 Host는 바라봄. 
하지만 또 다른 오류가 나타남.
 
[오류명]
Error from server: error when creating "deployment.yaml": admission webhook "trust.hooks.securityenforcement.admission.cloud.ibm.com" denied the request:
trust: policy denied the request: Deny "iksoon.registry.com/testproject/tomcat:1.0.0", failed to get content trust information: x509: certificate signed by unknown authority

 
[원인]
Private Registry의 Domain 주소에 접근할 수 없기 때문  ("iksoon.registry.com 접근할 수가 없음.)
Private Registry가 인증서를 검증 할 수 없기 때문에 오류가 발생한 상황.

 


 
 
 

두번째 ERROR 해결법

 
 
[해결책]
Private Regitsry에 접근하기 위해 인증서를 Portieris에 설정해줘야 함.
 
 

configMap 으로 인증서를 Pod에 직접배포

 
/etc/ssl/certs/ca-bundle.crt 파일을
Pod에 volume 형태로 배포.
해당 파일에는
iksoon.registry.com 접속용 CRT인증서와
notary-server 접속용 CRT인증서가 포함되어있음.
 
 

1. Portieris 가 정상적으로 배포되어 있는지 확인.

 
[명령어]
kubectl get all -n portieris

 

 

2. Notary Server와 Private Registry 접속 용 CRT 인증서 생성.

 
결국 따지고 보면 한가지의 방법과 다름없지만
작성자가 생성해본 2가지 방법을 모두 설명하겠음.
 
사용되는 인증서는 미리 복사해서 가져옴.
 
2.1. 직접 CRT 인증서 생성
 
root-ca.crt = Notary Server 인증서
server.crt = iksoon.registry.com Private Registry https 접근 용 인증서
2개의 파일을 하나로 합쳐서 인증서를 생성함.
 
[명령어]
cat root-ca.crt server.crt > notary_registry.crt

 
 
2.2. ca-bundle.crt 파일 사용법
 
centos 의 경우
/etc/pki/ca-trust/source/anchors 경로에
root-ca.crt = Notary Server 인증서
server.crt = iksoon.resgistry.com Private Registry https 접근 용 인증서
2개의 파일을 생성해놓음.

인증서 파일이 있는 상태에서
update-ca-trust 명령을하면 
해당 2개의 인증서가 
/etc/ssl/certs/ca-bundle.crt
파일에 추가됨.
ca-bundle.crt 파일에는 host가 사용하는 각종 인증서가 모두 포함되어있음

 

 
 

3. comfigMap 생성

 
portieris namespace에 상위에서 생성한 인증서 파일을 가지고있는 configMap 생성.
 
[configMap 생성 명령어]
(2.1. 방법으로 생성한 예)
kubectl -n portieris create cm iksoon-docker-crt --from-file=iksoon-crt=/경로/notary_registry.crt
 
(2.2. 방법으로 생성한 예)
kubectl -n portieris create cm iksoon-docker-crt --from-file=iksoon-crt=/etc/ssl/certs/ca-bundle.crt
 
[yaml 형식으로 생성 시 예제]
apiVersion: v1
kind: ConfigMap
metadata:
  name: iksoon-docker-crt
  namespace: portieris
data:
  iksoon-crt: |
    # Notary
    -----BEGIN CERTIFICATE-----
    MIIDTDCCAjSgAwIBAgIBATANBgk
    ~~~~ ca-bundle.crt 파일내용 ~~
 
 
 

4. portieris Container에 configMap을 Volume 으로 mount

 
[명령어]
kubectl edit deployment.apps/portieris -n portieris
 
편집창이 뜨면 아래와 같이 추가함.
        volumeMounts:
        - mountPath: /etc/certs
          name: portieris-certs
          readOnly: true
        - mountPath: /etc/ssl/certs
          name: iksoon-registry
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        runAsUser: 1000060001
      serviceAccount: portieris
      serviceAccountName: portieris
      terminationGracePeriodSeconds: 30
      volumes:
      - name: portieris-certs
        secret:
          defaultMode: 420
          secretName: portieris-certs
      - configMap:
          items:
          - key: iksoon-crt
            path: certificates.crt
          name: iksoon-docker-crt
        name: iksoon-registry
 
 
 

5. Deployment 설정 적용 확인

 
[명령어]
kubectl get all -n portieris
명령으로 확인해보면
pod가 1개씩 update되고 있는것을 확인가능함.
 
 

6. imagePolicy 생성

 
[예제 imagePolicy.yaml 파일]
namespace = iksoon-ns (해당 namespace에만 정책이 적용됨.)
Private Registry = iksoon.registry.com (해당 registry에서만 image pull 가능)
trustServer = notary-server (구축한 notary 서버에서 인증을 받도록 설정)
apiVersion: portieris.cloud.ibm.com/v1
kind: ImagePolicy
metadata:
  name: iksoon-custom
  namespace: iksoon-ns
spec:
   repositories:
    - name: "iksoon.registry.com/*"
      policy:
        trust:
          enabled: true
          trustServer: "https://notary-server:4443"
 
 

7. 결과 확인

 
[결과 확인 용도로 사용한 deployment.yaml 파일]
apiVersion: apps/v1
kind: Deployment
metadata:
  name: iksoon-deployment
  name: iksoon-ns
  labels:
    app: iksoon-tomcat
spec:
  replicas: 1
  selector:
    matchLabels:
      app: iksoon-pod
  template:
    metadata:
      labels:
        app: iksoon-pod
    spec:
      containers:
      - name: iksoon-tomcat
        image: iksoon.registry.com/testproject/tomcat:1.0.1 # 1.0.1 = 인증되어있음, 1.0.0 = 인증 안되어있음
        ports:
        - containerPort: 8080
      imagePullSecrets:
      - name: iksoon-secret-test
 
 
인증되어있는 1.0.1 version으로 배포
 
[배포 상태]
결과 : 성공적으로 배포됨

 

[Portieris log상태]
Portieris log 확인 참고 : kubectl logs -f   [생성된 portieris pod 3개중 1개] -n portieris 
명령어 : kubectl logs -f pod/portieris-5bb749cfc5-5shss -n portieris
I0118 06:52:20.239467       1 controller.go:227] Mutation #: containers 0  Image name: iksoon.registry.com/testproject/tomcat:1.0.1
W0118 06:52:20.239473       1 controller.go:230] Image iksoon.registry.com/testproject/tomcat:1.0.1 mutated to: iksoon.registry.com/testproject/tomcat@sha256:4170f20639c49bdb5d773837527961ebcf3d4ae4aa920762cadb8bd5b58ca379
I0118 06:52:20.239723       1 controller.go:236] Patch: { replace /spec/template/spec/containers/0/image iksoon.registry.com/testproject/tomcat@sha256:4170f20639c49bdb5d773837527961ebcf3d4ae4aa920762cadb8bd5b58ca379}
I0118 06:52:20.239797       1 controller.go:136] Mutation patch: [{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"iksoon.registry.com/testproject/tomcat@sha256:4170f20639c49bdb5d773837527961ebcf3d4ae4aa920762cadb8bd5b58ca379"}]
I0118 06:52:20.239803       1 controller.go:145] Allow for images:  [iksoon.registry.com/testproject/tomcat:4170f20639c49bdb5d773837527961ebcf3d4ae4aa920762cadb8bd5b58ca379]
I0118 06:52:20.355572       1 controller.go:64] Processing admission request for CREATE on iksoon-deployment-59bcdb7565
I0118 06:52:20.369884       1 controller.go:64] Processing admission request for CREATE on
I0118 22:05:04.073520       1 controller.go:64] Processing admission request for UPDATE on iksoon-deployment
I0118 22:05:04.110932       1 controller.go:176] Getting policy for container image: iksoon.registry.com/testproject/tomcat:1.0.1   namespace: iksoon-ns
I0118 22:05:04.117609       1 controller.go:145] Allow for images:  [iksoon.registry.com/testproject/tomcat:1.0.1]
I0118 22:05:04.136068       1 controller.go:64] Processing admission request for CREATE on iksoon-deployment-6cd94c7568
I0118 22:05:04.160311       1 controller.go:64] Processing admission request for CREATE on
I0118 22:05:06.254566       1 controller.go:64] Processing admission request for UPDATE on iksoon-deployment-59bcdb7565
 
 
 
인증이 안되어있는 1.0.0 version으로 배포
 
인증이 안되어있는
iksoon.registry.com/testproject/tomcat:1.0.0 
이미지 배포 해봄.
 
[배포 상태]
Error from server: error when creating "deployment.yaml": admission webhook "trust.hooks.securityenforcement.admission.cloud.ibm.com" denied the request:
trust: policy denied the request: Deny "iksoon.registry.com/testproject/tomcat:1.0.0", failed to get content trust information: No valid trust data for 1.0.0

결과 : 배포 안됨.
 
[참고 1] 
각 worker Node에 인증이 안된 image가 이전에 pull 받아져 있어도
Portieris에서 인증이 안되면 Pod 생성이 안됨.
(tag는 다르지만 hash값이 같은 image라도 서명이 안되어있다면 배포되지 않음.)
 
 
[Portieris log상태]
I0118 22:17:57.415388       1 controller.go:64] Processing admission request for CREATE on iksoon-deployment
I0118 22:17:57.420589       1 controller.go:176] Getting policy for container image: iksoon.registry.com/testproject/tomcat:1.0.0   namespace: iksoon-ns
I0118 22:17:57.423544       1 enforcer.go:95] policy.Trust {0xc0002e89b3 [] https://notary-server:4443}
I0118 22:17:57.476312       1 trust.go:65] GetAllTargetMetadataByName returned err: No valid trust data for 1.0.0
I0118 22:17:57.476416       1 responder.go:93] trust: policy denied the request: Deny "iksoon.registry.com/testproject/tomcat:1.0.0", failed to get content trust information: No valid trust data for 1.0.0
I0118 22:17:57.476425       1 controller.go:125] Deny for images:  [iksoon.registry.com/testproject/tomcat:1.0.0]

 

 
 
다른 Registry 에서 image pull 시도.
 
[Deployment 생성 시 사용한  image]
image: peksoon/iksoon_tomcat:1.0.6
iksoon.docker.com 에서 받지 않고 docker hub의 peksoon registry에서 배포 시도.
Error from server: error when creating "deployment.yaml": admission webhook "trust.hooks.securityenforcement.admission.cloud.ibm.com" denied the request:
Deny "peksoon/iksoon_tomcat:1.0.6", no matching repositories in the ImagePolicies

 
 
 
구축 완료.
 
 
 

 

반응형