commit cf1de3f894bf5a46a7bb70527b3f31ef351596b4
Author: Andrew Kloet <andrew@kloet.net>
Date: Tue, 17 Mar 2026 13:03:36 -0400
initial commit
Diffstat:
| A | .gitignore | | | 2 | ++ |
| A | README | | | 2 | ++ |
| A | go.mod | | | 20 | ++++++++++++++++++++ |
| A | go.sum | | | 45 | +++++++++++++++++++++++++++++++++++++++++++++ |
| A | ztip.go | | | 103 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 172 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+keys
+ztip
diff --git a/README b/README
@@ -0,0 +1,2 @@
+Zero-trust coordination server and nodes using the i2p network as a backend
+ALPHA ALPHA ALPHA
diff --git a/go.mod b/go.mod
@@ -0,0 +1,20 @@
+module git.kloet.net/ztip
+
+go 1.25.5
+
+require github.com/go-i2p/go-sam-go v0.33.0
+
+require (
+ github.com/go-i2p/common v0.0.1 // indirect
+ github.com/go-i2p/crypto v0.0.1 // indirect
+ github.com/go-i2p/i2pkeys v0.33.92 // indirect
+ github.com/go-i2p/logger v0.0.1 // indirect
+ github.com/oklog/ulid/v2 v2.1.1 // indirect
+ github.com/samber/lo v1.52.0 // indirect
+ github.com/samber/oops v1.19.3 // indirect
+ github.com/sirupsen/logrus v1.9.3 // indirect
+ go.opentelemetry.io/otel v1.38.0 // indirect
+ go.opentelemetry.io/otel/trace v1.38.0 // indirect
+ golang.org/x/sys v0.37.0 // indirect
+ golang.org/x/text v0.30.0 // indirect
+)
diff --git a/go.sum b/go.sum
@@ -0,0 +1,45 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-i2p/common v0.0.1 h1:gONnjtwrfo+2uWPai8SsTPDSa0++hhiD7Bvhn/dKI0Q=
+github.com/go-i2p/common v0.0.1/go.mod h1:pmh6KuO6gZsj/8rQnaaOK3YHUdnXPydnYE0k7n7OT9Q=
+github.com/go-i2p/crypto v0.0.1 h1:04yo8tX9+CRkT5zugEPhyJQ38syeXIW+1GykDfbB418=
+github.com/go-i2p/crypto v0.0.1/go.mod h1:ni/LJcr7MrPhXmYbQ2b5enpe8ppT3qhfiwr3I1QJeZM=
+github.com/go-i2p/go-sam-go v0.33.0 h1:4V9eSc0jBmWOEK6kWnujvoAcbdrYyZQRczZ3NRYSDFU=
+github.com/go-i2p/go-sam-go v0.33.0/go.mod h1:yh0p4igH39DL3ZLni40lzCb9NxQcZF0jqDYCiPNqAv8=
+github.com/go-i2p/i2pkeys v0.33.92 h1:e2vx3vf7tNesaJ8HmAlGPOcfiGM86jzeIGxh27I9J2Y=
+github.com/go-i2p/i2pkeys v0.33.92/go.mod h1:BRURQ/twxV0WKjZlFSKki93ivBi+MirZPWudfwTzMpE=
+github.com/go-i2p/logger v0.0.1 h1:OFDZMjqiNXbPIm+SDxiwYtI6ocC3mb9V/t5kvZ+6XQ0=
+github.com/go-i2p/logger v0.0.1/go.mod h1:te7Zj3g3oMeIl8uBXAgO62UKmZ6m6kHRNg1Mm+X8Hzk=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
+github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
+github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
+github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
+github.com/samber/oops v1.19.3 h1:GAfSUOCh/JbnKAj5Ia+r/3KNnBdK+VdVDq1F5I8nDfM=
+github.com/samber/oops v1.19.3/go.mod h1:1lIO/SwpPltzw5cDO8/oiyVuLiQt3/8iid21Vb8QP+8=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
+go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
+go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
+golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
+golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/ztip.go b/ztip.go
@@ -0,0 +1,103 @@
+package main
+
+import (
+ "io"
+ "log"
+ "net"
+ "time"
+
+ sam3 "github.com/go-i2p/go-sam-go"
+)
+
+type Node struct {
+ identity string
+ groups []string
+}
+
+type Forward struct {
+ Name string
+ KeyFile string
+ LocalAddr net.Addr
+}
+
+func main() {
+ samAddr := "127.0.0.1:7656"
+ log.Println("Connecting to SAM server:", samAddr)
+ sam, err := sam3.NewSAM(samAddr)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ forwards := []Forward{
+ {
+ Name: "navidrome",
+ KeyFile: "navidrome.dat",
+ LocalAddr: &net.TCPAddr{IP: net.ParseIP("192.168.0.3"), Port: 4533},
+ },
+ {
+ Name: "http8080",
+ KeyFile: "http8080.dat",
+ LocalAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8080},
+ },
+ }
+
+ startServices(sam, forwards)
+ select {}
+}
+
+func startServices(sam *sam3.SAM, forwards []Forward) {
+ for _, fwd := range forwards {
+ keys, err := sam.EnsureKeyfile("keys/" + fwd.KeyFile)
+ if err != nil {
+ log.Printf("Failed to setup keys for %s: %v", fwd.Name, err)
+ continue
+ }
+
+ session, err := sam.NewStreamSession(fwd.Name+time.Now().Format("15:04:05"), keys, sam3.Options_Warning_ZeroHop)
+ if err != nil {
+ log.Printf("Failed to create session for %s: %v", fwd.Name, err)
+ continue
+ }
+
+ go func(target net.Addr, s *sam3.StreamSession) {
+ listener, err := s.Listen()
+ if err != nil {
+ log.Printf("Failed to listen for [%s]:%s", fwd.Name, err)
+ }
+ log.Printf("Service [%s] listening at %s", fwd.Name, keys.Addr().Base32())
+
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ log.Println("accept error:", err)
+ continue
+ }
+ log.Printf("%s wants: %s", conn.RemoteAddr().String(), target)
+ go proxy(conn, target)
+ }
+ }(fwd.LocalAddr, session)
+ }
+}
+
+func proxy(i2pConn net.Conn, localAddr net.Addr) {
+ defer i2pConn.Close()
+
+ localConn, err := net.DialTimeout(localAddr.Network(), localAddr.String(), 5*time.Second)
+ if err != nil {
+ log.Printf("ERROR: Local dial failed: %v", err)
+ return
+ }
+ defer localConn.Close()
+
+ errChan := make(chan error, 2)
+
+ cp := func(dst io.Writer, src io.Reader) {
+ _, err := io.Copy(dst, src)
+ errChan <- err
+ }
+
+ go cp(localConn, i2pConn)
+ go cp(i2pConn, localConn)
+
+ <-errChan
+}