<?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 KrishnaKumar on Medium]]></title>
        <description><![CDATA[Stories by KrishnaKumar on Medium]]></description>
        <link>https://medium.com/@krishnakumart?source=rss-91d698aaa5a6------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*cSq-sDIhXK1Pu2epMeufgw.jpeg</url>
            <title>Stories by KrishnaKumar on Medium</title>
            <link>https://medium.com/@krishnakumart?source=rss-91d698aaa5a6------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 30 May 2026 16:47:34 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@krishnakumart/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[An opinionated decentralized authorization using JWT tokens and service mesh.]]></title>
            <link>https://krishnakumart.medium.com/an-opinionated-decentralized-authorization-using-jwt-tokens-and-service-mesh-15973af93a23?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/15973af93a23</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[authorization]]></category>
            <category><![CDATA[jwt-authentication]]></category>
            <category><![CDATA[istio-service-mesh]]></category>
            <category><![CDATA[microservices-pattern]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Thu, 18 Jan 2024 17:36:42 GMT</pubDate>
            <atom:updated>2024-01-18T17:36:42.310Z</atom:updated>
            <content:encoded><![CDATA[<h3>An opinionated decentralized authorization using JWT tokens and service mesh</h3><p>Authorization is act of granting access/privileges to a resource. Authentication precedes authorization step to assert the identity of a particular user. Here we have used JWT tokens to provide a decentralized authorization for micro services.</p><p>JWT in short is self-contained authorization token issued by server post successful authentication of user credentials. The token is in encoded form with 3 portions. First portion is an encoded header, second one is encoded body, and the last one is encoded signature. Body can contain the claims for the token issued by the server during authentication step. While the header and body are simply encoded but not encrypted, can still be asserted for integrity using the signature portion on the server before serving the request.</p><p>The primary idea of decentralized authorization is to have authorization to be granular and lives within the service that owns the data/resource. In a way it is also called Native authorization pattern where the authorization code and service are written in same programming language.</p><p>Taking a step back, Identifying the right pattern for authorization can become confusing and challenging when there are many ways of implementation.</p><p>In our case, it’s an online shopping portal with only one group of users who are customers who can place/cancel orders and track delivery status. All we wanted to evaluate primarily as part of authorization was to check whether the user has access to the requested resource. Apart from token validation checks.</p><p>Here I would like to mention different authorization patterns we have evaluated. I would be illustrating the same using a sample set of microservices as shown below. We have started with extremely naive thinking and slowly moved to a maintainable solution with few comprisable tradeoffs.</p><h3>The Setup</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VlQ1w5x5eKssomonKEQw0g.jpeg" /><figcaption>Sample Client Service Interaction</figcaption></figure><blockquote>In the above diagram, we have 3 services.</blockquote><blockquote>Service A: Takes external requests and talks to downstream service.</blockquote><blockquote>Service B: Takes both external and internal requests and talks to downstream service C.</blockquote><blockquote>Service C: Serves the requests coming from different sources from the database.</blockquote><p>Also, there is an external ingress which accepts requests from Client and an internal ingress which facilitates communication between services.</p><p><strong>Points and Requirements for evaluation:</strong></p><ul><li>Standard JWT token for authentication. It has claim data of the “userId” with a pre defined expiry time.</li><li>Have to check if the user has access to requested resource.</li><li>Should allow some endpoints to be used for both internal and external purposes without compromising security.</li><li>E.g: Let’s say we need to fetch checkout for a client request. When it is called from outside, should have user Id along with checkoutId in the request. Whereas, when an internal service (like a retry/consolidation service) would like to utilize the same endpoint, it should be able to fetch using checkoutId itself without the need to send user Id. As these internal requests need not be initiated from the user itself.</li><li>Ability to restrict certain endpoints for internal consumption only.</li><li>Less cost of maintaining authorization policies.</li><li>Assume there is a dedicated service which does authentication.</li></ul><h3>Authorization approaches</h3><h4>Authorization on API gateway intercepting requests, responses &amp; headers (Proxy/Gateway pattern)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/781/1*TdLzBbPCo4qooUgEYX7TQQ.jpeg" /></figure><ul><li>Place some sort of API gateway in front of all services. All external traffic is routed through API gateway.</li><li>This will allow us to possibly centralize authorization to the Api gateway itself and all downstream services need not have concern of authorization.</li></ul><p>Disadvantages include:</p><ul><li>Approach is very much centralized with too much of authorization logic specific to different types of endpoints. This can suffer maintainability in the long run.</li><li>Also reading and parsing entire request/response/headers will have performance cost.</li><li>In case of intercepting response and doing authorisation will have adverse consequences and can fail miserably for mutation requests</li><li>Needed additional endpoints on each service to fetch user Id for a resource type by the API gateway.</li></ul><h4>Authorization to be performed on Service A (<em>Variation of Native Authorization pattern</em>)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/741/1*a0QLUWmD1sfKBX4BKO9MDg.jpeg" /></figure><ul><li>Fetching user Id accessible for a given requested resource from Service C’s DB</li><li>Authorization is done in Service A where it doesn’t have direct access to the data, nor the service is owner of the data.</li></ul><p>Disadvantages:</p><ul><li>Have to create additional endpoints on service C for fetching userId for various resource types.</li><li>Also doesn’t feel natural to do authorization at a point where the service is clearly not owner of the resource.</li></ul><h4>Authorization on DB Query layer on Service C (<em>Variation of Native Authorization pattern</em>)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/791/1*kzHAOZMU9tKgdzqQLJgC8g.jpeg" /></figure><ul><li>When a DB query is executed, we make sure user Id will always be part of where clause of the query.</li></ul><p>This gives fine grained control on the resources being accessed, but have disadvantages like</p><ul><li>Purely depends on developer awareness to not forget to add userId in to the where clause.</li><li>Authorization responsibility is leaking to query layer, and it is not very obvious.</li><li>Also doesn’t provide flexibility to differentiate requests from internal and external sources without sending that information all the way from handler to Query layer. This will eventually pave way to create endpoints with internal and external scopes.</li><li>Certain service may have actual data needed, but do not have userId mapping within same database to perform a join and use the where clause.</li></ul><h4>Authorization middleware at service C (<em>Variation of Native Authorization pattern</em>)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/791/1*6ER7cKGwL-bPqctLGqL7QA.jpeg" /></figure><ul><li>Service owning data can perform pre-authorization using middleware for each request handler.</li><li>Ability to turn off authorization checks for internal endpoints (more details on this below) and the logic stays in middleware and is not leaked to rest of the service layers.</li><li>Authorization is decentralized and lives in the corresponding service and thus enables more maintainability for the developers.</li></ul><p>Disadvantages</p><ul><li>Duplicated logic in multiple services unless explicitly pay attention to de-duplicate of logic across services. (E.g. If something is repeated, can be moved to an authorization policies library)</li><li>No single place to know all the authorization policies in the entire system.</li></ul><p>Out of all the above approaches, we feel authorization middleware at service level is more approachable and maintainable given its advantages of being decentralized. In our case, given the number of user groups and accesses required for each group, we don’t anticipate the policy implementations exploding unmanageably. Also, this approach with the use of some additional marker headers can help to move this authorization from the first service (doesn’t necessarily own data) which receives the external request to the service that owns the data.</p><h3>How did we implement?</h3><p>We leveraged Istio service mesh and added an envoy filter which does the following.</p><ul><li>Adds X-App-User-Id header on verifying the JWT token expiry and validity.</li><li>Adds X-App-Ingress-Source header with value as external if request originate from external ingress.</li><li>Also adds X-App-Authz-Mandatory header if request is originating from external ingress.</li><li>Also added tests to make sure above filters are not altered without knowing as it can potentially harm security.</li></ul><p>What is the intent of these headers?</p><p>X-App-User-Id: Determines the User Id of the request which is parsed from JWT token.</p><p>X-App-Ingress-Source: Determines the source of request, value external determines that it’s a request from customer device.</p><p>X-App-Authz-Mandatory: Determines if authorization check needs to be performed on the receiving service.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zoEpjJJvvN55P8j2NROS4g.png" /></figure><p>Additionally, we created couple of shared middleware implementations to make sure these headers are honored while request moves from service to service. For the purpose of this article, we are going to have code snippets in Golang.</p><h4>Parsing headers from incoming web request:</h4><p>Below example is a middleware that parses x-app-authz-mandatory and x-app-user-id headers from incoming web request and add its it to the context object. As a standard go pattern, context object is sent as parameter to every function within code.</p><pre>// middleware.go<br>package middlewares<br><br>import (<br> &quot;github.com/gofiber/fiber/v2&quot;<br>)<br><br>// PreAuthHeader middleware to add pre auth headers to context<br>func PreAuthHeader(c *fiber.Ctx) error {<br> c.Locals(&quot;preAuthHeaders&quot;, getPreAuthHeader(c.Get))<br> return c.Next()<br>}<br><br>func getPreAuthHeader(headerGetter headerGetter) map[string]string {<br> return map[string]string{<br>  &quot;x-app-authz-mandatory&quot;: headerGetter(&quot;x-app-authz-mandatory&quot;),<br>  &quot;x-app-customer-id&quot;: headerGetter(&quot;x-app-customer-id&quot;),<br> }<br>}</pre><h4>Add headers to outgoing web request:</h4><p>Below example is a middleware that reads x-app-authz-mandatory and x-app-user-id from context and adds as additional request headers to outgoing web request from any service.</p><pre>// RoundTripper implementation to be used by HTTP client<br>package roundtrippers<br><br>import (<br> &quot;net/http&quot;<br>)<br><br>// PreAuth RoundTripper to add pre auth headers<br>type PreAuth struct {<br> next http.RoundTripper<br>}<br><br>// RoundTrip implementation of round tripper with pre auth headers<br>func (trt *PreAuth) RoundTrip(req *http.Request) (resp *http.Response, err error) {<br> if headers, ok := req.Context().Value(&quot;preAuthHeaders&quot;).(map[string]string); ok {<br>  for k, v := range headers {<br>   req.Header.Add(k, v)<br>  }<br> }<br> return trt.next.RoundTrip(req)<br>}<br><br>// Adding roundtripper while creating http client instance<br>func NewClientWithRoundTrippers(client http.Client) http.Client {<br> transport := http.DefaultTransport<br> if client.Transport != nil {<br>  transport = client.Transport<br> }<br><br> preAuthRT := roundtrippers.NewPreAuth(transport)<br><br> return http.Client{<br>  CheckRedirect: client.CheckRedirect,<br>  Timeout:       client.Timeout,<br>  Transport:     preAuthRT,<br> }<br>}</pre><p>Above middleware&#39;s ensure both X-App-User-Id and X-App-Authz-Mandatory headers are propagated to all downstream services.</p><p>Below are the scenarios explaining how headers will be propagated:</p><h4>1. Request originated from customer devices (E.g: Request to Service B directly)</h4><p>E.g. Create order request received from client application.</p><p>When request is received from the customer device, it will pass through External ingress and will land to respective service. Envoy filter does the following:</p><ul><li>Authorize and add X-App-User-Id by reading the JWT claims.</li><li>Adds X-App-Authz-Mandatory header to mark that the request needs mandatory authorization as it is originated from customer device.</li><li>Adds X-App-Ingress-Source header with value external to mark the request is routed through external ingress.</li></ul><h4>2. Request originated from internal service (E.g: Service B)</h4><p>E.g: Get order request by order Id from internal service.</p><p>When request is originated from internal service like Service B, authorization is skipped as both X-App-Authz-Mandatory and X-App-Ingress-Source headers won&#39;t be available. However, its optional to add X-App-User-Id header as it doesn&#39;t dictate any mandatory authorization but acts as additional information to upstream service.</p><h4>3. Request originated from customer device and served via an internal aggregator/proxy (E.g: Request to Service C routed via Service A)</h4><p>E.g: Get invoice request when made from customer device. Invoice service can talk to order service to get details of the order.</p><p>When request is received from the customer device, it will pass through External ingress and Service A and will land to Service C. Envoy filter does the following:</p><ul><li>Authorizes and adds X-App-User-Id by reading the JWT claims.</li><li>Adds X-App-Authz-Mandatory header to mark that the request needs mandatory authorization as it is originated from customer.</li><li>Adds X-App-Ingress-Source header with value external to mark the request is routed through external ingress</li></ul><p>In this case,X-App-Authz-Mandatory header will be sent to upstream services by using the PreAuthRoundTripper middleware at Service A. This header will allow Service C to perform authorisation instead of Service A as Service C being the owner of the data.</p><ul><li>However, X-App-Ingress-Source header is not propagated through.</li></ul><p>Another point to note here is, Scenarios 2 and 3 above solves multi purposing endpoints for external and internal access.</p><p>Let’s take a case, “Get Address for an order” is called from customer device. It must return unauthorized (401) error when customer is not associated with order itself. Whereas when the same endpoint is used in the internal context to fetch address for the given order id shouldn’t bother about the userId.</p><h3>Restrict some endpoints from external access:</h3><p>Whatever best authorization strategy you can have, it may not be thought through and implemented on day 1. Since we also have some existing liability of some endpoints not suitable for authorization.</p><p>E.g. A proxy endpoint which is supposed to be accessible by internal services but do not have sufficient information to do authorization at the middleware.</p><p>X-App-Ingress-Source allows us to restrict external access to certain endpoints. X-App-Ingress-Source is not propagated to downstream services. This is marker header to know that the request is directly received from an external ingress. RestrictExternalAccess middleware if added to any handler, checks if the header is present and the value = externaland returns unauthorized response. This helps when you have endpoints that needs to be restricted to be called from public domain while the rest of them continue to be available. However, this header is expected to be used only as a stop gap solution.</p><pre>// Implementation of the RestrictExternalAccess middleware<br>package middlewares<br><br>import (<br> &quot;github.com/gofiber/fiber/v2&quot;<br> &quot;errors&quot;<br>)<br><br>// RestrictExternalAccess rejects direct requests from external clients which have<br>// &quot;X-App-Ingress-Source: external&quot; header in the request<br>func RestrictExternalAccess(c *fiber.Ctx) error {<br> if c.Get(&quot;X-App-Ingress-Source&quot;) == &quot;external&quot; {<br>  return errors.Unauthorized(&quot;This endpoint is not directly accessible from external ingress&quot;)<br> }<br> return c.Next()<br>}</pre><pre>// Example of how the RestrictExternalAccess middleware can be used<br><br><br>// ....<br>// ....<br>// Restricting external access to particular endpoint<br>app.Get(&quot;/profiles/:id&quot;, middlewares.RestrictExternalAccess, h.GetProfiles)<br>// Rest of the code about starting web server, registering handlers etc ommitted for brevity</pre><h3>Conclusion:</h3><p>With Istio providing header injection, middleware providing support to implement authorization policies at each service level. We will have a decentralized authorization in place which allows high maintainability and also allowing to defer authorization to the service owning the data.</p><p>If you have reached till here, it means you may have few questions like — How are we going to handle if we have multiple groups of users? I think, the same Authorization middleware’s would have to be enriched to work based on claims which can be sent as separate header by authentication service once the token validation is successful.</p><h4>Advantages:</h4><ul><li>Decentralized, the logic resides in the service that owns the data.</li><li>Authorization is moved to middleware instead of retaining it as part of the service code.</li><li>Helps to have custom logic on how to retrieve userId from each type of request especially if the userId is not standardized across the requests.</li><li>Maintainability is high.</li><li>Identifying the authorization logic related to particular resource access is available with the service itself.</li><li>Each authorization function for an endpoint is focused and granular and allows modification easily.</li><li>Highly extensible when new endpoints require new authorization.</li></ul><h4>Disadvantages:</h4><ul><li>Authorization policy is tied to the implementation of the service; thus re-usability can be low.</li><li>Cannot find all the policies in one place for the purpose of re-use and auditability.</li><li>Istio playing major role in adding request headers. Slight misconfiguration can cause the authorization to be skipped entirely. Hence having tests for envoy filter configuration is very much necessary.</li><li>Benefits of centralized authorization like caching can be low.</li></ul><h4>References:</h4><ul><li>An example of delegating authentication and adding additional request header is available here — <a href="https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter#script-examples">https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter#script-examples</a></li><li>Envoy filter configuration samples — <a href="https://github.com/istio/istio/wiki/EnvoyFilter-Samples">https://github.com/istio/istio/wiki/EnvoyFilter-Samples</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=15973af93a23" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Golang like Done channel in Rustlang]]></title>
            <link>https://faun.pub/golang-like-done-channel-in-rustlang-59fe36750f5d?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/59fe36750f5d</guid>
            <category><![CDATA[programming-tips]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[concurrency]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[best-practices]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Tue, 15 Aug 2023 17:08:02 GMT</pubDate>
            <atom:updated>2023-08-17T14:54:12.010Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/768/1*J_chdJORPLEwZulKeBE6HQ.jpeg" /></figure><p>Golang channels are used to establish communication between go routines. The same channels are also used to inform Done state of a go routine.</p><p>Here is how we typically do it in Golang if you have to spin off a bunch of go routines and stop/conclude them using a dedicated <strong><em>Done </em></strong>channel</p><pre>package main<br><br>import (<br> &quot;sync&quot;<br> &quot;time&quot;<br>)<br><br>func main() {<br>   // Initialise a done channel<br>   doneChan := make(chan bool)<br>   var wg sync.WaitGroup<br>  <br>   // Creates 10 goroutines<br>   for i := 0; i &lt; 10; i++ {<br>      wg.Add(1)<br>      go waitOnDone(i, &amp;wg, doneChan)<br>   }<br>  <br>   // Wait for 10 seconds and close the doneChan<br>   time.Sleep(time.Second * 10)<br>   close(doneChan)<br>  <br>   // main program to wait until all go routines are closed<br>   wg.Wait()<br>}<br><br>func waitOnDone(idx int, wg *sync.WaitGroup, doneChan chan bool) {<br>   defer wg.Done()<br>   for {<br>      // Do some computation here<br>      select {<br>        // If its able to read, it means channel is closed<br>        case &lt;-doneChan:<br>           println(&quot;received done on: &quot;, idx)<br>           return<br>      }<br>   }<br>}</pre><p>Here are the couple of things about Golang that made this possible:</p><p>1. when a channel is closed it can be read at least once by all the waiting go routines on the channel.</p><p>2. A select statement can read from multiple channels at once.</p><p>Usually a separate channel can be used to signal end of a go routine. This channel is commonly referred as Done channel, although its not a specific type but the name itself.</p><h3>The Rust way</h3><p>Rust also has channels as part of the standard library. But I chose to use the channels from crossbeam crate.</p><p>There are two distinct ways to read data from a Rust channel. One is to use <strong><em>recv()</em></strong> method and other one is <strong><em>try_recv()</em></strong> method. The first one is blocking call while the second one is non-blocking and returns immediately with an empty error value when there is nothing sent on the channel.</p><blockquote>Would Rust allow you to read a single done channel from multiple threads when <strong><em>tx</em></strong> is dropped/closed? The answer is Yes.</blockquote><p>Unlike Golang, Rust do not have in built support to read from multiple channels at once. This can be achieved in the following way.</p><p>Sequentially calling <strong><em>try_recv()</em></strong> methods for different channels ignoring the empty error on each call would effectively simulate what <strong><em>select</em></strong> does in Golang.</p><pre>extern crate crossbeam;<br><br>use std::thread;<br>use std::time;<br><br>fn main() {<br><br>    // done channel<br>    let (done_tx, done_rx) = crossbeam::channel::unbounded::&lt;()&gt;();<br>    let mut handles = vec![];<br><br>    // some other compute channel which you would use for business logic<br>    let (compute_tx, compute_rx) = crossbeam::channel::unbounded::&lt;u8&gt;();<br><br>    // Spin off 10 worker threads<br>    for i in 0..10 {<br>        let done_rx_clone = done_rx.clone();<br>        let compute_rx_clone = compute_rx.clone();<br><br>        let h = thread::spawn(move || {<br>            loop {<br><br>                // Try checking on channel for disconnection<br>                match done_rx_clone.try_recv() {<br>                    Err(e) =&gt; {<br>                        match e {<br>                            crossbeam::channel::TryRecvError::Empty =&gt; {},<br>                            crossbeam::channel::TryRecvError::Disconnected =&gt; {<br>                                println!(&quot;looks like done on thread {}&quot;, i);<br>                                break;<br>                            },<br>                        }<br>                    },<br>                    _ =&gt; {}<br>                }<br><br>                // Try checking on compute channel if there is any data to process<br>                match compute_rx_clone.try_recv() {<br>                    Ok(v) =&gt; {<br>                        println!(&quot;received value on thread {}: {}&quot;, i, v)<br>                    },<br>                    Err(e) =&gt; {<br>                        match e {<br>                            crossbeam::channel::TryRecvError::Empty =&gt; {},<br>                            crossbeam::channel::TryRecvError::Disconnected =&gt; {<br>                                println!(&quot;err on compute channel {}: {}&quot;, i, e);<br>                                break;<br>                            },<br>                        }<br>                    },<br>                }<br><br>            }<br>        });<br><br>        handles.push(h);<br>    }<br><br><br>    // Drop done_tx after certain time to emulate done channel<br>    let _wh = thread::spawn(move || {<br>        thread::sleep(time::Duration::from_secs(10));<br>        println!(&quot;sending signal&quot;);<br>        drop(done_tx);<br>    });<br><br>    // Send some data on compute_tx<br>    let _ch = thread::spawn(move || {<br>        loop {<br>            thread::sleep(time::Duration::from_secs(2));<br>            compute_tx.send(u8::MAX).unwrap();<br>        }<br>    });<br><br>    // Wait for all handles to finish<br>    for h in handles.into_iter() {<br>        h.join().expect(&quot;error joining thread&quot;);<br>    }<br><br>    println!(&quot;all threads handled&quot;);<br>}</pre><h4>Beware of tight loops</h4><blockquote>Would non blocking read from multiple channels without a delay result in an effect of on infinite while loop? Let us find out.</blockquote><p>When I ran the above program on my Mac, CPU shot up to <strong>~1000% ⏫</strong> consistently. Here is a snippet from top command</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wW0E-82u_N5MQRtWtNEpZw.jpeg" /><figcaption>rust-chan process showing 1000% cpu 😮</figcaption></figure><p>This CPU spike seems to happen as we are doing nothing when the channel is <strong><em>empty error</em></strong> resulting in an infinite tight loop.</p><pre>// Try checking on compute channel if there is any data to process<br>match compute_rx_clone.try_recv() {<br>    Ok(v) =&gt; {<br>        println!(&quot;received value on thread {}: {}&quot;, i, v)<br>    },<br>    Err(e) =&gt; {<br>        match e {<br>            crossbeam::channel::TryRecvError::Empty =&gt; {},<br>            crossbeam::channel::TryRecvError::Disconnected =&gt; {<br>                println!(&quot;err on compute channel {}: {}&quot;, i, e);<br>                break;<br>            },<br>        }<br>    },<br>}</pre><p>While I don’t have a perfect answer for this. A quirk helped to resolve this. By adding a sleep on receiving an <strong><em>empty error </em></strong>on the compute channel brought down the CPU utilisation to almost <strong>~ 0.1%</strong> ⏬</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IkSMqQ8dKNfGsa1W-3MCaQ.jpeg" /><figcaption>rust-chan process cpu brought down 😃</figcaption></figure><p>Here is the updated snippet where the sleep is added.</p><pre>// Try checking on compute channel if there is any data to process<br>match compute_rx_clone.try_recv() {<br>    Ok(v) =&gt; {<br>        println!(&quot;received value on thread {}: {}&quot;, i, v)<br>    },<br>    Err(e) =&gt; {<br>        match e {<br>            crossbeam::channel::TryRecvError::Empty =&gt; {<br>                // sleep for 1 second on empty channel<br>                thread::sleep(time::Duration::from_secs(1))<br>            },<br>            crossbeam::channel::TryRecvError::Disconnected =&gt; {<br>                println!(&quot;err on compute channel {}: {}&quot;, i, e);<br>                break;<br>            },<br>        }<br>    },<br>}</pre><p>You should have noticed that adding sleep to one of the <strong><em>try_recv() </em></strong>reduced the CPU significantly. You wouldn’t have to do it for the other channel as the tight loop is already broken.</p><h4>Thoughts to ponder</h4><blockquote>Is there a better way to do this?</blockquote><p>I am not sure, this has worked for me so far.</p><blockquote>Can you avoid sleep on compute channel?</blockquote><p>You can bring down the sleep to a reasonable value based on your need. As long it breaks the tight loop, you are good.</p><blockquote>Can we use the same compute channel for the purpose checking for <strong><em>Done</em></strong> without a need for dedicated channel?</blockquote><p>Of course you could do that too as long you as you have ability to drop all references to <em>compute_tx, </em>otherwise stick to a dedicated done channel as described and keep it consistent across app.</p><blockquote>Have I explored <strong><em>select! </em></strong>from crossbeam?</blockquote><p>Not yet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*aEbTRHhckItG22AK.png" /></figure><h4>👋 If you find this helpful, please click the clap 👏 button below a few times to show your support for the author 👇</h4><h4>🚀<a href="http://from.faun.to/r/8zxxd">Join FAUN Developer Community &amp; Get Similar Stories in your Inbox Each Week</a></h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=59fe36750f5d" width="1" height="1" alt=""><hr><p><a href="https://faun.pub/golang-like-done-channel-in-rustlang-59fe36750f5d">Golang like Done channel in Rustlang</a> was originally published in <a href="https://faun.pub">FAUN.dev() 🐾</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DIY: In Meeting Indicator — A simple WFH utility]]></title>
            <link>https://krishnakumart.medium.com/diy-in-meeting-indicator-a-simple-wfh-utility-26c0202b22d7?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/26c0202b22d7</guid>
            <category><![CDATA[audio-and-visuals]]></category>
            <category><![CDATA[meetings]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[work-from-home]]></category>
            <category><![CDATA[utilities]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Wed, 07 Apr 2021 02:07:28 GMT</pubDate>
            <atom:updated>2021-04-07T02:07:28.883Z</atom:updated>
            <content:encoded><![CDATA[<h3>DIY: In Meeting Indicator — A simple WFH utility</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*dHEetN4Yk44CpWhY.png" /><figcaption><em>The need for in meeting indicator at home :)</em></figcaption></figure><p>So many of you have gotten accustomed to work from home by now. This pandemic has thrown a lot of challenges at our face when most of us are not ready for. While Work From Home is kind of boon, living with it for a long time comes with its own unique challenges. One of the simplest challenge I have faced in the initial days was to make my family understand that I am in a meeting where Mic/Camera/both are activated. It was becoming difficult sometimes to respond back to the family members for very trivial things while I am listening to a crucial conversation on zoom or trying to answer an important question on an ongoing video chat. Of course they understand that I am in work and it is difficult for me to respond. But these things can slowly be the trends at your home.</p><p>Gestures are great way to convey the same. But they don’t always go smooth on the receiving side especially when you do them for visitors.</p><p>As a technologist, we should be able to tackle this better.</p><ul><li>How about sending text messages to family members when you are on/off meeting(May be overwhelming)</li><li>Share your calendar with family members? (How to deal with unscheduled ones?)</li><li>Mobile app to show your availability(Too much of work?)</li><li><strong>How about placing visual indicators at our desk? (Can be a Good starter)</strong></li></ul><p>I have picked up the last idea out of all the above ones which seems doable with relatively little effort. Back to hardware mode.</p><p><strong>Outcome:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JpjImA1-em3xATwT.png" /><figcaption><em>Goal of in meeting indicator</em></figcaption></figure><p>Two simple LED light indicators of distinct colours, one for Microphone and another for Camera. Indicators turn on automatically based on your activity on your mac(picked up Mac as a starter) be it on Zoom, Meet, Discord or Facetime etc.</p><p>What do I need?</p><ol><li>Majority of people use either one or two meeting applications on a daily basis. Check for developer documentation and find out whether they expose any apis to read Mic and Camera usage status.</li><li>Unfortunately, I will not able to do this at least for zoom meetings when I last checked in their developer docs. Thats too disappointing to find out.</li><li>An app to detect Mic or Camera usage on your computer.</li><li>Doing this is not so easy especially to work with OS APIs. As I use Mac and I am no expert in OSX development. I am looking for some open source alternatives and found out a tool called “Oversight” from a security researcher Patrick Wardle. More about the tool <a href="https://objective-see.com/products/oversight.html">here</a></li><li>Again unfortunate that I don’t get any apis to query device status. But hold on, the tool writes the device status to a log file which I can scrape. Hoo yeah.</li><li>Two 10mm LED bulbs of different colours and jumper wire connectors which I can get from an online store.</li><li>I need a controller which has wifi support. I chose NodeMCU V2 for this.</li><li>I have a Raspberry Pi with on OLED display running in my living room all the time. I wanted to make this a central hub for any device communications within my home rather than my work laptop.</li><li>Idea was that my work laptop just updates device status to raspberry pi and NodeMCU queries it every few seconds and control the LED state accordingly. Later I also wanted to leverage this for my windows laptop, Mobile and tablet as well.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*IHFomXAMLxbsNBdo.png" /><figcaption><em>Interaction diagram of nodeMCU, Raspberry Pi4 and Mac Laptop</em></figcaption></figure><p>Here is a short demo video</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F7trE5USEL08%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D7trE5USEL08&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F7trE5USEL08%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7ed1bc25c66d12acec914dd31cadb01d/href">https://medium.com/media/7ed1bc25c66d12acec914dd31cadb01d/href</a></iframe><h3>On my Raspberry PI</h3><p>To make things easy, I have assigned static IP to my Raspberry pi4 device based on its Mac address in my home router configuration.</p><p>Wrote a simple Golang HTTP webserver with endpoints to query as well as update device status.</p><pre>package main<br><br>import(<br>	&quot;net/http&quot;<br>	&quot;fmt&quot;<br>	&quot;nodemculistener/devicestate&quot;<br>)<br><br>var devState *devicestate.Indicator<br><br>func main() {<br>	mux := http.NewServeMux()<br>	mux.HandleFunc(&quot;/mic&quot;, handleMicState)<br>	mux.HandleFunc(&quot;/camera&quot;, handleCameraState)<br>	mux.HandleFunc(&quot;/*&quot;, handleUnknown)<br><br>	setup()<br>	if err := http.ListenAndServe(&quot;:3000&quot;, mux); err != nil {<br>		fmt.Println(&quot;listen error: &quot;, err.Error())<br>	}<br>}<br><br>func setup() {<br>	devState = devicestate.NewIndicator()<br>}<br><br>func handleMicState(rw http.ResponseWriter, r *http.Request) {<br>	if r.Method == http.MethodGet {<br>		fmt.Println(&quot;mic status&quot;, devState.IsMicEnabled())<br>		rw.Write([]byte(fmt.Sprintf(&quot;%v&quot;, devState.IsMicEnabled())))<br>	} else if r.Method == http.MethodPut {<br>		qParams := r.URL.Query()<br>		devState.SetMic(qParams.Get(&quot;state&quot;) == &quot;on&quot;)<br>		rw.WriteHeader(http.StatusOK)<br>	} else {<br>		rw.WriteHeader(http.StatusBadRequest)<br>	}<br>}<br><br>func handleCameraState(rw http.ResponseWriter, r *http.Request) {<br>	if r.Method == http.MethodGet {<br>		fmt.Println(&quot;camera status&quot;, devState.IsMicEnabled())<br>		rw.Write([]byte(fmt.Sprintf(&quot;%v&quot;, devState.IsCameraEnabled())))<br>	} else if r.Method == http.MethodPut {<br>		qParams := r.URL.Query()<br>		devState.SetCamera(qParams.Get(&quot;state&quot;) == &quot;on&quot;)<br>		rw.WriteHeader(http.StatusOK)<br>	} else {<br>		rw.WriteHeader(http.StatusBadRequest)<br>	}<br>}<br><br>func handleUnknown(rw http.ResponseWriter, r *http.Request) {<br>	fmt.Println(&quot;unknown req&quot;, r)<br>}</pre><h3>On My Mac</h3><p>Installed OverSight which starts writing log to a standard location. /System/Volumes/Data/Users//Library/Application Support/Objective-See/OverSight/OverSight.log</p><p><strong>There is a catch here:</strong></p><p>Oversight has an additional functionality which sends system tray notifications when device is activated or deactivated with information of the process like PID and name etc. The catch lies here, if you click on Allow on the notification (let’s say for zoom process) badge, The process will be whitelisted by oversight and you will not further receive notifications and neither will write to log file for Zoom again.</p><p>To solve this, I have turned off notifications from the Mac system settings for Oversight application and also cleared all the whitelisted entries from Oversight application.</p><p><strong>Script to scrape OverSight logs and updates device status tp Web server running on Raspberry pi</strong></p><pre>#!/bin/bash<br><br>echo &quot;Base URL configured: $BASE_URL&quot;<br><br>function updateMicStatus() {<br>	if [ $1 -eq 0 ]; then<br>		curl -X PUT &quot;$BASE_URL/mic?state=on&quot;<br>		echo &quot;mic status updated to on&quot;<br>	else<br>		curl -X PUT &quot;$BASE_URL/mic?state=off&quot;<br>		echo &quot;mic status updated to off&quot;<br>	fi<br>}<br><br>function updateCameraStatus() {<br>	if [ $1 -eq 0 ]; then<br>		curl -X PUT &quot;$BASE_URL/camera?state=on&quot;<br>		echo &quot;camera status updated to on&quot;<br>	else<br>		curl -X PUT &quot;$BASE_URL/camera?state=off&quot;<br>		echo &quot;camera status updated to off&quot;<br>	fi<br>}<br><br>cat &quot;/System/Volumes/Data/Users/krishnak/Library/Application Support/Objective-See/OverSight/OverSight.log&quot; | grep &quot;Audio Device&quot; | tail -n 1 | grep -q -w &quot;active&quot;<br>AUDIO_INIT_STATUS=$?<br>cat &quot;/System/Volumes/Data/Users/krishnak/Library/Application Support/Objective-See/OverSight/OverSight.log&quot; | grep &quot;Video Device&quot; | tail -n 1 | grep -q -w &quot;active&quot;<br>CAMERA_INIT_STATUS=$?<br><br>updateMicStatus $AUDIO_INIT_STATUS<br>updateCameraStatus $CAMERA_INIT_STATUS<br><br>tail -1f &quot;/System/Volumes/Data/Users/krishnak/Library/Application Support/Objective-See/OverSight/OverSight.log&quot; | awk &#39;/Audio Device/ &amp;&amp; /[^[:alpha:]]active/ { system(&quot;curl -X PUT $BASE_URL/mic?state=on&quot;) } /Audio Device/ &amp;&amp; /inactive/ { system(&quot;curl -X PUT $BASE_URL/mic?state=off&quot;) }&#39; &amp;<br>tail -1f &quot;/System/Volumes/Data/Users/krishnak/Library/Application Support/Objective-See/OverSight/OverSight.log&quot; | awk &#39;/Video Device/ &amp;&amp; /[^[:alpha:]]active/ { system(&quot;curl -X PUT $BASE_URL/camera?state=on&quot;) } /Video Device/ &amp;&amp; /inactive/ { system(&quot;curl -X PUT $BASE_URL/camera?state=off&quot;) }&#39;</pre><p><strong>Run the script from Mac automator as an application:</strong></p><pre>BASE_URL=&quot;http://192.168.11.111:3111&quot; &lt;Path To Folder&gt;/monitor.sh</pre><p><strong>Creating a automator application on Mac:</strong></p><p>Followed the process from here <a href="https://michal.karzynski.pl/blog/2013/01/13/how-turn-shell-commands-mac-os-x-services/">https://michal.karzynski.pl/blog/2013/01/13/how-turn-shell-commands-mac-os-x-services/</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*in1akzh21YVihi-i.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*68AKORU98uRyYzq3.png" /></figure><h3>On My NodeMCU</h3><p><strong>NodeMCU program to listen to device state changes:</strong></p><pre>#include &lt;Arduino.h&gt;<br>#include &lt;WiFiManager.h&gt;<br>#include &quot;RestClient.h&quot;<br>#include &lt;ESP8266HTTPClient.h&gt;<br>#include &quot;Config.h&quot; //Keep your URL and SSID credentials in this<br><br>#define MICLED D5<br>#define CAMERALED D6<br><br>WiFiManager wifiManager;<br>String hostname;<br><br>int readMicStatus();<br>int readCameraStatus();<br>String getServerHostname();<br><br>void setup() {<br>  pinMode(MICLED, OUTPUT);<br>  pinMode(CAMERALED, OUTPUT);<br><br>  WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP<br>  Serial.begin(9600);<br>  //reset settings - wipe credentials for testing<br>  //wm.resetSettings();<br><br>  // Automatically connect using saved credentials,<br>  // if connection fails, it starts an access point with the specified name ( &quot;AutoConnectAP&quot;),<br>  // if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())<br>  // then goes into a blocking loop awaiting configuration and will return success result<br>	// wifiManager.setConfigPortalBlocking(false);<br><br>  if(!wifiManager.autoConnect(HOTSPOT_SSID, HOTSPOT_PWD)) {<br>        Serial.println(&quot;Failed to connect&quot;);<br>        delay(5000);<br>        ESP.restart();<br>  } else {<br>        //if you get here you have connected to the WiFi    <br>        Serial.println(&quot;connected...yeey :)&quot;);<br>  }<br>}<br><br>void loop() {<br>	// wifiManager.process();<br>  if (readMicStatus() == 1) {<br>    digitalWrite(MICLED, HIGH);<br>  } else {<br>    digitalWrite(MICLED, LOW);<br>  }<br><br>  if (readCameraStatus() == 1) {<br>    digitalWrite(CAMERALED, HIGH);<br>  } else {<br>    digitalWrite(CAMERALED, LOW);<br>  }<br><br>  delay(2000);<br>}<br><br>int readMicStatus() {<br>  int ret = -1;<br>  HTTPClient http;<br>  http.begin(MIC_ENDPOINT);<br>  int httpCode = http.GET();<br><br>  if (httpCode &gt; 0) {<br>    String payload = http.getString();<br>    Serial.print(&quot;mic status: &quot;);<br>    Serial.println(payload);<br>    if (payload == &quot;true&quot;) {<br>      ret = 1;<br>    } else {<br>      ret = 0;<br>    }<br>  }<br>  http.end();<br>  return ret;<br>}<br><br>int readCameraStatus() {<br>  int ret = -1;<br>  HTTPClient http;<br> <br>  http.begin(CAMERA_ENDPOINT);<br>  int httpCode = http.GET();<br><br>  if (httpCode &gt; 0) {<br>    String payload = http.getString();<br>    Serial.print(&quot;camera status: &quot;);<br>    Serial.println(payload);<br>    if (payload == &quot;true&quot;) {<br>      ret = 1;<br>    } else {<br>      ret = 0;<br>    }<br>  }<br>  http.end();<br>  return ret;<br>}</pre><p><strong>LED wiring:</strong></p><p><strong>WiFiManager library:</strong></p><p>When NodeMCU is not able to connect to your home router/hotspot for various reasons like SSID/password change. Instead of writing new SSID and password configuration by physically connecting and flashing NodeMCU with your computer, it turns on a Wifi Hotspot with pre defined SSID and allows you to connect via mobile/laptop and allows your add/correct router details. This library solves the most common problem of maintenance and I liked it.</p><p><strong>Config.h:</strong></p><p>Keep your URL and SSID credentials in a separate header file rather than in the code directly and don’t forget to ignore it from git tracking. I placed these details in include/config.h file and included it in the main CPP file. This is one of the preferred and simplest approaches for maintaining secrets with CPP.</p><p><a href="http://platformio.org">**PlatformIO.org</a> IDE for IOT development:**</p><p>Arduino is a great IDE for programming NodeMCU boards. But it definitely lacks some things which a regular C++ IDE would have like</p><ul><li>Auto code completion</li><li>Static code analysis</li><li>Library manager</li><li>Debugger</li><li>Test framework</li></ul><p>I personally found PlatformIO IDE solves these problems as well as supporting other existing features like inbuilt board management, serial plotter etc too.</p><p><strong>More Ideas/Coming Soon:</strong></p><ul><li>Trim Oversight code of additional functionality and do better integration</li><li>Make an API call using Alexa skills/IFTTT to change colour of your smart bulb instead of LEDs</li><li>Instead of using (Mac + NodeMCU + Raspberry Pi), this setup can be simplified by directly using either of wifi enabled NodeMCU or Raspberry Pi with your Mac as they both support GPIO pins for connecting LEDs.</li><li>Send push notifications to your family members phones using services like Twilio/Pushbullet etc.</li></ul><blockquote><strong><em>Note about VSCode extensions:</em></strong></blockquote><blockquote><em>While I generally develop on mac but the actual code is sitting on my Raspberry pi SSD. I use a cool “Remote SSH” VSCode extension to access files over SSH connection on VSCode editor as if they were on your local machine.</em></blockquote><blockquote><em>Moreover, VSCode is intelligent enough to let me choose my PlatformIDE extension to be installed on the Raspberry pi over SSH thus allowing Platform IDE to detect and work with connected devices on Raspberry pi natively. How cool is that.</em></blockquote><p><strong>Reference Links:</strong></p><p><a href="https://youtu.be/5C0CPxwEAz8">Patrick Wardle — OverSight: Exposing Spies on macos</a></p><p><em>Disclaimer</em>: Installing a third party tool like OverSight comes up with their own set of questions about security, privacy etc. This tool is open source and go see for yourself before you want to use it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*O_bIcaKhjpQRigq2.jpg" /><figcaption><em>Early Version: In Meeting indicator LEDs taped to the back of my external monitor</em></figcaption></figure><p><em>Originally published at </em><a href="https://github.com/krishnakumar4a4/in-meeting-indicator"><em>github.com</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=26c0202b22d7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Encrypted Minio Storage with KMS Setup]]></title>
            <link>https://medium.com/swlh/encrypted-minio-storage-with-kms-setup-40d0a0eab1e9?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/40d0a0eab1e9</guid>
            <category><![CDATA[encryption]]></category>
            <category><![CDATA[minio]]></category>
            <category><![CDATA[storage]]></category>
            <category><![CDATA[hashicorp-vault]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Sat, 06 Feb 2021 08:15:38 GMT</pubDate>
            <atom:updated>2021-02-10T14:00:13.232Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/913/1*RwgvgWYJ9Vk4fUWU5wSCOQ.jpeg" /><figcaption>Minio &lt;-&gt; KES &lt;-&gt; Hashicorp vault</figcaption></figure><p>Minio is an S3 compliant data storage service. It can be hosted on premises and even supports distribution across multiple nodes. To meet certain data protection regulations, data is required to be encrypted the moment it is written to disk. Minio supports two types of encryption schemes</p><ul><li>SSE-S3 (Server side encryption) — Encryption key is managed on server side typically using a KMS</li><li>SSE-C (Client side encryption) — Encryption key is managed by clients and provided as request headers to Minio</li></ul><p>Goal of this blog is to guide you through setting up Minio with server side encryption. For server side encryption a KMS(key management system) is required. We have chosen Hashicorp vault as KMS here. Minio also supports a Key Encryption Service(KES) which is a stateless cryptographic operations service for Minio with the keys provided from KMS.</p><p>KES supports only one form authentication currently which is mTLS. Minio authenticates itself to KES with client TLS certificate for every connection. At a time, KES also supports different policies to be applied for different clients(Eg: Minio). KES acts like a lightweight gateway to use KMS thus abstracting out KMS footprint to clients.</p><p>This blogs covers following in step wise manner:</p><ol><li>Concepts Overview — Some of my exploration notes</li><li>Generate a root CA certificate pair</li><li>Hashicorp vault setup</li><li>Minio KES setup</li><li>Minio Standalone Setup</li><li>Minio Distributed Mode Setup</li><li>Minio Benchmarking using S3 benchmark</li><li>Additional information</li></ol><p>Points 2 through 5 provides information on Minio Standalone setup. Where as Point 6 is required if we want to turn the Minio setup into distributed mode.</p><p>Point 7 discusses about benchmarking and how to read the results.</p><p>Point 8 is some additional information required especially for Devops or Sys Admins.</p><blockquote>If you want to read the same blog on notion — <a href="https://www.notion.so/Published-Encrypted-Minio-Storage-Setup-6ff3e91640b84ba59a6466ac95656503">https://www.notion.so/Published-Encrypted-Minio-Storage-Setup-6ff3e91640b84ba59a6466ac95656503</a></blockquote><h3>1. Concepts Overview:</h3><h3>Minio</h3><p><strong>Kubernetes Native,High Performance Object Storage</strong></p><ul><li>Claims 183GB/s (Read) and 171GB/s (Write) speed on standard hardware</li><li>Can be federated with other minio clusters spanning multiple datacenters</li><li>Native to kubernetes</li><li>MINIO S3 Gateway (Microsoft Azure uses it) and more compatible S3 API</li></ul><p><strong>Security Overview:</strong></p><p>Master Key → generates External Key(EK)(Data Key) → derives Key Encryption Key(KEK) → encrypts Object Encryption Key(OEK) → stored as part of object metadata</p><p>OEK is used in Secure channel to encrypt 65536 bytes at a time with unique secret key derived from OEK and with unique nonce ⇒ PRF(OEK, part_no)</p><ul><li>OEK is generated and encrypted with KEK. Encrypted OEK is part of object metadata</li><li>KEK is always derived from PRF(EK, IV, context_values). IV, context_values, Master Key ID and encrypted EK are part of object metadata</li><li>EK is generated from master key from KMS</li></ul><p>Ref: <a href="https://docs.min.io/docs/minio-security-overview.html">https://docs.min.io/docs/minio-security-overview.html</a></p><h3>Hashicorp Vault</h3><p><strong>Seal/Unseal:</strong></p><ul><li>Shamir seal</li><li>This is the unseal process: the shards are added one at a time (in any order) until enough shards are present to reconstruct the key and decrypt the master key.</li></ul><p><strong>Lease/Renew/Revoke:</strong></p><ul><li>Vault promises that the data will be valid for the given duration, or Time To Live (TTL).</li><li>consumers of secrets need to check in with Vault routinely to either renew the lease (if allowed) or request a replacement secret</li><li>To manually revoke → vault lease revoke</li><li>To renew lease from current time → vault lease renew -increment=3600 my-lease-id</li><li>Revoke with prefix → vault lease revoke -prefix aws/</li></ul><p><strong>Authentication:</strong></p><ul><li>Client should be authenticated and gets a token with attached policies</li><li>Enabling auth method → vault write sys/auth/my-auth type=userpass</li><li>Follow docs</li></ul><p><strong>Tokens:</strong></p><ul><li>Tokens maps to policies</li><li>Root tokens are generated in 3 ways — init, using another root token and using generate-root</li><li>Root tokens should be revoked as soon as initial setup/debug/exploration is done</li><li>Generate new root token with key shards → vault operator generate-root</li><li>Child tokens are revoked when parent is revoked, whereas orphan tokens can be created to make sure only that token is revoked but not the children.</li><li>Token accessors are used to renew, revoke, lookup props and caps.</li><li>Tokens can have TTLs except root(TTL=0 is infinite). Tokens TTLs can be renewed with vault token renew</li><li>All Tokens have hard MAX TTL of 32 days. For long living connections use periodic tokens(renew them frequently type). Tokens can be renewed with token store roles, using appRole, have root token with auth/token/create endpoint</li></ul><p><strong>Policies:</strong></p><ul><li>Refer — <a href="https://www.vaultproject.io/docs/concepts/policies">https://www.vaultproject.io/docs/concepts/policies</a></li><li>Policy-Authorization Workflow</li><li>Built in policies — default(attached with every token, cannot be deleted by can be excluded while token creation ) and root(available and cannot be deleted)</li><li>Root tokens are created on initialization and should be revoked before putting in prod</li><li>List policies → vault read sys/policy</li><li>create policy → vault policy write policy-name policy-file.hcl</li><li>update policy → vault write sys/policy/my-existing-policy policy=@updated-policy.json</li><li>delete policy → vault delete sys/policy/policy-name</li><li>Associating tokens with policy on create → vault token create -policy=dev-readonly -policy=logs</li></ul><p><strong>High availability mode:</strong></p><ul><li>support storage backend to get high HA mode</li><li>By default HA mode is turned. In general, the bottleneck of Vault is the data store itself, not Vault core. For example: to increase the scalability of Vault with Consul, you would generally scale Consul instead of Vault</li></ul><p><strong>GPG and keybase support:</strong></p><ul><li>On initialisation, the unseal keys generated can be encrypted with GPG public keys of corresponding users and only private key of that user can be used to decrypt it. This is useful when the terminal output is eavesdropped.</li><li>Below command ensures encryption with GPG keys</li></ul><p>vault operator init -key-shares=3 -key-threshold=2 \\ -pgp-keys=&quot;jeff.asc,vishal.asc,seth.asc&quot;</p><ul><li>Unsealing with GPG key → echo “wcBMA37…” | base64 — decode | gpg -dq</li></ul><h3>KES Server</h3><ul><li>Ref: <a href="https://blog.min.io/introducing-kes/">https://blog.min.io/introducing-kes/</a></li><li>Responsible for cryptographic operations, thus only delegating master key storage to KMS</li><li>Stateless and can scale very well</li><li>Pros — Simple, secure(with TLS avoids username and password)</li></ul><p><strong>Pre-Requisites for Setup:</strong></p><ul><li>Certificates for KMS and KES</li></ul><h3>2. Generate a Root CA certificate pair</h3><ol><li>Create a <strong><em>ca-config.json</em></strong> CA configuration file</li></ol><pre>{<br>  &quot;CN&quot;: &quot;MyCompany&quot;,<br>  &quot;signing&quot;: {<br>    &quot;default&quot;: {<br>      &quot;expiry&quot;: &quot;8760h&quot;<br>    },<br>    &quot;profiles&quot;: {<br>      &quot;server&quot;: {<br>        &quot;usages&quot;: [<br>          &quot;signing&quot;,<br>          &quot;key encipherment&quot;,<br>          &quot;server auth&quot;,<br>          &quot;client auth&quot;<br>        ],<br>        &quot;expiry&quot;: &quot;8760h&quot;<br>      }<br>    }<br>  }<br>}</pre><p>2. Create a <strong><em>ca-csr.json </em></strong>self signed certificate signing request file</p><pre>## Content of ca-csr.json<br>{<br>	  &quot;CN&quot;: &quot;MyCompany&quot;,<br>    &quot;key&quot;: {<br>        &quot;algo&quot;: &quot;rsa&quot;,<br>        &quot;size&quot;: 2048<br>    },<br>    &quot;names&quot;:[{<br>	    &quot;C&quot;: &quot;INDIA&quot;,<br>	    &quot;ST&quot;: &quot;MyState&quot;,<br>	    &quot;L&quot;: &quot;MyCity&quot;,<br>	    &quot;O&quot;: &quot;MyCompany&quot;,<br>	    &quot;OU&quot;: &quot;MyUnit&quot;<br>	  }]<br>}</pre><p>3. Generate certificates</p><pre>$ ./cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca</pre><pre>## Generates 3 files<br>- ca.pem # public certificate<br>- ca-key.pem # private key<br>- ca.csr # certificate signing request</pre><p>4. You can also configure ca certificate at node level</p><ul><li>copy <strong><em>ca.pem</em></strong> to system trust store</li><li>run <strong><em>update-ca-trust</em></strong> command</li></ul><h3>3. Hashicorp vault setup</h3><p><strong>Generate TLS certificates to access vault from KES:</strong> <em>vault.sample-ns.svc.cluster.local</em></p><ol><li>Create <strong><em>server.json</em></strong> file with supported domain names</li></ol><pre>{<br>    &quot;CN&quot;: &quot;MyCompany&quot;,<br>    &quot;hosts&quot;: [<br>        &quot;vault.sample-ns.svc.cluster.local&quot;,<br>        &quot;vault.sample-ns.pod.cluster.local&quot;,<br>        &quot;vault.sample-ns&quot;,<br>    ],<br>    &quot;key&quot;: {<br>        &quot;algo&quot;: &quot;rsa&quot;,<br>        &quot;size&quot;: 2048<br>    },<br>    &quot;names&quot;:[{<br>	    &quot;C&quot;: &quot;INDIA&quot;,<br>	    &quot;ST&quot;: &quot;MyState&quot;,<br>	    &quot;L&quot;: &quot;MyCity&quot;,<br>	    &quot;O&quot;: &quot;MyCompany&quot;,<br>	    &quot;OU&quot;: &quot;MyUnit&quot;<br>	  }]<br>}</pre><p>2. Run the command to generate certs</p><pre>cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server.json | cfssljson -bare server</pre><pre>## Generates 3 files<br>- server.pem # public certificate<br>- server-key.pem # private certificate<br>- server.csr # certificate signing request</pre><p><strong>Create <em>vault-config.json</em>:</strong></p><pre>{<br>   &quot;api_addr&quot;: &quot;&lt;https://0.0.0.0:8200&gt;&quot;,<br>   &quot;backend&quot;: {<br>     &quot;file&quot;: {<br>       &quot;path&quot;: &quot;file&quot;<br>     }<br>   },<br>  &quot;default_lease_ttl&quot;: &quot;168h&quot;,<br>  &quot;max_lease_ttl&quot;: &quot;720h&quot;,<br>  &quot;listener&quot;: {<br>    &quot;tcp&quot;: {<br>      &quot;address&quot;: &quot;0.0.0.0:8200&quot;,<br>      &quot;tls_cert_file&quot;: &quot;server.pem&quot;,<br>      &quot;tls_key_file&quot;: &quot;server-key.pem&quot;,<br>      &quot;tls_min_version&quot;: &quot;tls12&quot;<br>    }<br>  }<br>}</pre><p><strong>Start vault:</strong></p><pre># Starts up vault server on https://0.0.0.0:8200<br>./vault server -config vault-config.json</pre><p><strong>Configure Vault for KES:</strong></p><ol><li>Create a <strong><em>kes-policy.hcl </em></strong>policy file for KES</li></ol><pre>path &quot;kv/*&quot; {<br>     capabilities = [ &quot;create&quot;, &quot;read&quot;, &quot;delete&quot; ]<br>}</pre><p>2. Initialise Vault</p><pre>$ export VAULT_SKIP_VERIFY=true</pre><pre>##########################</pre><pre>$ ./vault operator init</pre><pre>Unseal Key 1: JGDVZdF1f9UKYxYvIiW/tgfB24StDcTevJzzWX0K+TAj<br>Unseal Key 2: USjqCkON4/gn/oX2PzU1iz6STLuBohf14aP0GpzP+rge<br>Unseal Key 3: JEX0IJMOEMMwX2Fbld3IwkNLd1OqIU3OUQfSj7YQkfcL<br>Unseal Key 4: TzxJ5GJuHn4sA4NQDr/BWNKsXhBtO5FjqfJoV48cbYes<br>Unseal Key 5: bz+kUh5iw/Fakz9aUVP1UWJ+VvQDC3tLrjiSZ8LFkdgg</pre><pre>Initial Root Token: s.CG8JQskBbOVz43Vn9pvE7bgq</pre><pre>Vault initialized with 5 key shares and a key threshold of 3. Please securely<br>distribute the key shares printed above. When the Vault is re-sealed,<br>restarted, or stopped, you must supply at least 3 of these keys to unseal it<br>before it can start servicing requests.</pre><pre>Vault does not store the generated master key. Without at least 3 key to<br>reconstruct the master key, Vault will remain permanently sealed!</pre><pre>It is possible to generate new unseal keys, provided you have a quorum of<br>existing unseal keys shares. See &quot;vault operator rekey&quot; for more information.</pre><pre>##########################</pre><pre>$ ./vault operator unseal JGDVZdF1f9UKYxYvIiW/tgfB24StDcTevJzzWX0K+TAj &amp;&amp; ./vault operator unseal JEX0IJMOEMMwX2Fbld3IwkNLd1OqIU3OUQfSj7YQkfcL &amp;&amp; ./vault operator unseal TzxJ5GJuHn4sA4NQDr/BWNKsXhBtO5FjqfJoV48cbYes</pre><pre>Key                Value<br>---                -----<br>Seal Type          shamir<br>Initialized        true<br>Sealed             true<br>Total Shares       5<br>Threshold          3<br>Unseal Progress    1/3<br>Unseal Nonce       675f42c6-978d-85bb-10db-c6fb1f591743<br>Version            1.5.4<br>HA Enabled         false<br>Key                Value<br>---                -----<br>Seal Type          shamir<br>Initialized        true<br>Sealed             true<br>Total Shares       5<br>Threshold          3<br>Unseal Progress    2/3<br>Unseal Nonce       675f42c6-978d-85bb-10db-c6fb1f591743<br>Version            1.5.4<br>HA Enabled         false<br>Key             Value<br>---             -----<br>Seal Type       shamir<br>Initialized     true<br>Sealed          false<br>Total Shares    5<br>Threshold       3<br>Version         1.5.4<br>Cluster Name    vault-cluster-d954d2ef<br>Cluster ID      81d91296-59dd-4048-0969-309439d16b2e<br>HA Enabled      false</pre><pre>##########################</pre><pre>$ export VAULT_TOKEN=s.CG8JQskBbOVz43Vn9pvE7bgq<br>$ export VAULT_SKIP_VERIFY=true</pre><pre>$ ./vault secrets enable kv</pre><pre># approle allows authentication and binds to a policy<br>$ ./vault auth enable approle</pre><pre>$ ./vault policy write kes-policy ./kes-policy.hcl</pre><pre>$ ./vault write auth/approle/role/kes-role token_num_uses=0  secret_id_num_uses=0  period=5m</pre><pre>$ ./vault write auth/approle/role/kes-role policies=kes-policy</pre><pre>$ ./vault read auth/approle/role/kes-role/role-id</pre><pre>$ ./vault write -f auth/approle/role/kes-role/secret-id</pre><h3>4. Minio KES setup</h3><p><strong>Generate TLS certificate to access KES from Minio:</strong></p><ol><li>Create <strong><em>server.json</em></strong> file with supported domain names</li></ol><pre>{<br>    &quot;CN&quot;: &quot;MyCompany&quot;,<br>    &quot;hosts&quot;: [<br>        &quot;minio-kes.sample-ns.svc.cluster.local&quot;,<br>        &quot;minio-kes.sample-ns.pod.cluster.local&quot;,<br>        &quot;minio-kes.sample-ns&quot;,<br>    ],<br>    &quot;key&quot;: {<br>        &quot;algo&quot;: &quot;rsa&quot;,<br>        &quot;size&quot;: 2048<br>    },<br>    &quot;names&quot;:[{<br>	    &quot;C&quot;: &quot;INDIA&quot;,<br>	    &quot;ST&quot;: &quot;MyState&quot;,<br>	    &quot;L&quot;: &quot;MyCity&quot;,<br>	    &quot;O&quot;: &quot;MyCompany&quot;,<br>	    &quot;OU&quot;: &quot;MyUnit&quot;<br>	  }]<br>}</pre><p>2. Run the command to generate certs:</p><pre>$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server.json | cfssljson -bare server</pre><pre>## Generates 3 files<br>- server.pem # public certificate<br>- server-key.pem # private certificate<br>- server.csr # certificate signing request</pre><p><strong>Generate app identity for Minio using KES client:</strong></p><pre>$ ./kes tool identity new --key=app.key --cert=app.cert app</pre><pre>$ ./kes tool identity of app.cert</pre><pre>$ export APP_IDENTITY=$(./kes tool identity of app.cert)</pre><p><strong>Create <em>server-config.yml</em>:</strong></p><pre>address: 0.0.0.0:7373<br>root:    disabled  # We disable the root identity since we don&#39;t need it in this guide</pre><pre>tls:<br>  key:  server-key.pem<br>  cert: server.pem</pre><pre>policy:<br>  my-app:<br>    paths:<br>    - /v1/key/create/my-app*<br>    - /v1/key/generate/my-app*<br>    - /v1/key/decrypt/my-app*<br>    identities:<br>    - ${APP_IDENTITY}</pre><pre>keys:<br>  vault:<br>    endpoint: &lt;https://vault.sample-ns:8200&gt;<br>    approle:<br>      id:     &quot;70bd11f5-2104-e4c0-8dbc-48f9066218ce&quot; # Your AppRole ID<br>      secret: &quot;3a06337c-0163-4c82-e81f-9ec9dce9cf5b&quot; # Your AppRole Secret ID<br>      retry:  15s<br>    status:<br>      ping: 10s<br>    tls:<br>      ca: ca.pem # Since we use self-signed certificates</pre><p><strong>Start KES:</strong></p><pre>$ ./kes server --config=server-config.yml --auth=off</pre><blockquote>To access KES, If certificates are configured properly in minio. Hopefully “ — auth=off” option can be avoided</blockquote><p><strong>Verify KES to Vault interaction:</strong></p><pre>$ export KES_SERVER=https://minio-kes.sample-ns.svc.cluster.local:7373</pre><pre>$ export KES_CLIENT_CERT=app.cert</pre><pre>$ export KES_CLIENT_KEY=app.key</pre><pre>$ ./kes key create my-app-key -k</pre><pre>$ ./vault kv list kv</pre><pre>$ ./kes key derive my-app-key -k</pre><pre>$ ./vault kv get kv/my-app-key</pre><h3>5. Minio Standalone Setup</h3><p><strong>Minio Startup configuration:</strong></p><pre>$ export MINIO_KMS_KES_ENDPOINT=https://minio-kes.sample-ns<br>svc.cluster.local:7373<br>$ export MINIO_KMS_KES_KEY_FILE=app.key<br>$ export MINIO_KMS_KES_CERT_FILE=app.cert<br>$ export MINIO_KMS_KES_KEY_NAME=my-app-key<br>$ MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin ./minio server minio-data</pre><pre># Place CA certs for minio communication to external services<br>$ cp ca.pem ~/.minio/certs/CAs/</pre><p><strong>Minio Client(mc):</strong></p><pre>$ ./mc alias set myminio [&lt;http://127.0.0.1:9000&gt;](&lt;http://127.0.0.1:9000/&gt;) minioadmin minioadmin</pre><pre># Set policy to a bucket<br>$ ./mc policy set upload myminio/test</pre><pre># Setup auto encryption on bucket<br>$ ./mc encrypt set sse-s3 myminio/test</pre><pre># verify if encryption is enabled on a bucket<br>$ ./mc encrypt info myminio/bucket/</pre><h3>6. Minio Distributed Mode Setup</h3><p>Generated template from <a href="https://min.io/download#/kubernetes">https://min.io/download#/kubernetes</a></p><p>Below configuration is for 4 nodes with four drive paths(1 path on each no)</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: minio<br>  labels:<br>    app: minio<br>spec:<br>  clusterIP: None<br>  ports:<br>    - port: 9000<br>      name: minio<br>  selector:<br>    app: minio<br>---<br>apiVersion: apps/v1<br>kind: StatefulSet<br>metadata:<br>	namespace: sample-ns<br>  name: minio<br>spec:<br>  serviceName: minio<br>  replicas: 4<br>  template:<br>    metadata:<br>      labels:<br>        app: minio<br>    spec:<br>      containers:<br>      - name: minio<br>        env:<br>        - name: MINIO_ACCESS_KEY<br>          value: &quot;minioadmin&quot;<br>        - name: MINIO_SECRET_KEY<br>          value: &quot;minioadmin&quot;<br>        image: minio/minio<br>        args:<br>        - server<br>        - http://minio-{0...3}.minio.sample-ns.svc.cluster.local/data<br>        ports:<br>        - containerPort: 9000<br>        # These volume mounts are persistent. Each pod in the PetSet<br>        # gets a volume mounted based on this field.<br>        volumeMounts:<br>        - name: data<br>          mountPath: /data<br>  # These are converted to volume claims by the controller<br>  # and mounted at the paths mentioned above.<br>  volumeClaimTemplates:<br>  - metadata:<br>      name: data<br>    spec:<br>      accessModes:<br>        - ReadWriteOnce<br>      resources:<br>        requests:<br>          storage: 5Gi<br>      # Uncomment and add storageClass specific to your requirements below. Read more &lt;https://kubernetes.io/docs/concepts/storage/persistent-volumes/#class-1&gt;<br>      #storageClassName:<br>---<br>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: minio-service<br>spec:<br>  type: LoadBalancer<br>  ports:<br>    - port: 9000<br>      targetPort: 9000<br>      protocol: TCP<br>  selector:<br>    app: minio</pre><p>Observations of distributed erasure encoding tolerance to failures:</p><ul><li>If a file is deleted in more than N/2 nodes from a bucket, file is not recovered, otherwise tolerable until N/2 nodes.</li><li>If a bucket is deleted in one node, request going to that replica node shows bucket is missing. Requests going through all other replica nodes, the bucket is visible.</li><li>Looks like buckets are recoverable by directly copying missing buckets from other nodes</li><li>Automatic healing happens once in 30 days. Manual healing is getting deprecated soon, hopefully we get knobs for automatic healing before that.</li><li>If mount point is removed, <strong>Error: unformatted disk found</strong> is seen</li><li>If mount point is restored, it is not recognised by the application, needed a restart of the pod. “Delete pod and wait for to scale back”</li></ul><p><strong>Storage class setup:</strong></p><p>Storage classes “STANDARD” and “REDUCED_REDUNDANCY” can be set to minio or can also be sent from application using “x-amz-storage-class” or using “mc admin config”</p><p>Ref: <a href="https://github.com/minio/minio/tree/master/docs/erasure/storage-class">https://github.com/minio/minio/tree/master/docs/erasure/storage-class</a></p><p><strong>Issue with distributed mode: </strong><a href="https://github.com/minio/minio/issues/9052">https://github.com/minio/minio/issues/9052</a></p><h3>7. Minio Benchmarking using S3 benchmark</h3><h3>Setup</h3><ul><li>4 node cluster — one drive each(5GB PV)</li><li>Node spec — 12 core 16GB RAM</li></ul><h3>Observations</h3><h4>Scenario 1</h4><ul><li>Encryption enabled</li><li>100KB average object size</li></ul><p><strong>Run1:</strong></p><p>$ ./s3-benchmark -a minioadmin -b new-test-bucket -s minioadmin -t 10 -u http://0.0.0.0:30100 -z 100K</p><pre>Wasabi benchmark program v2.0</pre><pre>Parameters: url=http://0.0.0.0:30100, bucket=new-test-bucket, region=us-east-1, duration=60, threads=10, loops=1, size=100K</pre><pre>2020/10/29 11:45:42 WARNING: createBucket new-test-bucket error, ignoring BucketAlreadyOwnedByYou: Your previous request to create the named bucket succeeded and you already own it.</pre><pre>status code: 409, request id: 164261F9CED6AF49, host id:</pre><pre>**Loop 1: PUT time 60.0 secs, objects = 17057, speed = 27.7MB/sec, 284.1 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: GET time 60.0 secs, objects = 43592, speed = 70.9MB/sec, 726.4 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: DELETE time 18.3 secs, 933.5 deletes/sec. Slowdowns = 0**</pre><p><strong>Run2:</strong></p><p>$ ./s3-benchmark -a minioadmin -b new-test-bucket -s minioadmin -t 10 -u http://0.0.0.0:30100 -z 100K</p><pre>Wasabi benchmark program v2.0</pre><pre>Parameters: url=http://0.0.0.0:30100, bucket=new-test-bucket, region=us-east-1, duration=60, threads=10, loops=1, size=100K</pre><pre>2020/10/29 11:54:41 WARNING: createBucket new-test-bucket error, ignoring BucketAlreadyOwnedByYou: Your previous request to create the named bucket succeeded and you already own it.</pre><pre>status code: 409, request id: 16426277335B8DBB, host id:</pre><pre>**Loop 1: PUT time 60.0 secs, objects = 17507, speed = 28.5MB/sec, 291.7 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: GET time 60.0 secs, objects = 43073, speed = 70.1MB/sec, 717.8 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: DELETE time 18.3 secs, 958.3 deletes/sec. Slowdowns = 0**</pre><h4>Scenario 2:</h4><ul><li>Encryption disabled</li><li>100KB average object size</li></ul><p><strong>Run1:</strong></p><p>$ ./s3-benchmark -a minioadmin -b new-unencrypted-test-bucket -s minioadmin -t 10 -u http://0.0.0.0:30100 -z 100K</p><pre>Wasabi benchmark program v2.0</pre><pre>Parameters: url=http://0.0.0.0:30100, bucket=new-unencrypted-test-bucket, region=us-east-1, duration=60, threads=10, loops=1, size=100K</pre><pre>2020/10/29 15:12:54 WARNING: createBucket new-unencrypted-test-bucket error, ignoring BucketAlreadyOwnedByYou: Your previous request to create the named bucket succeeded and you already own it.</pre><pre>status code: 409, request id: 16426D483783E4E3, host id:</pre><pre>**Loop 1: PUT time 60.0 secs, objects = 18746, speed = 30.5MB/sec, 312.4 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: GET time 60.0 secs, objects = 59048, speed = 96.1MB/sec, 984.0 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: DELETE time 20.0 secs, 937.8 deletes/sec. Slowdowns = 0**</pre><p><strong>Run2:</strong></p><p>$ ./s3-benchmark -a minioadmin -b new-unencrypted-test-bucket -s minioadmin -t 10 -u http://0.0.0.0:30100 -z 100K</p><pre>Wasabi benchmark program v2.0</pre><pre>Parameters: url=http://0.0.0.0:30100, bucket=new-unencrypted-test-bucket, region=us-east-1, duration=60, threads=10, loops=1, size=100K</pre><pre>2020/10/29 15:16:02 WARNING: createBucket new-unencrypted-test-bucket error, ignoring BucketAlreadyOwnedByYou: Your previous request to create the named bucket succeeded and you already own it.</pre><pre>status code: 409, request id: 16426D73F07F40A9, host id:</pre><pre>**Loop 1: PUT time 60.0 secs, objects = 19272, speed = 31.4MB/sec, 321.0 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: GET time 60.0 secs, objects = 52027, speed = 84.7MB/sec, 867.0 operations/sec. Slowdowns = 0**</pre><pre>**Loop 1: DELETE time 20.4 secs, 943.5 deletes/sec. Slowdowns = 0**</pre><p><strong>How do I read benchmarks?</strong></p><blockquote><em>Loop 1: PUT time 60.0 secs, objects = 17057, speed = 27.7MB/sec, 284.1 operations/sec. Slowdowns = 0</em></blockquote><p>In 60 seconds, 17057 objects whose average size is 100KB is uploaded to minio. Speed of upload is coming around 27.7MB/sec which is average bandwidth utilised during this operation. Also an average of 284.1 files uploaded per second.</p><blockquote><em>Loop 1: GET time 60.0 secs, objects = 43592, speed = 70.9MB/sec, 726.4 operations/sec. Slowdowns = 0</em></blockquote><p>In 60 seconds, 43592 objects whose average size is 100KB is downloaded from minio. Speed of download is coming around 70.9MB/sec which is average bandwidth utilised during this operation. Also an average of 726.4 files downloaded per second.</p><blockquote><em>Loop 1: DELETE time 18.3 secs, 933.5 deletes/sec. Slowdowns = 0</em></blockquote><p>In 18.3 seconds, deletes were performed at a rate of 933.5 files per second.</p><h3>8. Additional information:</h3><ol><li>For additional debugging, to log failed requests for <strong>minio</strong></li></ol><pre>$ ./mc admin trace -v -e myminio</pre><p>2. Root tokens generated as part of <strong>vault</strong> initialisation will not have expiry. It is always better to revoke the root token when the setup is finished. For creating a new root token when required, follow below steps</p><pre># vault operator generate-root -generate-otp<br>RgZjYVW2fIsl2avU5VNCpru6sZ</pre><pre># vault operator generate-root -init -otp=&quot;RgZjYVW2fIsl2avU5VNCpru6sZ&quot;<br>Nonce         c5cc8fb9-3226-71f2-865b-47d61bae4987<br>Started       true<br>Progress      0/3<br>Complete      false<br>OTP Length    26</pre><pre># vault operator generate-root -otp=&quot;RgZjYVW2fIsl2avU5VNCpru6sZ&quot;<br>Operation nonce: c5cc8fb9-3226-71f2-865b-47d61bae4987<br>Unseal Key (will be hidden):<br>Nonce       c5cc8fb9-3226-71f2-865b-47d61bae4987<br>Started     true<br>Progress    1/3<br>Complete    false</pre><pre># vault operator generate-root -otp=&quot;RgZjYVW2fIsl2avU5VNCpru6sZ&quot;<br>Operation nonce: c5cc8fb9-3226-71f2-865b-47d61bae4987<br>Unseal Key (will be hidden):<br>Nonce       c5cc8fb9-3226-71f2-865b-47d61bae4987<br>Started     true<br>Progress    2/3<br>Complete    false</pre><pre># vault operator generate-root -otp=&quot;RgZjYVW2fIsl2avU5VNCpru6sZ&quot;<br>Operation nonce: c5cc8fb9-3226-71f2-865b-47d61bae4987<br>Unseal Key (will be hidden):<br>Nonce            c5cc8fb9-3226-71f2-865b-47d61bae4987<br>Started          true<br>Progress         3/3<br>Complete         true<br>Encoded Token    IUk1JREjE1k8e0YLXDY0ZnljI3o+EDR5HGI</pre><pre># vault operator generate-root -decode IUk1JREjE1k8e0YLXDY0ZnljI3o+EDR5HGI -otp RgZjYVW2fIsl2avU5VNCpru6sZ<br>s.oOHuDkZ25gnWB3L5m9NbAOo8</pre><pre># export VAULT_TOKEN=s.oOHuDkZ25gnWB3L5m9NbAOo8</pre><pre># vault token lookup<br>Key                 Value<br>---                 -----<br>accessor            laQoKv6pyhO3kpkA1ozmG8Ev<br>creation_time       1604397451<br>creation_ttl        0s<br>display_name        root<br>entity_id           n/a<br>expire_time         &lt;nil&gt;<br>explicit_max_ttl    0s<br>id                  s.oOHuDkZ25gnWB3L5m9NbAOo8<br>meta                &lt;nil&gt;<br>num_uses            0<br>orphan              true<br>path                auth/token/root<br>policies            [root]<br>ttl                 0s<br>type                service</pre><p>3. When you need to do <strong>vault</strong> administration. Create an additional administrator app certificate as opposed to using the app certificate created for minio itself for better auditability.</p><pre># Create admin cert pair<br>kes tool identity new --key=minio-kes-admin.key --cert=minio-kes-admin.cert minio-kes-admin</pre><pre># Generate app identity from cert<br>kes tool identity of minio-kes-admin.cert</pre><pre># Configure required administration policies in KES server-config.yml file and reload KES service</pre><p>4. Once a bucket is created with some content, if we enable encryption later on the bucket, existing files are not re-encrypted automatically.</p><p>5. mc admin heal is about to be deprecated and no sufficient alternative is in place when I checked last time.</p><blockquote>A note on Automatic healing:</blockquote><blockquote>Healing is automatic on server side which runs on a continuous basis on a low priority thread, mc admin heal is deprecated and will be removed in future.</blockquote><blockquote>- Minio healing moved from once every day to once every month</blockquote><blockquote>- Option to disable self healing is under works</blockquote><blockquote><a href="https://github.com/minio/minio/issues/7892">- https://github.com/minio/minio/issues/7892</a></blockquote><blockquote><a href="https://github.com/minio/minio/pull/7934">- https://github.com/minio/minio/pull/7934</a></blockquote><blockquote><a href="https://github.com/minio/minio/pull/7868">- https://github.com/minio/minio/pull/7868</a></blockquote><p>6. While upgrading minio in distributed mode. Minio should be brought down one after another for maintenance and the instance should be immediately healed after being up before going for other instances. This is necessary, otherwise affects the redundancy factor</p><ul><li>Bring down minio</li><li>Upgrade</li><li>Bring up minio</li><li>Run mc admin heal to recover the missing data</li><li>Go for next node and upgrade only if the current one’s recovery is complete</li></ul><p>8. Minio will not tell you the health status of disks. You should have separate alerting and monitoring in place to keep track of hardware health.</p><p>9. Note that there are other reasons but servers restarts that can cause nodes to become out of sync. For example, if there is a network failure that causes some of the nodes to not be reachable, writes will be less durable too. It is thus important to have good monitoring in place and respond accordingly. Minio itself will auto-heal the cluster every month if the administrator doesn’t trigger a heal themselves.</p><p>10. MINIO_KMS_AUTO_ENCRYPTION=on environment variable is about to get deprecated in favour of mc encrypt command which provides encryption at rest on specific buckets. However this deprecation doesn&#39;t sound reasonable to me as we don&#39;t see any suitable alternative that provides auto encryption for all buckets on creation.</p><p>11. Unsealing vault for every restart is a pain. Fortunately, Vault exposes different integrations to make this automatic — <a href="https://learn.hashicorp.com/collections/vault/auto-unseal">https://learn.hashicorp.com/collections/vault/auto-unseal</a></p><p><strong>References:</strong></p><p>Administration</p><ul><li><a href="https://docs.wire.com/how-to/administrate/minio.html">https://docs.wire.com/how-to/administrate/minio.html</a></li></ul><p>Different certificate generation approaches</p><ul><li><a href="https://kubernetes.io/docs/concepts/cluster-administration/certificates/">https://kubernetes.io/docs/concepts/cluster-administration/certificates/</a></li><li><a href="https://gardener.cloud/documentation/guides/applications/https/">https://gardener.cloud/documentation/guides/applications/https/</a></li><li><a href="https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/">https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=40d0a0eab1e9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/encrypted-minio-storage-with-kms-setup-40d0a0eab1e9">Encrypted Minio Storage with KMS Setup</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using git with google drive]]></title>
            <link>https://krishnakumart.medium.com/using-git-with-google-drive-fb3db6116014?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/fb3db6116014</guid>
            <category><![CDATA[git]]></category>
            <category><![CDATA[gitlab]]></category>
            <category><![CDATA[cloud-storage]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[google-drive]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Sun, 15 Mar 2020 10:36:48 GMT</pubDate>
            <atom:updated>2020-03-15T10:36:48.779Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Initialising repo on Google Drive:</strong></p><ul><li>clone the repository you wanted to use with google drive into your local work directory.</li></ul><blockquote><strong>cd ~/work</strong></blockquote><blockquote><strong>git clone </strong><a href="https://example.com/project-group/sample-repo.git"><strong>https://example.com/project-group/sample-repo.git</strong></a></blockquote><ul><li>Download google file stream app from google(Streams files on demand instead of making entire drive offline)</li><li>Post installation, login with google email.</li><li>Let’s say Google Drive is mounted on<strong> /Volumes/GoogleDrive</strong> folder on your machine.</li><li>Create repo under <strong>/Volumes/GoogleDrive/Shared\ drives/Project\ Drive/work/ </strong>using the below command</li></ul><blockquote><strong>git init — bare sample-repo</strong></blockquote><ul><li>In your local work directory, set git remote to google drive’s sample-repo folder and push all the branches, tags etc.</li></ul><blockquote><strong>cd ~/work/sample-repo</strong></blockquote><blockquote><strong>git remote add origin file:///Volumes/GoogleDrive/Shared\ drives/Project\ Drive/work/sample-repo</strong></blockquote><blockquote><strong>git push origin — all</strong></blockquote><blockquote><strong>git push origin — tags</strong></blockquote><p><strong>Sharing initialised repo with everyone else:</strong></p><ul><li>Download google file stream app from google(Streams files on demand instead of making entire drive offline)</li><li>Post installation, login with google email.</li><li>Let’s say Google Drive is mounted on<strong> /Volumes/GoogleDrive</strong> folder on your machine.</li><li>If you have sample-repo repo under <strong>~/work</strong> already</li></ul><blockquote><strong>cd ~/work/sample-repo</strong></blockquote><blockquote><strong>git remote add origin file:///Volumes/GoogleDrive/Shared\ drives/Project\ Drive/work/sample-repo</strong></blockquote><blockquote><strong>git pull -r origin master</strong></blockquote><p><strong>Some common problems using git with google drive:</strong></p><ul><li>Coordination is required when multiple people would like to push to master at the same time. Sometimes when one person pushes to master and due to some network problem, if drive is not able to replicate your local commit to everyone else. There would problems as google drive has its own way of resolving conflicts when it comes online.</li></ul><blockquote>Eg: If A is pushing to master. Publish the last known <strong>commit id</strong> to everyone in the group. If B wants to push another commit to master, B can check if the last known commit id mentioned by A is available locally before pushing.</blockquote><blockquote>Same rules apply to any branch where multiple people are working at the same time.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fb3db6116014" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The unexplored subtleties on “Going autonomous”]]></title>
            <link>https://krishnakumart.medium.com/the-unexplored-subtleties-on-going-autonomous-405b36ff9ae3?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/405b36ff9ae3</guid>
            <category><![CDATA[autonomy]]></category>
            <category><![CDATA[autonomous-vehicles]]></category>
            <category><![CDATA[self-driving-cars]]></category>
            <category><![CDATA[autonomous-cars]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Sat, 04 Jan 2020 07:30:21 GMT</pubDate>
            <atom:updated>2020-01-04T07:48:37.185Z</atom:updated>
            <content:encoded><![CDATA[<p>Are autonomous vehicles fit for human driven roads yet?</p><h3>On a winter morning before the first light</h3><p>I was going on my bike on a silent road in the early hours of the day. The road looks entirely abandoned as it was one of the most chilling days of winter and I was not expecting anyone coming on to the road. But I was very much ambivalent and kept an eye on both sides of the road as far I can see through the fog. I was also ready to apply the break and stop if at all someone or some animal shows up suddenly. All those thoughts were running in my subconscious mind.</p><p>As per human nature, you get to adapt to things based on your past experiences. Your mental alertness is a precautious state expecting something suddenly might happen such as a human standing in the middle of the road or a stray dog trying to run across or a vehicle coming in the opposite direction etc. Because everyone would have had these experiences one time or the other.</p><p>I wanted to add, that I was also observing lot of vehicles parked around and the sight resembles a lone ranger passing through a graveyard of vehicles. As I don’t see any people around, I am not expecting any of those vehicles to move at all.</p><p>Imagine a situation where one of the vehicle started moving onto the road to reach the owners front courtyard as it was given a command to do so. The car tries to drive by itself as it was an autonomous car. All of a sudden, that action of car might create a sense of panic in your mind. What the heck? Did I see someone coming around it? How is it moving? Oh..it could be an autonomous car.</p><h3>You don’t feel this at all?</h3><p>Let’s not take this very hypothetically instead, let’s try and understand the facts. It’s a simple theory. While we are going on road, our brain does lot of calculations about myriad of <strong>dynamic personas</strong> that can appear up on the road. Having the sense to control the speed, your readiness on the brakes etc are based on that. Most of those reactions are involuntary and are instinctive.</p><p>Along with the dynamic personas, there are some constants too like trees, street lights, road dividers etc. Since you never expect them to walk down the road like a zombie, your brain would always assign them with lesser weights(lesser probability of obstruction) in the equation. Similarly, if you are seeing vehicles parked on a road, you involuntarily treat them as constants unless you see someone coming out of the door to take a ride. But you may and can simply say, why not consider everything as a dynamic persona which can move by itself without the need for humans. But it simply increases the number of variables in your equation and the human brain can only do so much after all.</p><p>Didn’t made sense? Let’s take a simple example of you riding a bike inside a crowded market. The chances of you hitting someone is very likely unless you are damn slow and careful. Just because you have so many dynamic factors in the equation your chances of hitting are high and sometimes imminent.</p><p>Now don’t tell me that you add more computational power to yourself, it is simply the physical limitation of humans as of today.(Augmented vision/AI assisted driving might help though)</p><p>Adding on, incase of autonomous vehicles, the complexity of this equation further beefs up if you have to consider the type of the car and it’s manufacturer and the sensors they might have used and intelligence etc.</p><blockquote>The scariest thing about these vendors are, they are all <strong>developing these self maneuvering technologies in silos without of much coordination between with each other fearing of revealing their so called patentable technology to competitors and may loose their position in the market.</strong> We wouldn’t be surprised if they come up with some crazy ideas to beat the competition. This further increases the unknowns in our equation.</blockquote><p>Well, if you say, I am opposed to technological advancement, no I am not. All I am trying to do is to highlight the nuances and spin off a thought process, so that we can start fixing it collectively.</p><h3>More situations!!</h3><p>You could ask me, are you saying all of this just because you had a random thought that slipped into your mind on a bad winter morning?</p><p>No, that’s just the beginning. I have started thinking about it more aggressively because it is something which is evident to many people but non trivial to put it in words or express it. In fact I have put my best effort to explain them in a pictorial way.</p><p><strong>The absence of human factor:</strong></p><p>Below described situation in the picture might completely seem normal to anyone. A motorist trying to move on a road on which lot of vehicles clogged on both sides of it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VOd9Wm1T7u8Sx13daCsuTA.jpeg" /><figcaption>Driving on a road with cars parked on both sides of the road and can see no drivers</figcaption></figure><p>Had there been no concept of autonomous vehicles, person rides freely as long as he/she sees no person in and around these vehicles. Glad, this is still going to be the situation at least few years down the line. But this landscape is quickly changing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YgD3JRZV0TH1tobjqPrd4w.jpeg" /><figcaption>Driving on a road with cars parked and clearly see their drivers</figcaption></figure><p><strong>Significance of human factor:</strong></p><p>Putting myself in bikers shoes. As soon as I see someone in one of the car I would carefully adjust my navigation to circumvent the path of the car by slightly slowing down.</p><p>Here the car has become from being a static factor to dynamic factor and the presence of human was very evident from a distance and I would have enough time to think and plan the maneuver.</p><p>One more interesting fact here is, I am also dealing with an actual human being in the car. So it gives me the freedom to think that any human would take time to lay up the gear, turn the steering wheel, look at the road in the rear view mirror and move on to the road. This is a generic human behaviour anyone can anticipate and plan accordingly.</p><p><strong>Autonomous driving vehicles:</strong></p><p>In case of autonomous vehicles, this is all invisible, unknown and differs from car to car and vendor to vendor. Is this any better than the previous case involving human beings?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8_RneGgsRsUUspbbhBquuQ.jpeg" /><figcaption>Driving on a road with autonomous vehicles parked on both sides of the road</figcaption></figure><p>Above portrayed, is a much puzzled situation for anyone taking this road. 6 to 7 years down the line, you would most probably end up being in similar situation on our roads. Remember the bad winter morning I have started with and the corresponding prognosis will seem very much real and will come to life then.</p><h3><strong>I still don’t agree with you</strong></h3><p>Let’s say you don’t agree with all the points above. I will now talk about much real situation I had been in.</p><p>On my usual office commute way. Some part of the road is really wider. It is so wider that the width of road on the side I am passing by, is much wider than length of a bus. There are lot of new buildings being constructed and new paths that are getting linked to this road being instituted everyday. One fine day, all of a sudden I see a bus coming out of a gate exactly cutting my path.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gu2TrRKfdz2w2t5TdP5mwg.jpeg" /><figcaption>When a large bus coming out of nowhere on to the road</figcaption></figure><p>Initially, I was very puzzled on seeing a bus coming out of the building. As the building was unoccupied until the previous day. Immediately after that, the next puzzle is, which direction would the bus go? Would it take a left turn and join my path or would cut across to take a U turn?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BkxrmaT00VFkmlio0bnfZA.jpeg" /><figcaption>And I observed the gestures of the driver and corrected my course</figcaption></figure><p>I have no clue about it and as I was approaching the bus more closer and closer, I have shifted my observation from the entire bus to the driver, which immediately helped. My speculations of how the bus would move are solved on observing the driver’s gestures and I have corrected my course accordingly.</p><p>The existence of an active human being driving the bus helped everyone heading that way to correct their course immeditely with less disruption. The nuance here is, the existence of an active human being controlling the bus and the person’s gesture helped me and everyone to take decision quickly.</p><p>The situation drawn in the picture looks pretty tidy with only two dynamic factors depicting just me and the bus. But the situation I had then was not so simple as this, the road is muddy and slippery because of constructions happening around with lot of traffic.</p><p><strong>Can autonomous vehicles understand typical human gestures like a human would do?</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mZB82PE1dl04YhABgOtb0Q.jpeg" /><figcaption>A small gesture to indicate that I wanted to overtake the car in front</figcaption></figure><p>If you look at the biker very closely. You can see the biker gave a gesture by looking right, which means person wants to go slight right and overtake the car before him/her. And you could have burst to laugh on imagining this, this is not a valid traffic rule/sign and how can we expect autonomous vehicles supposed to understand this?</p><p>Well if I am in the car following the biker, I would have definitely slowed down on biker’s gesture. The same would apply to more explicit gesture depicted below too.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IrdoxjMf5gjYoB75ARTqMQ.jpeg" /><figcaption>An explicit gesture to indicate that I wanted to overtake the car in front</figcaption></figure><p>This is very trivial human gesture that we do and observe everyday.</p><p>I wanted to ask, are the autonomous vehicles equipped to address this? Before I say I don’t know, I am really skeptical if manufacturers are really concerned about these subtleties!!</p><h3><strong>Conclusion</strong></h3><p>Technology is as disastrous as it could help human lives. If you follow autonomous vehicles developments closely, they actually makes the life of the people using the autonomous vehicles more safer and easier, simultaneously can push other people using non autonomous vehicles into various stages of dilemmas all along their daily commute.</p><p>I have always heard manufacturers and technologists talking about how safe is autonomous technology to people who use it, but very little of how accommodative it is for people who wish to still live with non autonomous choices. It is unfortunate that no one talks about it.</p><p>Why should we accept such a change if there are not going to cope up with our non autonomous world which we have been living.</p><p>Though we are living in a world racing towards autonomy, if someone chooses to not to use it, it should still be an easy choice to do so and this happens if this technology is ensured to be more humane for the non autonomous world.</p><p>When we all set to invite a change in the society. I think it should play well with existing rules.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=405b36ff9ae3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fault injection attacks on secure boot @ nullconf]]></title>
            <link>https://krishnakumart.medium.com/fault-injection-attacks-on-secure-boot-nullconf-b6bcf4d26c86?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/b6bcf4d26c86</guid>
            <category><![CDATA[embedded-systems]]></category>
            <category><![CDATA[secure-boot]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[nullcon]]></category>
            <category><![CDATA[fault-injection-attacks]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Sat, 16 Mar 2019 05:04:37 GMT</pubDate>
            <atom:updated>2019-03-16T05:04:37.797Z</atom:updated>
            <content:encoded><![CDATA[<p>Nullconf is one of the largest conferences in security started in 2010 (as I keep hearing that from everyone). This is the first time I have ever attended a security conference and was little over excited too. I had been there for two days and took a load of quite interesting aspects of security domain as a whole. Lot of less known and discussed topics like hardware hacking in real-time, security in the post quantum era, AI in threat detection and mitigation, vulnerability of existing telecom networking infrastructure etc are few highlights of the event.</p><p>One of the talks by Niek Timmers and Albert on compromising secure boot with various fault injection techniques was quite interesting to me. I wanna brief that here.</p><p>A regular boot sequence includes running a program from ROM which loads the first stage bootloader into SRAM from a flash storage. This code initializes DDR and loads second stage bootloader into DDR followed by kernel initialization etc.</p><p>External Flash storage is vulnerable to be tampered either by physical access or a software access. Hence the entire system control can be compromised if not taken proper care at the hardware storage layer.</p><p>A secure boot sequence ensures that an authentication mechanism in place by having a signature of the code that is about to be loaded into RAM and will be verified against at each and every stage of the boot process.</p><p><strong>Secure boot flow:</strong></p><p>Hardware → ROM → Bootloader → TEE bootloader → TEE OS → REE bootloader → REE OS → Apps.</p><blockquote>Along with authentication, hardware root of trust, privilege escalation control, encryption of all code and proper hardware and software exploitation defenses ensures a better secure boot mechanism.</blockquote><p><strong>How does the fault injection actually works?</strong></p><p>CPU always expects its supply voltage to be stable, if not, glitches can occur. This is exactly what can lead to a fault injection attack. Let’s say we have got a hardware that has capability to control supply voltage to the target processor and inject glitches at a high degree of precision. (Such hardwares do exist, I have seen them live). We can actually modify instructions that are currently getting executed. Isn’t cool??</p><h3>Instruction skipping:</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*R4iwjSxW8fN_nMRl.png" /></figure><p>As we know external flash storage is vulnerable to attacks if the attacker gets physical access to it. In an encrypted secure boot design. Assume that the first stage bootloader is residing on the internal flash memory of the controller and whereas BL2 can still be on external flash. Now, it would be extremely hard for an attacker to modify BL1 without tampering the controller. BL1 has code to decrypt and verify the BL2 on SRAM before actually loading to DDR. Hence ensures BL2 to be valid.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*b2WCgwSnvwu4F7Za.png" /></figure><p>How does the above setup be attacked? Can BL2 still be compromised? Yes.</p><p>If a glitch is introduced after BL2 is copied to SRAM before validation. It can potentially bypass the entire control flow until the verification step and directly jump to next set of instructions. Provided, the glitch is carefully timed and its duration is carefully chosen. This is called instruction skipping, where all the instructions in the control flow between load and verification will be corrupted and will have no impact on the state of execution.</p><p>How does an attacker determine the glitch timing?</p><p>Power analysis. True. An attacker can record the power consumption of the CPU and can easily determine various points of interest from the surges. Cryptographic operations typically consume more power while the execution is happening which will be very evident given the state of attacker’s familiarity on similar boards.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bn3u6FGT5WhZ5O5q.png" /></figure><h3>Instruction corruption:</h3><h4><strong>Attack on ARM load instructions LDR and LDMIA</strong></h4><blockquote>LDR — To copy a word from one memory address to a register.</blockquote><blockquote>LDMIA — To copy multiple words from a memory address to registers.</blockquote><p>Load instructions are particularly an interesting target for an attacker primarily because of the instruction encoding scheme.</p><h4><strong>Controlling PC during the secure boot:</strong></h4><p>A PC is special register which holds the memory address of the next instruction to be executed. ARM instructions which can accept PC as an argument are prone to fault injection attack. Assume the attacker has physical access to the setup and was able to load malicious code and pointers into an external flash memory. Whenever a load operation is about to happen for a copy operation from a memory location to register, the instruction can be corrupted using a glitch tricking the processor to load the value into a program counter(PC) instead of register.</p><p>How is this possible?</p><p>Attacked instruction: <strong>LDR R3, [R0]</strong> → HEX equivalent of the given instruction is E5903000.</p><p>Instruction to be: <strong>LDR PC, [R0]</strong> → HEX equivalent of the given instruction is E590F000.</p><p>If we closely observe the two HEX values are differ only at the 3rd byte which is “30” and “F0” values through which R3 and PC are represented in the instruction. The corresponding binary representations for “30” and “F0” are “00110000” and “11110000” respectively, and a glitch introduced with precision can actually flip the first two bits of R3 to become PC. By this glitch, an attacker would be able make the CPU load the content of R0 into a Program counter(PC) instead of an intermediate register R3. This attack has almost no impact, unless the attacker has some kind of access to the storage and was able to inject some malicious code in it from which R0 is read.</p><p>The attack surface of LDMIA instruction seems to be higher than LDR based on experimental results, primarily because of the instruction encoding scheme</p><h4><strong>Attack on TEE(Trusted Execution Environment) after the boot:</strong></h4><p>More secure sensitive embedded devices have something called TEEs which separates themselves from Rich Execution Environment(REE). REE will have access to dedicated APIs to make calls to TEE for running some operations in a secure environment. TEE APIs usually copies the payload from REE and executes them internally. Assuming the attacker has full control over REE and was able to make calls to TEE with malicious payload. An attacker can again introduce a fault while the malicious code pointers are being copied into the internal registers. Through a successful instruction corruption can make them copy to program counter(PC) and gain access to the TEE.</p><p><strong>How exactly a randomly corrupted instruction being helpful for attacker?</strong></p><p>This answer is quite interesting and is more about learning the behavior/patterns of corruption for a given glitch amplitude, duration and timing.</p><p><strong>How does an attacker determine the exact time, duration and amplitude of the glitch that can corrupt the instruction?</strong></p><p>Without doing a practical experiment it is impossible to determine these values. A sample setup for the experiment would look like below</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ukChkEAA6PLkzDLR.png" /><figcaption>Fault injection setup</figcaption></figure><p>Along with the the above setup, a sample program is used which runs in a loop continuously executing the load instructions.</p><p>And with glitch VCC ranging between -1.4V to -1.0V, glitch length between 700ns to 1000ns and glitch delay between 30us to 35us and for a total of 10,000 iterations. Results of the experiments are as follows.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Htyvyvp9MsM7RRyS.png" /><figcaption>LDR instruction</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*A6fWel7083T8gTPn.png" /><figcaption>LDMIA instruction</figcaption></figure><p>In the graphs, green dots(Expected) represents no affect on instructions. Yellow dots(Mute) represents the resets and red dots(Success) represents a successful glitch.</p><p>It is quite evident from the graphs, that the LDR instruction is quite less affected by the glitch compared to LDMIA instruction and hence the attack surface is larger for LDMIA instructions.</p><p>It is observed that power glitching is more difficult to be successful if it requires to have complex bit flips.</p><h3>Countermeasures:</h3><p><strong>At hardware level:</strong></p><ul><li>Having a variable clock speeds for the CPU would make the code execution flow less deterministic to the attacker.</li><li>Sensors monitoring the environmental conditions and alert on unusual activity.</li></ul><p><strong>At software level:</strong></p><ul><li>Introducing random delays inside code can also increase complexity of attack.</li><li>Double checking conditions before branching and verify at regular intervals whether expected program executed.</li><li>Redesign of instruction encoding that would maximize the hamming distance between valid to harmful instructions.</li></ul><h3>Conclusion:</h3><p>Having all of the above countermeasures wouldn’t really make a system completely secure but reasonably harder enough for an attacker to counter them. All the attacks described are observed on ARM 32 bit architecture, but the principles described can be applied to many other architectures as well.</p><blockquote>I would like to thank Niek Timmers @ riscure for reviewing this.</blockquote><p><strong>References:</strong></p><ul><li><a href="https://www.slideshare.net/tieknimmers/pew-pew-pew-designing-secure-boot-securely-134091830">https://www.slideshare.net/tieknimmers/pew-pew-pew-designing-secure-boot-securely-134091830</a></li><li><a href="https://www.riscure.com/uploads/2017/09/Controlling-PC-on-ARM-using-Fault-Injection.pdf">https://www.riscure.com/uploads/2017/09/Controlling-PC-on-ARM-using-Fault-Injection.pdf</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b6bcf4d26c86" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding jargon of AWS ECS — Elastic Container Service]]></title>
            <link>https://faun.pub/understanding-jargon-of-aws-ecs-elastic-container-service-7e3ab3b3ba80?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/7e3ab3b3ba80</guid>
            <category><![CDATA[elastic-load-balancer]]></category>
            <category><![CDATA[autoscaling]]></category>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[application-load-balancer]]></category>
            <category><![CDATA[aws]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Wed, 16 Jan 2019 08:24:07 GMT</pubDate>
            <atom:updated>2019-07-18T15:28:25.321Z</atom:updated>
            <content:encoded><![CDATA[<h3>Scaling with AWS ECS — Elastic Container Service</h3><blockquote>Note: Some terms and definitions may have changed. EKS may have superseded ECS.</blockquote><p>You may be a growing business and have to spin and scale your docker containers/services dynamically as your demand grows. All those containers that are created dynamically have to be load balanced, monitored for failures or restarts, autoscaled based on network traffic or based on cluster metrics like CPU and RAM utilization etc.</p><p>A <strong>container cluster</strong> is essentially a collection of docker containers running on multiple machines/servers. In our context of ECS, each and every cluster is specific to a particular docker image that is being run as multiple replicas for high availability. In other words, it is not a composition of different docker containers/services created from different docker images.</p><p>At first, we need to understand some AWS jargon before we jump start creating a container cluster.</p><blockquote><strong>Load balancers:</strong> AWS provides multiple load balancers that can be used based on the requirement.</blockquote><blockquote>Elastic load balancer(ELB) is one of the classic load balancer that balances load between EC2 instances/machines with services running on one single port (Eg: port 80 for web services). This is mostly used for non dockerized applications.</blockquote><blockquote>Application load balancer(ALB) is most recent edition to load balancer family that has more features like dynamic port mapping(multiple host ports can be dynamically mapped load balancer), path based routing etc. We have used ALB as our load balancer.</blockquote><blockquote><strong>VPC(Virtual Private Cloud):</strong> If your cluster has to interact with multiple other services in the cloud, they all should share the same VPC. VPCs are spread across availability zones within a region.<a href="https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Subnets.html"> More info</a></blockquote><blockquote><strong>Subnet: </strong>A subnet is again an isolation of resources by ip address range with in an availability zone and it cannot span across availability zones.</blockquote><blockquote><strong>IAM(Identity and Access management): </strong>Your cluster should also share the same IAM roles as other services if you have any IAM roles defined for either of the services.</blockquote><blockquote><strong>Security group:</strong> They are basically network ACL rules that are configured for EC2 instances that regulates the port communications in and out.</blockquote><blockquote><strong>availability zones:</strong> AWS spread across the world in multiple regions and each region is again sub divided into multiple availability zones. Your instances can exist in multiple availability zones for high availability. <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html">More info</a></blockquote><blockquote><strong>Cluster:</strong> A cluster is a logical grouping of EC2 instances that ECS uses to start containers. Each cluster resource(EC2 instance) will have a container agent that facilitates spinning of docker containers within it.</blockquote><blockquote><strong>Task definition:</strong> A task definition is one important chunk of configuration for ECS cluster. It basically is a composition of your Dockerfile and docker-compose.yml. Your docker image, hard and soft limits for RAM and CPU, type of EC2 instance to use/cluster to use, Environment variables and host port mapping are important things that you can configure. In case of ALB, you should set host port to 0 to make ECS figure out the port mapping of your instances with ALB. <a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html">More info</a></blockquote><blockquote><strong>Service:</strong> A service allows you to run specified number of tasks of particular task definition on the cluster and all these tasks/containers are monitored for failures and restarted automatically by service scheduler. <a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html">More info</a></blockquote><blockquote>— Prerequisites for creating an auto scaling group: Launch configuration, Target groups, ALB</blockquote><blockquote><strong>Auto scaling groups</strong>: As ECS takes care of scaling the docker container instances on the available cluster resources. Auto scaling group can take care of scaling the cluster resources like the count of EC2 instances available in the cluster. You can attach load balancer to autoscaling group and define policies to auto scale based on the load balancer health checks. There are few policies available for scaling like policies listed <a href="https://docs.aws.amazon.com/autoscaling/latest/userguide/scaling_plan.html">here</a>.</blockquote><blockquote>— You can either attach a load balancer or target groups to auto scaling group. A load balancer in turn can be attached a target group. <a href="https://docs.aws.amazon.com/autoscaling/latest/userguide/attach-load-balancer-asg.html">More info</a></blockquote><blockquote><strong>Launch configuration</strong>: An auto scaling group can be created from a launch configuration. It defines the type of EC2 instance, image on those instances, detailed cloudwatch monitoring, ip addresses, security groups, region, IAM roles and key pairs to login to those EC2 instances. <a href="https://docs.aws.amazon.com/autoscaling/latest/userguide/create-launch-config.html">More info</a>.</blockquote><blockquote>— Once you have launch configuration, you can start creating a auto scaling group with it. <a href="https://docs.aws.amazon.com/autoscaling/latest/userguide/create-asg.html">More info</a></blockquote><blockquote><strong>Target groups</strong>: Target groups are used by the load balancer to perform health checks and route traffic to them. It takes port, protocol, VPC and health check configuration.After a target group is created, you need to register some targets with load balancer to route traffic to specific targets.</blockquote><blockquote><strong>ECS(Elastic Container Service)</strong>: It is a service provided by AWS for dynamically scaling a docker container cluster across instances/regions/availability zones based on metrics from cloudwatch, Load balancers etc. <a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html">More info</a></blockquote><blockquote><strong>ECR(Elastic Container Registry)</strong>: It is similar to docker dub where docker images can be stored. You can have multiple repositories and each can hold multiple images. All the repositories under one AWS account logically grouped as a registry. <a href="https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html">More info</a></blockquote><blockquote><strong>CloudWatch</strong>: Each and every EC2 instance created from AWS has a daemon that constantly runs and collects statistics of the instance to estimate its health and also provide dashboard view of the various metrics like CPU, RAM, network in/out etc.</blockquote><p>There are two ways to create a container cluster using ECS.</p><ol><li>Using ECS first run wizard that walks through all the steps required to create a cluster like load balancer, task definition, auto scaling groups, target groups etc.</li><li>Manual way, where you create a target group, load balancer, launch configuration, auto scaling group, task definition, container service, container cluster, IAM roles(if needed), security groups, VPC, subnet, availability zones etc.</li></ol><p>Both the approaches have advantages and disadvantages:</p><p><strong>Option 1 advantages:</strong> Instantly spin up container cluster by just following the on screen steps without knowing much about security policies, auto scaling groups, load balancers etc. Also, each and every cluster created by this approach creates a cloud formation stack template. Having a template is easy to cleanup resources/left overs by just removing the template from cloud formation stack</p><p><strong>Option 1 disadvantages:</strong> It is not for production clusters. Every time you create a task definition, service definition, cluster definition, load balancer with default names and default IAM roles and VPCs. You don’t have much control on the naming conventions.</p><p><strong>Option 2 advantages:</strong> Much control on each every service. All the service definitions can be altered separately. Create services only once and reuse them.</p><p><strong>Option 2 disadvantages:</strong> Need to have good understanding on all of the above mentioned jargon to create a running cluster.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/727/0*Piks8Tu6xUYpF4DU" /></figure><p><strong>Join our community Slack and read our weekly Faun topics ⬇</strong></p><figure><a href="https://www.faun.dev/join/?utm_source=medium.com%2Ffaun&amp;utm_medium=medium&amp;utm_campaign=faunmediumbanner"><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*oSdFkACJxs5iy1oR" /></a></figure><h4>If this post was helpful, please click the clap 👏 button below a few times to show your support for the author! ⬇</h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7e3ab3b3ba80" width="1" height="1" alt=""><hr><p><a href="https://faun.pub/understanding-jargon-of-aws-ecs-elastic-container-service-7e3ab3b3ba80">Understanding jargon of AWS ECS — Elastic Container Service</a> was originally published in <a href="https://faun.pub">FAUN.dev() 🐾</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[[Announcement] “backer-rs” the missing versioned backup utility for boostnote/any note-taking app]]></title>
            <link>https://krishnakumart.medium.com/announcement-backer-rs-the-missing-versioned-backup-utility-for-boostnote-any-note-taking-app-b2b654e00b7b?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/b2b654e00b7b</guid>
            <category><![CDATA[backup]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[boostnote]]></category>
            <category><![CDATA[orgzly]]></category>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Sun, 21 Oct 2018 16:26:09 GMT</pubDate>
            <atom:updated>2023-05-23T17:51:17.747Z</atom:updated>
            <content:encoded><![CDATA[<h4>backer-rs | A light weight backup utility for note takers</h4><p>“Autosave notes”, it is a pretty important feature for any note taking app. Having autosave enabled and running in the background saves you from loosing any important notes you have taken. We would have felt autosave itself is a kind of backup feature for your notes.</p><p>Recently, I had been in a situation where the autosave feature of <a href="https://boostnote.io">boostnote</a> misbehaved and lost all my notes till date. It’s a big disappointment.</p><blockquote>I have finished my work, wrote a beautiful document on the work I have done on boostnote and left for the day. Next morning, when I logged back in, I noticed I am seeing an older version of my notes. After a while, some more content missing from the same notes. I restarted the boostnote app with a hope to restore everything back, but unfortunately ended up being a clean slate. I have literally gone back in time and all my notes are actually gone.</blockquote><p>I never thought that I would loose notes just like that. I did some googling and realized that boostnote stores all the notes in simple text files. The moment I saw that it is not backed by any sort of version control. I lost hope that my notes can be recovered at all by any means.</p><blockquote>I should honestly agree, the user interface and markdown editor of boostnote is simply wonderful though. So, I could not give up on boostnote.</blockquote><p>In spite of repenting(for not taking care of this before), I started writing a tool with the features that I felt would have been there in the first place if at all a tool exist.</p><p>It is a simple, auto commit application backed by git version control. It monitors for changes on a folder and makes a quick commit for you. It doesn’t commit, if there are no changes at all, thus preventing unnecessary commits in the history.</p><p>Initially I called it “boostnote-backup” and later felt that it can be a generic backup tool for any other note taking apps like orgzly etc.. and hence named it “backer-rs”.</p><p>It is actually opensourced out <a href="https://github.com/krishnakumar4a4/backer-rs">here</a>. I would definitely love some pull requests, suggestions and feedback.</p><p>Currently, it is very basic version out there. We can add features like</p><ul><li>Making it run as a system service.</li><li>Pushing to a remote repository after certain preconfigured time.</li><li>Reading configuration from a file instead of just command line args.</li><li>CI pipeline to setup Cross compiled binaries for windows, Mac, Linux and Android.</li><li>Making it an installable package for the above platforms.</li><li>Etc..</li></ul><p>Long live note taker’s!!</p><blockquote>My notes are my prized possession. It has my wonderful thoughts for the future and hard to remember facts of the past. — I made this up.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b2b654e00b7b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WebSocket fever for IOT]]></title>
            <link>https://krishnakumart.medium.com/websocket-fever-for-iot-f662498ff1d2?source=rss-91d698aaa5a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/f662498ff1d2</guid>
            <category><![CDATA[browsers]]></category>
            <category><![CDATA[websocket]]></category>
            <category><![CDATA[http-request]]></category>
            <category><![CDATA[https]]></category>
            <category><![CDATA[iot]]></category>
            <dc:creator><![CDATA[KrishnaKumar]]></dc:creator>
            <pubDate>Sat, 28 Jul 2018 00:07:38 GMT</pubDate>
            <atom:updated>2018-07-28T00:07:38.787Z</atom:updated>
            <content:encoded><![CDATA[<p>Internet of things is a buzz now. In simpler terms, connecting all the things to the internet so that they are monitored and controlled from anywhere.</p><p>What is the most common protocol of internet. It is http. You open a browser enter a url and then you see content on your browser. How is that working? When you enter a url, your browser makes http get request to the site and fetches the content.</p><p>Likewise multiple methods are supported for http like post, put, delete etc.</p><p><strong><em>How does http work internally? </em></strong><br>Http is equipped with lot of headers to actually reach it&#39;s destination over the internet. Http runs on tcp protocol and every request makes a connection and closes it after the response.</p><p><strong><em>How does WebSocket works?</em></strong><br>WebSocket is a simple tcp based protocol which does the initial handshake by http and keeps a persistent tcp connection to the server. <br>Unlike http, it is a duplex connection that allows client and server push frames from either side. Hence allowing the client and server to communicate in more real-time. <br>Except the typical initial handhsake which will be in http, rest of the communication is headerless and lightweight.<br>WebSockets are supported on every browser thus allowing you to fetch data in real time without using traditional http polling mechanisms that works by pull approach where the client asks for any updates server has and makes and breaks connection for every single request, which is overhead for both client and servers.</p><p>Tcp connection is not a standard for browsers and hence having a protocol as light as tcp other than the traditional http is very useful.</p><p>Http has certain advantages like requests can be cached by intermediate proxy servers there by reducing the load on the actual server when serving the same response to multiple clients. However this kind of caching is not supported by websockets, but it is highly unusable for a scenario where websockets has to be used. When things change in real time, caching is of least importance.</p><blockquote><strong><em>WebSockets for IOT:</em></strong><br>Given the need for visualizing lot of details on web dashboards in real time. Advent of cloud platforms for IOT etc. WebSockets would be a ideal choice for having lightweight communication between server and client.</blockquote><blockquote>A typical home IOT environment:<br>You might be having lot of devices at your home which needed to be visualized and controlled when you are away from your home or you might be lazy getting up from your couch to turn off something or you want your grandparents to have complete control of your home at their fingers and so on.<br>In a typical gateway based IOT architecture, your gateway is always connected to your server to see the current status of devices over the internet. Your server can send commands back to the gateway only if there is a two way communication. Doing the same thing over http will involve your gateway constantly polling for commands from server. WebSockets makes your life easier.<br>Let&#39;s say you wanted to offer your IOT system for all the residents of your apartment as a service. WebSocket would be a right choice in maintaining just one connection per gateway per flat. Otherwise, using http, your server will be flooded with lot of connections and disconnections for every poll per gateway per flat more than the data/commands to be sent across.</blockquote><p><em>Real-time audio streaming environment:<br>I have built an application that allows people within a wifi network to talk from their mobile web browser. This requires a real low latency audio streaming from the browser itself. WebSockets are useful here as it needed binary data streaming in real time. Using a traditional http for each and every frame would impact latency as it makes and breaks connection for each and every frame, WebSockets on the other hand makes connection once and streams data on that connection continuously. Also, the same WebSocket can be used to push messages,notifications and even transpiled string asynchronously to all the clients from the server.</em></p><p>A self driving car, pizza delivery service, realtime ambulance guiding system to accident spot etc. There are many usecase where websockets can be or will be or being used. See if your next usecase needs a WebSocket.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f662498ff1d2" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>