@@ -829,6 +829,153 @@ Checking:
829
829
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
830
830
[" 7f000001" ," 00000000000000000000000000000001" ," example.com" ," www2.example.com" ]
831
831
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
+
832
979
Fetch
833
980
-----
834
981
0 commit comments