Distroless & obfuscation
Spider Docker images have been upgraded to distroless and obfuscated images!
Originโ
I recently received the request from developers to be able to use Spider locally on their own computer.
That meant deploying Whisperer image potentially anywhere.
For this, I needed to protect Spider deliverables.
Whisperers are delivered as Dockers, with the code bundled with Webpack.
Thus the whole code of services and clients are delivered as a single javascript file, aside the (external, for licences) node modules.
Moving to distrolessโ
After much reading, I came to the conclusion that Google distroless images require much customisation to adapt, and are in fact not much secured than Alpine (which triggers less red flags in security scanners).
One way to secure even more Alpine is possible by removing busybox.
Then:
- Packages installation are not possible
- Connection inside the container is not possible
- Shell operations are not possible
Thus reducing many security attacks vectors. This can be easily done by adding these lines in Dockerfile:
find /sbin /bin /usr/bin /usr/local/bin/ -type l -exec busybox rm -rf {} \;; \
busybox rm /sbin/apk /bin/busybox
I thus added this to last stage of all Spider dockerfiles, rebuilt & redeployed.
Apko looks like a good solution for later (WIP):
GitHub - chainguard-dev/apko: Build OCI images using APK directly without Dockerfile
But it is not advised for production yet.
Obfuscationโ
Since I was already using Webpack to bundle the javascript code, I only added a nice webpack plugin: webpack-obfuscator.
This plugin uses javascript-obfuscator package to change source files so that they perform the same but cannot be read intelligibly or even navigated with debug tools.
function hi() {
console.log("Hello World!");
}
hi();
becomes
(function(_0x2aa05e,_0xf3be43){var _0x2f8c30=_0x5027,_0x576114=_0x2aa05e();while(!![]){try{var _0x13bd9e=parseInt(_0x2f8c30(0x159))/0x1+parseInt(_0x2f8c30(0x163))/0x2*(-parseInt(_0x2f8c30(0x166))/0x3)+-parseInt(_0x2f8c30(0x164))/0x4*(parseInt(_0x2f8c30(0x16a))/0x5)+-parseInt(_0x2f8c30(0x152))/0x6*(-parseInt(_0x2f8c30(0x155))/0x7)+-parseInt(_0x2f8c30(0x158))/0x8+parseInt(_0x2f8c30(0x151))/0x9*(parseInt(_0x2f8c30(0x157))/0xa)+parseInt(_0x2f8c30(0x14f))/0xb;if(_0x13bd9e===_0xf3be43)break;else _0x576114['push'](_0x576114['shift']());}catch(_0x303073){_0x576114['push'](_0x576114['shift']());}}}(_0x2b54,0x770c6));function _0x2b54(){var _0x3c15dd=['2813802ITmBxS','error','log','7nSuurB','(((.+)+)+)+$','7565270MdEONc','7769080kvzVxH','710491dIRrGj','console','apply','info','trace','__proto__','bind','warn','exception','return\x20(function()\x20','14OOcQBt','260xIzcsV','constructor','356295JgVJbk','search','Hello\x20World!','length','25615YPwmYx','toString','table','7558342ucTjJn','{}.constructor(\x22return\x20this\x22)(\x20)','9otLKMz'];_0x2b54=function(){return _0x3c15dd;};return _0x2b54();}function hi(){var _0x2d8b21=_0x5027,_0x34570d=(function(){var _0x2b1465=!![];return function(_0x22790a,_0x445d13){var _0x128fe2=_0x2b1465?function(){if(_0x445d13){var _0x2ef43e=_0x445d13['apply'](_0x22790a,arguments);return _0x445d13=null,_0x2ef43e;}}:function(){};return _0x2b1465=![],_0x128fe2;};}()),_0x4ee4d1=_0x34570d(this,function(){var _0x4caeee=_0x5027;return _0x4ee4d1['toString']()['search'](_0x4caeee(0x156))[_0x4caeee(0x16b)]()[_0x4caeee(0x165)](_0x4ee4d1)[_0x4caeee(0x167)](_0x4caeee(0x156));});_0x4ee4d1();var _0x225cf7=(function(){var _0x13ee3f=!![];return function(_0xa7923a,_0x4ca223){var _0x2e14e2=_0x13ee3f?function(){var _0x16f09d=_0x5027;if(_0x4ca223){var _0x66455d=_0x4ca223[_0x16f09d(0x15b)](_0xa7923a,arguments);return _0x4ca223=null,_0x66455d;}}:function(){};return _0x13ee3f=![],_0x2e14e2;};}()),_0x30fb56=_0x225cf7(this,function(){var _0xf5d605=_0x5027,_0x2dc88d;try{var _0x347cbe=Function(_0xf5d605(0x162)+_0xf5d605(0x150)+');');_0x2dc88d=_0x347cbe();}catch(_0x975ec1){_0x2dc88d=window;}var _0x3e838d=_0x2dc88d[_0xf5d605(0x15a)]=_0x2dc88d[_0xf5d605(0x15a)]||{},_0xbeda5b=['log',_0xf5d605(0x160),_0xf5d605(0x15c),_0xf5d605(0x153),_0xf5d605(0x161),_0xf5d605(0x14e),_0xf5d605(0x15d)];for(var _0x2b96ad=0x0;_0x2b96ad<_0xbeda5b[_0xf5d605(0x169)];_0x2b96ad++){var _0xe0c6e1=_0x225cf7['constructor']['prototype']['bind'](_0x225cf7),_0x23d4f0=_0xbeda5b[_0x2b96ad],_0x54eb75=_0x3e838d[_0x23d4f0]||_0xe0c6e1;_0xe0c6e1[_0xf5d605(0x15e)]=_0x225cf7[_0xf5d605(0x15f)](_0x225cf7),_0xe0c6e1[_0xf5d605(0x16b)]=_0x54eb75[_0xf5d605(0x16b)][_0xf5d605(0x15f)](_0x54eb75),_0x3e838d[_0x23d4f0]=_0xe0c6e1;}});_0x30fb56(),console[_0x2d8b21(0x154)](_0x2d8b21(0x168));}function _0x5027(_0x84155e,_0x42b1b7){var _0x130451=_0x2b54();return _0x5027=function(_0x4d97f1,_0x8e8c38){_0x4d97f1=_0x4d97f1-0x14e;var _0xe6422b=_0x130451[_0x4d97f1];return _0xe6422b;},_0x5027(_0x84155e,_0x42b1b7);}hi();
See obfuscator.io for demo.
Performance checkโ
I used the default profile from javascript-obfuscator, the one oriented to best performance. I then compare performance for 5 hours of Spider processing at night.
API speedโ
The following tables show performance comparison between before and after obfuscation for API and services calls:
Performance is globally the same, some APIs are faster (?!), some are slower.
Resource usageโ
Regarding resource usage over the both periods (over 5 nodes and 100 services):
Metric | Non obfuscated | Obfuscated |
---|---|---|
Total CPU used | 343% | 356% |
Total Memory allocated | 4 893 MB | 5 129 MB |
Obfuscated code uses a bit more CPU and RAM.
Store accessโ
Strangely, however there is no change in Redis access response time, Elasticsearch response times are faster :-/
Conclusionโ
Overall Spider deployment gets safer, more secure, with small changes and at little performance overhead!
All good :)