반응형
Service Accounts
- Service Account 의 개념은 Authentication, Authorization, RBAC 등의 쿠버네티스의 다른 보안 관련 개념과 연결되어 있다.
- 쿠버네티스에서 Account는 두 개의 유형으로 분리되어 있다. 하나는 User Account 또 다른 하나는 Service Account. User Account는 사람이 사용하고 Service Account는 컴퓨터가 사용한다.
- User Account는 관리 작업을 수행하기 위해 클러스터에 액세스하는 관리자가 될 수 있고 응용프로그램 배포 등을 위해 클러스터에 액세스하는 개발자가 될 수 있다.
- Service Account는 앱이 쿠버네티스 클러스터와 상호 작용할 때 사용하는 계정이다. 예를 들어 프로메테우스 같은 모니터링 애플리케이션은 Service Account를 이용해 쿠버네티스 APi를 퍼포먼스 메트릭으로 뽑아낸다. 또 Jenkins 같은 자동화 툴은 서비스 계정을 이용해 쿠버네티스 클러스터에 앱을 배포하기도 한다.
kubectl create sa dashboard-sa
kubectl get sa
kubectl describe sa dashboard-sa
# serviceaccount는 sa로 축약할 수 있다.
- Service Account를 생성 및 확인하는 명령어는 위와 같다.
- Service Account 가 생성되면 자동으로 토큰도 생성된다.
- 토큰은 Secret 객체로 저장이 되며, 토큰은 kubernetes API에 인증하는 데 사용된다.
- 서비스 어카운트가 생성되고 나서 그 다음에 토큰이 생성되고 토큰을 시크릿 오브젝트 내에 저장한다. 이후 시크릿 오브젝트는 서비스 어카운트에 연결된다.
- 이 토큰은 kubernetes API에 REST 호출을 하는 동안 인증 토큰으로 사용된다.
- 예를 들어 crul을 이용해서 api server에 요청을 날릴 때 헤더 정보에 토큰 값을 넣어 인증을 해준다.
- 서비스 어카운트를 생성하고 RBAC를 사용해 올바른 권한을 할당할 수도 있다. 그리고 서비스 어카운트를 export해서 타사 응용 프로그램을 구성하고 kubernetes API에 대한 인증을 수행할 수 있다.
- 그러나 타사 응용 프로그램이 kubernetes 자체에 호스팅되는 경우 어떻게 할까?
- 예를 들어 사용자 지정 kubernetes 대시보드 응용 프로그램이나 Prometheus 응용 프로그램이 kubernetes 클러스터에 배포된 경우이다. 이 경우 ServiceAccount 토큰을 내보내고 타사 응용 프로그램을 구성하는 전체 프로세스를 간단하게 만들 수 있다.
- 이를 위해 서비스 토큰 시크릿을 파드 내에 볼륨으로 자동으로 마운트하면 된다. kubernetes API에 액세스하기 위한 토큰이 이미 파드 내에 배치되어 응용 프로그램에서 쉽게 읽을 수 있다.
- curl 명령어를 입력할 때 옵션을 주어서 수동으로 입력하거나, 응용프로그램에서 외부에 있는 Secret 값을 참조하는 수동으로 제공하는 방법이 필요가 없어진다.
- ServiceAccount 목록을 확인하면 default 서비스 어카운트가 이미 존재하는 것을 볼 수 있다.
- kubernetes의 각 네임 스페이스에는 자동으로 default라는 이름의 ServiceAccount가 생성되며 각 네임스페이스마다 default ServiceAccount가 생성된다.
- 파드가 생성될 때마다 default 서비스 어카운트와 해당 토큰이 자동으로 볼륨 마운트로 파드에 마운트 된다.
- 예를 들어 따로 지정하지는 않았지만 파드를 생성한 직후 kubectl describe pod 명령을 실행해 세부 정보를 확인하면 default token 이라는 시크릿에서 자동으로 생성된 볼륨이 보이며, 이는 사실 default ServiceAccount에 대한 토큰을 포함하는 시크릿이다.
- 시크릿 토큰은 파드 내에서 '/var/run/secrets/kubernetes.io/serviceaccount' 위치에 마운트 된다. 해당 디렉토리를 확인하면 실제 쿠버네티스 API 액세스에 사용되는 토큰이 포함된 token이라는 파일이 있는 것을 알 수 있다.
- 그러나 default 서비스 어카운트는 기본적인 kubernetes API 쿼리를 실행할 권한만 가지고 있으며, 매우 제한적임을 명시해야한다.
- 방금 생성된 기본 ServiceAccount와 유사한 다른 ServiceAccount를 사용하고 싶다면 파드 정의 파일을 수정해 spec.serviceAccountName 필드를 포함시키면 된다.
- 기존 파드의 ServiceAccount는 수정되지 않으니 포함 시키고나서 파드를 삭제하고 다시 재생성 해야된다. Deployments의 경우엔 ServiceAccount를 edit 할 수 있다.
- 결론적으로 kubernetes는 명시적으로 serviceAccountName을 지정하지 않은 경우 default ServiceAccount를 자동으로 마운트한다. 파드의 spec 섹션에서 automountServiceAccountToken: false 로 설정하게 되면 ServiceAccount를 자동으로 마운트하지 않도록 선택할 수도 있다.
- kubernetes 1.22 및 1.24 버전에서는 몇가지 변경 사항이 있다.
- 먼저 1.22 버전을 알아보자
- ServiceAccount, 시크릿, 토큰이 작동하는 병식을 변경한 내용이다.
- 이전에는 각 네임 스페이스에는 기본 서비스 어카운트가 있으며 해당 서비스 어카운트에는 토큰이 연결된 시크릿 객체가 있다.
- 파드가 생성될 때 자동으로 서비스 어카운트를 파드에 연결하고 토큰을 파드 내의 '/var/run/secrets/kubernetes.io/serviceaccount' 디렉토리에 위치시킨다.
- 이렇게 하면 파드 내에서 실행 중인 프로세스에서 토큰에 액세스할 수 있고 해당 프로세스가 kubernetes API에 쿼리할 수 있게 된다.
- 그리고 해당 디렉토리에 있는 token 파일을 jwt로 디코딩해보면 페이로드 섹션에서 만료 날짜가 정의되지 않았음을 확일할 수 있다.
- 만료 날짜가 설정되어 있지 않은 토큰이기에 보안 및 확장성과 관련된 문제가 있다고 한다. 따라서 이 JWT는 ServiceAccount를 삭제하지 않는 한 지속적으로 권한을 갖는다는 이야기다.
- 그리고 각 JWT는 ServiceAccount 당 별도의 Secret 객체가 필요하며 이는 확장성 문제로 이어진다.
- 그래서 TokenRequestAPI가 도입되었다.
- TokenRequestAPI는 더 안전하고 확장 가능한 방식으로 ServiceAccount 토큰을 프로비저닝하기 위해 도입되었다.
- Audience Bound : 관객 바인딩, 토큰을 소비할 의도된 서비스 또는 구성 요소로 표시되는 특정 대상에 바인딩된다.
- Time Bound : 시간 바인딩, 토큰에 만료 시간을 부과하여 유효 기간을 제한한다.
- Object Bound : 객체 바인딩, 토큰을 특정 kubernetes 개체(일반적으로 파드)에 바인딩된다.
- 1.22 버전 이후에 파드를 생성할 때 TokenRequestAPI에서 생성된 토큰은 대상, 시간, 객체에 바운드 되어 보다 안전하다.
- 새로운 파드가 생성될 때 ServiceAccount 시크릿 토큰에 의존하지 않고, ServiceAccount 어드미션 컨트롤러에 의해 TokenRequestAPI를 통해 수명이 정의된(expirationSecends를 이용한) 토큰이 생성되고 이 토큰이 파드에 프로젝트된 볼륨으로 마운트된다.
- 1.24 버전에서는 또 다르다.
- 1.24에서는 ServiceAccount를 생성할 때 자동으로 시크릿이나 토큰 액세스 시크릿을 생성하지 않는다. 따라서 해당 ServiceAccount에 대한 토큰이 필요하면 kubectl create token 명령 다음에 서비스 어카운트의 이름을 지정해 해당 서비스 어카운트에 대한 토큰을 생성해야 한다.
kubectl create token <service account name>
- 토큰은 표준 출력으로 화면에 출력되며, 해당 토큰을 복사하고 이 토큰을 디코딩 해보면 페이로드 섹션에 만료 날짜가 정의된 것을 볼 수 있다. 시간 제한을 지정하지 않았다면 일반적으로 명령을 실행한 시간으로부터 1시간 후가 된다. 명령에 추가 옵션을 전달하면 토큰의 만료 기간을 늘릴 수도 있다.
- kubectl create token --duration=DURATION
- DURATION 은 SECONDS, MINUTE, HOURS, DAYS 등으로 지정 가능
- 예를 들어 --duration=10m, --duration=24h, --duration=100y
- kubectl create token --duration=DURATION
- 서비스 계정 토큰 비밀 객체보다 TokenRequestAPI를 사용하는 것이 ServiceAccount 토큰 Secret 객체보다 권장된다. kubernetes 문서에서도 "TokenRequest API를 사용할 수 없을 때만 서비스 어카운트 토큰 시크릿을 생성해야 한다" 라고 설명한다.
Image Security
- 이미지 보안에 대해 이야기 해보자.
- 이미지 이름의 기본 내용부터 시작해 안전한 이미지 저장소 및 파드를 구성해 이미지를 안전한 저장소에서 사용하는 방법에 이르기까지 살펴볼 것이다.
- 먼저 이미지 이름이다. 예를 들어 nginx 이미지를 예로 들어 설명하자면 이 이미지는 어디에서 가져오는가?
- 이름은 Docker의 이미지 명명 규칙을 따른다. 여기서 Nginx는 이미지 또는 레포지토리 이름이다.
- Nginx라고 말할 때 실제로는 library/Nginx 이다. 첫 부분은 사용자 또는 계정 이름을 나타내지만 사용자 또는 계정 이름을 제공하지 않으면 library로 간주된다.
- 제공하지 않은 경우 library는 Docker의 공식 이미지가 저장된 기본 계정으로 간주된다.
- 이 이미지는 공식 이미지를 검토하고 게재하는 팀이 유지보수 하고 최상의 이미지를 제공해준다.
- 자체 계정을 만들고 해당 계정 아래에 자체 레포지토리 또는 이미지를 만들 경우에도 비슷한 패턴을 사용할 것이다.
- library 대신에 자체 계정이나 회사 이름을 사용할 것이다.
- 이미지를 가져오는 위치를 지정하지 않으면 이 이미지는 Docker의 기본 레지스트리인 Docker Hub로 가정된다. 이 레지스트리 DNS는 docker.io이다.
- 공개적으로 사용할 수 없어야 하는 내부에서 개발한 어플리케이션이 있다면 내부의 Private 레지스트리를 호스팅하는 것이 좋은 솔루션일 수 있다.
- AWS, Azure, GCP와 같은 클라우드 서비스 제공자는 기본적으로 Private 레지스트리를 제공한다.
- Docker의 관점에서 비공개 이미지를 사용해 컨테이너를 실행하려면 Docker login 명령어를 사용해 개인 레지스트리에 자격 증명을 이용해 로그인 하고 비공개 레지스트리에서 이미지를 사용할 수 있다.
- 비공개 레지스트리에서 이미지를 사용하려면 파드 정의 파일에서 이미지의 이름을 Private 레지스트리의 전체 경로로 바꾸면 된다.
- 그러나 로그인 부분을 어떻게 구현해야 하는가?
- kubernetes에서 이미지는 워커 노드의 Docker 런타임에서 가져오고 실행된다. 워커 노드의 도커 런타임에 자격 증명을 전달하는 방법은 자격 증명이 들어있는 Secret 객체를 만드는 것이다.
- 이 Secret은 docker registry 유형이며 레지스트리 서버 이름, 사용자 이름, 사용자 암호, 사용자 이메일 주소를 지정해주어 생성해주면 되고, 파드에서 imagePullSecrets 섹션을 이용해 이를 참조해주면된다.
- Pod가 아니라 deployment에 imagePullSecrets 를 설정하는 경우 spec.template.spec에 넣어주면 된다.
- 파드가 생성되면 워커 노드에 있는 kubelet은 Secret에 있는 자격 증명을 이용해 이미지를 가져오게 된다.
Security in Docker (건너뛰어도 됨)
- Docker의 보안과 관련된 개념을 살펴보자
- Docker가 설치된 호스트가 있는 상황에서 이 호스트에는 OS, Dockerd, SSH 서버 등이 실행 중인 일련의 프로세스가 있다고 가정해보자
- 컨테이너와 호스트는 동일한 커널을 공유하기 때문에 가상 머신과 달리 컨테이너는 호스트와 완전히 격리되지 않는다
- Linux에서는 네임스페이스를 사용해 컨테이너를 격리한다.
- 호스트의 네임스페이스도 있고 컨테이너에는 또한 자체 네임스페이스가 있다. 컨테이너에서 실행된 모든 프로세스는 사실상 호스트 자체에서 실행되지만 자체 네임스페이스에서 실행된다.
- Docker 컨테이너는 자체 네임스페이스에 있으며 자체 프로세스만 볼 수 있다. 그 외 다른 네임스페이스에 있는 것을 볼수는 없다.
- 컨테이너 내에서 프로세스 목록을 나열하면 해당 컨테이너에 해당하는 프로세스만 표시된다. 예를 들어 sleep 프로세스가 실행되는 이미지로 컨테이너를 띄웠다고 가정하면 호스트에서는 sleep 프로세스를 볼 수 없고 컨테이너 내에서 확인해야 볼 수 있다.
- Docker 호스트에서는 자체 프로세스와 하위 네임스페이스에 있는 프로세스를 포함한 시스템의 다른 프로세스로 표시된다.
- 호스트에서 프로세스 목록을 나열하면 다른 프로세스 ID로 표시된 sleep 명령을 포함한 프로세스 목록이 표시된다. 이는 프로세스가 다른 네임스페이스에서도 다른 프로세스 ID를 가질 수 있기 때문에 발생하는 것이고, Docker가 시스템 내에서 컨테이너를 격리하는 방식으로 이해하면 된다.
- 이제 보안 관점에서 사용자를 살펴보면 Docker 호스트에는 루트 사용자와 여러 비루트 사용자가 있는 사용자 집합이 있다. 기본적으로 Docker는 컨테이너 내에서 프로세스를 루트 사용자로 실행한다.
- 컨테이너 내부와 호스트 외부에서 모두 프로세스는 루트 사용자로 실행된다.
- 컨테이너 내부의 프로세스가 루트 사용자로 실행되지 않도록하려면 docker run 명령의 user 옵션을 사용하고 새로운 사용자 ID를 지정할 수 있다.
- 또한 다른 방법으로는 Docker 이미지 자체에 User를 지정할 수 있다.
- 컨테이너를 루트 사용자로 실행하면 또 어떤가? 컨테이너 내부의 루트 사용자는 호스트의 루트 사용자와 같은 사용자인가? 컨테이너 내부의 프로세스는 시스템에서 루트 사용자가 할 수 있는 모든 작업을 수행할 수 있을까?
- 컨테이너 내부의 프로세스가 호스트의 역할까지 할수 있으면 위험하다.
- Dcoker는 루트 사용자 내부의 컨테이너에 대한 능력을 제한하는 보안 기능이 있다. 이를 Linux Capabilities를 통해 구현한다.
- 루트 사용자는 실제로 모든 작업을 수행할 수 있고, 루트 사용자가 실행하는 프로세스도 시스템에 무제한 액세스 권한이 있다.
- 파일 및 파일 권한 수정, 액세스 제어, 프로세스 생성 또는 종료, 그룹 ID 또는 사용자 ID 설정, 네트워크 관련 작업 수행(두 네트워크 포트 바인딩, 네트워크 브로드캐스팅, 네트워크 포트 제어 등), 시스템 관련 작업 수행 (호스트 재부팅, 시스템 Time 조작 등) 여러가지 기능이 있다.
- /usr/include/linux/capability.h 에서 전체 리스트를 볼 수 있다.
- 특정 capabilities를 사용할 수 있게끔 --cap 파라미터를 이용할수도 있다.
- 모든 capabilities 를 사용하고자 하는 경우 privileged 파라미터를 사용하면 된다.
Security Contexts
- 위에서 Docker에 대한 권한 관리를 알아 보았는데 Kubernetes에서도 이와 같은 보안 기준을 정의할 수 있다.
- 쿠버네티스에서는 컨테이너가 파드에 캡슐화된다. 파드 수준에서 설정하면 해당 설정이 파드 내의 모든 컨테이너로 전파된다.
- 파드 및 컨테이너 양쪽에서 구성하는 경우 컨테이너 설정이 파드의 설정을 덮어 쓴다. 컨테이너 설정이 더 우위라는 말
- 컨테이너에서 보안 컨텍스트를 구성하려면 파드의 spec 섹션 하위에 securityContext라는 필드를 추가하면 된다.
- 또한 Pod의 사용자 ID를 설정하려면 runAsUser 옵션을 사용하면 된다.
- 컨테이너 수준에서 동일한 구성을 설정하려면 securityContext 섹션을 컨테이너 범위로 이동하면 된다.
- 또한 기능을 추가하려면 capabilities 옵션을 사용하고 파드에 추가할 기능 목록을 지정하면 된다.
Network Policy
- 네트워크에는 인그레스와 이그레스라는 두 가지 유형의 트래픽이 있다.
- 쿠버네티스에서는 노드가 있고 각 노드에는 일련의 파드와 서비스가 있다. 각 노드와 파드 및 서비스는 IP 주소가 있고, 쿠버네티스 네트워킹은 파드가 경로와 같은 추가 설정을 구성하지 않고도 서로 통신할 수 있다.
- 모든 파드가 쿠버네티스 클러스터의 노드를 가로지르는 가상 프라이빗 네트워크에 속해 있으며, 기본적으로 서로의 IP나 파드 이름 또는 서비스를 통해 상호작용할 수 있다.
- 쿠버네티스는 기본적으로 모든 트래픽을 허용하는 all allow 규칙으로 구성되어 있어 클러스터 내의 모든 파드나 서비스에 대한 트래픽을 서로 전송할 수 있다.
- 그러나 네트워크 정책을 설정하면 트래픽을 제한하고 허용할 수 있다.
- 네트워크 정책은 파드, 레플리카셋, 서비스 등에 연결되며 한 개 이상의 파드에 네트워크 정책을 연결할 수 있다. 네트워크 정책 내에서 규칙을 정의해 트래픽을 허용 또는 제한할 수 있다.
- 위와같이 DB Pod에 3306 포트 인그레스 트래픽을 허용하고자 한다면
- 먼저 network policy와 pod를 label 기반으로 매칭 시켜주고, 그 다음 포트를 설정해주면 된다.
- apiVersion은 networking.k8s.io/v1 을 사용하고 스펙 섹션 아래에 selector와 policy에 대한 설정을 해주면 된다.
- 네트워크 정책은 쿠버네티스 클러스터에 구현된 네트워크 솔루션에 의해 강제되며 모든 네트워크 솔루션에서 네트워크 정책을 지원하지는 않는다.
- 지원되는 몇 가지 솔루션에는 kube-router, calico, romana, weave-net 등이 있고, Flannel을 사용하는 경우 networkpolicy를 지원하지 않는다.
Developing network policies
- 기본적으로 쿠버네티스는 모든 파드에서 모든 목적지로의 모든 트래픽을 허용한다. 그러나 명시적으로 거부를 하면 모든 트래픽이 차단된다.
- 요구 사항을 충족시키기 위해서는 이 network policy 객체에 어떤 유형의 정책을 정의해야 하는지 파악해야한다.
- ingress 와 egress 두 가지 유형의 정책이 있는데, 항상 특정 파드의 관점에서 바라보고 트래픽을 허용하는 것이 좋다.
- 인그레스 트래픽은 한번 허용이 되면 이그레스를 설정할 필요는 없다. 왜냐하면 한번 들어오는 트래픽을 허용하면 해등 트래픽에 대한 Response가 자동으로 허용되기 때문이다.
- 그래서 Request가 발생하는 방향에 대해서만 고려해야 한다.
- ingress의 경우 from 및 ports 필드가 있다.
- from 필드는 트래픽이 출발하는 원본을 정의하며 podSelector를 통해서 레이블 기반으로 파드를 결정한다.
- ports의 경우 인그레스의 경우 어떤 포트로 트래픽이 허용되는지를 정의한다.
- 그러나 동일한 레이블을 갖고 다른 네임스페이스에 여러 pod가 있는 경우 어떻게 될까?
- 아래 그림에서는 dev, test 및 prod 환경을 위한 서로 다른 네임스페이스에서 여러 pod가 DB pod로 접근하고자 허용해야한다.
- 이를 위해 namespace selector라는 새로운 셀렉터를 이용하게 된다.
- 이 경우에는 prod 네임스페이스에 속하는 pod만 DB pod에 접근이 가능할 것이다.
- podSelector 가 없고 namespaceSelector 만 있는 경우엔 지정된 네임스페이스 내의 모든 파드가 DB pod에 접근이 가능하다.
- 또한 from 밑을 유심히 보면 - 대시 항목에 podSelector와 namespaceSelector 가 같이 지정되어 있다. 이 말인 즉슨 두 개의 항목이 AND 연산으로 이루어져 둘 다 만족해야 허용이 된다는 이야기이다.
- 또 다른 예시로 쿠버네티스 외부의 어딘가에 백업 서버가 있다고 가정하고 이 서버가 DB Pod에 접근해야한다고 가정해보자.
- ipBlock을 정의하게 되면 IP 주소 범위에 해당하는 트래픽을 모두 허용할 수 있다. 백업 서버의 ip를 ipBlock을 이용해 허용시켜주면 된다.
- 위 그림에서는 podSelector 와 namespaceSelector 그리고 ipBlock을 각각 분리시켜 OR 연산을 하게끔 만들어 주었다.
- egress의 경우도 동일하다.
- 대신 egress 는 from이 아니라 to라는 섹션이 사용된다.
- 중요한 것은 어디서 어떤 트래픽이 허용되고 거절되어야하는 지 요구 사항을 충족 시키는 것이다.
Kubectx and Kubens
- kubectx는 컨텍스트 관련된 명령어이다.
- 컨텍스트를 변경하거나 현재 컨텍스트를 출력하거나 마지막으로 작업했던 컨텍스트로 돌아갈 수 있다.
설치
sudo git clone https://github.com/ahmetb/kubectx /opt/kubectx
sudo ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx
명령어
# 기본 명령어
kubectx
# 컨텍스트 전환 명령어
kubectx <context name>
# 직전 작업 컨텍스트로 전환
kubectx -
# 현재 컨텍스트 출력
kubectx -c
- kubens는 네임스페이스 간에 빠르게 전환할 수 있는 명령어이다.
설치
sudo git clone https://github.com/ahmetb/kubectx /opt/kubectx
sudo ln -s /opt/kubectx/kubens /usr/local/bin/kubens
명령어
# 네임스페이스 변경
kubens <namespace name>
# 직전 네임스페이스로 이동
kubens -
반응형
'자격증 > Kubernetes CKA' 카테고리의 다른 글
[CKA] Networking - 1 (1) | 2023.12.26 |
---|---|
[CKA] Storage (0) | 2023.12.19 |
[CKA] Security - 2 (0) | 2023.12.13 |
[CKA] Security - 1 (0) | 2023.12.12 |
[CKA] Cluster Maintenance (0) | 2023.12.04 |