{"id":5038,"date":"2026-08-30T03:50:38","date_gmt":"2026-08-29T18:50:38","guid":{"rendered":"https:\/\/donguri3.net\/server-tech\/ssh-multi-hop-port-forwarding-2\/"},"modified":"2026-08-30T03:50:39","modified_gmt":"2026-08-29T18:50:39","slug":"ssh-multi-hop-port-forwarding","status":"publish","type":"post","link":"https:\/\/donguri3.net\/en\/server-tech\/linux-server-network\/ssh-multi-hop-port-forwarding\/","title":{"rendered":"How to Implement Multi-Hop Port Forwarding in SSH via a One-Liner Command and Configuration File"},"content":{"rendered":"<p class=\"\">In secure network environments, SSH port forwarding is extremely useful for safely accessing internal servers. When you need to reach a destination by hopping through multiple servers, it traditionally required combining multiple SSH commands. In this article, we will introduce how to simply achieve multi-hop port forwarding using advanced SSH features, either with a single-line command or through settings in <code>~\/.ssh\/config<\/code>.<\/p>\n<h2 class=\"\">Network Architecture<\/h2>\n<p class=\"\"><span class=\"relative -mx-px my-[-0.2rem] rounded-sm px-px py-[0.2rem]\">We assume the following network environment.<\/span>\u200b<\/p>\n<ul>\n<li class=\"\">\n<p class=\"\"><strong>Client PC<\/strong>: <span class=\"relative -mx-px my-[-0.2rem] rounded-sm px-px py-[0.2rem]\">Cannot directly access test2 or test3.<\/span>\u200b<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>test1 Server<\/strong>: <span class=\"relative -mx-px my-[-0.2rem] rounded-sm px-px py-[0.2rem]\">Accessible from the client PC, but cannot directly access test3.<\/span>\u200b<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>test2 Server<\/strong>: <span class=\"relative -mx-px my-[-0.2rem] rounded-sm px-px py-[0.2rem]\">Accessible from test1, and can access test3.<\/span>\u200b<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>test3 Server<\/strong>: <span class=\"relative -mx-px my-[-0.2rem] rounded-sm px-px py-[0.2rem]\">The server you ultimately want to connect to via RDP.<br \/><\/span><\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2.jpg\"><img decoding=\"async\" class=\"aligncenter size-large wp-image-1255\" src=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-1024x576.jpg\" alt=\"Network Architecture 2\" width=\"1024\" height=\"576\" srcset=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-1024x576.jpg 1024w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-300x169.jpg 300w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-768x432.jpg 768w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-1536x864.jpg 1536w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-320x180.jpg 320w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-530x298.jpg 530w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-565x318.jpg 565w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-710x399.jpg 710w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-725x408.jpg 725w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2-160x90.jpg 160w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/2.jpg 1920w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<h2 class=\"\">The Challenge<\/h2>\n<p class=\"\">Let&#8217;s consider a case where there are two stages of port forwarding configuration.<\/p>\n<ol>\n<li class=\"\">\n<p class=\"\"><strong>Example of connecting to test2 via a bastion host (test1) and securing a local port<\/strong><\/p>\n<pre>ssh -L 10022:test2.example.com:22 user1@test1.example.com<\/pre>\n<p class=\"\">\u2192 Maps the SSH port of test2 to local port 10022 via test1.<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>Example of forwarding to the RDP port of test3 via test2<\/strong><\/p>\n<pre>ssh -L 13389:test3.example.com:3389 user2@localhost -p 10022<\/pre>\n<p class=\"\">\u2192 Connects to local port 10022 and further forwards traffic from test2 to port 3389 on test3.<\/p>\n<\/li>\n<\/ol>\n<p class=\"\">With this setup, commands must be executed at each stage, which is cumbersome in terms of management and automation. Furthermore, in proxy environments, it was necessary to prepend a backslash (<code>\\\\<\/code>) to the hostname to ensure that DNS resolution for <code>test3.example.com<\/code> was performed on the connection target (test2) side.<br \/>\n<a href=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3.jpg\"><img decoding=\"async\" class=\"aligncenter size-large wp-image-1256\" src=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-1024x576.jpg\" alt=\"Multi-hop SSH Port Forward\" width=\"1024\" height=\"576\" srcset=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-1024x576.jpg 1024w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-300x169.jpg 300w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-768x432.jpg 768w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-1536x864.jpg 1536w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-320x180.jpg 320w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-530x298.jpg 530w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-565x318.jpg 565w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-710x399.jpg 710w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-725x408.jpg 725w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3-160x90.jpg 160w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/3.jpg 1920w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<h2 class=\"\">Achieving It with a One-Liner Command<\/h2>\n<p class=\"\">By combining SSH&#8217;s <strong>ProxyJump<\/strong> and <strong>LocalForward<\/strong> options, you can accomplish the above procedure in a single SSH command. A specific command example is shown below.<\/p>\n<pre>ssh -J user1@test1.example.com user2@test2.example.com -L 13389:\\\\test3.example.com:3389<\/pre>\n<h3 class=\"\">Explanation of Command Options<\/h3>\n<ul>\n<li class=\"\">\n<p class=\"\"><strong>-J <a class=\"cursor-pointer\" rel=\"noopener\">user1@test1.example.com<\/a><\/strong><br \/>\u2192 First connects to <code>test1.example.com<\/code> (the bastion server) as <code>user1<\/code>, and jumps from there to the next host.<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong><a class=\"cursor-pointer\" rel=\"noopener\">user2@test2.example.com<\/a><\/strong><br \/>\u2192 The final destination host is <code>test2.example.com<\/code>, where authentication is performed as <code>user2<\/code>.<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>-L 13389:\\\\test3.example.com:3389<\/strong><br \/>\u2192 Local port 13389 is forwarded on the test2 side to <code>\\\\test3.example.com:3389<\/code>. By prepending a backslash (<code>\\\\<\/code>) here, DNS resolution for <code>test3.example.com<\/code> is performed on test2, allowing you to successfully reach the destination even in a proxy environment.<\/p>\n<\/li>\n<\/ul>\n<p class=\"\">Executing this as a one-liner simplifies multiple SSH connections and port forwarding settings, making it easy to incorporate into scripts.<br \/>\n<a href=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3.jpg\"><img decoding=\"async\" class=\"aligncenter size-large wp-image-1257\" src=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-1024x576.jpg\" alt=\"Multi-hop SSH Port Forward with ProxyJump\" width=\"1024\" height=\"576\" srcset=\"https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-1024x576.jpg 1024w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-300x169.jpg 300w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-768x432.jpg 768w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-1536x864.jpg 1536w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-320x180.jpg 320w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-530x298.jpg 530w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-565x318.jpg 565w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-710x399.jpg 710w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-725x408.jpg 725w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3-160x90.jpg 160w, https:\/\/donguri3.net\/wp-content\/uploads\/2025\/04\/36b5ae51908ce2bd7184fcd361ba1cc3.jpg 1920w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<h2 class=\"\">Configuring in ~\/.ssh\/config<\/h2>\n<p class=\"\">By writing the same connection configuration into your SSH configuration file, you can simply run the command as <code>ssh test3rdp<\/code>. Here is a configuration example:<\/p>\n<pre># Connection settings for the bastion server (test1)\nHost test1\n    HostName test1.example.com\n    User user1\n\n# Connection settings for the relay server (test2)\nHost test2\n    HostName test2.example.com\n    User user2\n    ProxyJump test1\n\n# Settings for the final RDP connection (test3rdp)\nHost test3rdp\n    HostName test2.example.com\n    User user2\n    ProxyJump test1\n    LocalForward 13389 \\\\test3.example.com:3389\n\n# Without separate bastion server connection entry\nHost test3rdp2\n    HostName test2.example.com\n    User user2\n    ProxyJump user1@test1\n    LocalForward 13389 \\\\test3.example.com:3389<\/pre>\n<h3 class=\"\">Key Points of the Configuration<\/h3>\n<ul>\n<li class=\"\">\n<p class=\"\"><strong>Host test1 and test2<\/strong><br \/>\u2192 Sets up the information for the bastion server (test1) and the relay server (test2) individually. The test2 configuration explicitly states to connect via test1 using <code>ProxyJump<\/code>.<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>Host test3rdp<\/strong><br \/>\u2192 This is the entry for actually performing the RDP connection, where <code>HostName<\/code> is set to <code>test2.example.com<\/code>.<br \/>\u2192 By prepending a backslash to the <code>LocalForward<\/code> destination, name resolution for <code>test3.example.com<\/code> is performed on the test2 side.<\/p>\n<\/li>\n<\/ul>\n<p class=\"\">By writing such settings into <code>~\/.ssh\/config<\/code>, you can achieve fully automated connections from the command line simply by typing:<\/p>\n<pre>ssh test3rdp<\/pre>\n<h2 class=\"\">Benefits of This Method<\/h2>\n<ul>\n<li class=\"\">\n<p class=\"\"><strong>Simplicity:<\/strong> Complex port forwarding configurations can be consolidated into a single-line command or a simple config entry, making management effortless.<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>Flexibility:<\/strong> Even in connection environments traversing multiple servers, authentication credentials and detailed transfer settings for each server can be specified individually, facilitating easier troubleshooting.<\/p>\n<\/li>\n<li class=\"\">\n<p class=\"\"><strong>Easy Automation:<\/strong> Simple to incorporate into scripts and routine tasks, streamlining remote environment access.<\/p>\n<\/li>\n<\/ul>\n<h2 class=\"\">Conclusion<\/h2>\n<p class=\"\">By utilizing advanced SSH options, we were able to consolidate multi-stage port forwarding configurations into a single-line command or an entry in <code>~\/.ssh\/config<\/code>. Even in proxy environments or complex network architectures, controlling the timing of name resolution allows for simpler and safer connections. We hope this article helps improve the efficiency of your SSH configurations.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In secure network environments, SSH port forwarding is extremely useful for safely accessing internal servers. [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":1257,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_locale":"en_US","_original_post":"https:\/\/donguri3.net\/?p=1253","footnotes":""},"categories":[1170],"tags":[488,663,10,381,99,1040],"class_list":["post-5038","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux-server-network","tag-ssh","tag-663","tag-server","tag-381","tag-99","tag-1040","en-US"],"_links":{"self":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts\/5038","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/comments?post=5038"}],"version-history":[{"count":1,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts\/5038\/revisions"}],"predecessor-version":[{"id":5041,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/posts\/5038\/revisions\/5041"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/media\/1257"}],"wp:attachment":[{"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/media?parent=5038"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/categories?post=5038"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/donguri3.net\/wp-json\/wp\/v2\/tags?post=5038"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}