Private Docker Register 구성기 (Root CA 인증서가 필요하다!)
본 포스팅은 오프라인 환경에서 kubeadmin을 사용한 Kubernetes 클러스터 설치를 위해 Docker Private Docker Registry를 구성하는 과정에서의 Try & Learn 에 대해 다루고 있습니다.
온라인 환경에서의 K8S 클러스터 설치 과정은 아래 포스팅을 참고해 주세요.
Hyper-V 에 VM을 만들어 kubernetes 클러스터 구성하기
Quick start systemctl stop firewalld ; \ systemctl disable firewalld ; \ systemctl mask --now firewalld ; \ firewall-cmd --state ; \ sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/confi..
wiki.tistory.com
kubeadm init --image-repository
kubeadm init --upload-certs --pod-network-cidr=10.10.0.0/16 --image-repository=docker-registry.private.net:5000
위 명령어를 실행할 경우 발생된 다양한 에러 상황을 해결하는 과정에서의 경험을 기록한다.
[root@vm ~]# kubeadm init --upload-certs --pod-network-cidr=10.10.0.0/16 --image-repository=registry:5000
[init] Using Kubernetes version: v1.24.0
[preflight] Running pre-flight checks
[WARNING Swap]: swap is enabled; production deployments should disable swap unless testing the NodeSwap feature gate of the kubelet
[WARNING Hostname]: hostname "vm.test" could not be reached
[WARNING Hostname]: hostname "vm.test": lookup vm.test on 8.8.8.8:53: no such host
- "vm.test" could not be reched --> /etc/hosts 파일에 호스트명과 IP 주소를 적절히 매핑해주면 된다.
- swap is enabled --> 시스템의 swap 기능을 비활성화 한다.
여기까지는 무난하다.
[ERROR ImagePull]: failed to pull image docker-registry.private.net:5000/coredns:v1.8.6: output: time="2022-05-23T01:22:19-04:00" level=fatal msg="pulling image: rpc error: code = Unknown desc = failed to pull and unpack image \"docker-registry.private.net:5000/coredns:v1.8.6\": failed to resolve reference \"docker-registry.private.net:5000/coredns:v1.8.6\": failed to do request: Head \"https://docker-registry.private.net:5000/v2/coredns/manifests/v1.8.6\": x509: certificate is not valid for any names, but wanted to match docker-registry.private.net"
위와 같이 x509 또는 certificate 관련 에러가 발생하면서 지정한 image-repository에 연결 문제가 발생하여 다음과 같이 인증서 작업을 진행하였다.
1. ROOT CA 인증서 생성
1.1. CA 가 사용할 RSA key pair(public, private key) 생성
openssl genrsa -aes256 -out privatenet-rootca.key 2048 # 비밀번호를 입력하고 잘 기억합니다.
chmod 600 privatenet-rootca.key # 소유자만 접근 가능하도록 권한을 변경하여 줍니다.
1.2. CSR(Certificate Signing Request) 생성
CSR 생성을 보다 편리하게 하기 위해 openssl 설정 파일을 만들어 사용합니다.
파일에서 다음의 항목을 주의깊게 살펴 작성합니다.
- default_keyfile
# rootca_openssl.conf
[ req ]
default_bits = 2048
default_md = sha1
default_keyfile = privatenet-rootca.key
distinguished_name = req_distinguished_name
extensions = v3_ca
req_extensions = v3_ca
[ v3_ca ]
basicConstraints = critical, CA:TRUE, pathlen:0
subjectKeyIdentifier = hash
##authorityKeyIdentifier = keyid:always, issuer:always
keyUsage = keyCertSign, cRLSign
nsCertType = sslCA, emailCA, objCA
[req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = KR
countryName_min = 2
countryName_max = 2
# 회사명 입력
organizationName = Organization Name (eg, company)
organizationName_default = Privatenet Inc.
# 부서 입력
#organizationalUnitName = Organizational Unit Name (eg, section)
#organizationalUnitName_default = K8S Project
# SSL 서비스할 domain 명 입력
commonName = Common Name (eg, your name or your server's hostname)
commonName_default = Privatenet's Self Signed CA
commonName_max = 64
1.3. Root CA 용 CSR 생성
Root CA 용 CSR 요청 파일을 생성합니다.
openssl req -new -key privatenet-rootca.key -out privatenet-rootca.csr -config rootca_openssl.conf
1.4. Self-signed 인증서 생성
self-signed 인증서를 생성한다.
다음의 코드에서는 10년짜리 인증서가 -out 옵션 뒤에 기술한 파일명(예: privatenet-rootca.crt) 으로 생성됩니다.
openssl x509 -req -days 3650 \
-extensions v3_ca \
-set_serial 1 \
-in privatenet-rootca.csr \
-signkey privatenet-rootca.key \
-out privatenet-rootca.crt \
-extfile rootca_openssl.conf
1.5. 인증서 정보 확인
생성한 인증서가 잘 만들어 졌는지 확인합니다.
openssl x509 -text -in privatenet-rootca.crt
2. SSL 인증서 발급
위 1에서 생성한 root ca 서명키로 SSL 인증서를 발급하도록 하겠습니다.
2.1.키 페어 생성
2.1.1. RSA 키페어 생성 (public, private key)
SSL 호스트에서 사용할 RSA key pair(public, private key) 생성을 합니다.
openssl genrsa -aes256 -out privatenet.key 2048 # 2048bit 개인키 생성
2.1.2. 개인키 암호 제거
개인키를 보호하기 위해 Key-Derived Function 으로 개인키 자체는 암호화 되어 있습니다. 인증서는 당연히 암호화 되어 보호되어야 하지만 서버에서 사용시 개인키에 암호가 걸려있을 경우 서버 구동시마다 pass phrase 를 입력해야 하므로 암호를 제거하도록 합니다.
cp privatenet.key privatenet.key.enc
openssl rsa -in privatenet.key.enc -out privatenet.key
2.1.3. 개인키 유출 방지
개인키 유출 방지를 위해 소유자 외 권한을 모두 제거합니다.
chmod 600 privatenet.key*
2.2. SSL 인증서 CSR
2.2.1. SSL 인증서 발급을 위한 CSR(Certificate Signing Request) 생성
CSR(Certificate Signing Request) 생성을 위한 openssl config 파일을 만들고 host_openssl.conf(변경 가능) 라는 이름으로 저장한다.
# host_openssl.conf
[ req ]
default_bits = 2048
default_md = sha1
default_keyfile = privatenet-rootca.key
distinguished_name = req_distinguished_name
extensions = v3_user
## 인증서 요청시에도 extension 이 들어가면 authorityKeyIdentifier 를 찾지 못해 에러가 나므로 막아둔다.
## req_extensions = v3_user
[ v3_user ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
authorityKeyIdentifier = keyid,issuer
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
## SSL 용 확장키 필드
extendedKeyUsage = serverAuth,clientAuth
subjectAltName = @alt_names
[ alt_names]
## Subject AltName의 DNSName field에 SSL Host 의 도메인 이름을 적어준다.
## 멀티 도메인일 경우 *.private.net 처럼 쓸 수 있다.
DNS.1 = docker-registry.private.net
DNS.2 = private.net
DNS.3 = *.private.net
[req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = KR
countryName_min = 2
countryName_max = 2
# 회사명 입력
organizationName = Organization Name (eg, company)
organizationName_default = privatenet Inc.
# 부서 입력
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = PrivateNet SSL Project
# SSL 서비스할 domain 명 입력
commonName = Common Name (eg, your name or your server's hostname)
commonName_default = docker-registry.private.net
commonName_max = 64
2.2.2. 인증서 발급 요청(CSR) 파일 생성
openssl req -new -key privatenet.key -out privatenet.csr -config host_openssl.conf
2.2.3. private.net 용 SSL 인증서 발급 (서명시 ROOT CA 개인키로 서명)
openssl x509 -req -days 1825 -extensions v3_user -in privatenet.csr \
-CA privatenet-rootca.crt -CAcreateserial \
-CAkey privatenet-rootca.key \
-out privatenet.crt -extfile host_openssl.conf
루트 인증서를 포함한 사설 인증서 발급 과정은 다음의 포스팅을 참조하였으니 본 포스팅에서 다루지 않는 보다 상세한 내용은 다음 페이지를 참조하시기 바랍니다.
[https://www.lesstif.com/system-admin/openssl-root-ca-ssl-6979614.html]
3. 생성된 인증서 관련 파일 목록
위 1 ~ 2 과정을 통해 생성된 파일 목록은 다음과 같습니다.
- host_openssl.conf
- privatenet.crt
- privatenet.csr
- privatenet.key
- privatenet.key.enc
- privatenet-rootca.crt
- privatenet-rootca.csr
- privatenet-rootca.key
- privatenet-rootca.srl
- rootca_openssl.conf
4. Docker 이미지로 Registry 컨테이너 생성
앞서 생성한 SSL 인증서 및 key 파일과 registry 기동을 위한 docker-compose.yml, registry 설정파일을 다음과 같이 준비합니다.
# tree registry
.
├── certs
│ ├── privatenet.crt
│ └── privatenet.key
├── config.yml
└── docker-compose.yml
# cat config.yml
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: docker-registry.private.net:5000
headers:
X-Content-Type-Options: [nosniff]
tls:
certificate: /certs/privatenet.crt
key: /certs/privatenet.key
#auth:
# htpasswd:
# realm: example.com
# path: /etc/docker-distribution/dockerpasswd
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
# cat docker-compose.yml
version: "3.1"
services:
docker-registry:
image: registry:latest
container_name: registry
hostname: docker-registry.private.net
environment:
- TZ=Asia/Seoul
- REGISTRY_HTTP_TLS_CERTIFICATE=/certs/privatenet.crt
- REGISTRY_HTTP_TLS_KEY=/certs/privatenet.key
ports:
- 5000:5000
restart: always
volumes:
- ./config.yml:/etc/docker/registry/config.yml
- ./registry:/var/lib/registry
- ./certs:/certs
준비된 컴포즈 파일로 컨테이너를 기동합니다.
docker compose up -d
5. Client 호스트에서 인증서 복사
5.1. ca-certificates 설치
yum install -y ca-certificates
5.2. 호스트에 인증서 파일에 복사
다음 위치에 registry 서버에서 발행한 루트 인증서 및 SSL용 인증서파일을 복사합니다.
# tree /etc/pki/ca-trust/source/anchors
├ privatenet.crt
└ privatenet-rootca.crt
5.3. docker에 인증서 파일 복사
/etc/docker/certs.d/docker-registry.private.net:5000/privatenet.crt
인증서 갱신 및 dockerd 재기동
update-ca-trust
systemctl restart docker
systemctl restart containerd
서버를 한번 재기동 하는 편이 좋습니다.