Tuesday, February 28, 2023

Routing TCP over Envoy using HTTP CONNECT

Tunneling TCP traffic using the L4 proxy capabilities of Envoy works well, but due to the nature of TCP, very little metadata useful for routing can be propagated via the TCP protocol itself. Using the HTTP CONNECT verb however, it is possible to instruct a proxy to tunnel the subsequent data as raw TCP to some target without interpreting it as http or some other L7 protocol. The way it works is listed below:

  1. A caller A wants to send some TCP traffic to a service B.
  2. The caller A calls some proxy P by making an HTTP request with the following header: CONNECT http://<address_of_B>[:port] HTTP/1.1
  3. The proxy P opens a TCP connection to the address and the optional port, and keeps the connection from A open.
  4. The caller A then uses its connection to P to stream the TCP payload it needs to send to B. P relays this traffic to B.
In the above, P is said to terminate the HTTP CONNECT. Equally well, it could be configured to propagate the HTTP CONNECT instead of terminating it, proxying everything it receives to a downstream proxy (upstream, if we use Envoy terminology). The final proxy in this chain would then terminate the HTTP CONNECT and forward the request to the target.

The elegance in this approach is that by encapsulating the request in an HTTP shim, we open up HTTP headers as a mechanism for specifying routing directives that the intermediate proxies could use. If the caller uses TLS, they can specify the serverName in TLS headers and use SNI for routing the request. The actual target address of B need not even be routable from A - it only needs to be routable from the final proxy in the chain (the one that terminates the HTTP CONNECT). With HTTP/2, the CONNECT header even allows a URL path. I'm not sure but perhaps this URL path too could be used for routing purposes just as with regular requests. With HTTP/2, multiple TCP streams could be multiplexed over a single HTTP/2 connection, achieving improved resource usage and better latencies when reusing connections from a pool.

The obvious downside is that the caller A would need to know the mechanics of HTTP CONNECT and have a dependency on it. But this is a small price to pay for not having to deal with TCP routing.

Envoy has supported HTTP CONNECT for a few years now (possibly since 1.14.x). Here is a small sample configuration which uses two Envoys, one propagating the HTTP CONNECT and the other terminating it, to route TCP traffic to a destination.

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 127.0.0.1, port_value: 10000 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: connect_tcp
              domains: ["fubar.xyz:1234"]
              routes:
              - match: { headers: [{name: ":authority", suffix_match: ":1234"}], connect_matcher: {} }
                route: { cluster: ssh, upgrade_configs: [{upgrade_type: "CONNECT", connect_config: {}}] }
              - match: { prefix: "/" }
                route: { cluster: ssh }
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: ssh }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          upgrade_configs:
          - upgrade_type: CONNECT
          http2_protocol_options:
            allow_connect: true
  - name: listener_1
    address:
      socket_address: { address: 127.0.0.1, port_value: 9000 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          codec_type: AUTO
          route_config:
            name: local_route1
            virtual_hosts:
            - name: connect_fwd
              domains: ["fubar.xyz", "fubar.xyz:*"]
              routes:
              - match: { connect_matcher: {} }
                #### route: { cluster: conti, upgrade_configs: [{upgrade_type: "websocket"}]}
                route: { cluster: conti, timeout: "0s"}
              - match: { prefix: "/" }
                route: { cluster: conti1 }
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: conti1 }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          upgrade_configs:
          - upgrade_type: CONNECT
          http2_protocol_options:
            allow_connect: true
  clusters:
  - name: ssh
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: ssh
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 22
  - name: conti
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: conti
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 10000
  - name: conti1
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: conti
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8000
(More explanation to follow.)

Read more!

Tuesday, February 14, 2023

Charting Your Course Through the Knowledge Economy

This is the age of the knowledge economy and the age of the autodidact. Formal education matters less than skills and the ability to think deeply, and to apply one's understanding of an area. Very little of the learning we need to do from here on would be formulaic. Multiple levels of formulaic thinking, with sophisticated patterns, would be delegated to machines and human intellectual power would be repeatedly summoned to solve hard problems. But that journey of a thousand miles would still need to start with a single step. This article is about building the intellectual discipline to absorb and produce knowledge, and hopefully, some wisdom too.

Learning discipline for the attention challenged

How do you study deep when you have attention span issues (like most of the early 21st century workforce) and need to understand and remember quickly? I don't have an answer, I am seeking answers myself. Two things I can think of though, are:

  1. Drawing diagrams with labels, *on paper*, illustrating the main ideas, if that's feasible.
  2. Avoiding phone, social media, and similar modern day "necessities" at all costs at study time.
There is a third thing, and this was popularized by the course / book on learning how to learn, but is possibly well-known:
  1. Recap what you read later (maybe the next morning or the same day in the night) using the drawing, and perhaps write notes. And then of course read this in about a week.

Building a reading list

If you're like me, you'd buy books, but read only a small subset of them. It's not inherently problematic and there is some evidence to say that having books you haven't yet gotten to reading might be a good thing - but it can only be so as long as you read with some regularity and burn down a reading list. Which means sporadic reading is not such a great idea.

An even harder problem presents itself with e-books. We download a great e-book that we always wanted to read and then forget about it. The greatest challenge with e-books is that they are not in front of your eyes, on your bookshelf or table, constantly reminding you of their existence. How does one remember them, and then make a mental note of a plan to read them? Again I don't know the answer, but here is something I can think of.
  1. Have a reading list, or rather a few reading lists.
    1. For example have a fiction reading list, a self-help / mgmt reading list (if that's your thing), and then stuff that is specific to your domain. If you're into software engineering for example, you would likely have lots to catch up on in various areas: distributed systems, security, concurrency, networking, data structures, operating systems. It's fine to have a list in each area - but make it a really short list.
    2. Curate the list. Especially for technical subjects, find those books which would help you learn faster and get to the next level. Be prepared to churn these lists, replacing some choices with others as you figure what works and what does not.
  2. Devote some time each day, even if that's 30 minutes, to reading. Identify ahead of time what you would be reading. Make a couple of hours maybe during weekends and holidays, when you can.
  3. Keep the books from your reading list close at hand.
  4. By all means, catalog your e-books (and physical books too, but especially e-books) in an online catalog such as Goodreads or LibraryThing or some such. And then gawk at your own collection fishing for the next book ideas at least once every week.
    1. This requires discipline - every time you download a book, you have to put an entry your online catalog. But then as Pythagoras once said, there's no royal road to geometry, nor to deep reading if I may add.
There is no reading without writing. Taking notes, and thinking about what you read make all the difference. Finding special interest groups / meetups in your locality that discuss what they are reading are a fantastic way to maximize your reading muscle.

Creating a body of work

This is perhaps the most important topic, and the one that requires maximum discipline. A body of work typically means a set of documents of some manner. This could be a set of academic papers, books or monographs, useful blog articles, instructional videos, significant long term contributions to one or more open source (or closed source) software, etc. or some combination thereof. It could even be photographs, paintings, performances as an actor or a musician or in some other performing art. However, here I would mostly focus on the former kind because this article lacks the space or the scope to talk about artistic creativity.

Your body of work is really a document of what you've built with your intellect. Instead of getting too prescriptive, I would like to focus on four aspects that are essential.
  1. Have a vision of what purpose your body of work serves.
    1. Maybe it teaches people difficult concepts in a simple way.
    2. Maybe it reveals new insights about some subject.
    3. Maybe it's an aid for other teachers, or researchers.
  2. Identify the form-factor of your body of work.
    1. Would it be a series of blog articles?
    2. Would it be a book, or a book series?
    3. Would it be code in a set of repositories?
  3. Identify how would build that body of work.
    1. Identify key concepts and notions, insights or even discoveries you've made, and start documenting them on an ad hoc basis.
    2. Periodically collate your notes and documents, and start building rough drafts of your book / blog / video, whatever.
    3. Publish, share with your consumers, and seek early feedback. Even when you want to directly earn money for your publications, this is still viable as lots of highly qualified people would happily read and review your work for free or for a small fee.
  4. Make sure you are making progress on building this, every week, month, and year.
    1. Progress can be slow, especially in the beginning. Speed (like in running, playing an instrument, or making money) is usually something you leave for later.
This is the undertaking that is the hardest to start on, but has the maximum bang for the buck. Having a vision, and having the discipline to pursue it are the key to achieving these goals.

Postscript

There are many other aspects that I haven't spoken of here. Building focus through mindfulness techniques, building a general outlook in life that is conducive to focus and attention (stoicism, anyone?), and the ability to think critically as well as pragmatically, are all vital ingredients of a solid knowledge career. The above article in some sense is more logistics, than principles. But hopefully the techniques give you a useful blueprint to follow.


Read more!