안수찬의 개발이야기

Docker Registry (3) : Nginx Basic Authentication 적용하기

Introduction

안수찬 @dobestan

안수찬 @dobestan

소프트웨어 생태계에 기여할 수 있는 실용주의 프로그래머가 되고자 합니다. 나는 안수찬이다. 그러므로 나는 할 수 있다.


Docker Registry (3) : Nginx Basic Authentication 적용하기

Posted by 안수찬 @dobestan on .
Featured

Docker Registry (3) : Nginx Basic Authentication 적용하기

Posted by 안수찬 @dobestan on .

지금까지 내가 Docker Registry를 로컬 머신에서 설치해서 사용해보고, 그 다음에는 AWS EC2에 올려서 사용해보았습니다. 이번 포스팅에서는 Docker Registory를 Public하게 사용하지 않고, Private 하게 사용하는 방법에 대해서 알아보겠습니다. 앞의 내용이 헷갈린다면 아래의 포스팅을 참고해주세요.

HOW

사실 Docker Registry를 완전히 Private하게 사용하기 위해서는 여러 가지 방법이 있을 것 같습니다.

  • 접속 IP를 제한해서 특정 IP에서만 접속 가능하도록 설정하기
  • 공인 SSL 인증서를 발급받아 HTTPS + User Auth로 사용하기 ( User Auth를 사용하기 위해서 반드시 HTTPS를 사용하여야 한다. 이 부분을 몰라서 고생했다. )
  • 사설 SSL 인증서를 생성하여 HTTPS + User Auth로 사용하기

사실 IP 제한이나, 공인 SSL을 발급받아 설정하는 방법은 정말 쉽고 일반적인 설정과 다른 부분이 없어 여기서는 "사설 SSL 인증서를 생성하여 HTTPS + User Auth로 인증하는 방식"을 알아보겠습니다.

이번 포스팅에서는 제가 시도하고 실패했던 과정들을 포함하여 공유합니다.

1. HTTP + User Auth

이 방법은 동작하지 않습니다. Docker는 HTTP를 통한 인증을 지원하지 않습니다.

사실 처음에는 당연히 Nginx 에서 기본적으로 제공하는 User Auth를 사용하면 매우 쉽게 할 수 있을 것 같았다. 그래서 htpasswd를 이용하여 계정을 생성하고, 이 계정으로 로그인 해야지만 Docker Registry 를 사용할 수 있게 하기로 결정하였습니다.

$ sudo apt-get -y install apache2-utils
$ htpasswd -c .htpasswd dobestan
New password:  
Re-type new password:  
Adding password for user dobestan  

생성된 .htpasswd 파일에는 사용자 이름과 암호화된 비밀번호가 저장됩니다.

$ cat .htpasswd
dobestan:$apr1$mtXLPDLn$YXdZDqy8Rrbtq39iieV2B0  

이렇게 .htpasswd라는 파일을 생성하고 이제 nginx.conf를 변경하였습니다. 기존의 설정 파일에서 location { ... } 부분에 auth_basic을 추가해주시면 됩니다.

...
 location / {
            proxy_pass          http://docker-registry:5000;
            proxy_set_header    Host  $host;
            proxy_read_timeout  900;

            auth_basic            "Restricted";
            auth_basic_user_file  ~/.htpasswd;
        }
...

이렇게 기본적인 Nginx User Auth 를 이용하여 설정하고 Docker Registry에 이미지를 Push 하였으나, 오류가 발생하였습니다. ( 미리 검색 좀 해볼걸 ... )

$ docker push 54.64.158.154/hello_world
The push refers to a repository [54.64.158.154/hello_world] (len: 1)  
Sending image list  
Pushing repository 54.64.158.154/hello_world (1 tags)  
511136ea3c5a: Pushing  
2014/09/20 23:36:39 HTTP code 401, Docker will not send auth headers over HTTP.  

Docker는 기본적으로 HTTP를 이용한 인증을 지원하지 않습니다.

2. HTTPS + User Auth

이 방법은 동작하지 않습니다.

일단 HTTP가 안되니, HTTPS로 당연히 바꾸기로 결정하였습니다. ( 공인 SSL을 구매하기에는 돈이 없으니, 그냥 사설 SSL을 사용하기로 생각했습니다. ) 일단 다들 사설 SSL을 이용해서 HTTPS를 이용하는 방법은 굉장히 쉽습니다.

  1. 개인키 생성하기 ( private.key )
  2. 개인키를 이용하여 인증서 서명 요청 ( Certificate Signing Request, CSR ) 생성하기 ( server.csr )
  3. 개인키와 CSR을 이용하여 사설 SSL 인증서 생성하기 ( server.crt )
  4. 사용하려는 서버에 사설 인증서 등록하기

위의 4단계를 간단하게 살펴보겠습니다.

1 . 개인키 생성하기

$ openssl genrsa -out private_key.pem 2048
Generating RSA private key, 2048 bit long modulus  
.+++
...................................+++
e is 65537 (0x10001)  
$ cat private_key.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuqRKX0qbrAIiS2kftTNZOX7HooLmAK17F8kFXtnO6JgI8/1Q  
...
9b6WQIS+duCXo8y06Zgj9ZCz7gZ0R+6GVKCh3bUjVig0KVqMgC8oGA  
-----END RSA PRIVATE KEY-----

2 . CSR 생성하기

# openssl req -new -key private_key.pem -out server.csr
You are about to be asked to enter information that will be incorporated  
into your certificate request.  
What you are about to enter is what is called a Distinguished Name or a DN.  
There are quite a few fields but you can leave some blank  
For some fields there will be a default value,  
If you enter '.', the field will be left blank.  
-----
Country Name (2 letter code) [AU]:KO  
State or Province Name (full name) [Some-State]:Seoul  
Locality Name (eg, city) []:Seoul  
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Dreampic  
Organizational Unit Name (eg, section) []:Dev  
Common Name (e.g. server FQDN or YOUR name) []:54.64.158.154  
Email Address []:dobestan@gmail.com

Please enter the following 'extra' attributes  
to be sent with your certificate request  
A challenge password []:  
An optional company name []:  
$ cat server.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICzzCCAbcCAQAwgYkxCzAJBgNVBAYTAktPMQ4wDAYDVQQIDAVTZW91bDEOMAwG  
...
2no5RB0bRRmPziEhLQOMN8AhmroTKHUeh0fYHtqRji9C6Y7tlc0z8mJ93BluITl3  
-----END CERTIFICATE REQUEST-----

3 . 사설 SSL 인증서 생성하기

$ openssl x509 -req -days 365 -in server.csr -signkey private_key.pem -out server.crt
Signature ok  
subject=/C=KO/ST=Seoul/L=Seoul/O=Dreampic/OU=Dev/CN=54.64.158.154/emailAddress=dobestan@gmail.com  
Getting Private key  
$ cat server.crt
-----BEGIN CERTIFICATE-----
MIIDkDCCAngCCQCq2/WKKlefDTANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC  
...
C7qVvREL+JroAgzOpkdUOy/Qh5G6d7Qfc0X4TwbPkE71R7GVHqF9AJocjrDed0HN  
-----END CERTIFICATE-----

4 . 사설 인증서 설치하기

우리가 생성한 사설 SSL server.crt 은 ( 너무 당연하게도 ) 공인 CA가 발급한 공인 SSL 인증서가 아니라, 서버에 설치를 해 주어야 사용이 가능하다. 아래 내용은 Ubuntu를 기준으로 작성되었다.

일단 공인 CA들이 발급한 인증서의 경우 기본적으로 /usr/share/ca-certificates/에 인증서가 저장되어 있다. 서버가 우리 사설 SSL 인증서도 공인 인증서로 인식하게 하기 위해서 이 server.crt 파일을 옮겨놓자.

$ sudo cp server.crt /usr/share/ca-certificates/

그리고 이 공인 인증서들에 대한 정보가 저장되어 있는 /etc/ca-certificates.conserver.crt에 대한 정보를 추가하자.

$ echo "server.crt" | sudo tee -a /etc/ca-certificates.conf
server.crt  

그리고 이제 CA 인증서를 업데이트해주면 된다. 지금까지의 과정을 충실하게 수행했다면 새로운 CA 인증서가 1개 추가되었을 것이다.

$ sudo update-ca-certificates
Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done.  
Running hooks in /etc/ca-certificates/update.d....done.  

다만, 이 부분을 주의하자. Docker 서비스를 재시작해야 새롭게 추가된 인증서가 적용된다.

$ sudo service docker restart
docker stop/waiting  
docker start/running, process 14683  

지금까지의 설정으로 테스트

일단 HTTP가 아닌 HTTPS을 사용하기 위해서 기존의 nginx.conf를 수정해주자. http { server { ... } } 내용을 수정해주면 된다. 기존의 80번 포트에서 443 포트를 이용하도록, ssl 관련 설정들을 수정하자.

...
http {  
    server {
        listen       443;
        server_name  localhost;

        ssl on;
        ssl_certificate /etc/server.crt;
        ssl_certificate_key /etc/server.key;
...
root@ip-172-31-3-77:~# docker login https://54.64.158.154  
Username: dobestan  
Password:  
Email: dobestan@gmail.com  
2014/09/25 14:16:25 Error response from daemon: Invalid Registry endpoint: Get https://54.64.158.154/v1/_ping: x509: cannot validate certificate for 54.64.158.154 because it doesn't contain any IP SANs  

http://stackoverflow.com/questions/23468530/use-docker-registry-with-ssl-certifictae-without-ip-sans

3. HTTPS + User Auth + Domain Name

root@ip-172-31-7-22:~# docker push registry.ansuchan.com/nginx  
The push refers to a repository [registry.ansuchan.com/nginx] (len: 1)  
Sending image list  
Pushing repository registry.ansuchan.com/nginx (1 tags)  
Image 511136ea3c5a already pushed, skipping  
634f7bff4884: Image successfully pushed  
cca2319dd1fb: Image successfully pushed  
507284b30015: Image successfully pushed  
fdbc35059810: Image successfully pushed  
dae7d9a7ca8e: Image successfully pushed  
c141a499ec5b: Image successfully pushed  
c0c58cc8a766: Image successfully pushed  
6aca84aad778: Image successfully pushed  
545eb2f29d3f: Image successfully pushed  
1d1e631ed04b: Image successfully pushed  
25aa8f39f356: Image successfully pushed  
57cb06536a6f: Image successfully pushed  
8c2b6d2f44f4: Image successfully pushed  
d2d79aebd368: Image successfully pushed  
Pushing tag for rev [d2d79aebd368] on {https://registry.ansuchan.com/v1/repositories/nginx/tags/latest}  
root@ip-172-31-7-22:~# curl -u dobestan:dkstncks https://registry.ansuchan.com/v1/repositories/hello-world/tags/latest  
"bf16b6e27882c0790071c95326e0186eccd2b8ac2bd5ef34fecdbb332a90926e"
docker login https://registry.ansuchan.com  
Username: dobestan  
Password:  
Email: dobestan@gmail.com  
Login Succeeded  
root@ip-172-31-7-22:~# docker tag hello-world registry.ansuchan.com/hello-world  
root@ip-172-31-7-22:~# docker push registry.ansuchan.com/hello-world  
The push refers to a repository [registry.ansuchan.com/hello-world] (len: 1)  
Sending image list  
Pushing repository registry.ansuchan.com/hello-world (1 tags)  
Image 511136ea3c5a already pushed, skipping  
Image 8f5550346e61 already pushed, skipping  
Image bf16b6e27882 already pushed, skipping  
Pushing tag for rev [bf16b6e27882] on {https://registry.ansuchan.com/v1/repositories/hello-world/tags/latest}  
root@ip-172-31-7-22:~# curl https://registry.ansuchan.com/v1/repositories/hello-world/tags/latest  
<html>  
<head><title>401 Authorization Required</title></head>  
<body bgcolor="white">  
<center><h1>401 Authorization Required</h1></center>  
<hr><center>nginx/1.7.5</center>  
</body>  
</html>  
root@ip-172-31-7-22:~# curl -u dobestan:dkstncks https://registry.ansuchan.com/v1/repositories/hello-world/tags/latest  
"bf16b6e27882c0790071c95326e0186eccd2b8ac2bd5ef34fecdbb332a90926e"
안수찬 @dobestan

안수찬 @dobestan

https://dobest.io/

소프트웨어 생태계에 기여할 수 있는 실용주의 프로그래머가 되고자 합니다. 나는 안수찬이다. 그러므로 나는 할 수 있다.

View Comments...