- commit
- 17217e6
- parent
- 72d930b
- author
- Antonio Mika
- date
- 2025-03-11 18:55:15 -0400 EDT
Almost fully working
11 files changed,
+125,
-171
M
go.mod
+1,
-15
1@@ -29,8 +29,6 @@ require (
2 github.com/antoniomika/syncmap v1.0.0
3 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
4 github.com/charmbracelet/lipgloss v1.0.0
5- github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef
6- github.com/charmbracelet/wish v1.4.6
7 github.com/containerd/console v1.0.4
8 github.com/darkweak/souin v1.7.5
9 github.com/darkweak/souin/plugins/souin/storages v1.7.5
10@@ -49,6 +47,7 @@ require (
11 github.com/picosh/pobj v0.0.0-20250304201248-a9c7179aa49b
12 github.com/picosh/pubsub v0.0.0-20241114191831-ec8f16c0eb88
13 github.com/picosh/send v0.0.0-20250304201154-e36cd3bbbb35
14+ github.com/picosh/tunkit v0.0.0-00010101000000-000000000000
15 github.com/picosh/utils v0.0.0-20241120033529-8ca070c09bf4
16 github.com/pkg/sftp v1.13.7
17 github.com/prometheus/client_golang v1.21.0-rc.0
18@@ -79,7 +78,6 @@ require (
19 github.com/PuerkitoBio/goquery v1.10.0 // indirect
20 github.com/RoaringBitmap/roaring v1.2.3 // indirect
21 github.com/andybalholm/cascadia v1.3.2 // indirect
22- github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
23 github.com/antlabs/stl v0.0.1 // indirect
24 github.com/antlabs/timer v0.0.11 // indirect
25 github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
26@@ -116,20 +114,12 @@ require (
27 github.com/caddyserver/zerossl v0.1.3 // indirect
28 github.com/cespare/xxhash v1.1.0 // indirect
29 github.com/cespare/xxhash/v2 v2.3.0 // indirect
30- github.com/charmbracelet/bubbletea v1.3.4 // indirect
31- github.com/charmbracelet/keygen v0.5.1 // indirect
32- github.com/charmbracelet/log v0.4.0 // indirect
33 github.com/charmbracelet/x/ansi v0.8.0 // indirect
34- github.com/charmbracelet/x/conpty v0.1.0 // indirect
35- github.com/charmbracelet/x/errors v0.0.0-20250226164017-59292a315e58 // indirect
36 github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b // indirect
37- github.com/charmbracelet/x/term v0.2.1 // indirect
38- github.com/charmbracelet/x/termios v0.1.1 // indirect
39 github.com/chzyer/readline v1.5.1 // indirect
40 github.com/coreos/go-semver v0.3.1 // indirect
41 github.com/coreos/go-systemd/v22 v22.5.0 // indirect
42 github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
43- github.com/creack/pty v1.1.24 // indirect
44 github.com/darkweak/go-esi v0.0.5 // indirect
45 github.com/darkweak/storages/badger v0.0.8 // indirect
46 github.com/darkweak/storages/etcd v0.0.8 // indirect
47@@ -157,7 +147,6 @@ require (
48 github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d // indirect
49 github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 // indirect
50 github.com/dustin/go-humanize v1.0.1 // indirect
51- github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
52 github.com/forPelevin/gomoji v1.2.0 // indirect
53 github.com/gammazero/deque v0.2.1 // indirect
54 github.com/gkampitakis/ciinfo v0.3.0 // indirect
55@@ -220,7 +209,6 @@ require (
56 github.com/maruel/natural v1.1.1 // indirect
57 github.com/mattn/go-colorable v0.1.13 // indirect
58 github.com/mattn/go-isatty v0.0.20 // indirect
59- github.com/mattn/go-localereader v0.0.1 // indirect
60 github.com/mattn/go-runewidth v0.0.16 // indirect
61 github.com/mattn/go-sixel v0.0.5 // indirect
62 github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
63@@ -239,8 +227,6 @@ require (
64 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
65 github.com/modern-go/reflect2 v1.0.2 // indirect
66 github.com/mschoch/smat v0.2.0 // indirect
67- github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
68- github.com/muesli/cancelreader v0.2.2 // indirect
69 github.com/muesli/termenv v0.16.0 // indirect
70 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
71 github.com/nats-io/nats.go v1.36.0 // indirect
M
go.sum
+0,
-31
1@@ -60,8 +60,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
2 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
3 github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
4 github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
5-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
6-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
7 github.com/antlabs/stl v0.0.1 h1:TRD3csCrjREeLhLoQ/supaoCvFhNLBTNIwuRGrDIs6Q=
8 github.com/antlabs/stl v0.0.1/go.mod h1:wvVwP1loadLG3cRjxUxK8RL4Co5xujGaZlhbztmUEqQ=
9 github.com/antlabs/timer v0.0.11 h1:z75oGFLeTqJHMOcWzUPBKsBbQAz4Ske3AfqJ7bsdcwU=
10@@ -152,30 +150,12 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
11 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
12 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
13 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
14-github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
15-github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
16-github.com/charmbracelet/keygen v0.5.1 h1:zBkkYPtmKDVTw+cwUyY6ZwGDhRxXkEp0Oxs9sqMLqxI=
17-github.com/charmbracelet/keygen v0.5.1/go.mod h1:zznJVmK/GWB6dAtjluqn2qsttiCBhA5MZSiwb80fcHw=
18 github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
19 github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
20-github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
21-github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
22-github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef h1:dNZwn4is5svUd+sQEGsrXtp7VwD2ipYaCkKMzcpAEIE=
23-github.com/charmbracelet/ssh v0.0.0-20250213143314-8712ec3ff3ef/go.mod h1:hg+I6gvlMl16nS9ZzQNgBIrrCasGwEw0QiLsDcP01Ko=
24-github.com/charmbracelet/wish v1.4.6 h1:27WRqMTUmyFoZASoaAaEe78Je7LTU4VqyoBxnl4d9XA=
25-github.com/charmbracelet/wish v1.4.6/go.mod h1:RRy2LFW3WQ3tlPmMMGgEeSMDVlFd5yqklGBVZWQSHmk=
26 github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
27 github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
28-github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
29-github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
30-github.com/charmbracelet/x/errors v0.0.0-20250226164017-59292a315e58 h1:UWrrJJrdFfi7Y5XNKjz8/1RtZzGbshaFEZzlI7CgJ7M=
31-github.com/charmbracelet/x/errors v0.0.0-20250226164017-59292a315e58/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
32 github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q=
33 github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
34-github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
35-github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
36-github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
37-github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
38 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
39 github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
40 github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
41@@ -208,8 +188,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0q
42 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
43 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
44 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
45-github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
46-github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
47 github.com/darkweak/go-esi v0.0.5 h1:b9LHI8Tz46R+i6p8avKPHAIBRQUCZDebNmKm5w/Zrns=
48 github.com/darkweak/go-esi v0.0.5/go.mod h1:koCJqwum1u6mslyZuq/Phm6hfG1K3ZK5Y7jrUBTH654=
49 github.com/darkweak/souin v1.7.5 h1:drNhZc0GhSbGcugiGfcYdLDTcx3DCZW6o13wwRj5o5Y=
50@@ -294,8 +272,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
51 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
52 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
53 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
54-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
55-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
56 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
57 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
58 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
59@@ -597,8 +573,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
60 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
61 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
62 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
63-github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
64-github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
65 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
66 github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
67 github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
68@@ -656,10 +630,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
69 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
70 github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
71 github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
72-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
73-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
74-github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
75-github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
76 github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
77 github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
78 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
79@@ -1106,7 +1076,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
80 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
81 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
82 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
83-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
84 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
85 golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
86 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+8,
-4
1@@ -14,6 +14,7 @@ import (
2 "github.com/picosh/send/protocols/rsync"
3 "github.com/picosh/send/protocols/scp"
4 "github.com/picosh/send/protocols/sftp"
5+ "github.com/picosh/tunkit"
6 "github.com/picosh/utils"
7 "golang.org/x/crypto/ssh"
8 )
9@@ -36,10 +37,10 @@ func StartSshServer(cfg *PgsConfig, killCh chan error) {
10
11 sshAuth := shared.NewSshAuthHandler(cfg.DB, logger)
12
13- // webTunnel := &tunkit.WebTunnelHandler{
14- // Logger: logger,
15- // HttpHandler: createHttpHandler(cfg),
16- // }
17+ webTunnel := &tunkit.WebTunnelHandler{
18+ Logger: logger,
19+ HttpHandler: createHttpHandler(cfg),
20+ }
21
22 // Create a new SSH server
23 server := pssh.NewSSHServer(ctx, logger, &pssh.SSHServerConfig{
24@@ -61,6 +62,9 @@ func StartSshServer(cfg *PgsConfig, killCh chan error) {
25 sftp.Middleware(handler),
26 pssh.LogMiddleware(handler, handler.Cfg.DB),
27 },
28+ ChannelMiddleware: map[string]pssh.SSHServerChannelMiddleware{
29+ "direct-tcpip": tunkit.LocalForwardHandler(webTunnel),
30+ },
31 })
32
33 pemBytes, err := os.ReadFile("ssh_data/term_info_ed25519")
+2,
-1
1@@ -408,7 +408,8 @@ func WriteFileWithSftp(cfg *PgsConfig, conn *ssh.Client) (*os.FileInfo, error) {
2 cfg.Logger.Error("could not write to file", "err", err)
3 return nil, err
4 }
5- f.Close()
6+
7+ cfg.Logger.Info("closing", "err", f.Close())
8
9 // check it's there
10 fi, err := client.Lstat("test/hello.txt")
+4,
-3
1@@ -6,9 +6,10 @@ import (
2 "strings"
3 "time"
4
5- "github.com/charmbracelet/ssh"
6 "github.com/picosh/pico/db"
7+ "github.com/picosh/pico/pssh"
8 "github.com/picosh/pico/shared"
9+ "golang.org/x/crypto/ssh"
10 )
11
12 type TunnelWebRouter struct {
13@@ -33,7 +34,7 @@ func (web *TunnelWebRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
14 web.UserRouter.ServeHTTP(w, r.WithContext(ctx))
15 }
16
17-type CtxHttpBridge = func(ssh.Context) http.Handler
18+type CtxHttpBridge = func(*pssh.SSHServerConnSession) http.Handler
19
20 func getInfoFromUser(user string) (string, string) {
21 if strings.Contains(user, "__") {
22@@ -45,7 +46,7 @@ func getInfoFromUser(user string) (string, string) {
23 }
24
25 func createHttpHandler(cfg *PgsConfig) CtxHttpBridge {
26- return func(ctx ssh.Context) http.Handler {
27+ return func(ctx *pssh.SSHServerConnSession) http.Handler {
28 logger := cfg.Logger
29 asUser, subdomain := getInfoFromUser(ctx.User())
30 log := logger.With(
+4,
-4
1@@ -11,12 +11,12 @@ import (
2 "strings"
3 "time"
4
5- "github.com/charmbracelet/ssh"
6 "github.com/picosh/pico/db"
7 "github.com/picosh/pico/pssh"
8 "github.com/picosh/pico/shared"
9 sendutils "github.com/picosh/send/utils"
10 "github.com/picosh/utils"
11+ "golang.org/x/crypto/ssh"
12 )
13
14 type UploadHandler struct {
15@@ -168,7 +168,7 @@ func authorizedKeysDiff(keyInUse ssh.PublicKey, curKeys []KeyWithId, nextKeys []
16 for _, nk := range nextKeys {
17 found := false
18 for _, ck := range curKeys {
19- if ssh.KeysEqual(nk.Pk, ck.Pk) {
20+ if pssh.KeysEqual(nk.Pk, ck.Pk) {
21 found = true
22
23 // update the comment field
24@@ -188,13 +188,13 @@ func authorizedKeysDiff(keyInUse ssh.PublicKey, curKeys []KeyWithId, nextKeys []
25 for _, ck := range curKeys {
26 // we never want to remove the key that's in the current ssh session
27 // in an effort to avoid mistakenly removing their current key
28- if ssh.KeysEqual(ck.Pk, keyInUse) {
29+ if pssh.KeysEqual(ck.Pk, keyInUse) {
30 continue
31 }
32
33 found := false
34 for _, nk := range nextKeys {
35- if ssh.KeysEqual(ck.Pk, nk.Pk) {
36+ if pssh.KeysEqual(ck.Pk, nk.Pk) {
37 found = true
38 break
39 }
+2,
-1
1@@ -101,6 +101,7 @@ type CliHandler struct {
2 }
3
4 func (h *CliHandler) GetLogger(s *pssh.SSHServerConnSession) *slog.Logger {
5+ return h.Logger
6 }
7
8 func toSshCmd(cfg *shared.ConfigSite) string {
9@@ -453,7 +454,7 @@ func WishMiddleware(handler *CliHandler) pssh.SSHServerMiddleware {
10 cancel()
11
12 if !*clean {
13- sesh.Fatal(fmt.Errorf("timeout reached, exiting ..."))
14+ sesh.Fatal(fmt.Errorf("timeout reached, exiting"))
15 } else {
16 err = sesh.Exit(1)
17 if err != nil {
+98,
-67
1@@ -2,6 +2,7 @@ package pssh
2
3 import (
4 "context"
5+ "crypto/subtle"
6 "errors"
7 "fmt"
8 "log/slog"
9@@ -166,75 +167,15 @@ func (sc *SSHServerConn) Handle(chans <-chan ssh.NewChannel, reqs <-chan *ssh.Re
10 if !ok {
11 return nil
12 }
13- sc.Logger.Info("new channel", "type", newChan.ChannelType(), "extraData", newChan.ExtraData())
14- switch newChan.ChannelType() {
15- case "session":
16- channel, requests, err := newChan.Accept()
17- if err != nil {
18- sc.Logger.Error("accept session channel", "err", err)
19- return err
20- }
21-
22- go func() {
23- for {
24- select {
25- case <-sc.Done():
26- return
27- case req, ok := <-requests:
28- if !ok {
29- return
30- }
31
32- sc.Logger.Info("new session request", "type", req.Type, "wantReply", req.WantReply, "payload", req.Payload)
33- if req.Type == "subsystem" {
34- if len(sc.SSHServer.Config.SubsystemMiddleware) == 0 {
35- req.Reply(false, nil)
36- continue
37- }
38-
39- h := func(*SSHServerConnSession) error { return nil }
40- for _, m := range sc.SSHServer.Config.SubsystemMiddleware {
41- h = m(h)
42- }
43-
44- if err := h(&SSHServerConnSession{
45- Channel: channel,
46- SSHServerConn: sc,
47- }); err != nil {
48- req.Reply(false, nil)
49- continue
50- }
51-
52- req.Reply(true, nil)
53- } else if req.Type == "exec" {
54- if len(sc.SSHServer.Config.Middleware) == 0 {
55- req.Reply(false, nil)
56- continue
57- }
58-
59- sesh := &SSHServerConnSession{
60- Channel: channel,
61- SSHServerConn: sc,
62- }
63-
64- sesh.SetValue("command", strings.Fields(string(req.Payload[4:])))
65-
66- h := func(*SSHServerConnSession) error { return nil }
67- for _, m := range sc.SSHServer.Config.Middleware {
68- h = m(h)
69- }
70-
71- if err := h(sesh); err != nil {
72- req.Reply(false, nil)
73- continue
74- }
75-
76- req.Reply(true, nil)
77- }
78- }
79- }
80- }()
81+ sc.Logger.Info("new channel", "type", newChan.ChannelType(), "extraData", newChan.ExtraData())
82+ chanFunc, ok := sc.SSHServer.Config.ChannelMiddleware[newChan.ChannelType()]
83+ if !ok {
84+ sc.Logger.Info("no channel middleware for type", "type", newChan.ChannelType())
85+ continue
86 }
87+
88+ go chanFunc(newChan, sc)
89 case req, ok := <-reqs:
90 if !ok {
91 return nil
92@@ -271,12 +212,14 @@ func NewSSHServerConn(
93
94 type SSHServerHandler func(*SSHServerConnSession) error
95 type SSHServerMiddleware func(SSHServerHandler) SSHServerHandler
96+type SSHServerChannelMiddleware func(ssh.NewChannel, *SSHServerConn) error
97
98 type SSHServerConfig struct {
99 *ssh.ServerConfig
100 ListenAddr string
101 Middleware []SSHServerMiddleware
102 SubsystemMiddleware []SSHServerMiddleware
103+ ChannelMiddleware map[string]SSHServerChannelMiddleware
104 }
105
106 type SSHServer struct {
107@@ -375,6 +318,84 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
108 config = &SSHServerConfig{}
109 }
110
111+ if config.ChannelMiddleware == nil {
112+ config.ChannelMiddleware = map[string]SSHServerChannelMiddleware{}
113+ }
114+
115+ if _, ok := config.ChannelMiddleware["session"]; !ok {
116+ config.ChannelMiddleware["session"] = func(newChan ssh.NewChannel, sc *SSHServerConn) error {
117+ channel, requests, err := newChan.Accept()
118+ if err != nil {
119+ sc.Logger.Error("accept session channel", "err", err)
120+ return err
121+ }
122+
123+ for {
124+ select {
125+ case <-sc.Done():
126+ return nil
127+ case req, ok := <-requests:
128+ if !ok {
129+ return nil
130+ }
131+
132+ go func() {
133+ sc.Logger.Info("new session request", "type", req.Type, "wantReply", req.WantReply, "payload", req.Payload)
134+ if req.Type == "subsystem" {
135+ if len(sc.SSHServer.Config.SubsystemMiddleware) == 0 {
136+ req.Reply(false, nil)
137+ sc.Close()
138+ return
139+ }
140+
141+ h := func(*SSHServerConnSession) error { return nil }
142+ for _, m := range sc.SSHServer.Config.SubsystemMiddleware {
143+ h = m(h)
144+ }
145+
146+ sesh := &SSHServerConnSession{
147+ Channel: channel,
148+ SSHServerConn: sc,
149+ }
150+
151+ if err := h(sesh); err != nil {
152+ req.Reply(false, nil)
153+ sesh.Close()
154+ }
155+
156+ req.Reply(true, nil)
157+ sesh.Close()
158+ } else if req.Type == "exec" {
159+ if len(sc.SSHServer.Config.Middleware) == 0 {
160+ req.Reply(false, nil)
161+ }
162+
163+ sesh := &SSHServerConnSession{
164+ Channel: channel,
165+ SSHServerConn: sc,
166+ }
167+
168+ sesh.SetValue("command", strings.Fields(string(req.Payload[4:])))
169+
170+ h := func(*SSHServerConnSession) error { return nil }
171+ for _, m := range sc.SSHServer.Config.Middleware {
172+ h = m(h)
173+ }
174+
175+ if err := h(sesh); err != nil {
176+ req.Reply(false, nil)
177+ sesh.Close()
178+ }
179+
180+ req.Reply(true, nil)
181+ sesh.Close()
182+ }
183+ }()
184+ }
185+ }
186+ }
187+ }
188+
189 server := &SSHServer{
190 Ctx: cancelCtx,
191 CancelFunc: cancelFunc,
192@@ -385,3 +406,13 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
193
194 return server
195 }
196+
197+func KeysEqual(a, b ssh.PublicKey) bool {
198+ if a == nil || b == nil {
199+ return false
200+ }
201+
202+ am := a.Marshal()
203+ bm := b.Marshal()
204+ return (len(am) == len(bm) && subtle.ConstantTimeCompare(am, bm) == 1)
205+}
1@@ -8,9 +8,9 @@ import (
2 "os"
3 "strings"
4
5- "github.com/charmbracelet/ssh"
6 "github.com/picosh/pico/db"
7 "github.com/picosh/utils"
8+ "golang.org/x/crypto/ssh"
9 )
10
11 type SubdomainProps struct {
1@@ -10,8 +10,8 @@ import (
2 "regexp"
3 "strings"
4
5- "github.com/charmbracelet/ssh"
6 "github.com/picosh/pico/db"
7+ "github.com/picosh/pico/pssh"
8 "github.com/picosh/pico/shared/storage"
9 )
10
11@@ -173,12 +173,12 @@ type ctxCfg struct{}
12
13 type CtxSubdomainKey struct{}
14 type ctxKey struct{}
15-type CtxSshKey struct{}
16+type CtxSessionKey struct{}
17
18-func GetSshCtx(r *http.Request) (ssh.Context, error) {
19- payload, ok := r.Context().Value(CtxSshKey{}).(ssh.Context)
20+func GetSshCtx(r *http.Request) (*pssh.SSHServerConnSession, error) {
21+ payload, ok := r.Context().Value(CtxSessionKey{}).(*pssh.SSHServerConnSession)
22 if payload == nil || !ok {
23- return payload, fmt.Errorf("sshCtx not set on `r.Context()` for connection")
24+ return payload, fmt.Errorf("ssh session not set on `r.Context()` for connection")
25 }
26 return payload, nil
27 }
+0,
-39
1@@ -1,39 +0,0 @@
2-package wish
3-
4-import (
5- "fmt"
6-
7- "github.com/charmbracelet/ssh"
8- "github.com/charmbracelet/wish"
9-)
10-
11-func SessionMessage(sesh ssh.Session, msg string) {
12- _, _ = sesh.Write([]byte(msg + "\r\n"))
13-}
14-
15-func DeprecatedNotice() wish.Middleware {
16- return func(next ssh.Handler) ssh.Handler {
17- return func(sesh ssh.Session) {
18- msg := fmt.Sprintf(
19- "%s\n\nRun %s to access pico's TUI",
20- "DEPRECATED",
21- "ssh pico.sh",
22- )
23- SessionMessage(sesh, msg)
24- next(sesh)
25- }
26- }
27-}
28-
29-func PtyMdw(mdw wish.Middleware) wish.Middleware {
30- return func(next ssh.Handler) ssh.Handler {
31- return func(sesh ssh.Session) {
32- _, _, ok := sesh.Pty()
33- if !ok {
34- next(sesh)
35- return
36- }
37- mdw(next)(sesh)
38- }
39- }
40-}