data engineering

about self signed certificate(ssc), apply my ssc to rke

qkqhxla1 2021. 10. 9. 20:55

인증서에 대해 아는 지식이 거의 없어서.. 이번에 self signed certificate를 만들면서 알게된 내용을 정리함. 여기저기 찾아보고 정리하긴 했는데 확실하지 않은 정보가 좀 있어서 틀린부분 있으면 지적해주시면 정말 감사하겠음..

읽을거리(개념정리용) : https://m.blog.naver.com/alice_k106/221468341565
대부분의 내용 참조함.(실습용) : https://www.lesstif.com/system-admin/openssl-root-ca-ssl-6979614.html
추가 읽을 거리(영어 버전) : https://phoenixnap.com/kb/openssl-tutorial-ssl-certificates-private-keys-csrs

일단은 개념정리를 먼저 하자면 CA(Certificate Authority)라는 곳에서 클라이언트와 서버 사이에서 인증서를 가지고 맞는 클라이언트인지 검증을 해주는데 평소의 인터넷에서 사용시에는 공신력이 있는 민간 기업이 CA를 맡고 있다.
CSR(Certificate Signing Request)란 CA에게 인증서 발급을 해달라고 요청하는 파일 포맷을 말한다. CSR에는 인증서 발급을 해달라고 요청하는 곳의 기관이름이나 국가 등의 정보가 포함되어 있다.

SSC(Self Signed Certificate)란 민간 기업(CA)를 사용하지 않고 스스로 root ca라는 내가 사용할 가상의 CA를 만든후, 내가 만든 키로 서명해서 최상위 인증기관의 인증서를 만들어서 사용하는걸 self signed certificate라고 한다. 즉

유저가 CSR을 사용해서 CA에게 인증서 요청을 보내면 CA에서 인증서를 발급해준다는 얘기이다.

self signed certificate를 만들고 사용해보자. 첫번째로 ROOT CA(최상위 인증기관)와 관련된것들을 만들어보자. 
1. root ca가 사용할 key 생성. 

openssl genrsa -aes256 -out rootca.key 2048

2. 위에서 만든 키로 CSR을 만들건데 CSR을 만들때는 config가 필요하다. config에는 위에서 언급했듯이 CSR을 할 때 필요한 기관이름이나 국가 등등의 정보가 포함되어 있다.

rootca.conf

[req]
default_bits            = 2048
default_md              = sha1
default_keyfile         = 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            = test_company Inc.
organizationalUnitName              = test_name (eg, section)
organizationalUnitName_default      = test_team
commonName                          = Common Name (eg, your name or your server's hostname)
commonName_default                  = test_self_signed_ca
commonName_max                      = 64

위의 rootca.conf과 rootca.key를 사용해서 아래처럼 csr을 만든다.

openssl req -new -key rootca.key -out rootca.csr -config rootca.conf

3. 그리고 위에서 만들어진 csr을 사용해서 root ca 인증서를 만든다. 아래는 10년짜리 rootca인증서를 만드는 예시.

openssl x509 -req -days 3650 -extensions v3_ca -set_serial 1 -in rootca.csr -signkey rootca.key -out rootca.crt -extfile rootca.conf

이제 위와 같은 과정들을 거치면서 다음과 같은 파일이 만들어졌다.
rootca.key -> rootca용 내가 만든 키
rootca.conf -> csr과 rootca 인증서를 만들기 위한 설정 파일.
rootca.csf -> rootca 인증서 요청을 하기 위한 csr파일.
rootca.crt -> rootca 인증서. (openssl x509 -text -in ./rootca.crt 로 잘 만들었는지 확인이 가능하다.)

그리고 이제는 rootca와 관련된 것들이 만들어졌으니 내가 사용할 인증서를 만들어보자. 위에서 적었듯이 원래는 사설 ca에서 인증을 받아야 하지만 self signed certificate은 내가 스스로 싸인해서 만든 root ca를 사용한다. 위에서 rootca를 만들때와 절차가 비슷하다.

1. 내가사용할 key 생성.

openssl genrsa -aes256 -out test.key 2048

2. Passphrase를 키에서 제거.
위의 링크에 따르면 아래처럼 작업을 해서 비밀번호를 입력하지 않아도 되도록 만들수 있다고 한다. 제거하지 않으면 httpd구동때마다 항상 입력해야 하므로 귀찮지 않게 이걸 제거해주는 과정이라고 한다.

cp test.key test.key.enc
openssl rsa -in test.key.enc -out test.key

3. 내가 사용할 CSR생성. 이번에도 conf가 필요한데 myhost.conf라고 이름짓고 만들어본다. 내부 내용은 위의 링크에 나온것에서 이름만 몇개 바꿨다.

myhost.conf

[req]
default_bits            = 2048
default_md              = sha1
default_keyfile         = 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 의 도메인 이름을 적어준다.
## 멀티 도메인일 경우 *.lesstif.com 처럼 쓸 수 있다.
DNS.1   = mytest.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        = mycompany Inc.
 
# 부서 입력
organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_default  = myorganization
 
# SSL 서비스할 domain 명 입력
commonName                      = Common Name (eg, your name or your server's hostname)
commonName_default              = mytest.net
commonName_max                  = 64

이 config와 나의 키를 사용해서 csr을 만든다.

openssl req -new -key test.key -out test.csr -config myhost.conf

위의 2번에서 Passphrase를 제거한 키를 사용하는데, rootca의 csr을 만들때는 rootca.key의 비밀번호를 물어봤지만, 이번에는 제거했으므로 csr을 만들때 비밀번호를 물어보지 않는다.

4. 위의 csr을 사용해 내 인증서 생성. 이번에도 10년짜리를 만든다.

openssl x509 -req -days 3650 -extensions v3_user -in test.csr -CA rootca.crt -CAcreateserial -CAkey rootca.key -out test.crt -extfile myhost.conf

이번에는 rootca를 만들때와는 달리 인자에 CA가 들어가고, 거기에 내가 만든 CA를 넣어줬다.

원래 있던 rootca.conf, rootca.key, rootca.csr, rootca.crt외에
test.key.enc, test.key, test.csr, test.crt 가 생겼다.

-----------------------------------------------------------------------------------------------

rke에 위에서 만든 인증서를 사용해보기 실습 테스트.

이전에 rke를 구축했었는데, rke구축시 기본적으로 cert-manager를 사용했지만 이번에는 cert-manager를 사용하지 않고 
내가 만든 인증서를 사용해서 구축되도록 테스트해보자. 

https://rancher.com/docs/rancher/v2.0-v2.4/en/installation/install-rancher-on-k8s/chart-options/#external-tls-termination
1. rke up에 사용할 cluster.yml에 헤더가 전달되도록 설정해주고 up을 해준다.

ingress:
  provider: nginx
  options:
    use-forwarded-headers: true

2. 그리고 위 링크에 나온것처럼 nginx.conf를 설정해주고 리로드해준다.
아래 설정파일에서 주의해야할건 ssl_certificate와 ssl_certificate_key는 내가 만든 rootca가 아닌, 위에서 만든 test.crt와 test.key를 넣는다는거다. 꼭 아래처럼 확장자가 .pem으로 끝나지 않아도 된다.

worker_processes 4;
worker_rlimit_nofile 40000;

events {
    worker_connections 8192;
}

http {
    upstream rancher {
        server IP_NODE_1:80;
        server IP_NODE_2:80;
        server IP_NODE_3:80;
    }

    map $http_upgrade $connection_upgrade {
        default Upgrade;
        ''      close;
    }

    server {
        listen 443 ssl http2;
        server_name FQDN;
        ssl_certificate /certs/fullchain.pem;
        ssl_certificate_key /certs/privkey.pem;

        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Port $server_port;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://rancher;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            # This allows the ability for the execute shell window to remain open for up to 15 minutes. Without this parameter, the default is 1 minute and will automatically close.
            proxy_read_timeout 900s;
            proxy_buffering off;
        }
    }

    server {
        listen 80;
        server_name FQDN;
        return 301 https://$server_name$request_uri;
    }
}

3. 그리고 랜쳐를 설치할때 --set tls=external, --set privateCA=true인자를 넣어준다.(위 링크에 있음.) 그리고 ingress.tls.source=secret도 넣어주는데, 이건 내가 만든 인증서를 사용하겠다는 의미로 설정하면 cert-manager를 설치하지 않는다. 
https://rancher.com/docs/rancher/v2.0-v2.4/en/installation/resources/advanced/helm2/helm-rancher/#certificates-from-files

helm install rancher rancher-stable/rancher \
  --namespace cattle-system \
  --set hostname=mytest.net \
  --set ingress.tls.source=secret \
  --set privateCA=true \
  --set tls=external

4. 위처럼 ingress.tls.source를 통해 파일에서 가져온다고 했다. 위처럼 랜쳐는 설치되었지만 certificate를 넣어줘야 한다. 위의 링크에도 나와있지만 요 링크에서 certificate를 추가하는 방법이 나와있다.
1) 사용자 인증서 추가. 아래의 tls.crt, tls.key는 rootca의 것이 아닌 위에서 만든 test.crt, test.key이다.

kubectl -n cattle-system create secret tls tls-rancher-ingress \
  --cert=tls.crt \
  --key=tls.key

2) private한 ca의 인증서 추가. 이건 위에서 만든 rootca.crt이다. cacerts.pem == rootca.crt 아래와 같이 cacerts.pem으로 저장해준다.(이름이 다르니 안되는 경우가 있었음..)

kubectl -n cattle-system create secret generic tls-ca --from-file=cacerts.pem=./cacerts.pem

위처럼 설정하고 도메인인 mytest.net으로 들어가보니 사설인증서라 빨간색으로 뜨긴 하는데 10년짜리로 잘 생성되어 동작하고 있다.