From c3d066d0be788b09598d59207af13ff5f04088ec Mon Sep 17 00:00:00 2001 From: "pu.li" Date: Sun, 22 Jun 2025 02:51:51 +0800 Subject: [PATCH] support credential-bcrypt auth --- go.mod | 2 +- go.sum | 19 ------------------- main.go | 32 ++++++++++++++++---------------- server/middleware.go | 39 +++++++++++++++++++++++++++++++++++---- server/options.go | 1 + 5 files changed, 53 insertions(+), 40 deletions(-) diff --git a/go.mod b/go.mod index f42db55..bb73e91 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/urfave/cli/v2 v2.25.7 github.com/yudai/hcl v0.0.0-20151013225006-5fa2393b3552 + golang.org/x/crypto v0.14.0 ) require ( @@ -30,7 +31,6 @@ require ( github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.14.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.14.0 // indirect diff --git a/go.sum b/go.sum index e266bfc..95a4f2b 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= @@ -17,8 +15,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -42,8 +38,6 @@ github.com/kost/regeorgo v0.0.0-20211119151427-d6c70e76b00e h1:VgDzuSnk+mf8aTLNb github.com/kost/regeorgo v0.0.0-20211119151427-d6c70e76b00e/go.mod h1:0cbpzZ1u82sObN1BJS5xLzsFUJ41BsQTmZOzLlyWd6Y= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= -github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/photostorm/pty v1.1.19-0.20221026012344-0a71ca4f0f8c h1:fxvLuEJrUWCzvA53SlUSLN1qqKWFdM8HRhHvX1TTaS0= @@ -59,8 +53,6 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY= -github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -69,30 +61,19 @@ github.com/yudai/hcl v0.0.0-20151013225006-5fa2393b3552 h1:tjsK9T2IA3d2FFNxzDP7A github.com/yudai/hcl v0.0.0-20151013225006-5fa2393b3552/go.mod h1:hg0ZaCmQL3rze1cH8Fh2g0a9q8vQs0uN8ESpePEwSEw= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/main.go b/main.go index 5bd47c9..af3fb21 100644 --- a/main.go +++ b/main.go @@ -46,14 +46,14 @@ func main() { app.Flags = append( cliFlags, &cli.StringFlag{ - Name: "config", - Value: "~/.tty2web", - Usage: "Config file path", + Name: "config", + Value: "~/.tty2web", + Usage: "Config file path", EnvVars: []string{"TTY2WEB_CONFIG"}, }, &cli.BoolFlag{ - Name: "help", - Usage: "Displays help", + Name: "help", + Usage: "Displays help", }, ) @@ -71,6 +71,7 @@ func main() { utils.ApplyFlags(cliFlags, flagMappings, c, appOptions, backendOptions) appOptions.EnableBasicAuth = c.IsSet("credential") + appOptions.CredentialBcrypt = c.IsSet("credential-bcrypt") appOptions.EnableTLSClientAuth = c.IsSet("tls-ca-crt") err = appOptions.Validate() @@ -79,28 +80,28 @@ func main() { exit(err, 6) } - if appOptions.Dns!="" { + if appOptions.Dns != "" { if appOptions.DnsKey == "" { - appOptions.DnsKey=GenerateKey() + appOptions.DnsKey = GenerateKey() log.Printf("No password specified, generated following (recheck if same on both sides): %s", appOptions.DnsKey) } if len(appOptions.DnsKey) != 64 { fmt.Fprintf(os.Stderr, "Specified key of incorrect size for DNS (should be 64 in hex)\n") os.Exit(1) } - if appOptions.DnsListen!="" { + if appOptions.DnsListen != "" { go func() { - log.Fatal(ServeDNS (appOptions.DnsListen,appOptions.Dns, appOptions.Server, appOptions.DnsKey, appOptions.DnsDelay)) + log.Fatal(ServeDNS(appOptions.DnsListen, appOptions.Dns, appOptions.Server, appOptions.DnsKey, appOptions.DnsDelay)) }() wait4Signals() return nil } } - if appOptions.Listen!="" { + if appOptions.Listen != "" { log.Printf("Listening for reverse connection %s", appOptions.Listen) go func() { - log.Fatal(listenForAgents(appOptions.Verbose, appOptions.AgentTLS, appOptions.Listen, appOptions.Server, appOptions.ListenCert, appOptions.Password)) + log.Fatal(listenForAgents(appOptions.Verbose, appOptions.AgentTLS, appOptions.Listen, appOptions.Server, appOptions.ListenCert, appOptions.Password)) }() wait4Signals() return nil @@ -137,7 +138,6 @@ func main() { exit(err, 5) } - log.Printf("tty2web is starting with command: %s", strings.Join(args.Slice(), " ")) ctx, cancel := context.WithCancel(context.Background()) @@ -170,10 +170,10 @@ func wait4Signals() { c := make(chan os.Signal) signal.Notify(c, os.Interrupt) select { - case sig := <-c: - fmt.Printf("Got %s signal. Aborting...\n", sig) - os.Exit(1) - } + case sig := <-c: + fmt.Printf("Got %s signal. Aborting...\n", sig) + os.Exit(1) + } } func waitSignals(errs chan error, cancel context.CancelFunc, gracefullCancel context.CancelFunc) error { diff --git a/server/middleware.go b/server/middleware.go index 11300db..5c66088 100644 --- a/server/middleware.go +++ b/server/middleware.go @@ -1,10 +1,13 @@ package server import ( + "bytes" "encoding/base64" "log" "net/http" "strings" + + "golang.org/x/crypto/bcrypt" ) func (server *Server) wrapLogger(handler http.Handler) http.Handler { @@ -39,10 +42,38 @@ func (server *Server) wrapBasicAuth(handler http.Handler, credential string) htt return } - if credential != string(payload) { - w.Header().Set("WWW-Authenticate", `Basic realm="tty2web"`) - http.Error(w, "authorization failed", http.StatusUnauthorized) - return + if !server.options.CredentialBcrypt { + if credential != string(payload) { + w.Header().Set("WWW-Authenticate", `Basic realm="tty2web"`) + http.Error(w, "authorization failed", http.StatusUnauthorized) + return + } + } else { + credentialParts := strings.SplitN(credential, ":", 2) + if len(credentialParts) != 2 { + log.Printf("Invalid credential format on server") + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + payloadParts := bytes.SplitN(payload, []byte(":"), 2) + if len(payloadParts) != 2 { + w.Header().Set("WWW-Authenticate", `Basic realm="tty2web"`) + http.Error(w, "authorization failed", http.StatusUnauthorized) + return + } + + if credentialParts[0] != string(payloadParts[0]) { + w.Header().Set("WWW-Authenticate", `Basic realm="tty2web"`) + http.Error(w, "authorization failed", http.StatusUnauthorized) + return + } + + err := bcrypt.CompareHashAndPassword([]byte(credentialParts[1]), payloadParts[1]) + if err != nil { + w.Header().Set("WWW-Authenticate", `Basic realm="tty2web"`) + http.Error(w, "authorization failed", http.StatusUnauthorized) + return + } } log.Printf("Basic Authentication Succeeded: %s", r.RemoteAddr) diff --git a/server/options.go b/server/options.go index 6310305..904e008 100644 --- a/server/options.go +++ b/server/options.go @@ -10,6 +10,7 @@ type Options struct { PermitWrite bool `hcl:"permit_write" flagName:"permit-write" flagSName:"w" flagDescribe:"Permit clients to write to the TTY (BE CAREFUL)" default:"false"` EnableBasicAuth bool `hcl:"enable_basic_auth" default:"false"` Credential string `hcl:"credential" flagName:"credential" flagSName:"c" flagDescribe:"Credential for Basic Authentication (ex: user:pass, default disabled)" default:""` + CredentialBcrypt bool `hcl:"credential_bcrypt" flagName:"credential-bcrypt" flagDescribe:"Treat credential as a bcrypt hash" default:"false"` EnableRandomUrl bool `hcl:"enable_random_url" flagName:"random-url" flagSName:"r" flagDescribe:"Add a random string to the URL" default:"false"` EnableWebGL bool `hcl:"enable_webgl" flagName:"enable-webgl" flagDescribe:"Enable WebGL renderer" default:"true"` All bool `hcl:"all" flagName:"all" flagDescribe:"Turn on all features: download /, upload /, api, regeorg, ..." default:"false"`