Skip to content

Commit ed85beb

Browse files
committed
doc: use shared_dict for dynamic certs demo
1 parent ce67de0 commit ed85beb

File tree

3 files changed

+413
-0
lines changed

3 files changed

+413
-0
lines changed

‎README.rst

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,153 @@ Checking:
829829
curl https://localhost/ --insecure --key njs/http/certs/ca/intermediate/private/client.key.pem --cert njs/http/certs/ca/intermediate/certs/client.cert.pem --pass secretpassword
830830
["7f000001","00000000000000000000000000000001","example.com","www2.example.com"]
831831
832+
Securely serve encrypted traffic without server restarts when certificate or key changes occur. [http/certs/dynamic]
833+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
834+
Configure NGINX to serve encrypted traffic without server restarts when certificate or key changes occur by using `js_shared_dict_zone <https://nginx.org/en/docs/http/ngx_http_js_module.html#js_shared_dict_zone>`_ as a cache.
835+
836+
Note: this example below work with njs >= `0.8.0 <http://nginx.org/en/docs/njs/changes.html#njs0.8.0>`_.
837+
838+
This example demonstrates:
839+
840+
- Use of `js_set <https://nginx.org/en/docs/http/ngx_http_js_module.html#js_set>`_ in combination with ``ssl_certificate data:$var;`` to use NJS to resolve value of cert/key during handshake.
841+
- Use of `js_shared_dict_zone <https://nginx.org/en/docs/http/ngx_http_js_module.html#js_shared_dict_zone>`_ to store cert/key in memory.
842+
- Implementation a simple RESTful API to manage ``shared_dict`` to get/set certificate/key files.
843+
- How to deal with ``Content-Disposition`` while handling file uploads in NJS.
844+
845+
nginx.conf:
846+
847+
.. code-block:: nginx
848+
849+
...
850+
851+
load_module modules/ngx_http_js_module.so;
852+
error_log /dev/stdout debug;
853+
events { }
854+
855+
http {
856+
js_path "/etc/nginx/njs/";
857+
js_import main from http/certs/js/dynamic.js;
858+
js_shared_dict_zone zone=kv:1m;
859+
860+
server {
861+
listen 80;
862+
listen 443 ssl;
863+
server_name www.example.com;
864+
865+
js_var $shared_dict_zone_name kv;
866+
js_var $cert_folder '/tmp/';
867+
868+
js_set $dynamic_ssl_cert main.js_cert;
869+
js_set $dynamic_ssl_key main.js_key;
870+
871+
ssl_password_file /etc/nginx/njs/http/certs/ca/password;
872+
ssl_certificate data:$dynamic_ssl_cert;
873+
ssl_certificate_key data:$dynamic_ssl_key;
874+
875+
location = / {
876+
js_content main.info;
877+
}
878+
879+
location /kv {
880+
js_content main.kv;
881+
}
882+
883+
location = /clear {
884+
js_content main.clear_cache;
885+
}
886+
}
887+
888+
}
889+
890+
891+
Here we would implement ``js_set`` handlers that reads cert/key from a FS or from `shared_dict`` (used as a cache here):
892+
893+
.. code-block:: js
894+
895+
function js_cert(r) {
896+
if (r.variables['ssl_server_name']) {
897+
return read_cert_or_key(r, '.cert.pem');
898+
} else {
899+
return '';
900+
}
901+
}
902+
903+
function js_key(r) {
904+
if (r.variables['ssl_server_name']) {
905+
return read_cert_or_key(r, '.key.pem');
906+
} else {
907+
return '';
908+
}
909+
}
910+
911+
function joinPaths(...args) {
912+
return args.join('/').replace(/\/+/g, '/');
913+
}
914+
915+
function read_cert_or_key(r, fileExtension) {
916+
let data = '';
917+
let path = '';
918+
const zone = r.variables['shared_dict_zone_name'];
919+
let certName = r.variables.ssl_server_name;
920+
let prefix = r.variables['cert_folder'] || '/etc/nginx/certs/';
921+
path = joinPaths(prefix, certName + fileExtension);
922+
r.log(`Resolving ${path}`);
923+
const key = ['certs', path].join(':');
924+
const cache = zone && ngx.shared && ngx.shared[zone];
925+
926+
if (cache) {
927+
data = cache.get(key) || '';
928+
if (data) {
929+
r.log(`Read ${key} from cache`);
930+
return data;
931+
}
932+
}
933+
try {
934+
data = fs.readFileSync(path, 'utf8');
935+
r.log('Read from cache');
936+
} catch (e) {
937+
data = '';
938+
r.log(`Error reading from file:', ${path}, . Error=${e}`);
939+
}
940+
if (cache && data) {
941+
try {
942+
cache.set(key, data);
943+
r.log('Persisted in cache');
944+
} catch (e) {
945+
const errMsg = `Error writing to shared dict zone: ${zone}. Error=${e}`;
946+
r.log(errMsg);
947+
}
948+
}
949+
return data
950+
}
951+
952+
The rest of code can be found in the `njs/http/certs/js/dynamic.js <njs/http/certs/js/dynamic.js>`_.
953+
954+
Checking:
955+
956+
.. code-block:: shell
957+
958+
# when started and there is no cert/key it fails to serve HTTPS
959+
curl -k --resolve www.example.com:443:127.0.0.1 https://www.example.com:443
960+
961+
curl http://localhost/
962+
963+
# Upload cert/key files. file name would be used to form a key for shared_dict
964+
curl -iv http://localhost:80/kv -F cert=@njs/http/certs/ca/intermediate/certs/www.example.com.cert.pem -F key=@njs/http/certs/ca/intermediate/private/www.example.com.key.pem
965+
966+
# Get Certificate from shared_dict:
967+
curl http://localhost/kv/www.example.com.cert.pem
968+
969+
# Get Private Key from shared_dict:
970+
curl http://localhost/kv/www.example.com.key.pem
971+
972+
# now we can test HTTPS again
973+
curl -k --resolve www.example.com:443:127.0.0.1 https://www.example.com
974+
975+
# Clear shared_dict
976+
curl http://localhost/clear
977+
978+
832979
Fetch
833980
-----
834981

‎conf/http/certs/dynamic.conf

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
load_module modules/ngx_http_js_module.so;
2+
3+
error_log /dev/stdout debug;
4+
5+
events { }
6+
7+
http {
8+
js_path "/etc/nginx/njs/";
9+
js_import main from http/certs/js/dynamic.js;
10+
js_shared_dict_zone zone=kv:1m;
11+
12+
server {
13+
listen 80;
14+
listen 443 ssl;
15+
server_name www.example.com;
16+
17+
js_var $shared_dict_zone_name kv;
18+
js_var $cert_folder '/tmp/';
19+
20+
js_set $dynamic_ssl_cert main.js_cert;
21+
js_set $dynamic_ssl_key main.js_key;
22+
23+
ssl_password_file /etc/nginx/njs/http/certs/ca/password;
24+
ssl_certificate data:$dynamic_ssl_cert;
25+
ssl_certificate_key data:$dynamic_ssl_key;
26+
27+
location = / {
28+
js_content main.info;
29+
}
30+
31+
location /kv {
32+
js_content main.kv;
33+
}
34+
35+
location = /clear {
36+
js_content main.clear_cache;
37+
}
38+
}
39+
40+
}

0 commit comments

Comments
 (0)