반응형
Portieris
[Container image 인증 관련 글 목록]
Docker Notary : Docker Content Trust (DCT)
DCT를 사용해서 Dcoker Hub에 서명된 Trust Image Push 하기
Notary와 DCT를 사용해서 Private Registry에 서명된 Trust Image Push하기
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)에 기록된다.
오류가 표준 출력(stdout)에 기록된다.
health:
CoreDNS의 상태(healthy)가 http://localhost:8080/health 에 기록된다.
이 확장 구문에서 lameduck 은 프로세스를 비정상 상태(unhealthy)로 만들고, 프로세스가 종료되기 전에 5초 동안 기다린다.
CoreDNS의 상태(healthy)가 http://localhost:8080/health 에 기록된다.
이 확장 구문에서 lameduck 은 프로세스를 비정상 상태(unhealthy)로 만들고, 프로세스가 종료되기 전에 5초 동안 기다린다.
ready:
8181 포트의 HTTP 엔드포인트가, 모든 플러그인이 준비되었다는 신호를 보내면 200 OK 를 반환한다.
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 옵션은 파드 레코드를 사용하지 않을 경우 사용된다.
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 에서 사용 가능하다.
CoreDNS의 메트릭은 프로메테우스 형식(OpenMetrics 라고도 알려진)의 http://localhost:9153/metrics 에서 사용 가능하다.
forward:
쿠버네티스 클러스터 도메인에 없는 쿼리들은 모두 사전에 정의된 리졸버(/etc/resolv.conf)로 전달된다.
쿠버네티스 클러스터 도메인에 없는 쿼리들은 모두 사전에 정의된 리졸버(/etc/resolv.conf)로 전달된다.
cache:
프론트 엔드 캐시를 활성화한다.
프론트 엔드 캐시를 활성화한다.
loop:
간단한 전달 루프(loop)를 감지하고, 루프가 발견되면 CoreDNS 프로세스를 중단(halt)한다.
간단한 전달 루프(loop)를 감지하고, 루프가 발견되면 CoreDNS 프로세스를 중단(halt)한다.
reload:
변경된 Corefile을 자동으로 다시 로드하도록 한다.
컨피그맵 설정을 변경한 후에 변경 사항이 적용되기 위하여 약 2분정도 소요된다.
변경된 Corefile을 자동으로 다시 로드하도록 한다.
컨피그맵 설정을 변경한 후에 변경 사항이 적용되기 위하여 약 2분정도 소요된다.
loadbalance:
응답에 대하여 A, AAAA, MX 레코드의 순서를 무작위로 선정하는 라운드-로빈 DNS 로드밸런서이다.
응답에 대하여 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
구축 완료.
반응형
'Kubernetes > Kubernetes 보안' 카테고리의 다른 글
Kubernetes 보안 설정 automountServiceAccountToken: false (0) | 2022.05.05 |
---|---|
kube-bench (0) | 2022.05.01 |
Kubernetes ImagePolicyWebhook (0) | 2021.04.05 |
Kubernetes image인증 - Portieris (0) | 2020.12.22 |
Kubernetes 인증 ( Proxy ) (0) | 2020.08.29 |