From bc6322e324a6e78a256795d77223df516b6aa7bf Mon Sep 17 00:00:00 2001 From: Eric Rodrigues Pires Date: Mon, 31 Mar 2025 00:40:52 -0300 Subject: [PATCH] Improve book --- book/.gitignore | 0 book/diagrams/example_flow.excalidraw | 907 ++++++++++++++++++++++++ book/diagrams/how_it_works.excalidraw | 205 +++--- book/src/SUMMARY.md | 2 +- book/src/compiling_from_source.md | 36 +- book/src/configuration.md | 6 +- book/src/custom_domains.md | 2 +- book/src/digitalocean_dns.png | Bin 0 -> 57170 bytes book/src/docker_compose.md | 6 +- book/src/example_flow.svg | 2 + book/src/exposing_your_first_service.md | 10 +- book/src/faq.md | 4 + book/src/how_it_works.svg | 2 +- book/src/introduction.md | 6 +- book/src/local_forwarding.md | 8 +- book/src/quick_start.md | 49 +- book/src/technical_overview.md | 24 + book/src/tls_support.md | 2 + 18 files changed, 1123 insertions(+), 148 deletions(-) mode change 100644 => 100755 book/.gitignore create mode 100644 book/diagrams/example_flow.excalidraw create mode 100644 book/src/digitalocean_dns.png create mode 100644 book/src/example_flow.svg diff --git a/book/.gitignore b/book/.gitignore old mode 100644 new mode 100755 diff --git a/book/diagrams/example_flow.excalidraw b/book/diagrams/example_flow.excalidraw new file mode 100644 index 0000000..f424974 --- /dev/null +++ b/book/diagrams/example_flow.excalidraw @@ -0,0 +1,907 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "BU7M0am1OF7YXOdJaMUyR", + "type": "rectangle", + "x": -39.26104726890509, + "y": 402.49999999999994, + "width": 219, + "height": 108.00000000000004, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1P", + "roundness": { + "type": 3 + }, + "seed": 1659367218, + "version": 277, + "versionNonce": 48311794, + "isDeleted": false, + "boundElements": [], + "updated": 1743390017964, + "link": null, + "locked": false + }, + { + "id": "lIKguSTb5wDBx0ltvupax", + "type": "arrow", + "x": 210.37385725600365, + "y": 413.42719343189395, + "width": 289.14938459087983, + "height": 38.927193431893954, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Q", + "roundness": { + "type": 2 + }, + "seed": 1581725938, + "version": 619, + "versionNonce": 247220466, + "isDeleted": false, + "boundElements": [], + "updated": 1743390195012, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -73.63490452490873, + -38.927193431893954 + ], + [ + -214.37385725600365, + -38.427193431893954 + ], + [ + -289.14938459087983, + -6.764589088379324 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "4pmmEwP4wj77hMkIB_fVb", + "focus": -0.360109088124097, + "gap": 10.764435989006659 + }, + "endBinding": { + "elementId": "zVCqnCRG6fib75dzvUL1I", + "focus": -0.042725826557456145, + "gap": 11.087884399589388 + }, + "startArrowhead": "arrow", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "7qO8V8e4TIHzr8yi6ECOf", + "type": "text", + "x": 54.763952826462344, + "y": 338.5, + "width": 11.949999809265137, + "height": 35, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1R", + "roundness": null, + "seed": 1251429042, + "version": 161, + "versionNonce": 87102898, + "isDeleted": false, + "boundElements": [], + "updated": 1743390191298, + "link": null, + "locked": false, + "text": "1", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "9Xfi-le1xJ-cd4WWcOUo0", + "type": "arrow", + "x": 213.2556344920415, + "y": 474.48950393245013, + "width": 291.0854249794058, + "height": 2.014432006777895, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1S", + "roundness": { + "type": 2 + }, + "seed": 1585390706, + "version": 277, + "versionNonce": 1511842098, + "isDeleted": false, + "boundElements": [], + "updated": 1743390070391, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -291.0854249794058, + 2.014432006777895 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": "arrow", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "XenjLZwtX7CEIW9DqpQ4T", + "type": "arrow", + "x": -75.11000655699667, + "y": 432.49702119548033, + "width": 287.54053966439216, + "height": 1.0089141742610082, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1T", + "roundness": { + "type": 2 + }, + "seed": 1673905714, + "version": 307, + "versionNonce": 200178734, + "isDeleted": false, + "boundElements": [], + "updated": 1743390066282, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 287.54053966439216, + 1.0089141742610082 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": "arrow", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "6CdD9GoSTA0eAMbVWEKDS", + "type": "text", + "x": 24.42228708168085, + "y": 513.5, + "width": 102.63333129882812, + "height": 25, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1U", + "roundness": null, + "seed": 1397895154, + "version": 209, + "versionNonce": 1307326130, + "isDeleted": false, + "boundElements": [], + "updated": 1743390017964, + "link": null, + "locked": false, + "text": "SSH tunnel", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "SSH tunnel", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "yCJVKg6Z9EnCmYLrmWj2J", + "type": "text", + "x": 57.54728612800653, + "y": 402, + "width": 16.383333206176758, + "height": 35, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1V", + "roundness": null, + "seed": 1898668466, + "version": 203, + "versionNonce": 5673074, + "isDeleted": false, + "boundElements": [], + "updated": 1743390017964, + "link": null, + "locked": false, + "text": "4", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "4", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "OYU8jjk1_iuZEK9q38nxW", + "type": "text", + "x": 59.08895311256464, + "y": 476.5, + "width": 17.299999237060547, + "height": 35, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1W", + "roundness": null, + "seed": 1752718194, + "version": 172, + "versionNonce": 802140722, + "isDeleted": false, + "boundElements": [], + "updated": 1743390017964, + "link": null, + "locked": false, + "text": "5", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "5", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "4pmmEwP4wj77hMkIB_fVb", + "type": "rectangle", + "x": 221, + "y": 387.5, + "width": 300, + "height": 120.99999999999999, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1X", + "roundness": { + "type": 3 + }, + "seed": 1081315698, + "version": 816, + "versionNonce": 1844611954, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "ePp2-mH0FmeRkuvmobDM-" + }, + { + "id": "jFGXYZSVS2_EDGOKNI_3l", + "type": "arrow" + }, + { + "id": "lIKguSTb5wDBx0ltvupax", + "type": "arrow" + } + ], + "updated": 1743390184048, + "link": null, + "locked": false + }, + { + "id": "ePp2-mH0FmeRkuvmobDM-", + "type": "text", + "x": 311.3833351135254, + "y": 430.5, + "width": 119.23332977294922, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Y", + "roundness": null, + "seed": 1185190706, + "version": 180, + "versionNonce": 224843314, + "isDeleted": false, + "boundElements": [], + "updated": 1743390024699, + "link": null, + "locked": false, + "text": "Sandhole", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4pmmEwP4wj77hMkIB_fVb", + "originalText": "Sandhole", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "jFGXYZSVS2_EDGOKNI_3l", + "type": "arrow", + "x": 507.38272812109227, + "y": 409.022182853632, + "width": 281.9309852143188, + "height": 42.62535315050255, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Z", + "roundness": { + "type": 2 + }, + "seed": 1791974642, + "version": 1380, + "versionNonce": 1437682085, + "isDeleted": false, + "boundElements": [], + "updated": 1743390493976, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -71.38272812109224, + -36.388816054115324 + ], + [ + -190.38272812109224, + -36.388816054115324 + ], + [ + -281.9309852143188, + 6.236537096387224 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "a1fMvw6bGzKY6D2wEFjF3", + "focus": 0.5601990672951844, + "gap": 8.55128580648237 + }, + "endBinding": { + "elementId": "4pmmEwP4wj77hMkIB_fVb", + "focus": 0, + "gap": 4.393657620002837 + }, + "startArrowhead": "arrow", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "K_3S0B-Ui6GNkVo5AMm3Z", + "type": "text", + "x": 365.19999980926514, + "y": 334.5, + "width": 19.600000381469727, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1a", + "roundness": null, + "seed": 1518005938, + "version": 156, + "versionNonce": 1950207214, + "isDeleted": false, + "boundElements": [], + "updated": 1743390119289, + "link": null, + "locked": false, + "text": "2", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "a1fMvw6bGzKY6D2wEFjF3", + "type": "rectangle", + "x": 510.5, + "y": 400.5, + "width": 26.000000000000018, + "height": 93.00000000000004, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1b", + "roundness": { + "type": 3 + }, + "seed": 178580850, + "version": 434, + "versionNonce": 2066287077, + "isDeleted": false, + "boundElements": [ + { + "id": "jFGXYZSVS2_EDGOKNI_3l", + "type": "arrow" + } + ], + "updated": 1743390497811, + "link": null, + "locked": false + }, + { + "id": "uzT8vL-7-z9R2B4FOiRTx", + "type": "text", + "x": 493.1499996185303, + "y": 332.5, + "width": 59.70000076293945, + "height": 50, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1c", + "roundness": null, + "seed": 2051698482, + "version": 166, + "versionNonce": 1865462507, + "isDeleted": false, + "boundElements": [], + "updated": 1743390497811, + "link": null, + "locked": false, + "text": "HTTP\nproxy", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "HTTP\nproxy", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "zVCqnCRG6fib75dzvUL1I", + "type": "rectangle", + "x": -269.5, + "y": 403.5, + "width": 185, + "height": 97.00000000000003, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1d", + "roundness": { + "type": 3 + }, + "seed": 1365163698, + "version": 1412, + "versionNonce": 1817695986, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "pRfrzrggFTJTz-BUO999s" + }, + { + "id": "lIKguSTb5wDBx0ltvupax", + "type": "arrow" + } + ], + "updated": 1743390184048, + "link": null, + "locked": false + }, + { + "id": "pRfrzrggFTJTz-BUO999s", + "type": "text", + "x": -252.29166412353516, + "y": 427, + "width": 150.5833282470703, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1e", + "roundness": null, + "seed": 70785138, + "version": 779, + "versionNonce": 1777534450, + "isDeleted": false, + "boundElements": [], + "updated": 1743390046660, + "link": null, + "locked": false, + "text": "Client A\n(HTTP service)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "zVCqnCRG6fib75dzvUL1I", + "originalText": "Client A\n(HTTP service)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "AzeVD0HCBmQ_YdzKkTAtj", + "type": "rectangle", + "x": 651.5, + "y": 399.5, + "width": 185, + "height": 97.00000000000003, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1l", + "roundness": { + "type": 3 + }, + "seed": 765278766, + "version": 1585, + "versionNonce": 9799022, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "T2yp443rMJNLEEU19eKLN" + } + ], + "updated": 1743390103391, + "link": null, + "locked": false + }, + { + "id": "T2yp443rMJNLEEU19eKLN", + "type": "text", + "x": 675.5833358764648, + "y": 423, + "width": 136.8333282470703, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1m", + "roundness": null, + "seed": 781304942, + "version": 954, + "versionNonce": 1792976814, + "isDeleted": false, + "boundElements": [], + "updated": 1743390103391, + "link": null, + "locked": false, + "text": "Client B\n(Web browser)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "AzeVD0HCBmQ_YdzKkTAtj", + "originalText": "Client B\n(Web browser)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "GHivxhyyZK0M12XKN36WS", + "type": "arrow", + "x": 546.4084386254126, + "y": 418.63127836235196, + "width": 99.40457544251265, + "height": 0.30912525563323356, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1n", + "roundness": null, + "seed": 1545514098, + "version": 166, + "versionNonce": 2060483339, + "isDeleted": false, + "boundElements": [], + "updated": 1743390497401, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 99.40457544251265, + 0.30912525563323356 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": "arrow", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "1X6El9wlKn69JnUl407j3", + "type": "arrow", + "x": 643.1893286889688, + "y": 472.67548869085056, + "width": 97.00234275689405, + "height": 0.977916056804844, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1o", + "roundness": null, + "seed": 338972210, + "version": 262, + "versionNonce": 886099749, + "isDeleted": false, + "boundElements": [], + "updated": 1743390497401, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -97.00234275689405, + 0.977916056804844 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": "arrow", + "endArrowhead": null, + "elbowed": false + }, + { + "id": "c1J4xxMGPHjAKEOpav-iO", + "type": "text", + "x": 590.194000151021, + "y": 386.5, + "width": 17.016666412353516, + "height": 35, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1p", + "roundness": null, + "seed": 78624754, + "version": 123, + "versionNonce": 1489166763, + "isDeleted": false, + "boundElements": [], + "updated": 1743390497401, + "link": null, + "locked": false, + "text": "3", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "K3tXRE3TkHFHVnTcFxbEk", + "type": "text", + "x": 589.7440003417559, + "y": 474.5, + "width": 17.91666603088379, + "height": 35, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#a5d8ff", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1q", + "roundness": null, + "seed": 764930482, + "version": 147, + "versionNonce": 848667269, + "isDeleted": false, + "boundElements": [], + "updated": 1743390497401, + "link": null, + "locked": false, + "text": "6", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "6", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/book/diagrams/how_it_works.excalidraw b/book/diagrams/how_it_works.excalidraw index dc22983..f111197 100644 --- a/book/diagrams/how_it_works.excalidraw +++ b/book/diagrams/how_it_works.excalidraw @@ -28,7 +28,7 @@ "version": 243, "versionNonce": 539285067, "isDeleted": false, - "boundElements": null, + "boundElements": [], "updated": 1743364407315, "link": null, "locked": false @@ -36,8 +36,8 @@ { "id": "XXkcphDBkS-7gjSOujTUm", "type": "rectangle", - "x": 222, - "y": 119, + "x": 255, + "y": 124, "width": 256, "height": 246.00000000000003, "angle": 0, @@ -55,19 +55,19 @@ "type": 3 }, "seed": 1114775845, - "version": 410, - "versionNonce": 1916450635, + "version": 479, + "versionNonce": 1131296638, "isDeleted": false, "boundElements": [], - "updated": 1743363899946, + "updated": 1743388038798, "link": null, "locked": false }, { "id": "fOMZ3bas6KENlE-dMH_59", "type": "diamond", - "x": 206, - "y": 162, + "x": 239, + "y": 167, "width": 29, "height": 46, "angle": 0, @@ -85,8 +85,8 @@ "type": 2 }, "seed": 637289771, - "version": 142, - "versionNonce": 1444210539, + "version": 211, + "versionNonce": 569808830, "isDeleted": false, "boundElements": [ { @@ -98,15 +98,15 @@ "type": "arrow" } ], - "updated": 1743364194943, + "updated": 1743388038798, "link": null, "locked": false }, { "id": "R2V0dILx8W4I1WDCDR0ML", "type": "diamond", - "x": 206.5, - "y": 219, + "x": 239.5, + "y": 224, "width": 29, "height": 46, "angle": 0, @@ -124,19 +124,19 @@ "type": 2 }, "seed": 1034867275, - "version": 161, - "versionNonce": 663273157, + "version": 230, + "versionNonce": 1151746110, "isDeleted": false, "boundElements": [], - "updated": 1743364329345, + "updated": 1743388038798, "link": null, "locked": false }, { "id": "qSW7cdA88CRCn0oq6Upcd", "type": "diamond", - "x": 206.5, - "y": 277, + "x": 239.5, + "y": 282, "width": 29, "height": 46, "angle": 0, @@ -154,19 +154,24 @@ "type": 2 }, "seed": 1245198757, - "version": 200, - "versionNonce": 743841195, + "version": 270, + "versionNonce": 488186238, "isDeleted": false, - "boundElements": [], - "updated": 1743364323546, + "boundElements": [ + { + "id": "F8S5vF78PzGEI884_BgcW", + "type": "arrow" + } + ], + "updated": 1743388072415, "link": null, "locked": false }, { "id": "XgpsNQlUwNjurkNauYGbk", "type": "text", - "x": 239.01666641235352, - "y": 179.5, + "x": 272.0166664123535, + "y": 184.5, "width": 97.96666717529297, "height": 20, "angle": 0, @@ -182,11 +187,11 @@ "index": "b0e", "roundness": null, "seed": 1706703717, - "version": 161, - "versionNonce": 1564078597, + "version": 230, + "versionNonce": 619038910, "isDeleted": false, - "boundElements": null, - "updated": 1743363740416, + "boundElements": [], + "updated": 1743388038798, "link": null, "locked": false, "text": ":2222 (SSH)", @@ -202,8 +207,8 @@ { "id": "zv9gyzcfTx9jlQfuEy4vq", "type": "text", - "x": 238.66666793823242, - "y": 231, + "x": 271.6666679382324, + "y": 236, "width": 92.66666412353516, "height": 20, "angle": 0, @@ -219,11 +224,11 @@ "index": "b0f", "roundness": null, "seed": 439688805, - "version": 119, - "versionNonce": 1593996133, + "version": 188, + "versionNonce": 686424318, "isDeleted": false, - "boundElements": null, - "updated": 1743364328903, + "boundElements": [], + "updated": 1743388038798, "link": null, "locked": false, "text": ":80 (HTTP)", @@ -239,8 +244,8 @@ { "id": "BGUs894bGmKV8xcDAt8Vk", "type": "text", - "x": 239.85833358764648, - "y": 290, + "x": 272.8583335876465, + "y": 295, "width": 110.28333282470703, "height": 20, "angle": 0, @@ -256,11 +261,11 @@ "index": "b0g", "roundness": null, "seed": 553622213, - "version": 163, - "versionNonce": 1931336075, + "version": 232, + "versionNonce": 1211969854, "isDeleted": false, "boundElements": [], - "updated": 1743364327101, + "updated": 1743388038798, "link": null, "locked": false, "text": ":443 (HTTPS)", @@ -319,8 +324,8 @@ { "id": "UGmoxHwIZcIESL4nYDRZ0", "type": "text", - "x": 284.93333435058594, - "y": 86.5, + "x": 317.93333435058594, + "y": 91.5, "width": 119.23332977294922, "height": 35, "angle": 0, @@ -336,11 +341,11 @@ "index": "b0i", "roundness": null, "seed": 594181029, - "version": 127, - "versionNonce": 44731621, + "version": 196, + "versionNonce": 775969150, "isDeleted": false, - "boundElements": null, - "updated": 1743363902288, + "boundElements": [], + "updated": 1743388038798, "link": null, "locked": false, "text": "Sandhole", @@ -393,10 +398,10 @@ { "id": "_K0Xwx0HT9zD0iH-JIfy7", "type": "arrow", - "x": 585.9410490072286, - "y": 65.23493367044395, - "width": 379.10066994380827, - "height": 100.96548708305139, + "x": 583.9410490072286, + "y": 65.23493367044397, + "width": 391.10066994380827, + "height": 109.76506632955603, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -410,11 +415,11 @@ "index": "b0k", "roundness": null, "seed": 1864090149, - "version": 1882, - "versionNonce": 1086209541, + "version": 2265, + "versionNonce": 9421950, "isDeleted": false, - "boundElements": null, - "updated": 1743365411262, + "boundElements": [], + "updated": 1743388118195, "link": null, "locked": false, "points": [ @@ -423,24 +428,28 @@ 0 ], [ - -379.10066994380827, - 2.7582426159375473 + -391.10066994380827, + 3.758242615937533 ], [ - -369.79514971545984, - 100.96548708305139 + -390.94104900722857, + 109.76506632955603 + ], + [ + -343.10515506358695, + 109.67519508083019 ] ], "lastCommittedPoint": null, "startBinding": { "elementId": "WmSxpGHH-CA1ShvBKpsp7", - "focus": 0.5487188693058546, - "gap": 6.936945256178183 + "focus": 0.5513666898717178, + "gap": 8.095957612813846 }, "endBinding": { "elementId": "fOMZ3bas6KENlE-dMH_59", - "focus": -0.17743221391829114, - "gap": 3.4789792562721287 + "focus": 0.6571158183600897, + "gap": 6.97077975607103 }, "startArrowhead": "arrow", "endArrowhead": "arrow", @@ -512,7 +521,7 @@ "version": 73, "versionNonce": 1790515787, "isDeleted": false, - "boundElements": null, + "boundElements": [], "updated": 1743364054746, "link": null, "locked": false, @@ -672,10 +681,10 @@ { "id": "ka8QAs1UibuFb3THMMsjX", "type": "arrow", - "x": 204.2503866850818, - "y": 185.8400838233369, - "width": 362.4719025774192, - "height": 142.8400838233369, + "x": 242.2503866850818, + "y": 204.8400838233369, + "width": 395.4719025774192, + "height": 161.8400838233369, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -689,11 +698,11 @@ "index": "b0r", "roundness": null, "seed": 1883731429, - "version": 3252, - "versionNonce": 1131211019, + "version": 3392, + "versionNonce": 475447486, "isDeleted": false, "boundElements": [], - "updated": 1743365414429, + "updated": 1743388109592, "link": null, "locked": false, "points": [ @@ -702,28 +711,28 @@ 0 ], [ - -180.80072290276902, - -0.599259518709431 + -211.80072290276902, + -2.599259518709431 ], [ - -186.2503866850818, - -142.8400838233369 + -214.2503866850818, + -161.8400838233369 ], [ - -362.4719025774192, - -142.24201710021367 + -395.4719025774192, + -161.24201710021367 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "EoBCO4fe_fWGHsIfZWNO9", - "focus": 0.2649254680496929, - "gap": 22.25038668508182 + "elementId": "fOMZ3bas6KENlE-dMH_59", + "focus": -0.6512235329620341, + "gap": 5.103740945936721 }, "endBinding": { "elementId": "VZ4fZvVEhGOkaDbGnAXOM", - "focus": -0.5751816556792378, - "gap": 3.457986333543673 + "focus": -0.5751293954776895, + "gap": 8.439974601191137 }, "startArrowhead": "arrow", "endArrowhead": "arrow", @@ -769,10 +778,10 @@ { "id": "F8S5vF78PzGEI884_BgcW", "type": "arrow", - "x": 204.37145245775514, - "y": 297.6377991549125, - "width": 352.7429049155103, - "height": 126.37140200826508, + "x": 234.37145245775514, + "y": 303.6377991549125, + "width": 385.7429049155103, + "height": 119.37140200826508, "angle": 0, "strokeColor": "#2f9e44", "backgroundColor": "#b2f2bb", @@ -786,11 +795,11 @@ "index": "b0u", "roundness": null, "seed": 1944016805, - "version": 3445, - "versionNonce": 831061611, + "version": 3498, + "versionNonce": 1818690366, "isDeleted": false, "boundElements": [], - "updated": 1743364363694, + "updated": 1743388104846, "link": null, "locked": false, "points": [ @@ -799,28 +808,28 @@ 0 ], [ - -172.79737489543066, - -2.650344670519303 + -204.79737489543066, + -1.6503446705193028 ], [ - -174.24703867774343, - 123.1088310248532 + -204.24703867774343, + 117.1088310248532 ], [ - -352.7429049155103, - 123.72105733774578 + -385.7429049155103, + 117.72105733774578 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "EoBCO4fe_fWGHsIfZWNO9", - "focus": -0.21924707175329883, - "gap": 22.37145245775514 + "elementId": "qSW7cdA88CRCn0oq6Upcd", + "focus": 0.05234894043415935, + "gap": 6.346420055914296 }, "endBinding": { "elementId": "fl0fjOe0gMGOmEH3Qej7N", - "focus": 0.4346005018633931, - "gap": 11.62854754224486 + "focus": 0.43431893151985146, + "gap": 8.62854754224486 }, "startArrowhead": "arrow", "endArrowhead": "arrow", diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index a6b9eb2..9312316 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -4,8 +4,8 @@ # Administrator guide - [Quick start](./quick_start.md) - - [Compiling from source](./compiling_from_source.md) - [Using Docker Compose](./docker_compose.md) + - [Compiling from source](./compiling_from_source.md) - [Configuration](./configuration.md) - [TLS support](./tls_support.md) - [Admin interface](./admin_interface.md) diff --git a/book/src/compiling_from_source.md b/book/src/compiling_from_source.md index fe06c93..0c66233 100644 --- a/book/src/compiling_from_source.md +++ b/book/src/compiling_from_source.md @@ -1,42 +1,26 @@ # Compiling from source -For this, you'll require [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) and Rust to be installed. +To build the project, [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) and Rust must be installed. If you're compiling from a separate workstation than the one that will be running Sandhole, then grab the source files, build the binary, and copy it over: ```bash git clone https://github.com/EpicEric/sandhole cd sandhole -cargo build --release +cargo build --locked --release scp target/release/sandhole user@sandhole.com.br:/usr/local/bin/sandhole ``` -If you're compiling on the machine that'll be running Sandhole, you can install it directly with `cargo install`. This should also add `sandhole` to your `PATH`: +If you're compiling on the machine where you'll run Sandhole, you can install it directly with `cargo install`. ```bash -cargo install --git https://github.com/EpicEric/sandhole +# Install from latest release +cargo install --locked sandhole +# # -- OR -- -git clone https://github.com/EpicEric/sandhole -cargo install --path sandhole -# -- OR -- -cargo install sandhole # Installs from latest release sources uploaded to crates.io -``` - -Once this is all done, you can start running Sandhole! Just make sure that it points to your own domain: - -```bash -sandhole --domain sandhole.com.br -``` - -By default, this will expose ports 80 (for HTTP), 443 (for HTTPS), and 2222 (for SSH). If it all succeeds, you should see the following: - -```log -[2024-11-03T13:10:51Z INFO sandhole] Starting Sandhole... -[2024-11-03T13:10:51Z INFO sandhole] Key file not found. Creating... -[2024-11-03T13:10:51Z INFO sandhole] Listening for HTTP connections on port 80. -[2024-11-03T13:10:51Z INFO sandhole] Listening for HTTPS connections on port 443. -[2024-11-03T13:10:51Z INFO sandhole] Listening for SSH connections on port 2222. -[2024-11-03T13:10:51Z INFO sandhole] Sandhole is now running. +# +# Install the current development version +cargo install --locked --git https://github.com/EpicEric/sandhole ``` -Now you're ready to dig sandholes like a crab! +Cargo should automatically add the binary to your `PATH`. diff --git a/book/src/configuration.md b/book/src/configuration.md index b6e1288..212b3d7 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -14,7 +14,7 @@ Similarly, there is a `./deploy/admin_keys/` directory (set by `--admin-keys-dir By default, Sandhole runs on ports 80, 443, and 2222. This assumes that your actual SSH server is running on port 22, and that no other services are listening on the HTTP/HTTPS ports. -However, it might be more desirable to have Sandhole listen on port 22 instead. In order to keep your SSH server running on a different port, edit the port in `/etc/ssh/sshd_config`, then restart your SSH daemon. +However, it might be desirable to have Sandhole listen on port 22 instead. In order to keep your OpenSSH server running on a different port, edit the `Port` entry in `/etc/ssh/sshd_config`, then restart your SSH daemon. Now you'll be able to run Sandhole on port 22: @@ -22,10 +22,6 @@ Now you'll be able to run Sandhole on port 22: sandhole --domain server.com --ssh-port 22 ``` -### What if I need to run another service on HTTP/HTTPS? - -It's simple: just let Sandhole take care of that for you! Nothing stops you from connecting to Sandhole on the localhost, and just like any proxy, it will redirect the traffic appropriately for you. See more on ["exposing your first service"](./exposing_your_first_service.md). - ## Allow binding on any subdomains/ports Without extra configuration, Sandhole will not let users bind to requested subdomains and ports, and will always allocate a random one instead. diff --git a/book/src/custom_domains.md b/book/src/custom_domains.md index 51a84e9..d65ee8f 100644 --- a/book/src/custom_domains.md +++ b/book/src/custom_domains.md @@ -17,7 +17,7 @@ Then, add the following entries to your DNS server (assuming that your custom do This instructs your DNS server to redirect requests to Sandhole, and tells Sandhole to authorize your SSH key for the given domain, respectively. -If you need to use multiple keys for the same domain, simply add a TXT record for each one. +If you need to allow multiple keys for the same domain, simply add a TXT record for each one. Then, expose your service at the given domain: diff --git a/book/src/digitalocean_dns.png b/book/src/digitalocean_dns.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ef9ab8fcaab115321b9081c5d318c860c13790 GIT binary patch literal 57170 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 literal 0 HcmV?d00001 diff --git a/book/src/docker_compose.md b/book/src/docker_compose.md index 782c264..8d8c3d7 100644 --- a/book/src/docker_compose.md +++ b/book/src/docker_compose.md @@ -1,6 +1,6 @@ # Using Docker Compose -The most straightforward way to have Sandhole up and running is with Docker Compose. Mainly, this takes care of running [Agnos](https://github.com/krtab/agnos) for you, and also daemonizes your application. +The most straightforward way to have Sandhole up and running is with Docker Compose. Mainly, this takes care of [managing TLS for you](./tls_support.md), and also daemonizes your application. For this, you'll first need to install the [Docker Engine](https://docs.docker.com/engine/install/) on your server. @@ -9,9 +9,9 @@ An example configuration is provided in the repository's [docker-compose-example Then, simply run: ```bash -docker compose up -d +docker compose up --detach ``` You should also re-run this command whenever you make changes to your configuration and/or after you update to the latest image (`docker compose pull`). See the [official Docker Compose documentation](https://docs.docker.com/compose/) for more information. -An alternate configuration using [dnsrobocert](https://adferrand.github.io/dnsrobocert/) is available under [docker-compose-example/sandhole-dnsrobocert](https://github.com/EpicEric/sandhole/tree/main/docker-compose-example/sandhole-dnsrobocert). +An alternate configuration using [dnsrobocert](https://adferrand.github.io/dnsrobocert/) is available under [docker-compose-example/sandhole-dnsrobocert](https://github.com/EpicEric/sandhole/tree/main/docker-compose-example/sandhole-dnsrobocert) in the repository. diff --git a/book/src/example_flow.svg b/book/src/example_flow.svg new file mode 100644 index 0000000..fe3685e --- /dev/null +++ b/book/src/example_flow.svg @@ -0,0 +1,2 @@ +1SSH tunnel45Sandhole2HTTPproxyClient A(HTTP service)Client B(Web browser)36 \ No newline at end of file diff --git a/book/src/exposing_your_first_service.md b/book/src/exposing_your_first_service.md index 2b24ccf..5067e65 100644 --- a/book/src/exposing_your_first_service.md +++ b/book/src/exposing_your_first_service.md @@ -1,6 +1,6 @@ # Exposing your first service -Once you have [an authorized public key](./configuration.md#adding-users-and-admins) in Sandhole, you can expose a local service. Assuming that your local HTTP service is running on port 3000, and that Sandhole is listening on `sandhole.com.br:2222`, all you have to do is run +Once you have [an authorized public key](./configuration.md#adding-users-and-admins) in Sandhole, you can expose a local service. Assuming that your local HTTP service is running on port 3000, and that Sandhole is listening on `sandhole.com.br:2222`, all you have to do is run: ```bash ssh -i /your/private/key -p 2222 -R 80:localhost:3000 sandhole.com.br @@ -8,6 +8,8 @@ ssh -i /your/private/key -p 2222 -R 80:localhost:3000 sandhole.com.br Yep, that's it! Sandhole will log that HTTP is being served for you on a certain subdomain, and you can access the URL printed to the console to see that your service is available to the public. +You'll also receive logs about each incoming HTTP request if the administrator hasn't disabled the logging option. + ## Requesting multiple tunnels You can request tunnels for several services in a single SSH command. @@ -24,7 +26,9 @@ For example, to bind under `test.sandhole.com.br`, we could use either of these ```bash ssh -i /your/private/key -p 2222 -R test:80:localhost:3000 sandhole.com.br +# # -- OR -- +# ssh -i /your/private/key -p 2222 -R test.sandhole.com.br:80:localhost:3000 sandhole.com.br ``` @@ -32,7 +36,9 @@ And if we'd like to bind to a specific port, say 4321: ```bash ssh -i /your/private/key -p 2222 -R 4321:localhost:3000 sandhole.com.br +# # -- OR -- +# ssh -i /your/private/key -p 2222 -R localhost:4321:localhost:3000 sandhole.com.br ``` @@ -48,4 +54,4 @@ ssh -p 2222 -o PubkeyAuthentication=no -o PreferredAuthentications=password user If you'd like to have persistent tunnels, use a tool like `autossh` to automatically reconnect when disconnected. Note that you might be assigned a new subdomain or port through disconnects, depending on the server configuration. -For a container-based alternative, [check out the Docker Compose client example](https://github.com/EpicEric/sandhole/tree/main/docker-compose-example/client) in the repository. +For a container-based alternative, [check out the Docker Compose example](https://github.com/EpicEric/sandhole/tree/main/docker-compose-example/client) in the repository. diff --git a/book/src/faq.md b/book/src/faq.md index 8b43899..c97a455 100644 --- a/book/src/faq.md +++ b/book/src/faq.md @@ -22,6 +22,10 @@ Host mysshserver.com Websockets are always enabled for HTTP services. +## What if I need to run another service on the HTTP/HTTPS port? + +It's simple: just let Sandhole take care of that for you! Nothing stops you from connecting to Sandhole on the localhost, and just like any reverse proxy, it will redirect the traffic appropriately for you. + ## How do I disable HTTP/TCP/aliasing? With the `--disable--http`, `--disable-tcp`, and `--disable-aliasing` [CLI flags](./cli.md) respectively. Note that you cannot disable all three at once, as that'd remove all of Sandhole's functionality. diff --git a/book/src/how_it_works.svg b/book/src/how_it_works.svg index 89bf9aa..4a7201f 100644 --- a/book/src/how_it_works.svg +++ b/book/src/how_it_works.svg @@ -1,2 +1,2 @@ :2222 (SSH):80 (HTTP):443 (HTTPS)SandholePublic serverLocal serviceRemote servicePrivate serverClientWeb browserInternet \ No newline at end of file + @font-face { font-family: Excalifont; src: url(data:font/woff2;base64,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); }:2222 (SSH):80 (HTTP):443 (HTTPS)SandholePublic serverLocal serviceRemote servicePrivate serverClientWeb browserInternet \ No newline at end of file diff --git a/book/src/introduction.md b/book/src/introduction.md index 05e2e08..207375c 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -8,9 +8,11 @@ Welcome to the **Sandhole book**. This is a guide on how to install, configure, [Sandhole](https://github.com/EpicEric/sandhole) is an unconventional reverse proxy which uses the built-in reverse port forwarding from SSH, allowing services to expose themselves to the Internet with minimal configuration. This is especially useful for services behind NAT, but you may also use Sandhole for: -- Quickly prototyping websites, APIs, and TCP services, and sharing them with others. +- Quickly prototyping and sharing websites or APIs. - Exposing endpoints or ports on IoT devices, game servers, and other applications. - Hosting a dual-stack HTTP+SSH service (via ProxyJump), such as a Git instance. -- Handling a multi-tenant network with several websites under the same domain. +- Handling a multi-tenant network with several websites and users. - Using the tunnel for ad hoc peer-to-peer connections, or [even as a basic VPN](./local_forwarding.md). - And possibly more! + +Fun fact: the Sandhole book runs behind a Sandhole instance! diff --git a/book/src/local_forwarding.md b/book/src/local_forwarding.md index 9477c0c..96c9590 100644 --- a/book/src/local_forwarding.md +++ b/book/src/local_forwarding.md @@ -18,9 +18,9 @@ Then you can access `localhost:4000`, and all traffic will be redirected to port ## Enforcing aliasing -Aliasing is always enabled for SSH hosts, and is conditionally enabled for TCP hosts that have requested a address different from `localhost` (for example, `my.tunnel` in the previous section). +Aliasing is always enabled for SSH hosts, and is conditionally enabled for TCP hosts that have requested an address other than `localhost`. -To enable aliasing for HTTP hosts, pass either the `tcp-alias` command to the remote forwarding command as follows: +To enable aliasing for HTTP hosts, pass the `tcp-alias` command to the remote forwarding command as follows: ```bash ssh -p 2222 -R my.tunnel:80:localhost:8080 sandhole.com.br tcp-alias @@ -37,7 +37,3 @@ ssh -p 2222 -R my.tunnel:3000:localhost:2000 sandhole.com.br allowed-fingerprint These fingerprints may belong to keys unrecognized by Sandhole, and they'll still be able to connect to your tunnel. This option will also enforce aliasing for HTTP hosts. - -## Disabling local forwarding - -The administrator can disable all local forwardings with the [`--disable-aliasing` CLI flag](./cli.md). diff --git a/book/src/quick_start.md b/book/src/quick_start.md index 4b37067..ba3c3e5 100644 --- a/book/src/quick_start.md +++ b/book/src/quick_start.md @@ -2,7 +2,50 @@ In order to run Sandhole, you'll need: -- A server with at least one public address. -- A domain (for example, `sandhole.com.br`), and its subdomains (`*.sandhole.com.br`), configured via DNS to point to the public address. +- A server with at least one public address. This tutorial assumes that you're using Linux. +- Control over a domain name (for example, `sandhole.com.br`) and its subdomains (`*.sandhole.com.br`). -Then, install the Sandhole binary in your server. Currently, you can do so [through Docker Compose](./docker_compose.md), by downloading [a binary from the latest release](https://github.com/EpicEric/sandhole/releases/latest), or by [compiling it yourself](./compiling_from_source.md). +## 1. Configure your DNS + +Make sure to point the `A` and `AAAA` records to your server's IP address(es). + +This step varies depending on your choice of DNS provider. For example, on DigitalOcean, this is what the configuration might look like: + +![A table showing DNS records for "sandhole.com.br" and "*.sandhole.com.br" pointing to IPv4 and IPv6 addresses.](./digitalocean_dns.png) + +## 2. Get the executable + +Download a copy of the latest release: + +```bash +# x64 +wget --output-document sandhole https://github.com/EpicEric/sandhole/releases/latest/download/sandhole-linux-amd64 +# +# -- OR -- +# +# AArch64 +wget --output-document sandhole https://github.com/EpicEric/sandhole/releases/latest/download/sandhole-linux-arm64 +``` + +If you prefer, you may also use [Docker Compose](./docker_compose.md) or [compile the binary yourself](./compiling_from_source.md). + +## 3. Run Sandhole + +You can now run Sandhole! Just make sure that it points to your domain: + +```bash +./sandhole --domain sandhole.com.br +``` + +By default, this will expose ports 80 (for HTTP), 443 (for HTTPS), and 2222 (for SSH). If it all succeeds, you should see the following: + +```log +[2024-11-03T13:10:51Z INFO sandhole] Starting Sandhole... +[2024-11-03T13:10:51Z INFO sandhole] Key file not found. Creating... +[2024-11-03T13:10:51Z INFO sandhole] Listening for HTTP connections on port 80. +[2024-11-03T13:10:51Z INFO sandhole] Listening for HTTPS connections on port 443. +[2024-11-03T13:10:51Z INFO sandhole] Listening for SSH connections on port 2222. +[2024-11-03T13:10:51Z INFO sandhole] Sandhole is now running. +``` + +Now you're ready to dig sandholes like a crab! 🦀 diff --git a/book/src/technical_overview.md b/book/src/technical_overview.md index 0bd62cb..6e43a09 100644 --- a/book/src/technical_overview.md +++ b/book/src/technical_overview.md @@ -24,3 +24,27 @@ Sandhole is essentially a reverse proxy. It leverages SSH for authentication and ![A diagram displaying Sandhole's usage as a reverse proxy. It's deployed to a public server, where a local service connects to its SSH port. A remote service in a private server also connects to the SSH port over the Internet. Meanwhile, a client's web browser connects to the HTTPS port of Sandhole over the Internet.](./how_it_works.svg) As such, it's possible to expose services publicly without needing a VPN, even behind NAT or firewalls. + +## Example flow + +Let's say client A wishes to expose a local service, running on port 8080, to the Internet. + +![A diagram showing a connection to Sandhole's HTTP proxy in six steps.](./example_flow.svg) + +1. Client A connects to a Sandhole instance while requesting a remote port forwarding: + +```bash +ssh -p 2222 -R mytunnel:80:localhost:8080 sandhole.com.br +``` + +2. Sandhole handles the forwarding request and starts proxying requests from `http://mytunnel.sandhole.com.br` to client A's port 8080. + +3. Client B accesses `http://mytunnel.sandhole.com.br` through a web browser. + +4. Sandhole opens a tunneling channel over SSH to client A, simulating a TCP stream containing client B's request. + +5. Client A's HTTP server replies over the SSH channel. + +6. Sandhole forwards the reply to client B. + +To client A, requests arrive normally at the socket. To client B, Sandhole acts as if it were the service itself. diff --git a/book/src/tls_support.md b/book/src/tls_support.md index 26209a9..49fdc13 100644 --- a/book/src/tls_support.md +++ b/book/src/tls_support.md @@ -18,4 +18,6 @@ sandhole --domain sandhole.com.br --certificates-directory ./letsencrypt/live ## ACME support +ACME allows you to generate certificates for user-provided domains automatically, without having to edit your configuration for each one. + Adding ACME support is as simple as adding your contact e-mail address via `--acme-contact-email you@your.email.com`, but first, make sure that you agree to the [Let's Encrypt Subscriber Agreement](https://letsencrypt.org/repository/). Sandhole will automatically manage the cache for your account and any certificates generated this way. -- 2.51.2