diff --git a/.github/workflows/pre-release.yaml b/.github/workflows/pre-release.yaml new file mode 100644 index 0000000..d893411 --- /dev/null +++ b/.github/workflows/pre-release.yaml @@ -0,0 +1,36 @@ +--- +name: "pre-release" + +on: + push: + branches: + - "master" + +jobs: + pre-release-docker: + name: "Pre Release Docker" + runs-on: "ubuntu-latest" + + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: docker/setup-qemu-action@v1 + + - uses: docker/setup-buildx-action@v1 + + - uses: docker/login-action@v1 + with: + username: "${{ secrets.DOCKER_HUB_USER }}" + password: "${{ secrets.DOCKER_HUB_TOKEN }}" + + - name: "Build and push docker image" + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm/v7,linux/arm64 + push: true + tags: "${{ secrets.DOCKER_REPO }}:latest" diff --git a/Dockerfile b/Dockerfile index 565e142..bd4b7dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,20 @@ -FROM golang:1.16 +FROM node:16 as js-build +WORKDIR /gotty +COPY js /gotty/js +COPY Makefile /gotty/ +RUN make bindata/static/js/gotty.js.map +FROM golang:1.16 as go-build WORKDIR /gotty COPY . /gotty +COPY --from=js-build /gotty/js/node_modules /gotty/js/node_modules +COPY --from=js-build /gotty/bindata/static/js /gotty/bindata/static/js RUN CGO_ENABLED=0 make FROM alpine:latest - RUN apk update && \ apk upgrade && \ - apk --no-cache add ca-certificates && \ - apk add bash + apk --no-cache add ca-certificates bash WORKDIR /root -COPY --from=0 /gotty/gotty /usr/bin/ +COPY --from=go-build /gotty/gotty /usr/bin/ CMD ["gotty", "-w", "bash"] diff --git a/backend/localcommand/factory.go b/backend/localcommand/factory.go index 9d0c916..be6da37 100644 --- a/backend/localcommand/factory.go +++ b/backend/localcommand/factory.go @@ -37,12 +37,12 @@ func (factory *Factory) Name() string { return "local command" } -func (factory *Factory) New(params map[string][]string) (server.Slave, error) { +func (factory *Factory) New(params map[string][]string, headers map[string][]string) (server.Slave, error) { argv := make([]string, len(factory.argv)) copy(argv, factory.argv) if params["arg"] != nil && len(params["arg"]) > 0 { argv = append(argv, params["arg"]...) } - return New(factory.command, argv, factory.opts...) + return New(factory.command, argv, headers, factory.opts...) } diff --git a/backend/localcommand/local_command.go b/backend/localcommand/local_command.go index 71b6c18..d0e0753 100644 --- a/backend/localcommand/local_command.go +++ b/backend/localcommand/local_command.go @@ -3,6 +3,7 @@ package localcommand import ( "os" "os/exec" + "strings" "syscall" "time" @@ -27,11 +28,22 @@ type LocalCommand struct { ptyClosed chan struct{} } -func New(command string, argv []string, options ...Option) (*LocalCommand, error) { +func New(command string, argv []string, headers map[string][]string, options ...Option) (*LocalCommand, error) { cmd := exec.Command(command, argv...) cmd.Env = append(os.Environ(), "TERM=xterm-256color") + // Combine headers into key=value pairs to set as env vars + // Prefix the headers with "http_" so we don't overwrite any other env vars + // which potentially has the same name and to bring these closer to what + // a (F)CGI server would proxy to a backend service + // Replace hyphen with underscore and make them all upper case + for key, values := range headers { + h := "HTTP_" + strings.Replace(strings.ToUpper(key), "-", "_", -1) + "=" + strings.Join(values, ",") + // log.Printf("Adding header: %s", h) + cmd.Env = append(cmd.Env, h) + } + pty, err := pty.Start(cmd) if err != nil { // todo close cmd? diff --git a/backend/localcommand/local_command_test.go b/backend/localcommand/local_command_test.go index ddda029..220cb73 100644 --- a/backend/localcommand/local_command_test.go +++ b/backend/localcommand/local_command_test.go @@ -23,7 +23,7 @@ func TestNewFactory(t *testing.T) { t.Errorf("factory.options = %v, expected %v", factory.options, &Options{}) } - slave, _ := factory.New(nil) + slave, _ := factory.New(nil, nil) lcmd := slave.(*LocalCommand) if lcmd.closeSignal != 123 { t.Errorf("lcmd.closeSignal = %v, expected %v", lcmd.closeSignal, 123) @@ -40,7 +40,7 @@ func TestFactoryNew(t *testing.T) { return } - slave, err := factory.New(nil) + slave, err := factory.New(nil, nil) if err != nil { t.Errorf("factory.New() returned error") return diff --git a/js/package-lock.json b/js/package-lock.json index 97d3210..4c2069c 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1384,9 +1384,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -3770,9 +3770,9 @@ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" }, "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", diff --git a/server/handlers.go b/server/handlers.go index 641cc7f..3393b22 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -72,7 +72,11 @@ func (server *Server) generateHandleWS(ctx context.Context, cancel context.Cance } defer conn.Close() - err = server.processWSConn(ctx, conn) + if server.options.PassHeaders { + err = server.processWSConn(ctx, conn, r.Header) + } else { + err = server.processWSConn(ctx, conn, nil) + } switch err { case ctx.Err(): @@ -87,7 +91,7 @@ func (server *Server) generateHandleWS(ctx context.Context, cancel context.Cance } } -func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn) error { +func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn, headers map[string][]string) error { typ, initLine, err := conn.ReadMessage() if err != nil { return errors.Wrapf(err, "failed to authenticate websocket connection") @@ -116,7 +120,7 @@ func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn) e } params := query.Query() var slave Slave - slave, err = server.factory.New(params) + slave, err = server.factory.New(params, headers) if err != nil { return errors.Wrapf(err, "failed to create backend") } diff --git a/server/options.go b/server/options.go index b7dc3d3..b03d78d 100644 --- a/server/options.go +++ b/server/options.go @@ -26,6 +26,7 @@ type Options struct { Once bool `hcl:"once" flagName:"once" flagDescribe:"Accept only one client and exit on disconnection" default:"false"` Timeout int `hcl:"timeout" flagName:"timeout" flagDescribe:"Timeout seconds for waiting a client(0 to disable)" default:"0"` PermitArguments bool `hcl:"permit_arguments" flagName:"permit-arguments" flagDescribe:"Permit clients to send command line arguments in URL (e.g. http://example.com:8080/?arg=AAA&arg=BBB)" default:"false"` + PassHeaders bool `hcl:"pass_headers" flagName:"pass-headers" flagDescribe:"Pass HTTP request headers as environment variables (e.g. Cookie becomes HTTP_COOKIE)" default:"false"` Width int `hcl:"width" flagName:"width" flagDescribe:"Static width of the screen, 0(default) means dynamically resize" default:"0"` Height int `hcl:"height" flagName:"height" flagDescribe:"Static height of the screen, 0(default) means dynamically resize" default:"0"` WSOrigin string `hcl:"ws_origin" flagName:"ws-origin" flagDescribe:"A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default" default:""` diff --git a/server/slave.go b/server/slave.go index 52cd9fe..db9d731 100644 --- a/server/slave.go +++ b/server/slave.go @@ -13,5 +13,5 @@ type Slave interface { type Factory interface { Name() string - New(params map[string][]string) (Slave, error) + New(params map[string][]string, headers map[string][]string) (Slave, error) }