{"id":37507,"date":"2020-07-03T08:47:25","date_gmt":"2020-07-02T23:47:25","guid":{"rendered":"https:\/\/jirak.net\/wp\/nginx-unit-1-18-0-adds-filesystem-isolation-and-other-enhancements\/"},"modified":"2020-07-03T10:34:08","modified_gmt":"2020-07-03T01:34:08","slug":"nginx-unit-1-18-0-adds-filesystem-isolation-and-other-enhancements","status":"publish","type":"post","link":"https:\/\/jirak.net\/wp\/nginx-unit-1-18-0-adds-filesystem-isolation-and-other-enhancements\/","title":{"rendered":"NGINX Unit 1.18.0 Adds Filesystem Isolation and Other Enhancements"},"content":{"rendered":"<p>NGINX Unit 1.18.0 Adds Filesystem Isolation and Other Enhancements<\/p>\n<p>Usually, we&#8217;d start a blog post like this one rather light&#8209;heartedly, but the mood of the times is undoubtedly somber, the reasons for that being too numerous to joke about. However, we hope you&#8217;re doing well&nbsp;&ndash; and again, we have some news to share. <\/p>\n<p>For starters, we at the NGINX&nbsp;Unit team believe that the term &#8220;isolation&#8221; doesn&#8217;t necessarily deserve the bad rap it has gained recently around the globe, and our most recent release, <span>NGINX Unit 1.18.0<\/span>, has arrived with an accordingly themed major update.<\/p>\n<h2>Filesystem Isolation<\/h2>\n<p>The <code>isolation<\/code> family of application settings, <a href=\"https:\/\/www.nginx.com\/blog\/nginx-unit-1-11-0-now-available\/#Application-Isolation\">introduced in <span>NGINX Unit 1.11.0<\/span><\/a>, now includes a new <code>rootfs<\/code> object. If the underlying OS allows it, you can use <code>rootfs<\/code> to designate an arbitrary directory as the filesystem root.<\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"rootfs\" data-gist-line=\"1-9\"><\/code><!--{\n    \"type\": \"python 2.7\",\n    \"path\": \"\/\",\n    \"home\": \"\/venv\/\",\n    \"module\": \"wsgi\",\n    \"isolation\": {\n        \"rootfs\": \"\/var\/app\/sandbox\/\"\n    }\n} rootfs --><\/p>\n<p>An application configured this way is locked within the confines of the <strong>\/var\/app\/sandbox\/<\/strong> directory, unable to access any files or directories outside of it. All path settings that you provide for the app need to account for that, as illustrated by the <code>path<\/code> and <code>home<\/code> objects in the example above.<\/p>\n<p>The potential applications for this capability, especially combined with other isolation features, are significant. Effectively, it enables you to configure and run apps as lightweight on&#8209;demand containers, improving their security, isolating them from each other and the underlying OS, and enhancing the granularity of your infrastructure. That is exactly the goal of our <a href=\"https:\/\/www.nginx.com\/blog\/application-isolation-nginx-unit\/\">containerization initiative<\/a>, and isolation of the filesystem root is an important milestone in our endeavor. <\/p>\n<p>While isolating your app, NGINX&nbsp;Unit conveniently ensures the appropriate language runtime is still available for all languages we support. Moreover, you can use <code>rootfs<\/code> to maintain different versions of your app\u2019s runtime (modules, libraries, and so on) and toggle between them seamlessly. An appropriately detailed hands&#8209;on demonstration is beyond the scope of this blog post, so keep an eye out for an upcoming blog dedicated to the topic. Meanwhile, you can try this addition to see the benefits it can bring to your own infrastructure.<\/p>\n<p>For further details, see our <a target=\"_blank\" href=\"https:\/\/unit.nginx.org\/configuration\/#process-isolation\" rel=\"noopener noreferrer\">Process Isolation<\/a> configuration guide.<\/p>\n<h2>PHP Targets<\/h2>\n<p>The second innovation introduced in <span>NGINX Unit 1.18.0<\/span> is specific to PHP. It aims to simplify the daily work of the <span>PHP-on-Unit<\/span> crowd who previously had to maintain rather cumbersome configurations for their beloved apps. <\/p>\n<p>The NGINX&nbsp;Unit team initially noticed that more often than not PHP web apps require several modes of operation for the different ways that individual scripts within the same application can be run&nbsp;&ndash; and that&#8217;s not even mentioning the myriad existing ways to handle static content. Usually, the approach boils down to one of the following:<\/p>\n<ul>\n<li>A single script handles incoming requests for all URIs, routing them internally<\/li>\n<li>Each request is handled by the script explicitly named in the URI<\/li>\n<\/ul>\n<p>NGINX&nbsp;Unit has included options for both approaches since the very introduction of PHP support. Nonetheless, the sheer number of possible combinations in real deployments was begging for a change. To give you a picture: almost every time a PHP application was set up in NGINX&nbsp;Unit, several application objects had to be defined, along with intricate routing rules that channeled the requests among them, as in this example:<\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"php_config_without_targets\" data-gist-line=\"1-48\"><\/code> <!-- {\n    \"routes\": [\n        {\n            \"match\": {\n                \"uri\": [\n                     \"*.php\",\n                     \"*.php\/*\"\n                ]\n            },\n\n\n            \"action\": {\n                \"pass\": \"applications\/direct\"\n            }\n        },\n        {\n            \"action\": {\n                \"pass\": \"applications\/script_index\"\n            }\n        },\n    ],\n\n\n\n    \"applications\": {\n        \"direct\": {\n            \"type\": \"php\",\n            \"processes\": {\n                \"max\": 5,\n                \"spare\": 0\n            },\n            \"user\": \"www-data\",\n            \"group\": \"www-data\",\n            \"root\": \"\/var\/www\/app\/\",\n            \"index\": \"index.php\"\n        },\n        \"script_index\": {\n            \"type\": \"php\",\n            \"processes\": {\n                \"max\": 20,\n                \"spare\": 5\n            },\n            \"user\": \"www-data\",\n            \"group\": \"www-data\",\n            \"root\": \"\/var\/www\/app\/\",\n            \"script\": \"index.php\"\n        }\n    }\n} php_config_without_targets --><\/p>\n<p>Here, requests that explicitly name a PHP script are passed to the <strong>direct<\/strong> app, while all other requests are passed to the <strong>script_index<\/strong> app, which handles them by means of the multipurpose <strong>index.php<\/strong> script at the root of the app (pretty URIs are a common use case for the latter). Because the two apps are distinct, they must be separately configured in the <code>applications<\/code> object, but you can readily see there are quite a few duplicated options.<\/p>\n<p>Such redundancy makes sense if you wish to fine&#8209;tune the settings for each section of your web application (<em>section<\/em> being loosely defined as an arbitrary set of PHP scripts and URIs that need to be configured as a single entity) or assign a dedicated process pool to each individual section of your app. However, NGINX&nbsp;Unit runs such sections as separate apps even when that is not your end goal, which can result in unwanted overhead: the app requires several processes to run (at least one per each section), but some of them remain idle most of the time (wasting CPU cycles and memory), while others may be constantly overloaded with a torrent of requests. There needs to be a way to share processes between the individual sections of an app.<\/p>\n<p>Moreover, the <span>one-app-per-section<\/span> approach requires repeating the same setting values over and over, causing redundancy, unnecessary risk of misconfiguration, and lack of logical cohesion between the individual sections of the app that were represented as standalone entities. To update a redundant setting, multiple non&#8209;atomic configuration updates (API calls) are necessary, which raises concerns about potential inconsistencies. All of these problems led us to introduce a new approach in version&nbsp;1.18.0.<\/p>\n<p>Now, instead of having to set up a single application object for each and every section of your app that handles its scripts in a customized way, you can use the new <code>targets<\/code> option to make them all peacefully work together under the roof of a single app: <\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"php_config_with_targets\" data-gist-line=\"1-40\"><\/code><!-- {\n    \"routes\": [\n        {\n            \"match\": {\n                \"uri\": [\n                     \"*.php\",\n                     \"*.php\/*\"\n                ]\n            },\n\n\n            \"action\": {\n                \"pass\": \"applications\/phpapp\/direct\"\n            }\n        },\n        {\n            \"action\": {\n                \"pass\": \"applications\/phpapp\/script_index\"\n            }\n        },\n    ],\n\n\n    \"applications\": {\n        \"phpapp\": {\n            \"type\": \"php\",\n            \"user\": \"www-data\",\n            \"group\": \"www-data\",\n            \"targets\": {\n                \"direct\": {\n                    \"root\": \"\/var\/www\/app\/\"\n                },\n\n\n                \"script_index\": {\n                    \"root\": \"\/var\/www\/app\/\",\n                    \"script\": \"index.php\"\n                }\n            }\n        }\n    }\n} php_config_with_targets --><\/p>\n<p>Each target within the app can have its own combination of the <code>root<\/code>, <code>index<\/code>, and <code>script<\/code> options, independently running its own scripts. However, all targets share such application&#8209;wide settings as <code>isolation<\/code>, <code>limits<\/code>, <code>options<\/code>, and <code>processes<\/code>. Keep in mind, however, that it&#8217;s not currently possible to manage resource distribution between individual targets within an app. <\/p>\n<p>The new approach centralizes and error&#8209;proofs application management while allowing you to flexibly control the many endpoints of any application with individual targets. Moreover, you can combine it with the older method if need be, grouping or decoupling your app configurations as you see fit.<\/p>\n<p>For further details and examples, see our <a target=\"_blank\" href=\"https:\/\/unit.nginx.org\/configuration\/#targets\" rel=\"noopener noreferrer\">Targets<\/a> configuration guide and <a target=\"_blank\" href=\"https:\/\/unit.nginx.org\/howto\/\" rel=\"noopener noreferrer\">how&#8209;tos<\/a> for PHP&#8209;based apps.<\/p>\n<h2>URL Encoding<\/h2>\n<p><span>NGINX Plus 1.18.0<\/span> implements <em>URL encoding<\/em> (also called <a target=\"_blank\" href=\"https:\/\/tools.ietf.org\/html\/rfc3986#section-2.1\" rel=\"noopener noreferrer\"><em>percent encoding<\/em><\/a>), which is used in URIs most often to represent characters outside the English&#8209;language alphabet (such as characters with diacritical markings like the umlaut or tilde) and characters that can have a special meaning (such as the forward slash as the divider between elements in a URI) when they don&#8217;t have that meaning. You can use URL encoding to escape characters in the <code>pass<\/code>, <code>arguments<\/code>, and <code>uri<\/code> options. There are two main use cases.<\/p>\n<p>First, you can escape the forward&#8209;slash character as <code>%2F<\/code> in the argument to a <code>pass<\/code> option:<\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"escape_forward_slash\" data-gist-line=\"1-12\"><\/code><!-- {\n    \"listeners\": {\n         \"*:80\": {\n             \"pass\": \"routes\/slashes%2Fin%2Froute%2Fname\"\n         }\n    },\n\n    \"routes\": {\n         \"slashes\/in\/route\/name\": [\n         ]\n    }\n} escape_forward_slash --><\/p>\n<p>Second, you can create filters with the <code>uri<\/code> and <code>arguments<\/code> options that contain characters which have special meaning in NGINX&nbsp;Unit routing, or even target individual bytes:<\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"fancy_urls\" data-gist-line=\"1-19\"><\/code><!-- {\n    \"routes\": {\n        \"slashes\/in\/route\/name\": [\n            {\n                \"match\": {\n                    \"uri\": \"\/%2A\",\n                    \"arguments\": {\n                        \"%25\": \"%21%C3*\"\n                    }\n                },\n\n                \"action\": {\n                    \"return\": 301,\n                    \"location\": \"http:\/\/fancyurls.example.com\"\n                }\n            }\n        ]\n    }\n} fancy_urls --><\/p>\n<p>Here, the <code>uri<\/code> filter expects a single literal asterisk (<code>*<\/code>, URL&#8209;encoded as <code>%2A<\/code>), and the <code>arguments<\/code> filter expects the percent sign (<code>%<\/code>, URL&#8209;encoded as <code>%25<\/code>) as the key with a value consisting of an exclamation point (<code>!<\/code>, URL&#8209;encoded as <code>%21<\/code>) followed by a diacritic UTF&#8209;8 character such as <strong>\u00d6<\/strong> or <strong>\u00c5<\/strong>: the byte value <code>%C3<\/code> usually signals a set of diacritic characters and is followed here by an arbitrary byte sequence (represented by the asterisk in the value definition). <\/p>\n<p>These two sample queries illustrate the effect of the complete configuration: <\/p>\n<pre><code class=\"terminal\">$ <strong>curl -v 'http:\/\/localhost:80\/*?%=!\u00dc'<\/strong>\r\n...\r\n&lt; HTTP\/1.1 301 Moved Permanently\r\n&lt; Location: http:\/\/fancyurls.example.com\r\n&lt; Server: Unit\/1.18.0\r\n\r\n$ <strong>curl -v 'http:\/\/localhost:80\/*?%=!\u00f2'<\/strong>\r\n...\r\n&lt; HTTP\/1.1 301 Moved Permanently\r\n&lt; Location: http:\/\/fancyurls.example.com\r\n&lt; Server: Unit\/1.18.0<\/code><\/pre>\n<p>You can see that both requests are successfully redirected which means NGINX&nbsp;Unit has matched the asterisk and exclamation point literals and the diacritic characters as intended. Effectively, this means you can single out arbitrary byte sequences and characters in different encodings.<\/p>\n<h2>Enhancements Introduced in NGINX Unit 1.17.0<\/h2>\n<p>In addition to the headliner changes we made in <span>NGINX Unit 1.18.0<\/span>, version&nbsp;1.17.0 introduced some notable but smaller&#8209;scale updates.<\/p>\n<h3>Instant Responses and Redirects<\/h3>\n<p><span>NGINX Unit 1.17.0<\/span> introduced support for instant responses and redirections during routing. The new route <code>action<\/code> called <code>return<\/code> allows you to reply to a request with an arbitrary HTTP response status code and also provide a redirect <code>location<\/code> when that is required by the status code&#8217;s semantics. Here we define a permanent redirect for the <strong>\/legacy\/<\/strong> URI:<\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"redirect\" data-gist-line=\"1-11\"><\/code><!-- {\n    \"match\": {\n        \"uri\": \"\/legacy\/*\"\n    },\n\n\n    \"action\": {\n        \"return\": 301,\n        \"location\": \"http:\/\/legacy.example.com\"\n    }\n} redirect --><\/p>\n<h3>Fractional Server Weights in Upstreams<\/h3>\n<p>Version&nbsp;1.17.0 also added support for fractional weights in the <code>upstreams<\/code> object <a href=\"https:\/\/www.nginx.com\/blog\/nginx-unit-1-16-0-now-available\/#Round-Robin-Load-Balancing-with-Upstreams\">introduced in version&nbsp;1.16.0<\/a>. With the original integer weights, you often had to adjust the entire weighting scheme to change a single server&#8217;s share of requests. Consider this initial configuration, which divides requests evenly among three servers (each has the default weight of&nbsp;1):<\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"equal_weights\" data-gist-line=\"1-7\"><\/code><!-- {\n    \"servers\": {\n        \"192.168.0.100:8080\",\n        \"192.168.0.101:8080\",\n        \"192.168.0.102:8080\"\n    }\n} equal_weights --><\/p>\n<p>If you then want one server to get only half as many requests as the others (a distribution represented with integers as&nbsp;2:2:1), you actually have to change the weight on the two servers whose relative proportion is staying the same, which is somewhat counterintuitive: <\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"2_2_1_weights\" data-gist-line=\"1-11\"><\/code><!-- {\n    \"servers\": {\n        \"192.168.0.100:8080\": {\n            \"weight\": 2,\n        },\n        \"192.168.0.101:8080\": {\n            \"weight\": 2,\n        },\n        \"192.168.0.102:8080\"\n    }\n} 2_2_1_weights --><\/p>\n<p>With fractional weights, you need to change the weight only on the server whose proportion is changing: <\/p>\n<p><code data-gist-id=\"e5dcdc96e159e3d243fd8d16a9a34bfb\" data-gist-file=\"fractional_weights\" data-gist-line=\"1-9\"><\/code><!-- {\n    \"servers\": {\n        \"192.168.0.100:8080\",\n        \"192.168.0.101:8080\",\n        \"192.168.0.102:8080\": {\n            \"weight\": 0.5\n        }\n    }\n} fractional_weights --><\/p>\n<p>The difference between the two schemes is not great with just three servers, but fractional weights can significantly reduce the number of necessary changes when there are many servers.<\/p>\n<h2>Conclusion<\/h2>\n<p>This is a brief recap of the new features in versions&nbsp;1.17.0 and&nbsp;1.18.0. As mentioned above, very soon we&#8217;ll publish a dedicated post delving into the details of the newly introduced <code>rootfs<\/code> feature: its technicalities and advantages, potential use cases, <span>less-than-obvious<\/span> risks, and unexpected side effects. Stay tuned!<\/p>\n<p>Meanwhile, the NGINX&nbsp;Unit team continues to work on even more fabulous improvements while simultaneously perfecting what&#8217;s already there: our <a target=\"_blank\" href=\"https:\/\/github.com\/orgs\/nginx\/projects\/1\" rel=\"noopener noreferrer\">current roadmap<\/a> includes plans to support configuration variables, add some HTTP goodness, and extend wildcard support in request matching patterns.<\/p>\n<p>For a list of the changes and bug fixes in releases&nbsp;1.17.0 and&nbsp;1.18.0, see the <span><a target=\"_blank\" href=\"https:\/\/unit.nginx.org\/CHANGES.txt\" rel=\"noopener noreferrer\">NGINX Unit changelog<\/a><\/span>.<\/p>\n<p>NGINX&nbsp;Plus subscribers get support for NGINX&nbsp;Unit at no additional charge. <a href=\"https:\/\/www.nginx.com\/free-trial-request\/\">Start a free 30&#8209;day trial<\/a> of NGINX&nbsp;Plus today.<\/p>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/www.nginx.com\/blog\/nginx-unit-1-18-0-now-available\/\">NGINX Unit 1.18.0 Adds Filesystem Isolation and Other Enhancements<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/www.nginx.com\">NGINX<\/a>.<\/p>\n<p>Source: <a href=\"https:\/\/www.nginx.com\/blog\/nginx-unit-1-18-0-now-available\/\" target=\"_blank\" rel=\"noopener noreferrer\">NGINX Unit 1.18.0 Adds Filesystem Isolation and Other Enhancements<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"mh-excerpt\"><p>NGINX Unit 1.18.0 Adds Filesystem Isolation and Other Enhancements Usually, we&#8217;d start a blog post like this one rather light&#8209;heartedly, but the mood of the times is undoubtedly somber, the reasons for that being too numerous to joke about. However, we hope you&#8217;re doing well&nbsp;&ndash; and again, we have some news to share. For starters, we at the NGINX&nbsp;Unit team believe that the term &#8220;isolation&#8221; doesn&#8217;t necessarily deserve the bad rap it has gained recently around the globe, and our most recent release, NGINX Unit 1.18.0, has arrived with an accordingly themed major update. Filesystem Isolation The isolation family of application settings, introduced in NGINX Unit 1.11.0, now includes a new rootfs object. If the underlying OS allows it, you can use rootfs to designate an arbitrary directory as the filesystem root. An application configured this way is locked within <a class=\"mh-excerpt-more\" href=\"https:\/\/jirak.net\/wp\/nginx-unit-1-18-0-adds-filesystem-isolation-and-other-enhancements\/\" title=\"NGINX Unit 1.18.0 Adds Filesystem Isolation and Other Enhancements\">[ more&#8230; ]<\/a><\/p>\n<\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[169],"tags":[652],"class_list":["post-37507","post","type-post","status-publish","format-standard","hentry","category-news","tag-nginx"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/37507","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/comments?post=37507"}],"version-history":[{"count":1,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/37507\/revisions"}],"predecessor-version":[{"id":37508,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/posts\/37507\/revisions\/37508"}],"wp:attachment":[{"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/media?parent=37507"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/categories?post=37507"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jirak.net\/wp\/wp-json\/wp\/v2\/tags?post=37507"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}