diff --git a/README.md b/README.md index 2a4b4d8..81f299f 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,13 @@ It's **fast** :rocket:, **secured** :lock: and pretty **lite** :mouse2: What do you really need ? ### ngrok token + **https tunneling** (port-forwarding) is provided by [ngrok](https://ngrok.com/) go implementation. So, you **need a ngrok token** :key: ... if don't have a ngrok account, don't worry, **it's free** :grimacing: ### docker + If you're familiar with golang, you can build your own server but we recommand to **use our docker image** :whale: You can download [docker desktop](https://www.docker.com/products/docker-desktop/) and use default settings. @@ -30,13 +32,12 @@ You can download [docker desktop](https://www.docker.com/products/docker-desktop Ok, so now, you may have a **ngrok token** and **run docker** on your machine. -### :one: Let's start the server. +### :one: Let's start the server Run the following docker command in order to share the content of your local path. `docker run -it --rm -e NGROK_AUTHTOKEN="YOUR_TOKEN" -v $(pwd):/shared ghcr.io/trendev/ngrok-file-server` > this example is based on Linux/MacOS usage. If you run `ngrok-file-server` on Windows, you may change local path command `$(pwd)` by something like `%cd%` or direcly set the path... - > do not forget to replace "YOUR_TOKEN" by your ngrok token... It will pull our docker image and then, build and start a new container :thumbsup: @@ -47,12 +48,6 @@ You may see an output like this : Copy the URL :memo: -#### ... or start the server with a static domain ! - -At first, you may claim a `static domain` (cf [ngrok static domain](https://ngrok.com/blog-post/free-static-domains-ngrok-users?utm_campaign=Monthly%20Newsletter&utm_medium=email&_hsmi=273371496&_hsenc=p2ANqtz--jWYbTK8jp0_nOfOtdv6J-xx3yPPThs-yue05TvNqnWnV4cddDpbDVOBgfdT9o-xuo6-7UUEBImW1PlHTFoh3ZCmJCtw&utm_content=273371496&utm_source=hs_email)) - -`docker run -it --rm -e NGROK_AUTHTOKEN="YOUR_TOKEN" -v $(pwd):/shared ghcr.io/trendev/ngrok-file-server --static_domain={your_static_domain}.ngrok-free.app` - This URL is easier to remember, isn't it ? ### :two: Visit the file server from anywhere @@ -68,16 +63,16 @@ You can browse your content and share the URL with anyone and access to your fil You can also control access using an oauth2 provider (like Google, Facebook, Github, Linkedin, etc) and setting an authorized oauth2_domain (for eg, only `trendev.fr` users). -> you can find the supported list [here](https://ngrok.com/docs/cloud-edge/modules/oauth/#oauth-providers-supported-by-ngrok) +> you can find the [supported list](https://ngrok.com/docs/cloud-edge/modules/oauth/#oauth-providers-supported-by-ngrok) ### Enable oauth2 authentication + `docker run -it --rm -e NGROK_AUTHTOKEN=$NGROK_AUTHTOKEN -v $(pwd):/shared ghcr.io/trendev/ngrok-file-server --provider=google` ### Enable oauth2 authentication + oauth2_domain control + `docker run -it --rm -e NGROK_AUTHTOKEN=$NGROK_AUTHTOKEN -v $(pwd):/shared ghcr.io/trendev/ngrok-file-server --provider=google --oauth2_domain=trendev.fr` ## :hand: Something else ? If you need more, you can open an issue and describe your requirement... and BTW, you can also star the repo :wink: - - diff --git a/api/main.go b/api/main.go index 4a8e4d3..a7159f8 100644 --- a/api/main.go +++ b/api/main.go @@ -7,47 +7,95 @@ import ( "log" "net/http" + "github.com/Jeffail/gabs/v2" "github.com/trendev/ngrok-file-server/pkg/colorlog" - "golang.ngrok.com/ngrok" - "golang.ngrok.com/ngrok/config" + "golang.ngrok.com/ngrok/v2" ) -func setConfigHTTPEndpoint() config.Tunnel { - sd := flag.String("static_domain", "", "ngrok static domain") - p := flag.String("provider", "", "oauth2 provider") +const config = ` +{ + "on_http_response": [ + { + "actions": [ + { + "type": "add-headers", + "config": { + "headers": { + "x-ngrok-file-server": "trendev.fr", + "x-endpoint-id": "${endpoint.id}", + "x-client-ip": "${conn.client_ip}", + "x-client-conn-start": "${conn.ts.start}", + "x-client-loc": "${conn.geo.country}", + "x-client-path": "${req.url.path}" + } + } + } + ] + } + ] +}` + +func setConfigHTTPEndpoint() []ngrok.EndpointOption { + var opts []ngrok.EndpointOption + p := flag.String("provider", "", "oauth2 provider (google, github, etc.)") o2d := flag.String("oauth2_domain", "", "oauth2 authorized oauth2_domain") flag.Parse() - var opts []config.HTTPEndpointOption - - if *sd != "" { - opts = append(opts, config.WithDomain(*sd)) + c, err := gabs.ParseJSON([]byte(config)) + if err != nil { + log.Fatal(err) } + if *p != "" { + // Create OAuth action structure + a1 := gabs.New() + a1.Set("oauth", "type") + a1.Set(*p, "config", "provider") + + // Create request entry + r := gabs.New() + r.ArrayAppend(a1.Data(), "actions") + // Append to on_http_request array + c.ArrayAppend(r.Data(), "on_http_request") + if *o2d != "" { - opts = append(opts, config.WithOAuth(*p, config.WithAllowOAuthDomain(*o2d)), - config.WithRequestHeader("email", "${.oauth.user.email}")) - } else { - opts = append(opts, config.WithOAuth(*p), config.WithRequestHeader("email", "${.oauth.user.email}")) + dr := gabs.New() // domain rule + + da := gabs.New() // deny action + da.Set("deny", "type") + da.Set(401, "config", "status_code") + + expression := fmt.Sprintf( + "!actions.ngrok.oauth.identity.email.endsWith('%s')", + *o2d, + ) + + dr.ArrayAppend(expression, "expressions") + dr.ArrayAppend(da.Data(), "actions") + + // Ajouter la deuxième règle + c.ArrayAppend(dr.Data(), "on_http_request") } + + log.Println(c.StringIndent("", " ")) } - return config.HTTPEndpoint(opts...) + + opts = append(opts, ngrok.WithTrafficPolicy(c.String())) + return opts } func main() { fs := http.FileServer(http.Dir("./shared")) - l, err := ngrok.Listen(context.Background(), - setConfigHTTPEndpoint(), - ngrok.WithAuthtokenFromEnv(), - ) + l, err := ngrok.Listen(context.Background(), setConfigHTTPEndpoint()...) + if err != nil { log.Fatal(err) } fmt.Println("ngrok ingress url: ", l.URL()) http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { colorlog.LogRequest(*r) - w.Header().Add("x-ngrok-file-server", "trendev.fr") + // w.Header().Add("x-ngrok-file-server", "trendev.fr") ww := colorlog.NewResponseWriterWrapper(w) fs.ServeHTTP(ww, r) colorlog.LogResponse(*ww, r) diff --git a/go.mod b/go.mod index 7014bde..3ac0b3f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,9 @@ module github.com/trendev/ngrok-file-server go 1.24 -require golang.ngrok.com/ngrok v1.13.0 +require golang.ngrok.com/ngrok/v2 v2.0.0 + +require github.com/Jeffail/gabs/v2 v2.7.0 require ( github.com/go-stack/stack v1.8.1 // indirect @@ -14,10 +16,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.ngrok.com/muxado/v2 v2.0.1 // indirect golang.org/x/net v0.41.0 // indirect - golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 82b61b9..efde550 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg= +github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= @@ -12,11 +14,8 @@ github.com/inconshreveable/log15/v3 v3.0.0-testing.5 h1:h4e0f3kjgg+RJBlKOabrohjH github.com/inconshreveable/log15/v3 v3.0.0-testing.5/go.mod h1:3GQg1SVrLoWGfRv/kAZMsdyU5cp8eFc1P3cw+Wwku94= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -27,40 +26,20 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.ngrok.com/muxado/v2 v2.0.1 h1:jM9i6Pom6GGmnPrHKNR6OJRrUoHFkSZlJ3/S0zqdVpY= golang.ngrok.com/muxado/v2 v2.0.1/go.mod h1:wzxJYX4xiAtmwumzL+QsukVwFRXmPNv86vB8RPpOxyM= -golang.ngrok.com/ngrok v1.11.0 h1:lvbBcoOvH+Ek15wgrjvxpCB+PBM7vinU6jQPsrCdOLw= -golang.ngrok.com/ngrok v1.11.0/go.mod h1:1/gLOyOJm7ygHJlcEbtldFLQwQnQ42z+rucpLE2YsvA= -golang.ngrok.com/ngrok v1.13.0 h1:6SeOS+DAeIaHlkDmNH5waFHv0xjlavOV3wml0Z59/8k= -golang.ngrok.com/ngrok v1.13.0/go.mod h1:BKOMdoZXfD4w6o3EtE7Cu9TVbaUWBqptrZRWnVcAuI4= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.ngrok.com/ngrok/v2 v2.0.0 h1:eUEF7ULph6hUdOVR9r7oue2UhT2vvDoLAo0q//N6vJo= +golang.ngrok.com/ngrok/v2 v2.0.0/go.mod h1:nppMCtZ44/KeGrDHOV0c4bRyMGdHCEBo2Rvjdv/1Uio= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=