NGINX Unit 앱서버 SSL 및 JavaScript 앱 구성
NGINX 엔지니어팀이 NGINX Unit TLS 지원을 위한 초기 코드를 보여 주었을 때 구성 및 설정 단계를 완료하기 위해 몇 시간을 차단했습니다. 너무 쉬워서 30분만에 끝냈습니다.
2018년 9월 20일, TLS 지원, 인증서 저장소 및 구성체에 대한 새 /config URL 경로를 추가하는 NGINX Unit를 릴리스했습니다. 그 해 10월 25일에 출시된 NGINX Unit에서는 Node.js 애플리케이션에 대한 지원이 추가되었습니다.
이 포스트에서는 TLS 인증서 및 Node.js 애플리케이션의 구성에 대해 자세히 다룹니다.
목차
1. NGINX Unit SSL/TLS 인증서의 동적 구성
1-1. 인증서 생성
1-2. 인증서 체인 업로드
1-3. 리스너에 인증서 적용
2. Node.js 애플리케이션 지원
2-1. unit-http 패키지 설치
2-2. NGINX Unit 에서 앱 구성
3. NGINX Unit 결론
1. NGINX Unit SSL/TLS 인증서의 동적 구성
보안 및 암호화는 모든 프로덕션 애플리케이션에 필수적인 요소입니다. 오늘날의 애플리케이션 연결에는 적절하고 신속한 구성 및 재구성이 필요합니다. 보다 동적인 성격의 애플리케이션의 경우 연결 및 보안 인증서를 지속적으로 변경하는 것이 일반적입니다.
NGINX Unit는 정적 구성 파일에 의존하지 않고 프로세스를 다시 로드할 필요 없이 즉시 구성됩니다. 이제 이 개념을 TLS 인증서로도 확장합니다.
NGINX Unit 을 사용하면 인증서를 업로드하고, 리스너에서 인증서를 적용하거나 제거하고, 이전 인증서를 교체할 수 있습니다. 이 모든 작업은 다운타임 없이 애플리케이션 프로세스에 대한 변경 없이 모두 동적으로 이루어집니다.
빠르고 쉬운 구성 외에도 NGINX Unit와 함께 TLS를 사용하면 Go 및 Node.js 엔지니어가 애플리케이션 코드 자체에서 암호화를 구현해야 하는 부담을 덜 수 있습니다.
이제 Go 및 Node.js를 포함한 모든 앱을 중단 시간 없이 더 빠르고 더 안전하게 대규모 프로덕션 환경에 배포할 수 있습니다.
NGINX Unit을 사용하면 애플리케이션 프로세스를 다시 로드하지 않고도 인증서를 추가, 변경 및 제거할 수 있습니다.
NGINX Unit 초기 제어 API 응답의 다음 예는 리스너 및 애플리케이션 객체가 있는 기존 구성 구조 외에도 certificates
객체가 있음을 보여줍니다.
$ curl --unix-socket /run/control.unit.sock http://localhost/
{
"certificates": {},
"config": {
"listeners": {},
"applications": {}
}
}
인증서 및 키는 API의 다른 구성 객체와 다른 형식으로 표시됩니다. PEM 형식으로 PUT 요청의 본문으로 인증서 저장소에 업로드합니다.
PEM 형식의 인증서 및 키(Key)를 PUT 요청 본문으로 업로드합니다.
1-1. 인증서 생성
간단한 자체 서명 인증서를 사용하여 이 문제를 해결하려면 먼저 다음 명령을 실행하여 인증서를 생성합니다.
$ openssl req -x509 -subj /CN=localhost -days 365 -set_serial 2 -newkey rsa:4096 -keyout cert.key -nodes -out cert.pem
인증서와 키를 별도의 파일에 보관했습니다. 이를 결합해 보겠습니다.
$ cat cert.key cert.pem > chain.pem
프로덕션 환경의 경우 Let’s Encrypt와 같은 공급자로부터 전체 인증서 체인을 가져오는 것이 좋습니다. 결과 파일은 다음과 유사합니다.
-----BEGIN CERTIFICATE-----
MIIDHDCCAgQCCQCVgasbCyT1BjANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJV
...
vyYJp0qUWRWeYm1CTZaudpR74cZVhz/RKGFfXkK2bD6Vv8uHI0hbK0p3KlKZ333j
kMSJ9yD+dF9quUbvS9yJUEHcF+96ZcmFVGRnwvnwXUE=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAqb8VkMnAKqqIERHwNklelCE6gzfMd99yX+YoeerB9QVKZkx/
...
qhtqYRlezYQOUbMGAsREtKqZCn+urL8Bql2IbPQ9vwSfioxNUNyHod+2E/JDoEF8
OzsBSkR8H7NcSaHCbZCMKtIvGZGM4ODRqP484/KiXpqjhq8BOtTKJA==
-----END RSA PRIVATE KEY-----
중간 인증서 또는 CA 인증서(또는 둘 다)가 있는 경우 인증서 및 키와도 연결합니다.
1-2. 인증서 체인 업로드
그리고 나서 우리는 NGINX Unit API를 통해 전체 체인을 업로드합니다. HTTP 도구를 사용하여 인증서를 업로드하고 구성을 업데이트할 수 있습니다. 다음 예에서는 curl을 사용합니다.
$ curl -X PUT --data-binary @path/to/chain.pem --unix-socket /run/control.unit.sock 'http://localhost/certificates/mychain'
이렇게 하면 /certificates/mychain URL 경로에서 확인할 수 있는 새 체인이 생성됩니다. 자체 서명된 인증서 예에서 구성은 다음과 같습니다.
{
"key": "RSA (4096 bits)",
"chain": [
{
"subject": {
"common_name": "localhost"
},
"issuer": {
"common_name": "localhost"
},
"validity": {
"since": "Oct 25 18:59:18 2018 GMT",
"until": "Oct 25 18:59:18 2019 GMT"
}
}
]
}
1-3. 리스너에 인증서 적용
이제 리스너의 구성에 인증서 매개 변수가 있는 tls 객체를 포함하여 새로 생성된 인증서를 리스너에 적용합니다.
$ curl -X PUT --data-binary '{"application":"myapp","tls":{"certificate":"mychain"}}' --unix-socket /run/control.unit.sock 'http://localhost/config/listeners/*:8043'
새 tls 객체를 사용하여 리스너를 구성하면 다음과 같은 결과가 발생합니다.
"*:8043": {
"application": "myapp",
"tls": {
"certificate":"mychain"
}
}
요약하면 TLS 지원 애플리케이션의 전체 구성은 다음과 같습니다.
{
"certificates": {
"mychain": {
"key": "RSA (4096 bits)",
"chain": [
{
"subject": {
"common_name": "localhost"
},
"issuer": {
"common_name": "localhost"
},
"validity": {
"since": "Oct 25 18:59:18 2018 GMT",
"until": "Oct 25 18:59:18 2019 GMT"
}
}
]
}
},
"config": {
"settings": {},
"listeners": {
"*:8043": {
"application": "myapp",
"tls": {
"certificate":"mychain"
}
}
},
"applications": {
"myapp": {
"type": "php",
"root": "/path/to/php",
"script": "index.php"
}
}
}
}
https://nginxstore.com/docs/unit/ 에서 NGINX Unit 으로 TLS를 구성하는 방법에 대해 자세히 알아보세요.
2. NGINX Unit Node.js 애플리케이션 지원
NGINX Unit이 지원하는 언어 세트에 Node.js(JavaScript)를 추가했습니다.
Node.js와 Go 앱은 기본적으로 애플리케이션을 빌드할 때 자체 서버를 생성한다는 점에서 유사합니다. 애플리케이션은 직접 또는 프로세스 관리자를 통해 별도의 프로세스로 시작됩니다.
많은 환경에서 NGINX(또는 다른 로드 밸런서)는 Node.js 및 Go 앱 앞에 배치됩니다. 이 추가 소프트웨어는 종종 환경을 복잡하게 만듭니다. NGINX Unit을 사용하면 일관되고 사용하기 쉬운 하나의 API를 통해 단 하나의 소프트웨어 서버를 사용하여 이 기능을 구성할 수 있습니다.
다음은 각각 Go와 Node.js의 두 가지 예입니다.
http.ListenAndServe(":8000", nil)
var http = require('http');
http.createServer(function (req, res) {
// ...
}).listen(8080);
2-1. unit-http 패키지 설치
Node.js 애플리케이션을 NGINX Unit에서 실행하기 위해 외부 런타임이 포트에서 직접 수신하는 대신 NGINX Unit의 라우터 프로세스와 통신할 수 있도록 특수 패키지(모듈)를 추가합니다.
NGINX Unit은 앱을 실행하고 공유 메모리를 통해 통신합니다. 다른 앱 유형과의 이러한 차이를 반영하기 위해 이러한 앱에 대한 개념을 “type”: “external” 로 지칭하면서 더 높은 수준으로 끌어올렸습니다.
Node.js의 경우 관련 패키지는 unit-http라고 하며 이제 https://www.npmjs.com/package/unit-http의 NPM에서 사용할 수 있습니다.
NGINX Unit을 설치한 후에는 먼저 필수 unit-dev 패키지(표시된 명령은 Debian 및 Ubuntu 시스템용)를 설치한 다음 NPM을 사용하여 unit-http 패키지를 설치합니다.
$ apt-get install unit-dev
$ npm install unit-http
그런 다음 Node.js 앱의 코드를 두 가지 변경합니다:
- 파일 상단에 shebang(#!)과 런처 경로를 추가하여 실행 가능하게 합니다.
(파일에 chmod +x shell 명령도 실행합니다.) - 필요한 패키지를 http에서 unit-http로 변경합니다.
다음은 간단한 “Hello World” 애플리케이션입니다. 두 가지 변경 사항이 있습니다.
#!/usr/bin/env node
var unit = require('unit-http');
var server = unit.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.end("Read more at unit.nginx.org");
});
server.listen();
2-2. NGINX Unit 에서 앱 구성
Go 애플리케이션과 동일한 방식으로 제어 API를 통해 NGINX Unit에 앱을 추가합니다. 최소 애플리케이션 구성은 다음과 같습니다.
"mynodeapp": {
"type": "external",
"executable": "/path/to/app.js"
}
NGINX Unit API로 이 구성 코드를 추가하기 위해 다음을 실행합니다.
$ curl -X PUT --data-binary '{"type":"external", "executable":"/path/to/app.js"}' --unix-socket /run/control.unit.sock 'http://localhost/config/applications/mynodeapp'
이제 리스너 객체(기존 객체 또는 명령으로 생성된 새 객체)에서 이 애플리케이션을 참조할 수 있습니다.
$ curl -X PUT --data-binary '{"application":"mynodeapp"}' --unix-socket /run/control.unit.sock 'http://localhost/config/listeners/*:8071'
Node.js 앱이 구성되면 NGINX Unit의 동적 프로세스 관리로 확장하여 성능을 높일 수 있습니다.
$ curl -X PUT --data-binary '{"max":10, "spare":5}' --unix-socket /run/control.unit.sock 'http://localhost/config/applications/mynodeapp/processes'
전체 구성은 다음과 같습니다.
{
"settings": {},
"listeners": {
"*:8071": {
"application": "mynodeapp"
}
},
"applications": {
"mynodeapp": {
"type": "external",
"executable": "/path/to/app.js",
"processes": {
"max": 10,
"spare": 5
}
}
}
}
3. 결론
해당 포스트에서는 NGINX Unit의 기능을 검토하고 기존 애플리케이션에 대해 SSL/TLS로 암호화를 구성했으며 NGINX Unit 구성에 Node.js 앱을 추가하는 방법을 보여주었습니다.
https://nginxstore.com/docs/unit/에서 NGINX Unit 구성에 대해 자세히 알아보세요.