
<rss version="2.0" 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" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Sat, 04 Apr 2026 05:29:19 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Give us a ping. (Cloudflare) One ping only.]]></title>
            <link>https://blog.cloudflare.com/the-most-exciting-ping-release/</link>
            <pubDate>Fri, 13 Jan 2023 14:00:00 GMT</pubDate>
            <description><![CDATA[ Now Zero Trust administrators can use the familiar debugging tools that we all know and love like ping, traceroute, and MTR to test connectivity to private network destinations running behind their Tunnels ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1nZa6ahqyj7z2sii9QERbV/2c9ee66f5628c47da9a20fab9c85516e/image1-35.png" />
            
            </figure><p>Ping was born in 1983 when the Internet needed a simple, effective way to measure reachability and distance. In short, ping (and subsequent utilities like traceroute and MTR)  provides users with a quick way to validate whether one machine can communicate with another. Fast-forward to today and these network utility tools have become ubiquitous. Not only are they now the de facto standard for troubleshooting connectivity and network performance issues, but they also improve our overall quality of life by acting as a common suite of tools almost all Internet users are comfortable employing in their day-to-day roles and responsibilities.</p><p>Making network utility tools work as expected is very important to us, especially now as more and more customers are building their private networks on Cloudflare. Over 10,000 teams now run a private network on Cloudflare. Some of these teams are among the world's largest enterprises, some are small crews, and yet others are hobbyists, but they all want to know - can I reach that?</p><p>That’s why today we’re excited to incorporate support for these utilities into our already expansive troubleshooting toolkit for Cloudflare Zero Trust. To get started, <a href="https://forms.gle/gpfGAJW2jsxykC6y9">sign up</a> to receive beta access and start using the familiar debugging tools that we all know and love like ping, traceroute, and MTR to test connectivity to private network destinations running behind Tunnel.</p>
    <div>
      <h2>Cloudflare Zero Trust</h2>
      <a href="#cloudflare-zero-trust">
        
      </a>
    </div>
    <p>With Cloudflare Zero Trust, we’ve made it <a href="/ridiculously-easy-to-use-tunnels/">ridiculously easy</a> to build your private network on Cloudflare. In fact, it takes just three steps to get started. First, download Cloudflare’s device client, WARP, to connect your users to Cloudflare. Then, create identity and device aware policies to determine who can reach what within your network. And finally, connect your network to Cloudflare with Tunnel directly from the Zero Trust dashboard.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Fn9l1D4DFiBYv2JSmpT1Z/c8566a62163b04b8dafb8752f1dd7104/Untitled-1.png" />
            
            </figure><p>We’ve designed Cloudflare Zero Trust to act as a single pane of glass for your organization. This means that after you’ve deployed <i>any</i> part of our <a href="https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/">Zero Trust</a> solution, whether that be <a href="https://www.cloudflare.com/learning/access-management/what-is-ztna/">ZTNA</a> or <a href="https://www.cloudflare.com/learning/access-management/what-is-a-secure-web-gateway/">SWG</a>, you are clicks, not months, away from deploying <a href="https://www.cloudflare.com/products/zero-trust/browser-isolation/">Browser Isolation</a>, <a href="https://www.cloudflare.com/products/zero-trust/dlp/">Data Loss Prevention</a>, <a href="https://www.cloudflare.com/products/zero-trust/casb/">Cloud Access Security Broker</a>, and <a href="https://www.cloudflare.com/products/zero-trust/email-security/">Email Security</a>. This is a stark contrast from other solutions on the market which may require distinct implementations or have limited interoperability across their portfolio of services.</p><p>It’s that simple, but if you’re looking for more prescriptive guidance watch our <a href="https://www.cloudflare.com/products/zero-trust/interactive-demo/">demo</a> below to get started:</p><div></div>
<p></p><p>To get started, sign-up for early access to the closed beta. If you’re interested in learning more about how it works and what else we will be launching in the future, keep scrolling.</p>
    <div>
      <h2>So, how do these network utilities actually work?</h2>
      <a href="#so-how-do-these-network-utilities-actually-work">
        
      </a>
    </div>
    <p>Ping, traceroute and MTR are all powered by the same underlying <a href="https://www.cloudflare.com/learning/network-layer/what-is-a-protocol/">protocol</a>, ICMP. Every <a href="https://www.cloudflare.com/learning/ddos/glossary/internet-control-message-protocol-icmp/">ICMP</a> message has 8-bit type and code fields, which define the purpose and semantics of the message. While ICMP has many types of messages, the network diagnostic tools mentioned above make specific use of the echo request and echo reply message types.</p><p>Every ICMP message has a type, code and checksum. As you may have guessed from the name, an echo reply is generated in response to the receipt of an echo request, and critically, the request and reply have matching identifiers and sequence numbers. Make a mental note of this fact as it will be useful context later in this blog post.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7D6dGG8IM5rnQXjS4easil/c691a4f6500fe4fd901e6fa33d0377a5/ICMP-header-format.png" />
            
            </figure>
    <div>
      <h2>A crash course in ping, traceroute, and MTR</h2>
      <a href="#a-crash-course-in-ping-traceroute-and-mtr">
        
      </a>
    </div>
    <p>As you may expect, each one of these utilities comes with its own unique nuances, but don’t worry. We’re going to provide a quick refresher on each before getting into the nitty-gritty details.</p>
    <div>
      <h3>Ping</h3>
      <a href="#ping">
        
      </a>
    </div>
    <p>Ping works by sending a sequence of echo request packets to the destination. Each router hop between the sender and destination decrements the TTL field of the IP packet containing the ICMP message and forwards the packet to the next hop. If a hop decrements the TTL to 0 before reaching the destination, or doesn’t have a next hop to forward to, it will return an ICMP error message – “TTL exceeded” or “Destination host unreachable” respectively – to the sender. A destination which speaks ICMP will receive these echo request packets and return matching echo replies to the sender. The same process of traversing routers and TTL decrementing takes place on the return trip. On the sender’s machine, ping reports the final TTL of these replies, as well as the roundtrip latency of sending and receiving the ICMP messages to the destination. From this information a user can determine the distance between themselves and the origin server, both in terms of number of network hops and time.</p>
    <div>
      <h3>Traceroute and MTR</h3>
      <a href="#traceroute-and-mtr">
        
      </a>
    </div>
    <p>As we’ve just outlined, while helpful, the output provided by ping is relatively simple. It does provide some useful information, but we will generally want to follow up this request with a traceroute to learn more about the specific path to a given destination. Similar to ping, traceroutes start by sending an ICMP echo request. However, it handles TTL a bit differently. You can <a href="https://www.cloudflare.com/learning/network-layer/what-is-mtr/">learn more</a> about why that is the case in our <a href="https://www.cloudflare.com/learning/">Learning Center</a>, but the important takeaway is that this is how traceroutes are able to map and capture the IP address of each unique hop on the network path. This output makes traceroute an incredibly powerful tool to understanding not only <i>if</i> a machine can connect to another, but also <i>how</i> it will get there! And finally, we’ll cover MTR. We’ve grouped traceroute and MTR together for now as they operate in an extremely similar fashion. In short, the output of an MTR will provide everything traceroute can, but with some additional, aggregate statistics for each unique hop. MTR will also run until explicitly stopped allowing users to receive a statistical average for each hop on the path.</p>
    <div>
      <h2>Checking connectivity to the origin</h2>
      <a href="#checking-connectivity-to-the-origin">
        
      </a>
    </div>
    <p>Now that we’ve had a quick refresher, let’s say I cannot connect to my private application server. With ICMP support enabled on my Zero Trust account, I could run a traceroute to see if the server is online.</p><p>Here is simple example from one of our lab environments:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7auWBc7axco0ez11m2sOSd/e4c1fa9c86f91efe2282dc7800887cbc/ICMP-support-for-Warp-to-Tunnel_d.png" />
            
            </figure><p>Then, if my server is online, traceroute should output something like the following:</p>
            <pre><code>traceroute -I 172.16.10.120
traceroute to 172.16.10.120 (172.16.10.120), 64 hops max, 72 byte packets
 1  172.68.101.57 (172.68.101.57)  20.782 ms  12.070 ms  15.888 ms
 2  172.16.10.100 (172.16.10.100)  31.508 ms  30.657 ms  29.478 ms
 3  172.16.10.120 (172.16.10.120)  40.158 ms  55.719 ms  27.603 ms</code></pre>
            <p>Let’s examine this a bit deeper. Here, the first hop is the Cloudflare data center where my Cloudflare WARP device is connected via our <a href="https://www.cloudflare.com/learning/cdn/glossary/anycast-network/">Anycast</a> network. Keep in mind this IP may look different depending on your location. The second hop will be the server running cloudflared. And finally, the last hop is my application server.</p><p>Conversely, if I could not connect to my app server I would expect traceroute to output the following:</p>
            <pre><code>traceroute -I 172.16.10.120
traceroute to 172.16.10.120 (172.16.10.120), 64 hops max, 72 byte packets
 1  172.68.101.57 (172.68.101.57)  20.782 ms  12.070 ms  15.888 ms
 2  * * *
 3  * * *</code></pre>
            <p>In the example above, this means the ICMP echo requests are not reaching cloudflared. To troubleshoot, first I will make sure cloudflared is running by checking the status of the Tunnel in the <a href="https://dash.teams.cloudflare.com/">ZeroTrust dashboard</a>. Then I will check if the Tunnel has a route to the destination IP. This can be found in the Routes column of the Tunnels table in the dashboard. If it does not, I will add a route to my Tunnel to see if this changes the output of my traceroute.</p><p>Once I have confirmed that cloudflared is running and the Tunnel has a route to my app server, traceroute will show the following:</p>
            <pre><code>raceroute -I 172.16.10.120
traceroute to 172.16.10.120 (172.16.10.120), 64 hops max, 72 byte packets
 1  172.68.101.57 (172.68.101.57)  20.782 ms  12.070 ms  15.888 ms
 2  172.16.10.100 (172.16.10.100)  31.508 ms  30.657 ms  29.478 ms
 3  * * *</code></pre>
            <p>However, it looks like we still can’t quite reach the application server. This means the ICMP echo requests reached cloudflared, but my application server isn’t returning echo replies. Now, I can narrow down the problem to my application server, or communication between cloudflared and the app server. Perhaps the machine needs to be rebooted or there is a firewall rule in place, but either way we have what we need to start troubleshooting the last hop. With ICMP support, we now have many network tools at our disposal to troubleshoot connectivity end-to-end.</p><p>Note that the route cloudflared to origin is always shown as a single hop, even if there are one or more routers between the two. This is because cloudflared creates its own echo request to the origin, instead of forwarding the original packets. In the next section we will explain the technical reason behind it.</p>
    <div>
      <h2>What makes ICMP traffic unique?</h2>
      <a href="#what-makes-icmp-traffic-unique">
        
      </a>
    </div>
    <p>A few quarters ago, Cloudflare Zero Trust <a href="/extending-cloudflares-zero-trust-platform-to-support-udp-and-internal-dns/">extended support for UDP</a> end-to-end as well. Since UDP and ICMP are both datagram-based protocols, within the Cloudflare network we can reuse the same infrastructure to proxy both UDP and ICMP traffic. To do this, we send the individual datagrams for either protocol over a QUIC connection using <a href="https://datatracker.ietf.org/doc/html/rfc9221">QUIC datagrams</a> between Cloudflare and the cloudflared instances within your network.</p><p>With UDP, we establish and maintain a <i>session</i> per client/destination pair, such that we are able to send <b>only</b> the UDP payload and a session identifier in datagrams. In this way, we don’t need to send the IP and port to which the UDP payload should be forwarded with every single packet.</p><p>However, with ICMP we decided that establishing a session like this is far too much overhead, given that typically only a handful of ICMP packets are exchanged between endpoints. Instead, we send the entire IP packet (with the ICMP payload inside) as a single datagram.</p><p>What this means is that cloudflared can read the destination of the ICMP packet from the IP header it receives. While this conveys the eventual destination of the packet to cloudflared, there is still work to be done to actually send the packet. Cloudflared cannot simply send out the IP packet it receives without modification, because the source IP in the packet is still the <i>original</i> client IP, and not a source that is routable to the cloudflared instance itself.</p><p>To receive ICMP echo replies in response to the ICMP packets it forwards, cloudflared must apply a source NAT to the packet. This means that when cloudflared receives an IP packet, it must complete the following:</p><ul><li><p>Read the destination IP address of the packet</p></li><li><p>Strip off the IP header to get the ICMP payload</p></li><li><p>Send the ICMP payload to the destination, meaning the source address of the ICMP packet will be the IP of a network interface to which cloudflared can bind</p></li><li><p>When cloudflared receives replies on this address, it must rewrite the destination address of the received packet (destination because the direction of the packet is reversed) to the original client source address</p></li></ul><p>Network Address Translation like this is done all the time for <a href="https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/">TCP</a> and UDP, but is much easier in those cases because ports can be used to disambiguate cases where the source and destination IPs are the same. Since ICMP packets do not have ports associated with them, we needed to find a way to map packets received from the upstream back to the original source which sent cloudflared those packets.</p><p>For example, imagine that two clients 192.0.2.1 and 192.0.2.2 both send an ICMP echo request to a destination 10.0.0.8. As we previously outlined, cloudflared must rewrite the source IPs of these packets to a source address to which it can bind. In this scenario, when the echo replies come back, the IP headers will be identical: source=10.0.0.8 destination=&lt;cloudflared’s IP&gt;. So, how can cloudflared determine which packet needs to have its destination rewritten to 192.0.2.1 and which to 192.0.2.2?</p><p>To solve this problem, we use fields of the ICMP packet to track packet flows, in the same way that ports are used in TCP/UDP NAT. The field we’ll use for this purpose is the Echo ID. When an echo request is received, conformant ICMP endpoints will return an echo reply with the same identifier as was received in the request. This means we can send the packet from 192.0.2.1 with ID 23 and the one from 192.0.2.2 with ID 45, and when we receive replies with IDs 23 and 45, we know which one corresponds to each original source.</p><p>Of course this strategy only works for ICMP echo requests, which make up a relatively small percentage of the available ICMP message types. For security reasons, however, and owing to the fact that these message types are sufficient to implement the ubiquitous ping and traceroute functionality that we’re after, these are the only message types we currently support. We’ll talk through the security reasons for this choice in the next section.</p>
    <div>
      <h2>How to proxy ICMP without elevated permissions</h2>
      <a href="#how-to-proxy-icmp-without-elevated-permissions">
        
      </a>
    </div>
    <p>Generally, applications need to send ICMP packets through raw sockets. Applications have control of the IP header using this socket, so it requires elevated privileges to open. Whereas the IP header for TCP and UDP packets are added on send and removed on receive by the operating system. To adhere to security best-practices, we don’t really want to run cloudflared with additional privileges. We needed a better solution. To solve this, we found inspiration in the ping utility, which you’ll note can be run by <i>any</i> user, <i>without</i> elevated permissions. So then, how does ping send ICMP echo requests and listen for echo replies as a normal user program? Well, the answer is less satisfying: it depends (on the platform). And as cloudflared supports all the following platforms, we needed to answer this question for each.</p>
    <div>
      <h3>Linux</h3>
      <a href="#linux">
        
      </a>
    </div>
    <p>On linux, ping opens a datagram socket for the ICMP protocol with the syscall <b><i>socket(PF_INET, SOCK_DGRAM, PROT_ICMP).</i></b> This type of socket can only be opened if the group ID of the user running the program is in <b><i>/proc/sys/net/ipv4/ping_group_range</i></b>, but critically, the user does not need to be root. This socket is “special” in that it can only send ICMP echo requests and receive echo replies. Great! It also has a conceptual “port” associated with it, despite the fact that ICMP does not use ports. In this case, the identifier field of echo requests sent through this socket are rewritten to the “port” assigned to the socket. Reciprocally, echo replies received by the kernel which have the same identifier are sent to the socket which sent the request.</p><p>Therefore, on linux cloudflared is able to perform source NAT for ICMP packets simply by opening a unique socket per source IP address. This rewrites the identifier field and source address of the request. Replies are delivered to this same socket meaning that cloudflared can easily rewrite the destination IP address (destination because the packets are flowing <i>to</i> the client) and echo identifier back to the original values received from the client.</p>
    <div>
      <h3>Darwin</h3>
      <a href="#darwin">
        
      </a>
    </div>
    <p>On Darwin (the UNIX-based core set of components which make up macOS), things are similar, in that we can open an unprivileged ICMP socket with the same syscall <i><b>socket(PF_INET, SOCK_DGRAM, PROT_ICMP)</b></i>. However, there is an important difference. With Darwin the kernel does not allocate a conceptual “port” for this socket, and thus, when sending ICMP echo requests the kernel does not rewrite the echo ID as it does on linux. Further, and more importantly for our purposes, the kernel does not demultiplex ICMP echo replies to the socket which sent the corresponding request using the echo identifier. This means that on macOS, we effectively need to perform the echo ID rewriting manually. In practice, this means that when cloudflared receives an echo request on macOS, it must choose an echo ID which is unique for the destination. Cloudflared then adds a key of (chosen echo ID, destination IP) to a mapping it then maintains, with a value of (original echo ID, original source IP). Cloudflared rewrites the echo ID in the echo request packet to the one it chose and forwards it to the destination. When it receives a reply, it is able to use the source IP address and echo ID to look up the client address and original echo ID and rewrite the echo ID and destination address in the reply packet before forwarding it back to the client.</p>
    <div>
      <h3>Windows</h3>
      <a href="#windows">
        
      </a>
    </div>
    <p>Finally, we arrived at Windows which conveniently provides a Win32 API IcmpSendEcho that sends echo requests and returns echo reply, timeout or error. For ICMPv6 we just had to use Icmp6SendEcho. The APIs are in C, but cloudflared can call them through CGO without a problem. If you also need to call these APIs in a Go program, <a href="https://github.com/cloudflare/cloudflared/blob/master/ingress/icmp_windows.go">checkout our wrapper</a> for inspiration.</p><p>And there you have it! That’s how we built the most exciting ping release since 1983. Overall, we’re thrilled to announce this new feature and can’t wait to get your feedback on ways we can continue improving our implementation moving forward.</p>
    <div>
      <h2>What’s next</h2>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>Support for these ICMP-based utilities is just the beginning of how we’re thinking about improving our Zero Trust administrator experience. Our goal is to continue providing tools which make it easy to identify issues within the network that impact connectivity and performance.</p><p>Looking forward, we plan to add more dials and knobs for <a href="https://www.cloudflare.com/learning/performance/what-is-observability/">observability</a> with announcements like <a href="/introducing-digital-experience-monitoring/">Digital Experience Monitoring</a> across our Zero Trust platform to help users <a href="https://www.cloudflare.com/application-services/solutions/app-performance-monitoring/">proactively monitor</a> and stay alert to changing network conditions. In the meantime, try applying Zero Trust controls to your private network for free by <a href="https://dash.cloudflare.com/sign-up">signing up</a> today.</p> ]]></content:encoded>
            <category><![CDATA[CIO Week]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Zero Trust]]></category>
            <category><![CDATA[Private Network]]></category>
            <category><![CDATA[Cloudflare Tunnel]]></category>
            <guid isPermaLink="false">6GPeSDV02jXldOr3L43yxx</guid>
            <dc:creator>Abe Carryl</dc:creator>
            <dc:creator>Chung-Ting Huang</dc:creator>
            <dc:creator>John Norwood</dc:creator>
        </item>
        <item>
            <title><![CDATA[How Argo Tunnel engineering uses Argo Tunnel]]></title>
            <link>https://blog.cloudflare.com/how-argo-tunnel-engineering-uses-argo-tunnel/</link>
            <pubDate>Thu, 27 Aug 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ Argo Tunnel provides remote access to development environments by creating secure outbound-only connections to Cloudflare’s edge network from a resource exposing it to the Internet. That model helps protect servers and resources from being vulnerable to attack by an exposed IP address.  ]]></description>
            <content:encoded><![CDATA[ <p>Whether you are managing a fleet of machines or sharing a private site from your localhost, Argo Tunnel is here to help. On the Argo Tunnel team we help make origins accessible from the Internet in a secure and seamless manner. We also care deeply about productivity and developer experience for the team, so naturally we want to make sure we have a development environment that is reliable, easy to set up and fast to iterate on.</p>
    <div>
      <h2>A brief history of our development environment (dev-stack)</h2>
      <a href="#a-brief-history-of-our-development-environment-dev-stack">
        
      </a>
    </div>
    
    <div>
      <h3>Docker compose</h3>
      <a href="#docker-compose">
        
      </a>
    </div>
    <p>When our development team was still small, we used a docker-compose file to orchestrate the services needed to develop Argo Tunnel. There was no native support for hot reload, so every time an engineer made a change, they had to restart their dev-stack.</p><p>We could hack around it to hot reload with docker-compose, but when that failed, we had to waste time debugging the internals of Docker. As the team grew, we realized we needed to invest in improving our dev stack.</p><p>At the same time Cloudflare was in the process of migrating from Marathon to kubernetes (k8s). We set out to find a tool that could detect changes in source code and automatically upgrade pods with new images.</p>
    <div>
      <h3>Skaffold + Minikube</h3>
      <a href="#skaffold-minikube">
        
      </a>
    </div>
    <p>Initially <a href="https://skaffold.dev/docs/quickstart/">Skaffold</a> seemed to match the criteria. It watches for change in source code, builds new images and deploys applications onto any k8s. Following Skaffold’s tutorial, we picked minikube as the local k8s, but together they didn’t meet our expectations. Port forwarding wasn’t stable, we got frequent connections refused or timeout.</p><p>In addition, iteration time didn’t improve, because spinning up minikube takes a long time and it doesn’t use the host's docker registry and so it can’t take advantage of caching. At this point we considered reverting back to using docker compose, but the k8s ecosystem is booming, so we did some more research.</p>
    <div>
      <h3>Tilt + Docker for mac k8s</h3>
      <a href="#tilt-docker-for-mac-k8s">
        
      </a>
    </div>
    <p>Eventually we found a great <a href="https://docs.tilt.dev/choosing_clusters.html">blog post</a> from Tilt comparing different options for local k8s, and they seem to be solving the exact problem we are having. Tilt is a tool that makes local development on k8s easier. It detects changes in local sources and updates your deployment accordingly.</p><p>In addition, it supports live updates without having to rebuild containers, a process that used to take around 20 minutes. With live updates, we can copy the newest source into the container, run <code>cargo build</code> within the container, and restart the service without building a new image. Following Tilt’s blog post, we switched to Docker for Mac’s built-in k8s. Combining Tilt and Docker for Mac k8s, we finally have a development environment that meets our needs.</p><p>Rust services that could take 20 minutes to rebuild now take less than a minute.</p>
    <div>
      <h2>Collaborating with a distributed team</h2>
      <a href="#collaborating-with-a-distributed-team">
        
      </a>
    </div>
    <p>We reached a much happier state with our dev-stack, but one problem remained: we needed a way to share it. As our teams became distributed with people in Austin, Lisbon and Seattle, we needed better ways to help each other.</p><p>One day, I was helping our newest member understand an error observed in <code>cloudflared</code>, Argo Tunnel’s command line interface (CLI) client. I knew the error could either originate from the backend service or a mock API gateway service, but I couldn’t tell for sure without looking at logs.</p><p>To get them, I had to ask our new teammate to manually send me the logs of the two services. By the time I discovered the source of the error, reviewed the deployment manifest, and determined the error was caused by a secret set as an empty string, two full hours had elapsed!</p><p>I could have solved this in minutes if I had remote access to her development environment. That’s exactly what Argo Tunnel can do! Argo Tunnel provides remote access to development environments by creating secure outbound-only connections to Cloudflare’s edge network from a resource exposing it to the Internet. That model helps protect servers and resources from being vulnerable to attack by an exposed IP address.</p><p>I can use Argo Tunnel to expose a remote dev environment, but the information stored is sensitive. Once exposed, we needed a way to prevent users from reaching it unless they are an authenticated member of my team. Cloudflare Access solves that challenge. Access sits in front of the hostname powered by Argo Tunnel and checks for identity on every request. I can combine both services to share the dev-stack details with the rest of the team in a secure deployment.</p><p>The built-in k8s dashboard gives a great overview of the dev-stack, with the list of pods, deployments, services, config maps, secrets, etc. It also allows us to inspect pod logs and exec into a container. By default, it is secured by a token that changes every time the service restarts. To avoid the hassle of distributing the service token to everyone on the team, we wrote a simple reverse proxy that injects the service token in the authorization header before forwarding requests to the dashboard service.</p><p>Then we run Argo Tunnel as a sidecar to this reverse proxy, so it is accessible from the Internet. Finally, to make sure no random person can see our dashboard, we put an Access policy that only allows team members to access the hostname.</p><p>The request flow is eyeball -&gt; Access -&gt; Argo Tunnel -&gt; reverse proxy -&gt; dashboard service</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5WXj2BDFGXX2TzlcH5b5y0/6ec84bb848e272a0619d39eb468988dc/image5-3.png" />
            
            </figure>
    <div>
      <h2>Working example</h2>
      <a href="#working-example">
        
      </a>
    </div>
    <p>Your team can use the same model to develop remotely. Here’s how to get started.</p><ol><li><p>Start a local k8s cluster. <a href="https://docs.tilt.dev/choosing_clusters.html">https://docs.tilt.dev/choosing_clusters.html</a> offers great advice in choosing a local cluster based on your OS and experience with k8s</p></li></ol>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3MAKtp7Q2gNCiC4o60KyaX/1455eca1f8f75c44a040632988503f5a/image3-7.png" />
            
            </figure><p>2. Enable dashboard service:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1aiXLesCpLni724ZNznPkp/411535dcd0afab9a76ce5563ee63ac3d/image2-8.png" />
            
            </figure><p>3. Create a reverse proxy that will inject the service token of the kubernetes-dashboard service account in the Authorization header before forwarding requests to kubernetes dashboard service</p>
            <pre><code>package main
 
import (
   "crypto/tls"
   "fmt"
   "net/http"
   "net/http/httputil"
   "net/url"
   "os"
)
 
func main() {
   config, err := loadConfigFromEnv()
   if err != nil {
       panic(err)
   }
   reverseProxy := httputil.NewSingleHostReverseProxy(config.proxyURL)
   // The default Director builds the request URL. We want our custom Director to add Authorization, in
   // addition to building the URL
   singleHostDirector := reverseProxy.Director
   reverseProxy.Director = func(r *http.Request) {
       singleHostDirector(r)
       r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", config.token))
       fmt.Println("request header", r.Header)
       fmt.Println("request host", r.Host)
       fmt.Println("request ULR", r.URL)
   }
   reverseProxy.Transport = &amp;http.Transport{
       TLSClientConfig: &amp;tls.Config{
           InsecureSkipVerify: true,
       },
   }
   server := http.Server{
       Addr:    config.listenAddr,
       Handler: reverseProxy,
   }
   server.ListenAndServe()
}
 
type config struct {
   listenAddr string
   proxyURL   *url.URL
   token      string
}
 
func loadConfigFromEnv() (*config, error) {
   listenAddr, err := requireEnv("LISTEN_ADDRESS")
   if err != nil {
       return nil, err
   }
   proxyURLStr, err := requireEnv("DASHBOARD_PROXY_URL")
   if err != nil {
       return nil, err
   }
   proxyURL, err := url.Parse(proxyURLStr)
   if err != nil {
       return nil, err
   }
   token, err := requireEnv("DASHBOARD_TOKEN")
   if err != nil {
       return nil, err
   }
   return &amp;config{
       listenAddr: listenAddr,
       proxyURL:   proxyURL,
       token:      token,
   }, nil
}
 
func requireEnv(key string) (string, error) {
   result := os.Getenv(key)
   if result == "" {
       return "", fmt.Errorf("%v not provided", key)
   }
   return result, nil
}
</code></pre>
            <p>4. Create an Argo Tunnel sidecar to expose this reverse proxy</p>
            <pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
 name: dashboard-auth-proxy
 namespace: kubernetes-dashboard
 labels:
   app: dashboard-auth-proxy
spec:
 replicas: 1
 selector:
   matchLabels:
     app: dashboard-auth-proxy
 template:
   metadata:
     labels:
       app: dashboard-auth-proxy
   spec:
     containers:
       - name: dashboard-tunnel
         # Image from https://hub.docker.com/r/cloudflare/cloudflared
         image: cloudflare/cloudflared:2020.8.0
         command: ["cloudflared", "tunnel"]
         ports:
           - containerPort: 5000
         env:
           - name: TUNNEL_URL
             value: "http://localhost:8000"
           - name: NO_AUTOUPDATE
             value: "true"
           - name: TUNNEL_METRICS
             value: "localhost:5000"
       # dashboard-proxy is a proxy that injects the dashboard token into Authorization header before forwarding
       # the request to dashboard_proxy service
       - name: dashboard-auth-proxy
         image: dashboard-auth-proxy
         ports:
           - containerPort: 8000
         env:
           - name: LISTEN_ADDRESS
             value: localhost:8000
           - name: DASHBOARD_PROXY_URL
             value: https://kubernetes-dashboard
           - name: DASHBOARD_TOKEN
             valueFrom:
               secretKeyRef:
                 name: ${TOKEN_NAME}
                 key: token</code></pre>
            <p>5. Find out the URL to access your dashboard from Tilt’s UI</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3aubLRXDHG7YRQDWir8wYd/216a31d185d112b6997b4a0c59ec4359/image1-17.png" />
            
            </figure><p>6. Share the URL with your collaborators so they can access your dashboard anywhere they are through the tunnel!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/61Z1uIFPgr41mOw524Z0tC/ac3cf2116f14d74394b173061f834808/image4-7.png" />
            
            </figure><p>You can find the source code for the example in <a href="https://github.com/cloudflare/argo-tunnel-examples/tree/master/sharing-k8s-dashboard">https://github.com/cloudflare/argo-tunnel-examples/tree/master/sharing-k8s-dashboard</a></p><p>If this sounds like a team you want to be on, we are <a href="https://www.cloudflare.com/careers/">hiring</a>!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Access]]></category>
            <category><![CDATA[Remote Work]]></category>
            <category><![CDATA[Cloudflare Zero Trust]]></category>
            <category><![CDATA[Cloudflare Tunnel]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Zero Trust]]></category>
            <guid isPermaLink="false">2Qn6wupi9o8CewrHpyEz6</guid>
            <dc:creator>Chung-Ting Huang</dc:creator>
        </item>
    </channel>
</rss>