Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial work on adding audit capabilities to lottip #12

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6a59be4
Updated pingPeriod and writeDeadlinePeriod
brainz80 Feb 16, 2021
b250cb5
Updated Dockerfile
brainz80 Feb 16, 2021
47060c2
Added .gitattributes to docker -folder
brainz80 Feb 16, 2021
6fa5f0e
Update run.sh
brainz80 Feb 16, 2021
6869848
Replaced imports
brainz80 Feb 16, 2021
8e0e02a
Updated title
brainz80 Feb 16, 2021
f5e18c5
Added prism
brainz80 Feb 16, 2021
68a5a19
Add log to request and response
brainz80 Feb 17, 2021
bfcb813
Fix run.sh to run lottip from /root/go/bin
brainz80 Feb 17, 2021
3d89e89
Remove src
brainz80 Feb 17, 2021
78b28e0
Major refactoring and reworking
rkoshy May 23, 2022
026723f
Significant refactoring & cleanup to allow proper logging of queries …
rkoshy Jun 19, 2022
99dc6ac
Changed some defaults for the CLI
rkoshy Jun 19, 2022
5813b1c
Minor cleanup to remove unnecessary exports
rkoshy Jun 19, 2022
32ba19f
Added the go module file
rkoshy Jun 19, 2022
af7a916
Added the ability to enable and customize the logfiles [default to wr…
rkoshy Jun 20, 2022
0b24b00
Updated the docker run script
rkoshy Jun 20, 2022
73fa4cb
fixed the console logging flag in the run script
rkoshy Jun 20, 2022
b312afd
Fixed the Dockerfile
rkoshy Jun 20, 2022
b3df05e
Updated to use cross platform file times lib
rkoshy Jun 21, 2022
069b0e7
Rolled back changes to the file time
rkoshy Jun 21, 2022
3f27789
Commented out file rotation for now
rkoshy Jun 21, 2022
e7bfcbb
Fixed the run.sh
rkoshy Jun 21, 2022
627c2ee
Changed the way file rolling works. Added support for sending OK pack…
rkoshy Jun 21, 2022
7642af0
Fixed packet logging to support logging packets in either direction
rkoshy Jun 21, 2022
1668972
Fixed the server handshake parsing/forcing of uncompressed protocol
rkoshy Jun 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions chat/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
)

const (
pingPeriod = time.Millisecond * 5000
writeDeadlinePeriod = time.Second * 2
pingPeriod = time.Millisecond * 60000
writeDeadlinePeriod = time.Second * 60
)

// Client represents client(browser) connected via websocket
Expand Down
37 changes: 37 additions & 0 deletions client_to_server_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"io"
"lottip/chat"
"lottip/protocol"
)

type ClientToServerHandler struct {
connInfo *ConnectionInfo
queryChan chan chat.Cmd
connStateChan chan chat.ConnState
server io.Writer
}

// CLIENT to SERVER
func (pp *ClientToServerHandler) Write(buffer []byte) (n int, err error) {
extractPacketsFromBuffer(pp.connInfo, pp.connInfo.clientPacketFragment, buffer, func(packet []byte) {
// Switch based on the state
fsm := pp.connInfo.fsm

if ok, _ := fsm.IsInState(StateIdle); ok {
pp.connStateChan <- chat.ConnState{pp.connInfo.ConnId, protocol.ConnStateFinished}
} else if ok, _ := fsm.IsInState(StateAuthRequested); ok {
fsm.Fire(MsgLogin, packet)
} else {
if len(packet) < 4 {
LogInvalid(pp.connInfo, "?Request?", packet)
} else {
// We are processing queries
fsm.Fire(GetPacketType(packet), packet)
}
}
})

return len(buffer), nil
}
2 changes: 2 additions & 0 deletions docker/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Dockerfile text eol=LF
*.sh text eol=LF
19 changes: 9 additions & 10 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
FROM frolvlad/alpine-go


RUN apk update && apk upgrade && \
apk add --no-cache bash git

RUN go get github.com/orderbynull/lottip && \
go get github.com/mjibson/esc && \
go install github.com/mjibson/esc && \
cd /root/go/src/github.com/orderbynull/lottip && \
/root/go/bin/esc -o fs.go -prefix web -include=".*\.css|.*\.js|.*\.html|.*\.png" web && \
go build
RUN git clone http://github.com/rkoshy/lottip
WORKDIR lottip
RUN go get github.com/mjibson/esc
RUN go install github.com/mjibson/esc
RUN /root/go/bin/esc -o fs.go -prefix web -include=".*\.css|.*\.js|.*\.html|.*\.png" web
RUN go get
RUN go install
WORKDIR /
RUN rm -rf /root/go/bin/esc && rm -rf lottip

ADD run.sh /run.sh
RUN chmod +x /run.sh
Expand All @@ -18,6 +20,3 @@ EXPOSE 4041
EXPOSE 9999

CMD /run.sh



27 changes: 23 additions & 4 deletions docker/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,30 @@ LOTTIP_GUI="${LOTTIP_GUI:-0.0.0.0:9999}"
# MySQL DSN (credentials)
LOTTIP_DSN="${LOTTIP_DSN:-root:root@/}"

# Log directory
LOTTIP_LOG_DIRECTORY="${LOTTIP_LOG_DIRECTORY:-./logs}"

# Run lottip
# Log filename
LOTTIP_LOGFILE="${LOTTIP_LOGFILE:-logfile.log}"

# Query Log filename
LOTTIP_QUERY_LOGFILE="${LOTTIP_QUERY_LOGFILE:-queries.log}"

/root/go/src/github.com/orderbynull/lottip/lottip \
# Enable
if [ "${LOTTIP_CONSOLE_LOGGING}" == "true" ];
then
LOTTIP_CONSOLE_LOGGING="--enable-console-logging"
else
LOTTIP_CONSOLE_LOGGING=""
fi

# Run lottip
/root/go/bin/lottip \
--proxy "$LOTTIP_PROXY" \
--mysql "$LOTTIP_MYSQL" \
--gui "$LOTTIP_GUI" \
--mysql-dsn "$LOTTIP_DSN"
--gui-addr "$LOTTIP_GUI" \
--mysql-dsn "$LOTTIP_DSN" \
"$LOTTIP_CONSOLE_LOGGING" \
--query-log-file "$LOTTIP_QUERY_LOGFILE" \
--log-file "$LOTTIP_LOGFILE" \
--log-directory "$LOTTIP_LOG_DIRECTORY"
412 changes: 318 additions & 94 deletions fs.go

Large diffs are not rendered by default.

207 changes: 207 additions & 0 deletions fsm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package main

import (
"context"
"fmt"
"github.com/qmuntal/stateless"
"lottip/chat"
"lottip/protocol"
"net"
"reflect"
"time"
)

type MySQLProtocolFSM struct {
*stateless.StateMachine
connectionInfo *ConnectionInfo
clientConn net.Conn
serverConn net.Conn
}

const (
StateIdle = "Idle"
StateAuthRequested = "AuthRequested"
StateAuthSent = "AuthSent"
StateAuthorized = "stateAuthorized"
StateUnauthorized = "stateUnauthorized"
)

const (
PacketReceived = "PacketReceived"
MsgServerHello = "ServerHello"
MsgLogin = "Login"
MsgOK = "OK"
MsgERROR = "ERROR"
MsgServerToClient = "MsgServerToClient"
)

func CreateStateMachine(ci *ConnectionInfo, clientConn net.Conn, serverConn net.Conn, cmdChan chan chat.Cmd, resultChan chan chat.CmdResult, stateChan chan chat.ConnState) *MySQLProtocolFSM {
fsm := stateless.NewStateMachine(StateIdle)

fsm.SetTriggerParameters(PacketReceived, reflect.TypeOf([]byte{}))
fsm.OnUnhandledTrigger(func(ctx context.Context, state stateless.State, trigger stateless.Trigger, unmetGuards []string) error {
LogOther(ci, "fsm - Unhandled event", "%d in state %s", trigger, state)
return nil
})

fsm.Configure(StateIdle).
Permit(MsgServerHello, StateAuthRequested, func(ctx context.Context, args ...interface{}) bool {
// Server's initial response asking for loging info -- passthrough after changing compression flag
packet := args[0].([]byte)
index := 4
if packet[index] == 0x0A {
// Valid protocol, skip server version
for ; packet[index] != 0 && index < len(packet); index++ {
}
index++
// Skip thread-id
index += 4
// Skip salt
for ; packet[index] != 0 && index < len(packet); index++ {
}
index++

packet[index] = packet[index] & 0xDF
LogResponse(ci, packet, "Handshake", "Server Handshake/Challenge response (forcing uncompressed protocol)")

//LogOther(ci, "fsm - Writing to client", "% x", packet)
clientConn.Write(packet)
return true
} else {
LogOther(ci, "INVALID PROTOCOL", "%x is not a supported protocol version", packet[4])

clientConn.Close()
serverConn.Close()
return false
}
})

fsm.Configure(StateAuthRequested).
Permit(MsgLogin, StateAuthSent, func(ctx context.Context, args ...interface{}) bool {
// Client's Auth Info
packet := args[0].([]byte)

start := 3 + 1 + 2 + 2 + 4 + 1
for ; packet[start] == 0; start++ {
}
stop := start + 1
for ; packet[stop] != 0; stop++ {
}
ci.User = string(packet[start:stop])
LogRequest(ci, packet, "Login", "Authorizing as user: '%s' (forcing uncompressed protocol)", ci.User)

// Disable compression
packet[4] = packet[4] & 0xDF

//LogOther(ci, "fsm - Writing to server", "% x", packet)
serverConn.Write(packet)
return true
})

fsm.Configure(StateAuthSent).
Permit(MsgOK, StateAuthorized, func(ctx context.Context, args ...interface{}) bool {
packet := args[0].([]byte)
LogResponse(ci, packet, "Authorized", "Client auth successful")
//LogOther(ci, "fsm - Writing to client", "% x", packet)
clientConn.Write(packet)
return true
}).
Permit(MsgERROR, StateUnauthorized, func(ctx context.Context, args ...interface{}) bool {
// Server's initial response asking for loging info -- passthrough after changing compression flag
packet := args[0].([]byte)
LogResponse(ci, packet, "Unauthorized", "Client auth failed")
//LogOther(ci, "fsm - Writing to client", "% x", packet)
clientConn.Write(packet)
return true
})

fsm.Configure(StateAuthorized).
InternalTransition(protocol.ComChangeUser, func(ctx context.Context, args ...interface{}) error {
// Do not allow this!
packet := args[0].([]byte)
start := 3 + 1 + 2 + 2 + 4 + 1
for ; packet[start] == 0; start++ {
}
stop := start + 1
for ; packet[stop] != 0; stop++ {
}
LogRequest(ci, packet, "ChangeUser", "Will reject request to change user to %s", string(packet[start:stop]))
return nil
}).
InternalTransition(protocol.ComPing, func(ctx context.Context, args ...interface{}) error {
packet := args[0].([]byte)
LogRequest(ci, packet, "Ping")
serverConn.Write(packet)
return nil
}).
InternalTransition(protocol.ComCreateDB, logAndSendQueryToServer(ci, serverConn, "CreateDB", cmdChan)).
InternalTransition(protocol.ComDropDB, logAndSendQueryToServer(ci, serverConn, "DropDB", cmdChan)).
InternalTransition(protocol.ComShutdown, logAndDrop(ci, "Shutdown", cmdChan)).
InternalTransition(protocol.ComProcessKill, logAndSendQueryToServer(ci, serverConn, "ProcessKill", cmdChan)).
InternalTransition(protocol.ComQuery, logAndSendQueryToServer(ci, serverConn, "Query", cmdChan)).
InternalTransition(protocol.ComStmtPrepare, logAndSendQueryToServer(ci, serverConn, "StmtPrepare", cmdChan)).
InternalTransition(protocol.ComStmtExecute, logAndSendQueryToServer(ci, serverConn, "StmtExecute", cmdChan)).
InternalTransition(protocol.ComStmtClose, logAndSendQueryToServer(ci, serverConn, "StmtClose", cmdChan)).
InternalTransition(protocol.ComStmtSendLongData, logAndSendQueryToServer(ci, serverConn, "StmtSendLongData", cmdChan)).
InternalTransition(protocol.ComStmtReset, logAndSendQueryToServer(ci, serverConn, "StmtReset", cmdChan)).
InternalTransition(protocol.ComQuit, logAndSendQueryToServer(ci, serverConn, "Quit", cmdChan)).
InternalTransition(protocol.ComBinlogDump, logAndDrop(ci, "BinlogDump", cmdChan)).
InternalTransition(protocol.ComBinlogDump, logAndDrop(ci, "BinlogDump", cmdChan)).
InternalTransition(protocol.ComTableDump, logAndDrop(ci, "TableDump", cmdChan)).
InternalTransition(protocol.ComConnectOut, logAndDrop(ci, "ConnectOut", cmdChan)).
InternalTransition(MsgOK, func(ctx context.Context, args ...interface{}) error {
packet := args[0].([]byte)
LogResponse(ci, packet, "OK")
duration := fmt.Sprintf("%.3f", time.Since(*ci.timer).Seconds())
resultChan <- chat.CmdResult{ci.ConnId, ci.QueryId, protocol.ResponseOk, "", duration}
clientConn.Write(packet)
return nil
}).
InternalTransition(MsgERROR, func(ctx context.Context, args ...interface{}) error {
packet := args[0].([]byte)
LogResponse(ci, packet, "ERROR")
duration := fmt.Sprintf("%.3f", time.Since(*ci.timer).Seconds())
resultChan <- chat.CmdResult{ci.ConnId, ci.QueryId, protocol.ResponseErr, "", duration}
clientConn.Write(packet)
return nil
}).
InternalTransition(MsgServerToClient, func(ctx context.Context, args ...interface{}) error {
packet := args[0].([]byte)
LogResponsePacket(ci, packet)
clientConn.Write(packet)
return nil
})

return &MySQLProtocolFSM{fsm, ci, clientConn, serverConn}
}

func logAndSendQueryToServer(ci *ConnectionInfo, serverConn net.Conn, command string, cmdChan chan chat.Cmd) func(ctx context.Context, args ...interface{}) error {
return func(ctx context.Context, args ...interface{}) error {
packet := args[0].([]byte)
query := string(packet[5:])
LogRequest(ci, packet, command, query)

ci.QueryId++
*ci.timer = time.Now()

cmdChan <- chat.Cmd{ci.ConnId, ci.QueryId, "", query, nil, false}
//LogOther(ci, "fsm - Writing to server", "% x", packet)
serverConn.Write(packet)
return nil
}
}

func logAndDrop(ci *ConnectionInfo, command string, cmdChan chan chat.Cmd) func(ctx context.Context, args ...interface{}) error {
return func(ctx context.Context, args ...interface{}) error {
packet := args[0].([]byte)
query := string(packet[5:])
LogRequest(ci, packet, "BLOCKED:"+command, query)
cmdChan <- chat.Cmd{ci.ConnId, ci.QueryId, "", query, nil, false}
LogOther(ci, "fsm - Dropping packet", "% x", packet)
return nil
}
}

func (fsm *MySQLProtocolFSM) ProcessPacket(packet []byte) {
fsm.Fire(MsgERROR)
}
24 changes: 24 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module lottip

go 1.18

require (
github.com/go-sql-driver/mysql v1.6.0
github.com/gorilla/websocket v1.5.0
github.com/olekukonko/tablewriter v0.0.5
)

require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
)

require (
github.com/kjk/dailyrotate v0.0.0-20220410204435-837f1ef47fc4
github.com/pubnative/mysqlproto-go v0.0.0-20210816144457-71d8293daef4
github.com/qmuntal/stateless v1.6.0
github.com/rs/zerolog v1.27.0
github.com/stretchr/testify v1.7.1 // indirect
)
13 changes: 8 additions & 5 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package main

import (
"fmt"
"log"
"github.com/rs/zerolog/log"
"lottip/chat"
"net/http"

"encoding/json"
"github.com/gorilla/websocket"
"github.com/olekukonko/tablewriter"
"github.com/orderbynull/lottip/chat"
)

const (
Expand All @@ -23,7 +23,7 @@ func runHttpServer(hub *chat.Hub) {

conn, err := upgr.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
log.Error().Err(err).Msg("Could not upgrade connection from " + r.RemoteAddr + " to websocket")
return
}

Expand All @@ -46,7 +46,7 @@ func runHttpServer(hub *chat.Hub) {
http.HandleFunc("/execute", func(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Println(err)
log.Error().Err(err).Msg("Could not parse query execution request from " + r.RemoteAddr)
return
}

Expand Down Expand Up @@ -80,5 +80,8 @@ func runHttpServer(hub *chat.Hub) {

http.Handle(webRoute, http.FileServer(FS(*useLocalUI)))

log.Fatal(http.ListenAndServe(*guiAddr, nil))
err := http.ListenAndServe(*guiAddr, nil)
if err != nil {
log.Fatal().Err(err).Msg("Could not start HTTP server at " + *guiAddr)
}
}
Loading