<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Tommaso Allevi on Medium]]></title>
        <description><![CDATA[Stories by Tommaso Allevi on Medium]]></description>
        <link>https://medium.com/@allevo?source=rss-ad339891ff8f------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*CiRbo24VdvElU3YdZbEIDA.jpeg</url>
            <title>Stories by Tommaso Allevi on Medium</title>
            <link>https://medium.com/@allevo?source=rss-ad339891ff8f------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 18:07:41 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@allevo/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[How enumerations in Rust help you in DDD]]></title>
            <link>https://medium.com/@allevo/how-enumerations-in-rust-help-you-in-ddd-1750cde07e49?source=rss-ad339891ff8f------2</link>
            <guid isPermaLink="false">https://medium.com/p/1750cde07e49</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[rust-programming-language]]></category>
            <category><![CDATA[domain-driven-design]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Tommaso Allevi]]></dc:creator>
            <pubDate>Sat, 18 Mar 2023 16:02:46 GMT</pubDate>
            <atom:updated>2023-03-18T16:02:46.585Z</atom:updated>
            <content:encoded><![CDATA[<p>Rust is a language entered in world recently and immediately is become the most loved language. But why ? Why it become so popular and loved?</p><p>I started to play with it and something was triggered in me because my mental model was under a challenge, so I started to study it deeply and my answer for the previous question is the type system. I was a NodeJS developer so the type system never catch me, but the Rust one attract me a lot.</p><p>In particolar the enumeration system captured my attention: it is very interesting and works differently from other languages.</p><h3>The enums</h3><p>An enumeration is commonly defined as set of predefined items called variants. In a lot of languages the enumerations are represented as an integer: the first item is identified by 0, the second with 1 and so on…</p><pre>// C example<br>enum Direction {<br>    UP, // = 0<br>    LEFT, // = 1<br>    RIGTH, // = 2<br>    DOWN// = 3<br>};</pre><p>In Rust you can specify as well a simple enumeration in the following way:</p><pre>enum Direction {<br>    Up,<br>    Left,<br>    Rigth,<br>    Down,<br>};</pre><p>The comments are removed because Rust abstracts it allowing us to refer to a variant only through its name, not using the index.</p><p>Another difference, actually the main one, is that Rust allows you to attach types to the variants. But not only that, you can attach different type to different variant of a enumeration.</p><p>This feature opens my mind to a new world.</p><p>Modelling your data type becomes much easier. But let’s try to make some examples.</p><pre>struct GameState { /* ... */ }<br>struct GameOverReason { /* ... */ }<br><br>enum GameStatus {<br>    NotStarted,<br>    Playing(GameState),<br>    Paused(GameState),<br>    GameOver(GameOverReason)<br>};<br><br>let not_started = GameStatus::NotStarted;<br>let playing = GameStatus::Playing(GameState { /* ... */ } );<br>let paused = GameStatus::Paused(GameState { /* ... */ } );<br>let game_over = GameStatus::GameOver(GameOverReason { /* ... */ });<br><br>// The following line throws a compile error !!<br>let not_possible = GameStatus::GameOver(GameState{ /* ... */ });<br></pre><p>In the example above, the <em>GameStatus</em> is enumeration that holds different types based on which variant the variable assumed.</p><h3>Use enums in DDD</h3><p>As previously said, an enum describes an exclusive <em>or</em> condition on own variants: an enum instance cannot identify 2 different variants at the same time.</p><p>This property, combined with the capability to hold data, allows your code to be more clear avoiding impossible states.</p><p>Let’s make some examples</p><h4>A ticket system</h4><p>We want to build a ticketing system to support the customer care: a client want to know why something went wrong with a product. For that, the customer creates the ticket inserting some data (texts, images and so on). Our business requires that the ticket is workable only if the customer care team accepts it. Otherwise the ticket is declined. At the end the customer closes the ticket.</p><p>In this situation, the application should consider:</p><ul><li>It is not possible to create ticket without the initial customer data</li><li>After the open, only the customer care team can decide to accept or reject the ticket.</li><li>If the ticket is accepted, the customer care has to reply with some data (texts, images, link to the documentation)</li></ul><p>With those constraints, a possible solution could be the following one:</p><pre>enum TicketState {<br>    Open,<br>    Taken,<br>    Declined,<br>    Closed,<br>}<br>struct CustomerInitialInformation { /* ... */ }<br>struct CustomerCareTakenInformation { /* ... */ }<br>struct DeclineReason { /* ... */ }<br>struct CloseInformation { /* ... */ }<br><br>struct Ticket {<br>    // Track the state of the ticket<br>    state: TicketState,<br>    // Track when the ticket is opened<br>    open_date: Date,<br>    // Hold the initial customer request<br>    customer_intial_information: CustomerInitialInformation,<br>    // Track when the ticket is taken by the customer care team<br>    taken_date: Option&lt;Date&gt;,<br>    // Hold the response from the customer care team<br>    customer_care_taken_information: Option&lt;CustomerCareTakenInformation&gt;,<br>    // Track when the ticket is declined<br>    declined_date: Option&lt;Date&gt;,<br>    // Hold the deline reason<br>    decline_reason: Option&lt;DeclineReason&gt;,<br>    // Track close date<br>    close_date: Option&lt;Date&gt;,<br>    // Hold the close information<br>    close_information: Option&lt;CloseInformation&gt;,<br>}<br><br>// But we can write something like:<br>let ticket1 = {<br>    taken_date: Some(Date::now()),<br>    customer_care_taken_information: None,<br>    // ...<br>};<br>let ticket2 = {<br>    close_date: Some(Date::now()),<br>    declined_date: Some(Date::now()),<br>    // ...<br>};</pre><p><strong>NB</strong>: In Rust there is not <em>null</em>. Option allows us to store the <em>None</em> value as null and <em>Some</em> to hold the not-null data.</p><p>Using the above structure, we could face up with some illegal states: what happen if we have the <em>taken_date</em> without <em>customer_care_taken_information</em>? And is a valid state if the <em>close_date </em>is set and also <em>declined_date </em>? Our structure allows illegal states.</p><p>How can we improve this ? Using enums!</p><pre>struct CustomerInitialInformation { /* ... */ }<br>struct CustomerCareTakenInformation { /* ... */ }<br>struct DeclineReason { /* ... */ }<br>struct CloseInformation { /* ... */ }<br><br>enum Ticket {<br>    Open(CustomerInitialInformation),<br>    Taken(CustomerInitialInformation, CustomerCareTakenInformation),<br>    Declined(CustomerInitialInformation, DeclineReason),<br>    Closed(CustomerInitialInformation, CustomerCareTakenInformation, CloseInformation),<br>}</pre><p>In this way, the code doesn’t contain <em>Option</em> anymore. And illegal states are not possibile: your domain is safer.</p><h4>A shopping cart</h4><p>A common problem is the shopping cart.<br>Suppose an e-commerce needs to implement that feature with the following requirements:</p><ul><li>A customer creates a shopping cart only adding an item</li><li>A customer adds or remores an item from the cart</li><li>A customer finalizes the a not-empty cart paying it</li><li>No change can be made after the payment</li></ul><p>In this situation your application has to be designed to avoid inconsistent states.</p><p>A possible design can be the following one:</p><pre>struct Item { /* ... */ }<br><br>enum ShoppingCartState {<br>    Empty,<br>    Full,<br>    Paid,<br>}<br>struct PaymentInformation { /* ... */ }<br><br>struct ShoppingCart {<br>    // Track the state of the cart<br>    state: ShoppingCartState,<br>    // Hold the items<br>    items: Vec&lt;Item&gt;,<br>    // Hold payment information<br>    payment_information: Option&lt;PaymentInformation&gt;,<br>}<br><br>// But we can write something like:<br>let cart1 = ShoppingCart {<br>    items: Vec::new(),<br>    payment_information: Some(PaymentInformation { /* ... */ }),<br>    // ...<br>};<br>let cart2 = ShoppingCart {<br>    state: ShoppingCartState::Full,<br>    payment_information: Some(PaymentInformation { /* ... */ }),<br>    // ...<br>};</pre><p>In the above code, the data type doesn’t enforce inconsistent state: in fact who guarantee that the <em>items </em>array is not empty when the <em>payment_information</em> is fulfilled ? <em>payment_information</em><strong><em> </em></strong>can be set also when the state is <em>Full</em> ? Again, we can do it better.</p><p>Let’s have a look instead to the following code:</p><pre>struct Item { /* ... */ }<br><br>struct PaymentInformation { /* ... */ }<br>struct FullCart { items: Vec&lt;Item&gt; }<br>struct PaidCart {<br>    paid_items: Vec&lt;Item&gt;,<br>    payment_information: PaymentInformation,<br>}<br><br>enum ShoppingCart {<br>    Empty,<br>    Full(FullCart),<br>    Paid(PaidCart),<br>}</pre><p>Now we are not able to have a <em>ShoppingCart </em>in illegal state: our domain is safe, much better!</p><h3>Conclusion</h3><p>Rust has a strong type system. The capabilities of the enumerations are amazing allowing your application to consider only the valid state.</p><p>In the next article we will see how to translate safety your enumeration from one state to another one!</p><p>Did you tried it? Which is your experience on using enumerations?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1750cde07e49" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Microservice pattern: a way to rework your project safely]]></title>
            <link>https://medium.com/@allevo/microservice-pattern-a-way-to-rework-your-project-safely-25dcc913814?source=rss-ad339891ff8f------2</link>
            <guid isPermaLink="false">https://medium.com/p/25dcc913814</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[microservices]]></category>
            <category><![CDATA[performance]]></category>
            <dc:creator><![CDATA[Tommaso Allevi]]></dc:creator>
            <pubDate>Fri, 14 Aug 2020 19:11:31 GMT</pubDate>
            <atom:updated>2020-08-14T19:19:29.504Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gw_2-1o6T64jBYw07A-4yQ.png" /></figure><p>In the past years the microservice architecture pattern has become more and more widespread. A lot of articles have been published telling the benefits that this architecture brings to the table. Today I would like to tell my story about this approach from the points of view of a developer and a business.</p><p>During this pandemic global issue, some colleagues had an idea to allow people to know which shop deliveries its good to consumers at home. From this idea born <a href="https://acasatua.app/">https://acasatua.app/</a>: a website where users find shops and their information. This idea is simple and we wanted to make a POC as soon as possible because the users are in need of a tool like this right now.</p><p>Currently working for <a href="https://www.mia-platform.eu/en/">Mia Platform</a>, we asking to our boss to use our product to design e build this project. So, after writing a mini-backlog, we structured the architecture using microservice pattern. Mia Platform suite helps us to make:</p><ul><li>a shop service is responsible for sending the customers a list of shops ordered by user distance;</li><li>a CMS enable us to check which shop is published;</li><li>a DevOps pipeline supports us to build and deploy what we need ;</li><li>Logs &amp; Monitoring;</li><li>Kubernetes managements</li></ul><h4>First implementation</h4><p>For the first implementation, we have built the shop service using Node.js and use MongoDB as database. Fortunately MongoDB supports <a href="https://docs.mongodb.com/manual/reference/operator/query/near/">geo-spatial queries</a> natively so we are able to sort the shops by user distance easly. So the first implementation was based on this kind of query and written in Node.js.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/33aea11b1cf9371af95e1cd855215bd3/href">https://medium.com/media/33aea11b1cf9371af95e1cd855215bd3/href</a></iframe><h4>Second implementation</h4><p>On the first days the amount of registered shop on database was about 200. The days after we introduced other shops reaching 500, the most of them based on Milan. The first implementation has a limitation: every API request triggered a geo-spatial query to the database. To avoid that, I have used setInterval to fetch all shops from database and stored them into process memory. On every invocation, the web server made a copy of the array, calculated the user distance and sorted the array by that value. With this implementation the database is not stressed at all and we can use kubernetes <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">Horizontal Pod Autoscaler</a> for handling potentially spikes of traffic.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6eda4b917f12a7fc1280c69012279438/href">https://medium.com/media/6eda4b917f12a7fc1280c69012279438/href</a></iframe><h4>The problem</h4><p>Anyway after some weeks, our database grew up reaching 2000 shops. So, in memory, our API calculated the distance and sorted an array of 2000 elements. From our dashboard we saw that the API performance dropped down reaching 400ms response time. After a debug session with <a href="https://clinicjs.org/">clinicjs</a>, I have realized that the problem wasn’t the distance calculation or the array sorting: the issue was rendering the JSON output.</p><p>Node.js webserver is a single thread process: using the event loop Node.js is able to handle multiple request. Anyway when your process does a CPU intensive task like object serialization, the event loop doesn’t loop fluently. For this reason Node.js doesn’t perform well on this kind of operation: to solve this kind of problem, we thought it could be interesting to experiment with other languages.</p><p>Because this project is for fun, I wanted to experiment with something new: Rust or Go. Keeping Node.js as base of my comparison, I wrote 3 little webserver that serving an array of 2000 elements serializing them as JSON:</p><ul><li><a href="https://www.fastify.io/">fastify</a>: a Node.js web framework. Under the hood it use <a href="https://github.com/fastify/fast-json-stringify">fast-json-stringify</a> for json serialization.</li><li><a href="https://golang.org/">Go</a>: a plain webserver with <a href="https://golang.org/pkg/encoding/json/#Marshal">json.Marshall</a></li><li><a href="https://github.com/actix/actix-web">Actix web</a>: a web framwork with <a href="https://serde.rs/">serde</a> for JSON serialization</li></ul><p>Below a table that shows the result on my Mac (2,4 GHz Intel Core i5 quad-core 16 GB):<br><strong>DISCLAIMER</strong>: I didn’t want to spend too much time on creating an extremely accurate benchmark suite, I just wanted a rough idea of the performance differences between languages for this specific use case, so don’t take this data too seriously</p><pre>+-----------------------+--------------+<br>|  Framwork / language  |     Req/sec  |<br>+--------------------------------------+<br>|  Fastify (Node.js)    |       65     |<br>+--------------------------------------+<br>|  Go                   |     1400     |<br>+--------------------------------------+<br>|  Actix web (Rust)     |     1600     |<br>+-----------------------+--------------+</pre><h4>Third implementation</h4><p>The third implementation was written in Rust in 3 days (3 night truly). The response time drop down to 80ms: not so bad.</p><p>Always just for fun, I wasn’t so happy: the code was not structured well and the code makes a lot of memory copy. In order to avoid that, I start to use <a href="https://doc.rust-lang.org/std/sync/struct.Arc.html">Arc</a> that allows you to copy a pointer instead of entire string.</p><h4>Fourth implementation</h4><p>So the fourth implementation use massively Arc in the code. The response time now is 15ms per request: this is a huge improvement!</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bf6c131f7377d59a703bf27f3fb6ab05/href">https://medium.com/media/bf6c131f7377d59a703bf27f3fb6ab05/href</a></iframe><h3>What I have learnt</h3><p>I’ve used Node.js for the first 2 implementation because I have experience with that language and I’m able to develop fast. Develop fast means reaching the user early. And reaching user early means has feedback. We use that feedback to see if the project makes sense for the end-user, which improvements the user want and which part is not so interested. For that reason choosing your favourite language helps you to get feedback as soon as possible. This is a big deal.</p><p>Anyway problems always came. When they came, you should be ready to handle it. The microservice pattern allows you to handle problems: in this case we have to change only the issued microservice without impact other pieces of project. For this project the issue was a performance one: the users was waiting for a long before seeing the results. This was a bad user experience.</p><p>Another hidden problem was the cost: Kubernetes has deployed 3 replicas for handling all incoming requests, with Rust only one. The four implementation uses less resource and spend less that the third one.</p><p>The performance issue is only an example: in general as a developer you will meet a lot of features or issues to implement. Depends on which impact those features/issues have, part of the software has to be rewritten entirely or partially. If your microservices are small, the cost of rewriting one or more services entirely may be sustainable.</p><h3>Conclusion</h3><p>When we talk about microservice we say a lot of things. In this article we have seen how that pattern helps us to reach the user as soon as possible with an example. When the problem arrives, you can choose to keep the same codebase (such as the first and the second implementations have the same same code base mostly) or change language completely (as the third implementation).</p><p>Anyway the business reaches the user early and the developers don’t create a lock-in on the technology used initially.</p><p>This is my story about microservice rework by replacement. Which one is yours? Tell me!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=25dcc913814" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nginx for managing your API access]]></title>
            <link>https://medium.com/mia-platform/nginx-for-managing-your-api-access-4e24e56e4aee?source=rss-ad339891ff8f------2</link>
            <guid isPermaLink="false">https://medium.com/p/4e24e56e4aee</guid>
            <category><![CDATA[nginx]]></category>
            <dc:creator><![CDATA[Tommaso Allevi]]></dc:creator>
            <pubDate>Fri, 27 Apr 2018 10:15:53 GMT</pubDate>
            <atom:updated>2018-11-19T09:04:48.343Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j6jUL9sMeWqsbI2aU1QLZQ.png" /><figcaption>The typical nginx configuration</figcaption></figure><p>In microservice environment, the first gate we can thought to is the API access point. There, all your requests arrive. There, your security is important. There, your company should invest its attention for security enhancement. It is important.</p><h3>The permission management</h3><p>The first kind of security is user permission. But the permission management is hard. In the most of case, the tools that implement this kind of security are very complex and difficult to manage without some expertise. So, your company should make a choice: uses some commercial solution or builds the own one. But in every enterprise context, there’s need one. In this article, we’ll see how it could be implemented using <em>nginx</em>.</p><p>The simplest security enhancement could be implemented checking if the user is able to access to an endpoint. This is our goal in this article. In particular we’ll able to declare that a path can be called by a subset of our user base. To manage that requirement, let’s say we are tagging our users with some labels called <em>groups</em>: each user can have zero, one or more strings that represent the grants given to him.</p><p>So, our goal is to find a technical solution to implement, for instance, those restrictions:</p><ul><li><em>GET /service1</em>: all users</li><li><em>POST /service1</em>: only logged users</li><li><em>DELETE /service1:</em> only admin users</li><li><em>GET /service2</em>: only the users that are <em>admin</em> and <em>editor</em></li><li><em>POST /service2:</em> only the user that are <em>admin</em> or <em>editor</em></li><li><em>GET /service3</em>: only the users that are either <em>admin</em> or <em>editor</em> but not both</li><li>POST /service3: users that are <em>admin</em> but not <em>editor</em></li><li>all other APIs are forbidden for all users</li></ul><p>In this article we’ll see how to build this simple access management using only <em>nginx</em> and a little custom application.</p><h3>Group login expression</h3><p>Before staring, we should translate the restriction requirements into a logic expressions: in this way, we’ll use the logic expression to checking the user grants.</p><ul><li><em>GET /service1</em>: all users ⇔ <em>true</em></li><li><em>POST /service1</em>: only logged users ⇔ <em>logged</em></li><li><em>DELETE /service1:</em> only admin users ⇔ <em>admin</em></li><li><em>GET /service2</em>: only the users that are <em>admin</em> and <em>editor</em> ⇔<em> admin &amp;&amp; editor</em></li><li><em>POST /service2:</em> only the user that are <em>admin</em> or <em>editor</em> ⇔<em> admin || editor</em></li><li><em>GET /service3</em>: only the users that are either <em>admin</em> or <em>editor</em> but not both ⇔ <em>(!admin &amp;&amp; editor) || (admin &amp;&amp; !editor)</em></li><li>POST /service3: users that are <em>admin</em> but not <em>editor</em> ⇔ <em>admin &amp;&amp; !editor</em></li><li>the other paths are forbidden for all users ⇔ <em>false</em></li></ul><p><strong>NB: </strong>the <em>logged</em> group is a special group used to identify all logged users.</p><p>Now we have the requirements formatted in logic expression!</p><p>This is powerful. For each request, we should understand which user calls the backend resolving its session, fetches its groups and evaluates the logic expression associated to that path to know if he has the grant to make that request or not.</p><p>In the next chapter we’ll see which tools allow us to make those checks.</p><h3>Set up</h3><p>In my previous article <a href="https://medium.com/@allevo/nginx-for-surviving-in-microservices-era-28fa65295feb">Nginx for surviving in microservice era</a>, we had seen how to setup <em>docker</em>, <em>docker-compose</em> and how we use them to simulate a production environment. If you don’t read it yet, please do it!</p><p>For our purpose, we need some files:</p><ul><li><em>my-server.conf</em> nginx configuration file</li><li><em>docker-compose.yml</em> file</li></ul><p>For our purpose, the following <em>docker-compose.yml</em> fits well</p><pre>version: &#39;3&#39;</pre><pre>services:<br>  reverse-proxy:<br>    image: nginx<br>    ports:<br>      - &quot;8888:80&quot;<br>    volumes:<br>      - ./my-server.conf:/etc/nginx/conf.d/default.conf:ro<br>  be1:<br>    image: jmalloc/echo-server<br>    environment:<br>    - PORT=80<br>  be2:<br>    image: tutum/hello-world<br>  be3:<br>    image: breerly/hello-server<br>    environment:<br>    - HELLO_PORT=80<br>    - HELLO_RESPONSE_DELAY=1<br>  auth:<br>    image: allevo/nginx_cerbero<br>    environment:<br>    - MONGODB_URL=mongodb://mongo/auth<br>    - REDIS_URL=redis://redis<br>    - HTTP_PORT=80<br>    link:<br>    - mongo<br>    - redis</pre><pre>  mongo:<br>    image: mongo<br>  redis:<br>    image: redis</pre><p>The first part of this file is the same of my previous post: three application backend and a <em>reverse-proxy</em> implemented by <em>nginx</em>. We added <em>auth</em> service that will be described in the follow chapters. <em>mongo</em> and <em>redis</em> services are used by <em>auth</em> service for storing the users and their sessions respectively.</p><p>We’ll study <em>my-server.conf</em> nginx configuration file in the next chapters too.</p><h3>auth_request directive</h3><p>We’ll use <em>reverse-proxy</em> service as entry point in our system. Before each request reaches the backend, the <em>auth</em> service is queried to ask if the current user is able to access to that backend.</p><p>To do that, <em>nginx</em> offers a simple directive called <a href="http://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request">auth_request</a>. That directive allows us to make a subrequest to another service to check if the incoming request could be performed. From documentation:</p><blockquote>If the subrequest returns a 2xx response code, the access is allowed. If it returns 401 or 403, the access is denied with the corresponding error code. Any other response code returned by the subrequest is considered an error.</blockquote><p>So the <em>auth</em> service interface is clear: <em>nginx</em> automatically blocks the incoming requests if the <em>auth </em>service return a non 2xx status code.</p><p>Our <em>auth</em> service implements this interface: it responses <em>204 No Content </em>if the group logic expression returns true; <em>403 Forbidden</em> otherwise.</p><p>Because we want to keep our permission configuration in a one place, we will store it in the <em>nginx</em> conf. So let’s start to write it!</p><pre>map $request_method-$uri $groupExpression {<br>  default &quot;0&quot;;</pre><pre>  &quot;~^GET-/auth/&quot; &quot;true&quot;;<br>  &quot;POST-/auth/login&quot; &quot;true&quot;;<br>  &quot;POST-/auth/signup&quot; &quot;true&quot;;</pre><pre>  &quot;GET-/service1&quot; &quot;true&quot;;<br>  &quot;POST-/service1&quot; &quot;logged&quot;;<br>  &quot;DELETE-/service1&quot; &quot;admin&quot;;<br>  &quot;GET-/service2&quot; &quot;<em>admin &amp;&amp; editor</em>&quot;;<br>  &quot;POST-/service2&quot; &quot;<em>admin || editor</em>&quot;;<br>  &quot;GET-/service3&quot; &quot;<em>(!admin &amp;&amp; editor) || (admin &amp;&amp; !editor)</em>&quot;;<br>  &quot;POST-/service3&quot; &quot;<em>admin &amp;&amp; !editor</em>&quot;;</pre><pre>}</pre><pre>map $uri $upstreamName {<br>  &quot;~^/auth&quot; auth;<br>  &quot;~^/my-service1&quot; service1;<br>  &quot;~^/my-service2&quot; service2;<br>  &quot;~^/my-service3&quot; service3;<br>}<br>server {<br>  listen 80;<br>  server_name localhost;<br>  default_type text/plain;</pre><pre>  location / { # A<br>    auth_request /auth; # 1<br>    <br>    proxy_pass http://$upstreamName; # 4<br>  }</pre><pre>  location = /auth { # B<br>    proxy_set_header &#39;group-expression&#39; $groupExpression; # 2</pre><pre>    proxy_pass <a href="http://auth;/">http://auth;</a> # 3<br>  }<br>}</pre><p>For each request, nginx check witch location block matches. It found the (A) block.</p><p>In that block the first thing defined there is the <em>auth_request</em> directive(1). Because of this directive, <em>nginx</em> makes another request before continuing with the incoming one. So, nginx “forwards” the request to <em>/auth</em>.</p><p><em>nginx</em> looks for which block matches that path finding the location (B). In this block, <em>nginx</em> add a new header to the request called <em>group-expression</em> (2). The value of this header is calculated by the <em>$groupExpression</em> map. As you can see, the group logic expressions are defined there. Than, forwards the request to the <em>auth</em> service (3). This service resolves the group check and returns 204 or 403.</p><p>If auth responses 204, nginx continues with the incoming request. Finally, <em>nginx</em> proxies the incoming request to the backend evaluating <em>$upstreamName</em> map.</p><h3>The tests</h3><p>For our purpose some CURLs work fine.</p><p>First of all we need to register an user. In this example, <em>auth</em> exposes <em>/signup </em>API to insert the user in <em>mongo</em> database. In your context, this is made by your application.</p><pre>curl http://localhost:8888/auth/signup -XPOST -d &#39;{&quot;username&quot;:&quot;my_user&quot;,&quot;password&quot;:&quot;my_password&quot;,&quot;groups&quot;:[&quot;admin&quot;, &quot;editor&quot;]}&#39; -H&#39;Content-type: application/json&#39;</pre><p>With this CURL, a new user is registered. <strong>Obviously during the user registration, the client should not declare him groups!</strong></p><p>After the user registration, we need to log in.</p><pre>curl http://localhost:8888/auth/login -XPOST -d &#39;{&quot;username&quot;:&quot;my_user&quot;,&quot;password&quot;:&quot;my_password&quot;}&#39; -H&#39;Content-type: application/json&#39; -vvvv</pre><p>The output of this CURL is like this:</p><pre>*   Trying ::1...<br>* TCP_NODELAY set<br>* Connected to localhost (::1) port 8888 (#0)<br>&gt; POST /auth/login HTTP/1.1<br>&gt; Host: localhost:8888<br>&gt; User-Agent: curl/7.54.0<br>&gt; Accept: */*<br>&gt; Content-type: application/json<br>&gt; Content-Length: 47<br>&gt;<br>* upload completely sent off: 47 out of 47 bytes<br>&lt; HTTP/1.1 200 OK<br>&lt; Server: nginx/1.13.8<br>&lt; Date: Fri, 26 Jan 2018 21:39:28 GMT<br>&lt; Content-Type: application/json<br>&lt; Content-Length: 83<br>&lt; Connection: keep-alive<br>&lt; Set-Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW; Path=/<br>&lt;<br>* Connection #0 to host localhost left intact<br>{&quot;_id&quot;:&quot;5a6b9f61af897500108d1dcc&quot;,&quot;username&quot;:&quot;my_user&quot;,&quot;groups&quot;:[&quot;admin&quot;,&quot;editor&quot;]}</pre><p>As you can see, the backend sends a cookie. Please copy it: we’ll use it for the following CURLs.</p><p>Now we are ready to start our test!</p><p>Our user has the groups “admin” and “editor”. So, we expect that <em>GET /service1</em> (true), <em>POST /service1</em> (logged), <em>DELETE /service1</em> (admin), <em>GET /service2</em> (admin &amp;&amp; editor), <em>POST /service2</em> (admin || editor) work well.</p><p>In fact the following CURLs work fine!</p><pre>curl http://localhost:8888/service1 -H &#39;Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW&#39;<br>curl http://localhost:8888/service1 -H &#39;Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW&#39; -X POST<br>curl http://localhost:8888/service1 -H &#39;Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW&#39; -X DELETE<br>curl http://localhost:8888/service2 -H &#39;Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW&#39;<br>curl http://localhost:8888/service2 -H &#39;Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW&#39; -X POST</pre><p>Instead the “my_user” user has not grant to access to <em>GET /service3</em> ((!admin &amp;&amp; editor) || (admin &amp;&amp; !editor)) and <em>POST /service3</em> (admin &amp;&amp; !editor).</p><pre>curl http://localhost:8888/service3 -H &#39;Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW&#39;<br>curl http://localhost:8888/service3 -H &#39;Cookie: sid=Wn4E5hZjwcssv90eCOj00dsZUi3897tW&#39; -X POST</pre><p>Both of last CURLs return <em>403 Forbidden</em>.</p><h3>Conclusion</h3><p>As you can see, using this approach, you configuration</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e24e56e4aee" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mia-platform/nginx-for-managing-your-api-access-4e24e56e4aee">Nginx for managing your API access</a> was originally published in <a href="https://medium.com/mia-platform">Mia-Platform</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nginx for surviving in microservices era]]></title>
            <link>https://medium.com/@allevo/nginx-for-surviving-in-microservices-era-28fa65295feb?source=rss-ad339891ff8f------2</link>
            <guid isPermaLink="false">https://medium.com/p/28fa65295feb</guid>
            <category><![CDATA[network-security]]></category>
            <category><![CDATA[microservices]]></category>
            <category><![CDATA[nginx]]></category>
            <category><![CDATA[network]]></category>
            <dc:creator><![CDATA[Tommaso Allevi]]></dc:creator>
            <pubDate>Mon, 15 Jan 2018 21:49:40 GMT</pubDate>
            <atom:updated>2018-01-20T16:50:23.486Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*12oKwpsrRHaF7uwKyieJuQ.png" /><figcaption>The typical nginx configuration</figcaption></figure><p>In the microservices ecosystem the network is the base upon you build your application. Managing it with the right tool could help you.</p><p><em>nginx</em> is a great tool but, sometimes, its configuration can be tricky: the copy-paste approach is an evil friend making the refactoring not simple. In this article we will introduce some concepts to help you with your network configuration. We’ll make an overview how your network can be managed to dispatch the requests to the right backend.</p><h4>Nginx is a great tool</h4><p>From <a href="https://nginx.org/en/">nginx</a> website:</p><blockquote>nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server,</blockquote><p>In other words: <em>nginx</em> receives all your requests and forwards them to the right backend. But why should we have to bring an application behind another network layer?</p><p>There’re lots of benefits. Only for making some examples:</p><ul><li>resolves SSL</li><li>makes HTTP compression as gzip or deflate</li><li>adds/removes headers</li><li>is single entry point to dispatch requests to the right place</li><li>hides your real application backend</li></ul><p><em>nginx</em> is a very good tool, but it has a lot of feature: we are going to cover a little piece of the whole its world to see how the growing complexity can be managed easier. So, let’s start from the beginning!</p><h4>Set up</h4><p>In this article we’ll use docker containers for our examples to keep the setup independent from the underlying operative system. In particular, <em>docker-compose</em> is very useful to simulate a production environment containing more than one application backends.</p><p>We are going to write two files: <em>docker-compose.yml</em> and <em>my-server.conf</em>. The former contains the docker compose services definition; the latter will be used to study and play with <em>nginx</em> configuration. For simplicity, keep those files in the same directory.</p><p>First of all, install <a href="https://docs.docker.com/engine/installation/"><em>docker</em></a> and <a href="https://docs.docker.com/compose/install/"><em>docker-compose</em></a>. Then we create a new file <em>docker-compose.yml </em>with the following content:</p><pre>version: &#39;3&#39;</pre><pre>services:<br>  reverse-proxy:<br>    image: nginx<br>    ports:<br>      - &quot;8888:80&quot;<br>    volumes:<br>      - ./my-server.conf:/etc/nginx/conf.d/default.conf:ro<br>  be1:<br>    image: jmalloc/echo-server<br>    environment:<br>    - PORT=80<br>  be2:<br>    image: tutum/hello-world<br>  be3:<br>    image: breerly/hello-server<br>    environment:<br>    - HELLO_PORT=80<br>    - HELLO_RESPONSE_DELAY=1</pre><p>This file describes four services: a reverse proxy implemented by <em>nginx</em> and three application backends for simulating real backends. The reverse proxy configuration describe that we’ll use the 8888 port on localhost to make requests to it. We are mounting the volumes to change the nginx configuration easily.</p><p>The configuration for the application backends is not important: <em>be1</em>, <em>be2</em>, <em>be3</em> services listen on 80 port. In this way we can “forget the ports” in <em>nginx</em> configuration during the service definitions because the 80 is the HTTP standard port.</p><p>Now we are ready to play with <em>nginx</em> configuration! First of all, our configuration is kept as simple as possible to add complexity little by little.</p><p>So, let’s start creating the simplest possible configuration: in this first iteration, <em>nginx</em> will always respond directly with “200 OK” to every request, without forwarding the request to an application backend.</p><p>Create another file in the same directory called <em>my-server.conf</em> with the following content:</p><pre>server {<br>  listen 80;<br>  <br>  default_type text/plain;<br>  <br>  location / {<br>    return 200 &#39;OK!&#39;;<br>  }<br>}</pre><p>To use our configuration with <em>nginx</em>, we’ll use the following command in a terminal:</p><pre>docker-compose up</pre><p>Our configuration servers “<em>OK!</em>” at <a href="http://localhost:8888/"><em>http://localhost:8888</em></a>. In the terminal, the previous command has an output like:</p><pre>reverse-proxy_1  | 172.21.0.1 - - [19/Dec/2017:22:41:10 +0000] &quot;GET / HTTP/1.1&quot; 200 3 &quot;-&quot; &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&quot; &quot;-&quot;</pre><p>This output proves that the service called <em>reverse-proxy</em> numbered <em>1 </em>has received and served the request returning 200 status code. Well done!</p><p>To stop docker compose, please send it a SIGINT signal using the <em>ctrl+C</em>.</p><p>All requests have the same response for all HTTP methods. This is not a big deal but is a good baseline to explore and to understand <em>nginx</em> better<em>.</em></p><h4>Your first proxy to an application backend</h4><p>In the microservices era your application is made by one or more backends. For starting easier, we’ll consider only one service.</p><p>We simulate it using the docker image <a href="https://hub.docker.com/r/jmalloc/echo-server/">jmalloc/echo-server</a>. In our docker compose we have already declared that the <em>be1</em> uses that image for running.</p><p>So, let’s overwrite the previous <em>nginx</em> configuration with the following:</p><pre>upstream service1 { server be1; }</pre><pre>server {<br>  listen 80;</pre><pre>  location /my-service1 {<br>    proxy_pass http://service1;<br>  }<br>}</pre><p>This configuration tells to <em>nginx</em> that all requests that have <em>/my-service1</em> as path prefix will be proxied to the <a href="http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream">upstream</a> <em>service1</em>. For our purpose we can consider upstreams as aliases for the given hostname. Thanks to docker compose the <em>be1</em> hostname is resolved pointing to the “jmalloc/echo-service” container.</p><p>So, CURLing <a href="http://localhost:8888/my-service1"><em>http://localhost:8888/my-service1</em></a><em>,</em> the request reaches <em>nginx</em> that sees the path matches the location block and, though the <em>service1</em> upstream, proxies it to the <em>be1</em> “aka” <em>jmalloc/echo-service</em> image.</p><p>In the output in your terminal there’re lines like those:</p><pre>be1_1 | 172.21.0.5:48330 | GET /my-service1<br>reverse-proxy_1 | 172.21.0.1 — — [28/Dec/2017:19:29:02 +0000] “GET /my-service1 HTTP/1.1” 200 538 “-” “Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36” “-”</pre><p>As you can see, the request reached the <em>be1</em> service numbered <em>1</em> through the <em>reverse-proxy</em> numbered <em>1</em>.</p><h4>Add a lot of services</h4><p>Your webserver may be built using a lot of application backends. So, we suppose three application backends (<em>be1</em>, <em>be2</em>, <em>be3</em>). We are already defined them in our <em>docker-compose-yml.</em> <em>nginx</em> proxies the requests based on the first part of the url.</p><pre>upstream service1 { server be1; }<br>upstream service2 { server be2; }<br>upstream service3 { server be3; }</pre><pre>server {<br>  listen 80;<br>  default_type text/plain;</pre><pre>  location /my-service1 {<br>    proxy_pass http://service1;<br>  }</pre><pre>  location /my-service2 {<br>    proxy_pass http://service2;<br>  }</pre><pre>  location /my-service3 {<br>    proxy_pass http://service3;<br>  }<br>}</pre><p>In this configuration, we have described three locations to handle the three different urls, one for each application backend.</p><p>Now you can request <a href="http://localhost:8888/my-service1">http://localhost:8888/my-service1</a>, <a href="http://localhost:8888/my-service1">http://localhost:8888/</a><a href="http://localhost:8888/my-service2"><em>my-service2</em></a> and <a href="http://localhost:8888/my-service1">http://localhost:8888</a><a href="http://localhost:8888/my-service3"><em>/my-service3</em></a> and retrieve right responses.</p><p>But… how can we manage a <em>lot</em> of microservices? Or how can we write the configuration better avoiding listing all locations?</p><h4>Simplify the configuration</h4><p>Our goal is to keep the network configuration as simple as possible and in only one place. In the previous configuration we have three <em>location</em> blocks with three different <em>proxy_pass</em> definitions. We can do better!</p><p><em>nginx</em> defines some default variable like <a href="http://nginx.org/en/docs/varindex.html"><em>$uri</em>, <em>$request_method</em> and so on</a>. We can use them to map the request to the upstream name.</p><p>We are going to use <a href="http://nginx.org/en/docs/http/ngx_http_map_module.html"><em>map</em></a>. From <em>nginx</em> documentation:</p><blockquote>Creates a new variable whose value depends on values of one or more of the source variables specified in the first parameter.</blockquote><p>The following configuration defines a <em>map</em> called <em>$upstreamName</em>. For each request, the map defines the <em>$upstreamName</em> variable using the url path mapping the incoming request url to upstream name.</p><pre>upstream service1 { server be1; }<br>upstream service2 { server be2; }<br>upstream service3 { server be3; }</pre><pre>map $uri $upstreamName {<br>  &quot;~^/my-service1&quot; service1;<br>  &quot;~^/my-service2&quot; service2;<br>  &quot;~^/my-service3&quot; service3;<br>}</pre><pre>server {<br>  listen 80;<br>  server_name localhost;<br>  default_type text/plain;</pre><pre>  location / {<br>    proxy_pass http://$upstreamName;<br>  }<br>}</pre><p><em>$upstreamName</em> map is valorized according to the map:</p><ul><li><em>GET /my-service1</em>: <em>$upstreamName</em> is set to <em>service1</em></li><li><em>DELETE /my-service2</em>: <em>$upstreamName</em> is set to <em>service2</em></li><li><em>GET /my-service3/foo/bar</em>: <em>$upstreamName</em> is set to <em>service3</em></li><li><em>GET /my-service15</em>: <em>$upstreamName</em> is set to <em>service1</em></li></ul><p>When a request arrives, the <em>nginx</em> finds the right location, set the <em>$upstreamName</em> variable according to the regexes above and proxies the request to the right upstream.</p><p>Now we have only one place to write out configuration! With this configuration the output is the same.</p><p>We can complicate the <em>$upstreamName </em>definition using other predefined variables to send the requests to the right backend.</p><pre>map $request_method-$uri $<em>upstreamName</em> {<br>  &quot;~^(GET|POST|PUT|DELETE)-/my-service1&quot; service1;<br>  &quot;~^POST-/my-service2&quot; service2;<br>  &quot;~^(GET|DELETE)-/my-service3&quot; service3;<br>}</pre><p>The above example builds a <em>map</em> using the HTTP method and the request url.</p><ul><li>The GET, POST, PUT and DELETE requests that have the url path that starts with <em>/myservice1</em> will be proxied to <em>service1</em> aka <em>be1</em>.</li><li>Only the <em>POST</em> requests that have the url path that starts with <em>/my-service2</em> will be proxies to <em>service2</em> aka <em>be2</em>.</li><li>The GET and DELETE requests that have the url path that start with <em>/myservice3</em> will be proxied to <em>service3</em> aka <em>be3</em></li></ul><p>The following CULRs work well:</p><pre>curl -X GET http://localhost:8888/my-service1<br>curl -X POST http://localhost:8888/my-service1<br>curl -X PUT http://localhost:8888/my-service1<br>curl -X DELETE http://localhost:8888/my-service1</pre><pre>curl -X POST http://localhost:8888/my-service2</pre><pre>curl -X GET http://localhost:8888/my-service3<br>curl -X DELETE http://localhost:8888/my-service3</pre><p>Instead, the following ones don’t:</p><pre>curl -X CUSTOM_METHOD http://localhost:8888/my-service1</pre><pre>curl -X GET http://localhost:8888/my-service2<br>curl -X PUT http://localhost:8888/my-service2<br>curl -X DELETE http://localhost:8888/my-service2</pre><pre>curl -X POST http://localhost:8888/my-service3<br>curl -X PUT http://localhost:8888/my-service3</pre><pre>curl -X GET http://localhost:8888/unknown-service</pre><p>This last block of CURLs returns always <em>500</em> response code. It is not nice: another HTTP status can be used. The following chapter explains how the different status code can be managed.</p><h4>Add HTTP server utilities</h4><p><em>nginx</em> allows us to define multiple servers in the same configuration file. Previously our server listens on <em>80</em> TCP port. Another way to create an HTTP server is to bind a <a href="https://en.wikipedia.org/wiki/Unix_domain_socket">unix socket</a>. This allows us to speed the TCP connection up but with the drawback that the connections can be made only from localhost.</p><p>Let’s define a simple <em>nginx</em> server that responses always <em>404 Not found</em> page.</p><pre>server {<br>  listen unix:/var/run/not-found.sock;<br>  return 404;<br>}</pre><p>We can use it to proxy all requests that not matching any map rules. Update the <em>nginx</em> configuration as following:</p><pre>server {<br>  listen unix:/var/run/not-found.sock;<br>  return 404;<br>}</pre><pre>upstream not_found { server unix:/var/run/not-found.sock; }</pre><pre>upstream service1 { server be1; }<br>upstream service2 { server be2; }<br>upstream service3 { server be3; }</pre><pre>map $request_method-$uri $upstreamName {<br>  default not_found;</pre><pre>  &quot;~^(GET|POST|PUT|DELETE)-/my-service1&quot; service1;<br>  &quot;~^POST-/my-service2&quot; service2;<br>  &quot;~^(GET|DELETE)-/my-service3&quot; service3;<br>}</pre><pre>server {<br>  listen 80;<br>  server_name localhost;<br>  default_type text/plain;</pre><pre>  location / {<br>    proxy_pass http://$upstreamName;<br>  }<br>}</pre><p>The above configuration defines the <em>not_found</em> upstream used as default value of <em>$upstreamName</em> map using the <em>default</em> keyword. If an incoming request doesn’t match any rules, the map will assign the default value <em>not_found</em> to <em>$upstreamName</em> variable.</p><p><strong>TIP:</strong> the <em>not_found</em> server can be customized serving your HTML page.</p><p>With the previous configuration, a <em>GET</em> to <em>/my-service2</em> path returns a <em>404 Not found</em>. In REST the right status code is <em>405 Method not allowed</em>. So, let’s add another server that responses always that status code and use it in the <em>$upstreamName</em> map definition:</p><pre>server {<br>  listen unix:/var/run/not-found.sock;<br>  return 404;<br>}</pre><pre>server {<br>  listen unix:/var/run/not-allowed.sock;<br>  return 405;<br>}</pre><pre>upstream not_found { server unix:/var/run/not-found.sock; }<br>upstream not_allowed { server unix:/var/run/not-allowed.sock; }</pre><pre>upstream service1 { server be1; }<br>upstream service2 { server be2; }<br>upstream service3 { server be3; }</pre><pre>map $request_method-$uri $upstreamName {<br>  default not_found;<br>  &quot;~^(GET|POST|PUT|DELETE)-/my-service1&quot; service1;<br>  &quot;~^\w+-/my-service1&quot; not_allowed;<br>  &quot;~^POST-/my-service2&quot; service2;<br>  &quot;~^\w+-/my-service2&quot; not_allowed;<br>  &quot;~^(GET|DELETE)-/my-service3&quot; service3;<br>  &quot;~^\w+-/my-service3&quot; not_allowed;<br>}</pre><pre>server {<br>  listen 80;<br>  server_name localhost;<br>  default_type text/plain;</pre><pre>  location / {<br>    proxy_pass http://$upstreamName;<br>  }<br>}</pre><p>The <em>not_found</em> and <em>not_allowed</em> upstreams are used for all not matching requests or for not allowed methods respectively.</p><h4>Handle secreted API</h4><p>Continuing with the map usage, we are able to manage the API secrets automatically.</p><p>As we have defined <em>not-found</em> and <em>not-allowed</em> servers, we can define an <em>unauthorized</em> HTTP server too.</p><pre>server {<br>  listen unix:/var/run/unauthorized.sock;<br>  return 401;<br>}</pre><p>The above server always responses <em>401 Unauthorized</em> status code.</p><p>Assuming that our APIs are under authorization: a secret has to be provided to access to the backend. For instance, <em>my-secret</em> and <em>another-secret</em> are the only right value of the <em>secret HTTP header</em> to access the backend.</p><p>To check the secret correctness, we can build a map called <em>$secretResolution</em> defined as below:</p><pre>map $http_secret $secretResolution {<br>  default n;<br>  &quot;my-secret&quot; y;<br>  &quot;another-secret&quot; y;<br>}</pre><p><em>nginx</em> defines the <em>$http_*</em> variables with the right HTTP header. In our case the <em>$http_secret </em>variable is valorized with the value of the “secret” HTTP header. For each request, <em>$secretResolution</em> is <em>y</em> if the request has the right value in the HTTP secret header, <em>n</em> otherwise.</p><p>We change the <em>$upstreamName</em> map to include the new <em>$secretResolution</em> variable. Adding the right upstreams, we are able to list our network dispatcher. Use the following configuration:</p><pre>server {<br>  listen unix:/var/run/not-found.sock;<br>  return 404;<br>}<br>server {<br>  listen unix:/var/run/not-allowed.sock;<br>  return 405;<br>}<br>server {<br>  listen unix:/var/run/unauthorized.sock;<br>  return 401;<br>}<br>upstream not_found { server unix:/var/run/not-found.sock; }<br>upstream not_allowed { server unix:/var/run/not-allowed.sock; }<br>upstream unauthorized { server unix:/var/run/unauthorized.sock; }</pre><pre>upstream service1 { server be1; }<br>upstream service2 { server be2; }<br>upstream service3 { server be3; }</pre><pre>map $http_secret $secretResolution {<br>  default n;<br>  my-secret y;<br>  another-secret y;<br>}</pre><pre>map $secretResolution-$request_method-$uri $upstreamName {<br>  default not_found;<br>  &quot;~^n&quot; unauthorized;<br>  &quot;~^y-(GET|POST|PUT|DELETE)-/my-service1&quot; service1;<br>  &quot;~^y-\w+-/my-service1&quot; not_allowed;<br>  &quot;~^y-POST-/my-service2&quot; service2;<br>  &quot;~^y-\w+-/my-service2&quot; not_allowed;<br>  &quot;~^y-(GET|DELETE)-/my-service3&quot; service3;<br>  &quot;~^y-\w+-/my-service3&quot; not_allowed;<br>}</pre><pre>server {<br>  listen 80;<br>  server_name localhost;<br>  default_type text/plain;<br>  location / {<br>    proxy_pass http://$upstreamName;<br>  }<br>}</pre><p>The <em>$upstreamName</em> map now includes the new <em>$secretResolution</em> variable too. Only if the secret, HTTP method and the first path of url match, the request is proxied to the right backend.</p><p>The following CURLs work well:</p><pre>curl -X GET -H &#39;secret: my-secret&#39; http://localhost:8888/my-service1<br>curl -X GET -H &#39;secret: another-secret&#39; http://localhost:8888/my-service1<br>curl -X GET -H &#39;secret: my-secret&#39; http://localhost:8888/my-service1/foo/bar</pre><p>Instead the following ones return 401:</p><pre>curl -X GET http://localhost:8888/my-service1<br>curl -X GET -H &#39;secret: &#39; http://localhost:8888/my-service1<br>curl -X GET -H &#39;secret: unknown-secret&#39; http://localhost:8888/my-service1</pre><h4>Conclusion</h4><p><em>nginx</em> is a great tool. Writing <em>nginx</em> configuration is not easy. <em>map</em> is a good feature. Be wise!</p><p>If you liked this article, please clap it!</p><p>I will share other <em>nginx</em> tips, so stay tuned!</p><p>For more infos:</p><ul><li><a href="http://nginx.org/en/docs/http/ngx_http_core_module.html">http://nginx.org/en/docs/http/ngx_http_core_module.htm</a>l</li><li><a href="http://nginx.org/en/docs/http/ngx_http_upstream_module.html">http://nginx.org/en/docs/http/ngx_http_upstream_module.html</a></li><li><a href="http://nginx.org/en/docs/http/ngx_http_map_module.html">http://nginx.org/en/docs/http/ngx_http_map_module.html</a></li></ul><p>A special thanks go to <a href="https://twitter.com/loige">Luciano Mammino</a> for helping me to write this article better.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=28fa65295feb" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>