Skip to content

Commit

Permalink
Feat/jit plugin (#93)
Browse files Browse the repository at this point in the history
* added jit API, service and storage

* dummy jit plugin created

* added jit plugin to connect verb

* fixed loader messages

* fixed the review vs jit permission to connect and exec verbs

* removed verbose logs

* added timeout to jit connections

* asking user how many minutes he wants to be connected

* remove unecessary return statement

* error msg
  • Loading branch information
danielbonilha authored Jan 2, 2023
1 parent 9ccc637 commit e9c5951
Show file tree
Hide file tree
Showing 14 changed files with 853 additions and 26 deletions.
122 changes: 119 additions & 3 deletions client/cmd/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type connect struct {
clientArgs []string
connectionName string
waitingReview *pb.Packet
waitingJit *pb.Packet
loader *spinner.Spinner
}

Expand All @@ -75,12 +76,12 @@ func runConnect(args []string) {
pkt, err := c.client.Recv()
c.processGracefulExit(err)
if pkt != nil {
c.processPacket(pkt, config)
c.processPacket(pkt, config, loader)
}
}
}

func (c *connect) processPacket(pkt *pb.Packet, config *Config) {
func (c *connect) processPacket(pkt *pb.Packet, config *Config, loader *spinner.Spinner) {
switch pb.PacketType(pkt.Type) {

// connect
Expand Down Expand Up @@ -141,11 +142,126 @@ func (c *connect) processPacket(pkt *pb.Packet, config *Config) {
default:
c.processGracefulExit(fmt.Errorf(`connection type %q not implemented`, string(connnectionType)))
}
case pb.PacketClientGatewayConnectRequestTimeoutType:
loader.Stop()
c.waitingJit = pkt
tty, err := os.Open("/dev/tty")
if err != nil {
log.Fatal(err)
}
defer tty.Close()

oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }()

os.Stdin = tty

var input string
fmt.Print("For how many minutes do you want to connect? ")

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
s := scanner.Text()
input += s
break
}

if _, err := strconv.Atoi(input); err != nil {
c.processGracefulExit(fmt.Errorf(`invalid number %q`, input))
}

c.waitingJit.Type = string(pb.PacketClientGatewayConnectType)
c.waitingJit.Spec[pb.SpecJitTimeout] = []byte(input)
_ = c.client.Send(c.waitingJit)

case pb.PacketClientGatewayConnectWaitType:
loader.Stop()
loader.Start()
loader.Suffix = " Waiting approval"
c.waitingJit = pkt
reviewID := string(pkt.Spec[pb.SpecGatewayJitID])
fmt.Println("\nThis connection requires review. We will notify you here when it is approved.")
fmt.Printf("Check the status at: %s\n\n", buildReviewUrl(config, reviewID, "jits"))
case pb.PacketClientGatewayConnectApproveType:
loader.Stop()
tty, err := os.Open("/dev/tty")
if err != nil {
log.Fatal(err)
}
defer tty.Close()

oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }()

os.Stdin = tty

var input string
fmt.Print("The connection was approved! Do you want to continue?\n (y/n) [y] ")

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
s := scanner.Text()
input += s
break
}

if input == "" {
input = "y"
}

input = input[0:1]

if input == "y" {
c.waitingJit.Type = string(pb.PacketClientGatewayConnectType)
_ = c.client.Send(c.waitingJit)
c.waitingJit = nil
return
}
c.processGracefulExit(errors.New("user cancelled the action"))
case pb.PacketClientGatewayConnectRejectType:
c.processGracefulExit(errors.New("connection rejected. Sorry"))
case pb.PacketClientAgentConnectErrType:
sessionID := pkt.Spec[pb.SpecGatewaySessionID]
errMsg := fmt.Errorf("session=%s - failed connecting with gateway, err=%v",
string(sessionID), string(pkt.GetPayload()))
c.processGracefulExit(errMsg)
case pb.PacketClientConnectAgentOfflineType:
loader.Stop()
tty, err := os.Open("/dev/tty")
if err != nil {
log.Fatal(err)
}
defer tty.Close()

oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }()

os.Stdin = tty

var input string
fmt.Print("Agent is offline. Do you want to try again?\n (y/n) [y] ")

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
s := scanner.Text()
input += s
break
}

if input == "" {
input = "y"
}

input = input[0:1]

if input == "y" {
pkt.Type = string(pb.PacketClientGatewayConnectType)
_ = c.client.Send(pkt)
return
}
c.processGracefulExit(errors.New("user cancelled the action"))
case pb.PacketClientConnectTimeoutType:
fmt.Println("Time is up. Please reconnect to continue using")

// exec
case pb.PacketClientExecAgentOfflineType:
Expand Down Expand Up @@ -186,7 +302,7 @@ func (c *connect) processPacket(pkt *pb.Packet, config *Config) {
c.waitingReview = pkt
reviewID := string(pkt.Spec[pb.SpecGatewayReviewID])
fmt.Println("\nThis command requires review. We will notify you here when it is approved.")
fmt.Printf("Check the status at: %s\n\n", buildReviewUrl(config, reviewID))
fmt.Printf("Check the status at: %s\n\n", buildReviewUrl(config, reviewID, "reviews"))
case pb.PacketClientGatewayExecApproveType:
tty, err := os.Open("/dev/tty")
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions client/cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ func runExec(args []string) {
pkt, err := c.client.Recv()
c.processGracefulExit(err)
if pkt != nil {
c.processPacket(pkt, config)
c.processPacket(pkt, config, loader)
}
}
}

func buildReviewUrl(conf *Config, reviewID string) string {
func buildReviewUrl(conf *Config, id string, url string) string {
protocol := "https"
if strings.HasPrefix(conf.Host, "127.0.0.1") {
protocol = "http"
}
return fmt.Sprintf("%s://%s/plugins/reviews/%s", protocol, conf.Host, reviewID)
return fmt.Sprintf("%s://%s/plugins/%s/%s", protocol, conf.Host, url, id)
}
17 changes: 13 additions & 4 deletions common/proto/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ const (
PacketAgentGatewayConnectOK PacketType = "Agent::Gateway::ConnectOK"

// client->agent connection
PacketClientGatewayConnectType PacketType = "Client::Gateway::Connect"
PacketClientAgentConnectType PacketType = "Client::Agent::Connect"
PacketClientAgentConnectOKType PacketType = "Client::Agent::ConnectOK"
PacketClientAgentConnectErrType PacketType = "Client::Agent::ConnectErr"
PacketClientGatewayConnectType PacketType = "Client::Gateway::Connect"
PacketClientAgentConnectType PacketType = "Client::Agent::Connect"
PacketClientGatewayConnectWaitType PacketType = "Client::Gateway::ConnectWait"
PacketClientGatewayConnectApproveType PacketType = "Client::Gateway::ConnectApprove"
PacketClientGatewayConnectRejectType PacketType = "Client::Gateway::ConnectReject"
PacketClientAgentConnectOKType PacketType = "Client::Agent::ConnectOK"
PacketClientAgentConnectErrType PacketType = "Client::Agent::ConnectErr"
PacketClientConnectAgentOfflineType PacketType = "Client::Gateway::ConnectAgentOffline"
PacketClientGatewayConnectRequestTimeoutType PacketType = "Client::Gateway::ConnectRequestTimeout"
PacketClientConnectTimeoutType PacketType = "Client::Gateway::ConnectTimeout"

// client->agent exec
PacketClientGatewayExecType PacketType = "Client::Gateway::Exec"
Expand Down Expand Up @@ -48,6 +54,9 @@ const (
SpecTCPServerConnectKey string = "tcp.server_connect"
SpecReviewDataKey string = "review.data"
SpecGatewayReviewID string = "review.id"
SpecJitDataKey string = "jit.data"
SpecGatewayJitID string = "jit.id"
SpecJitTimeout string = "jit.timeout"

DefaultKeepAlive time.Duration = 10 * time.Second

Expand Down
12 changes: 12 additions & 0 deletions gateway/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"fmt"
"github.com/runopsio/hoop/gateway/review/jit"
"os"
"strings"

Expand All @@ -28,6 +29,7 @@ type (
PluginHandler plugin.Handler
SessionHandler session.Handler
ReviewHandler review.Handler
JitHandler jit.Handler
SecurityHandler security.Handler
IDProvider *idp.Provider
Profile string
Expand Down Expand Up @@ -120,6 +122,16 @@ func (api *Api) buildRoutes(route *gin.RouterGroup) {
api.Authenticate,
api.ReviewHandler.Put)

route.GET("/jits",
api.Authenticate,
api.JitHandler.FindAll)
route.GET("/jits/:id",
api.Authenticate,
api.JitHandler.FindOne)
route.PUT("/jits/:id",
api.Authenticate,
api.JitHandler.Put)

route.POST("/agents",
api.Authenticate,
api.AdminOnly,
Expand Down
22 changes: 12 additions & 10 deletions gateway/client/service.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package client

import (
"context"
"github.com/runopsio/hoop/gateway/user"
)

Expand All @@ -15,16 +16,17 @@ type (
}

Client struct {
Id string `json:"-" edn:"xt/id"`
SessionID string `json:"session_id" edn:"client/session-id"`
OrgId string `json:"-" edn:"client/org"`
UserId string `json:"-" edn:"client/user"`
Hostname string `json:"hostname" edn:"client/hostname"`
MachineId string `json:"machine_id" edn:"client/machine-id"`
KernelVersion string `json:"kernel_version" edn:"client/kernel-version"`
Status Status `json:"status" edn:"client/status"`
ConnectionId string `json:"-" edn:"client/connection"`
AgentId string `json:"-" edn:"client/agent"`
Id string `json:"-" edn:"xt/id"`
SessionID string `json:"session_id" edn:"client/session-id"`
OrgId string `json:"-" edn:"client/org"`
UserId string `json:"-" edn:"client/user"`
Hostname string `json:"hostname" edn:"client/hostname"`
MachineId string `json:"machine_id" edn:"client/machine-id"`
KernelVersion string `json:"kernel_version" edn:"client/kernel-version"`
Status Status `json:"status" edn:"client/status"`
ConnectionId string `json:"-" edn:"client/connection"`
AgentId string `json:"-" edn:"client/agent"`
Context context.Context `json:"-" edn:"-"`
}

Status string
Expand Down
5 changes: 5 additions & 0 deletions gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"github.com/runopsio/hoop/gateway/notification"
"github.com/runopsio/hoop/gateway/review"
"github.com/runopsio/hoop/gateway/review/jit"
"os"

"github.com/runopsio/hoop/gateway/plugin"
Expand Down Expand Up @@ -41,6 +42,7 @@ func Run() {
clientService := client.Service{Storage: &client.Storage{Storage: s}}
sessionService := session.Service{Storage: &session.Storage{Storage: s}}
reviewService := review.Service{Storage: &review.Storage{Storage: s}}
jitService := jit.Service{Storage: &jit.Storage{Storage: s}}
notificationService := notification.NewMagicBell()
securityService := security.Service{
Storage: &security.Storage{Storage: s},
Expand All @@ -54,6 +56,7 @@ func Run() {
PluginHandler: plugin.Handler{Service: &pluginService},
SessionHandler: session.Handler{Service: &sessionService},
ReviewHandler: review.Handler{Service: &reviewService},
JitHandler: jit.Handler{Service: &jitService},
SecurityHandler: security.Handler{Service: &securityService},
IDProvider: idProvider,
Profile: profile,
Expand All @@ -67,12 +70,14 @@ func Run() {
PluginService: pluginService,
SessionService: sessionService,
ReviewService: reviewService,
JitService: jitService,
NotificationService: notificationService,
IDProvider: idProvider,
Profile: profile,
GcpDLPRawCredentials: os.Getenv("GOOGLE_APPLICATION_CREDENTIALS_JSON"),
}
reviewService.TransportService = g
jitService.TransportService = g

if profile == pb.DevProfile {
if err := a.CreateTrialEntities(); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion gateway/notification/magicbell.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (m *MagicBell) Send(notification Notification) {
log.Printf("failed to send magic bell notification: %v", err)
return
}
fmt.Printf("Sent notification to %d recipients", len(notification.Recipients))
fmt.Printf("Sent notification to %d recipients\n", len(notification.Recipients))
defer resp.Body.Close()
}
}
Expand Down
Loading

0 comments on commit e9c5951

Please sign in to comment.