add webinterface WIP
This commit is contained in:
parent
d20e749038
commit
3503e6abe6
|
@ -24,7 +24,7 @@ test-my-project:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- go get github.com/client9/misspell/cmd/misspell
|
- go get github.com/client9/misspell/cmd/misspell
|
||||||
- misspell -error .
|
- find . -type f | grep -v webroot/assets | xargs misspell -error
|
||||||
- ./.ci/check-gofmt
|
- ./.ci/check-gofmt
|
||||||
- ./.ci/check-testfiles
|
- ./.ci/check-testfiles
|
||||||
- go test $(go list ./... | grep -v /vendor/) -v -coverprofile .testCoverage.txt
|
- go test $(go list ./... | grep -v /vendor/) -v -coverprofile .testCoverage.txt
|
||||||
|
@ -44,7 +44,10 @@ deploy:
|
||||||
script:
|
script:
|
||||||
- go install "dev.sum7.eu/$CI_PROJECT_PATH"
|
- go install "dev.sum7.eu/$CI_PROJECT_PATH"
|
||||||
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
||||||
|
- 'which rsync || ( apt-get update -y && apt-get install rsync -y )'
|
||||||
- eval $(ssh-agent -s)
|
- eval $(ssh-agent -s)
|
||||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
||||||
|
- rsync -e "ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT" -a --delete "/builds/$CI_PROJECT_PATH/webroot/" "$CI_PROJECT_NAME@$SSH_HOST":/opt/$CI_PROJECT_NAME/webroot/
|
||||||
|
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl stop $CI_PROJECT_NAME
|
||||||
- scp -6 -o StrictHostKeyChecking=no -P $SSH_PORT "/go/bin/$CI_PROJECT_NAME" "$CI_PROJECT_NAME@$SSH_HOST":/opt/$CI_PROJECT_NAME/bin
|
- scp -6 -o StrictHostKeyChecking=no -P $SSH_PORT "/go/bin/$CI_PROJECT_NAME" "$CI_PROJECT_NAME@$SSH_HOST":/opt/$CI_PROJECT_NAME/bin
|
||||||
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl restart $CI_PROJECT_NAME
|
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl start $CI_PROJECT_NAME
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -12,7 +13,9 @@ import (
|
||||||
"dev.sum7.eu/genofire/wifictld-analyzer/capture"
|
"dev.sum7.eu/genofire/wifictld-analyzer/capture"
|
||||||
"dev.sum7.eu/genofire/wifictld-analyzer/config"
|
"dev.sum7.eu/genofire/wifictld-analyzer/config"
|
||||||
"dev.sum7.eu/genofire/wifictld-analyzer/controller"
|
"dev.sum7.eu/genofire/wifictld-analyzer/controller"
|
||||||
|
"dev.sum7.eu/genofire/wifictld-analyzer/data"
|
||||||
"dev.sum7.eu/genofire/wifictld-analyzer/database"
|
"dev.sum7.eu/genofire/wifictld-analyzer/database"
|
||||||
|
"dev.sum7.eu/genofire/wifictld-analyzer/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// queryCmd represents the query command
|
// queryCmd represents the query command
|
||||||
|
@ -31,7 +34,22 @@ var controllerCmd = &cobra.Command{
|
||||||
ctr := controller.NewController(db)
|
ctr := controller.NewController(db)
|
||||||
defer ctr.Close()
|
defer ctr.Close()
|
||||||
|
|
||||||
coll := capture.NewCollector(ctr.Handler, configObj.Interfaces)
|
var handlers []data.Handler
|
||||||
|
|
||||||
|
if configObj.Webserver.Enable {
|
||||||
|
log.Infof("starting webserver on %s", configObj.Webserver.Bind)
|
||||||
|
srv := web.New(configObj.Webserver)
|
||||||
|
go srv.Start()
|
||||||
|
handlers = append(handlers, srv.Handler)
|
||||||
|
defer srv.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||||
|
for _, a := range handlers {
|
||||||
|
a(addr, msg)
|
||||||
|
}
|
||||||
|
return ctr.Handler(addr, msg)
|
||||||
|
}, configObj.Interfaces)
|
||||||
defer coll.Close()
|
defer coll.Close()
|
||||||
|
|
||||||
ctr.Send = coll.Send
|
ctr.Send = coll.Send
|
||||||
|
@ -47,5 +65,5 @@ var controllerCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCmd.AddCommand(controllerCmd)
|
RootCMD.AddCommand(controllerCmd)
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ var dumpCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCmd.AddCommand(dumpCmd)
|
RootCMD.AddCommand(dumpCmd)
|
||||||
dumpCmd.Flags().IntVar(&port, "port", capture.Port, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
dumpCmd.Flags().IntVar(&port, "port", capture.Port, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
||||||
dumpCmd.Flags().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
dumpCmd.Flags().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
||||||
}
|
}
|
||||||
|
|
19
cmd/root.go
19
cmd/root.go
|
@ -6,19 +6,22 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var debug bool
|
var (
|
||||||
|
debug bool
|
||||||
|
timestamps bool
|
||||||
|
)
|
||||||
|
|
||||||
// RootCmd represents the base command when called without any subcommands
|
// RootCMD represents the base command when called without any subcommands
|
||||||
var RootCmd = &cobra.Command{
|
var RootCMD = &cobra.Command{
|
||||||
Use: "analyzer",
|
Use: "analyzer",
|
||||||
Short: "wifictld analyzer",
|
Short: "wifictld analyzer",
|
||||||
Long: `capture wifictld traffic and display thus`,
|
Long: `capture wifictld traffic and display thus`,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
// This is called by main.main(). It only needs to happen once to the RootCMD.
|
||||||
func Execute() {
|
func Execute() {
|
||||||
if err := RootCmd.Execute(); err != nil {
|
if err := RootCMD.Execute(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +30,11 @@ func init() {
|
||||||
if debug {
|
if debug {
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
}
|
}
|
||||||
|
log.SetFormatter(&log.TextFormatter{
|
||||||
|
DisableTimestamp: timestamps,
|
||||||
|
})
|
||||||
log.Debug("show debug")
|
log.Debug("show debug")
|
||||||
})
|
})
|
||||||
RootCmd.PersistentFlags().BoolVar(&debug, "v", false, "show debug log")
|
RootCMD.PersistentFlags().BoolVar(&debug, "v", false, "show debug log")
|
||||||
|
RootCMD.PersistentFlags().BoolVar(×tamps, "timestamps", false, "Enables timestamps for log output")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"dev.sum7.eu/genofire/wifictld-analyzer/capture"
|
"dev.sum7.eu/genofire/wifictld-analyzer/capture"
|
||||||
|
"dev.sum7.eu/genofire/wifictld-analyzer/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
StatePath string `toml:"state_path"`
|
StatePath string `toml:"state_path"`
|
||||||
|
Webserver *web.Config `toml:"webserver"`
|
||||||
Interfaces []*capture.IFaceConfig `toml:"interfaces"`
|
Interfaces []*capture.IFaceConfig `toml:"interfaces"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
state_path = "/tmp/wifictld.json"
|
state_path = "/tmp/wifictld.json"
|
||||||
|
|
||||||
|
[webserver]
|
||||||
|
enable = true
|
||||||
|
bind = ":8080"
|
||||||
|
webroot = "./webroot/"
|
||||||
|
|
||||||
[[interfaces]]
|
[[interfaces]]
|
||||||
ifname = "wlp4s0"
|
ifname = "wlp4s0"
|
||||||
#port = 1000
|
#port = 1000
|
||||||
#ip_address = "ff02::31f1"
|
#ip_address = "ff02::31f1"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Enable bool `toml:"enable"`
|
||||||
|
Bind string `toml:"bind"`
|
||||||
|
Webroot string `toml:"webroot"`
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/NYTimes/gziphandler"
|
||||||
|
"github.com/bdlm/log"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/golang-lib/websocket"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/wifictld-analyzer/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
web *http.Server
|
||||||
|
ws *websocket.WebsocketHandlerService
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new webserver and starts it
|
||||||
|
func New(config *Config) *Server {
|
||||||
|
ws := websocket.NewWebsocketHandlerService()
|
||||||
|
ws.Listen("/ws")
|
||||||
|
|
||||||
|
http.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webroot))))
|
||||||
|
return &Server{
|
||||||
|
web: &http.Server{
|
||||||
|
Addr: config.Bind,
|
||||||
|
},
|
||||||
|
ws: ws,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) Handler(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||||
|
srv.ws.SendAll(&websocket.Message{
|
||||||
|
Body: msg,
|
||||||
|
})
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
func (srv *Server) Start() {
|
||||||
|
// service connections
|
||||||
|
if err := srv.web.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
log.Panicf("webserver crashed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) Close() {
|
||||||
|
srv.web.Close()
|
||||||
|
srv.ws.Close()
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWebserver(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
srv := New(&Config{
|
||||||
|
Bind: ":12345",
|
||||||
|
Webroot: "/tmp",
|
||||||
|
})
|
||||||
|
assert.NotNil(srv)
|
||||||
|
|
||||||
|
go srv.Start()
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 200)
|
||||||
|
|
||||||
|
assert.Panics(func() {
|
||||||
|
srv.Start()
|
||||||
|
}, "not allowed to listen twice")
|
||||||
|
|
||||||
|
srv.Close()
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
wget -O vanilla-framework.css https://assets.ubuntu.com/v1/vanilla-framework-version-1.8.1.min.css
|
||||||
|
wget -O vue.js https://cdn.jsdelivr.net/npm/vue@2.6.7/dist/vue.js
|
||||||
|
wget -O vue-route.js https://unpkg.com/vue-router/dist/vue-router.js
|
||||||
|
wget -O vuex.js https://unpkg.com/vuex@2.0.0/dist/vuex.min.js
|
||||||
|
wget -O vue-websocket.js https://raw.githubusercontent.com/nathantsoi/vue-native-websocket/master/dist/build.js
|
||||||
|
wget -O vue-websocket.js.map https://raw.githubusercontent.com/nathantsoi/vue-native-websocket/master/dist/build.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,4 @@
|
||||||
|
.p-navigation__logo {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 3rem;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<title>Wifictld</title>
|
||||||
|
<link rel="stylesheet" href="assets/vanilla-framework.css" />
|
||||||
|
<link rel="stylesheet" href="css/main.css" />
|
||||||
|
<script src="assets/vue.js"></script>
|
||||||
|
<script src="assets/vue-route.js"></script>
|
||||||
|
<script src="assets/vuex.js"></script>
|
||||||
|
<script src="assets/vue-websocket.js"></script>
|
||||||
|
</head>
|
||||||
|
<body role="document">
|
||||||
|
<div id="app">
|
||||||
|
<header id="navigation" class="p-navigation">
|
||||||
|
<div class="p-navigation__banner">
|
||||||
|
<div class="p-navigation__logo">
|
||||||
|
<a class="p-navigation__link" href="/">Wifictld</a>
|
||||||
|
</div>
|
||||||
|
<a href="#navigation" class="p-navigation__toggle--open" title="menu">Menu</a>
|
||||||
|
<a href="#navigation-closed" class="p-navigation__toggle--close" title="close menu">Close menu</a>
|
||||||
|
</div>
|
||||||
|
<nav class="p-navigation__nav" role="menubar">
|
||||||
|
<span class="u-off-screen">
|
||||||
|
<a href="#main-content">Jump to main content</a>
|
||||||
|
</span>
|
||||||
|
<ul class="p-navigation__links" role="menu">
|
||||||
|
<li class="p-navigation__link is-selected">
|
||||||
|
<router-link to="/">Access Points</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<div class="content">
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="js/view/accesspoint.js"></script>
|
||||||
|
<script src="js/store.js"></script>
|
||||||
|
<script src="js/data.js"></script>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,3 @@
|
||||||
|
store.commit('setEvent', "wifictld_pkg", (msg)=>{
|
||||||
|
console.log(msg)
|
||||||
|
})
|
|
@ -0,0 +1,14 @@
|
||||||
|
const router = new VueRouter({
|
||||||
|
routes: [
|
||||||
|
{ path: '/', component: ViewAccessPoints }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
VueNativeSock.default.install(Vue, `//${location.host}${location.pathname}ws`, {
|
||||||
|
store: store,
|
||||||
|
format: 'json',
|
||||||
|
})
|
||||||
|
|
||||||
|
const app = new Vue({
|
||||||
|
router
|
||||||
|
}).$mount('#app')
|
|
@ -0,0 +1,98 @@
|
||||||
|
|
||||||
|
function newUUID () {
|
||||||
|
/* eslint-disable */
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
|
const r = Math.random() * 16 | 0,
|
||||||
|
v = c === 'x' ? r : r & 0x3 | 0x8;
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
/* eslint-enable */
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = new Vuex.Store({
|
||||||
|
state: {
|
||||||
|
socket: {
|
||||||
|
_session: localStorage.getItem('session'),
|
||||||
|
isConnected: false,
|
||||||
|
reconnectError: false,
|
||||||
|
eventMSGID: {},
|
||||||
|
eventTo: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations:{
|
||||||
|
SOCKET_ONOPEN (state, event) {
|
||||||
|
state.socket.isConnected = true
|
||||||
|
},
|
||||||
|
SOCKET_ONCLOSE (state, event) {
|
||||||
|
state.socket.isConnected = false
|
||||||
|
},
|
||||||
|
SOCKET_ONERROR (state, event) {
|
||||||
|
console.error(state, event)
|
||||||
|
},
|
||||||
|
// default handler called for all methods
|
||||||
|
SOCKET_ONMESSAGE (state, msg) {
|
||||||
|
if (msg.subject === 'session_init') {
|
||||||
|
if (state.socket._session === null) {
|
||||||
|
state.socket._session = newUUID();
|
||||||
|
localStorage.setItem('session', state.socket._session);
|
||||||
|
}
|
||||||
|
msg.id = state.socket._session;
|
||||||
|
Vue.prototype.$socket.sendObj(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msgFunc = eventMSGID[msg.id];
|
||||||
|
if (msgFunc) {
|
||||||
|
msgFunc(msg);
|
||||||
|
delete eventMSGID[msg.id];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventFuncs = eventTo[msg.subject];
|
||||||
|
if (typeof eventFuncs === 'object' && eventFuncs.length > 0) {
|
||||||
|
for (const key in eventFuncs) {
|
||||||
|
const func = eventFuncs[key];
|
||||||
|
if (func) {
|
||||||
|
func(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`unable to identify message: ${msg.subject}`);
|
||||||
|
|
||||||
|
},
|
||||||
|
// mutations for reconnect methods
|
||||||
|
SOCKET_RECONNECT(state, count) {
|
||||||
|
console.info(state, count)
|
||||||
|
},
|
||||||
|
SOCKET_RECONNECT_ERROR(state) {
|
||||||
|
state.socket.reconnectError = true;
|
||||||
|
},
|
||||||
|
setEvent (state, to, func) {
|
||||||
|
state.socket.eventTo[to] = [func];
|
||||||
|
},
|
||||||
|
addEvent (state, to, func) {
|
||||||
|
if (typeof state.socket.eventTo[to] !== 'object') {
|
||||||
|
state.socket.eventTo[to] = [];
|
||||||
|
}
|
||||||
|
state.socket.eventTo[to].push(func);
|
||||||
|
},
|
||||||
|
delEvent (state, to, func) {
|
||||||
|
if (typeof state.socket.eventTo[to] === 'object' && eventTo[to].length > 1) {
|
||||||
|
state.socket.eventTo[to].pop(func);
|
||||||
|
} else {
|
||||||
|
state.socket.eventTo[to] = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
call (state, msg, callback) {
|
||||||
|
if (!msg.id) {
|
||||||
|
msg.id = newUUID();
|
||||||
|
}
|
||||||
|
const ret = Vue.prototype.$socket.sendObj(msg);
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
state.socket.eventMSGID[msg.id] = callback;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,25 @@
|
||||||
|
const ViewAccessPoints = {
|
||||||
|
template: `<div class="row">
|
||||||
|
<h1>AccessPoints</h1>
|
||||||
|
<table class="p-table--mobile-card" role="grid">
|
||||||
|
<thead>
|
||||||
|
<tr role="row">
|
||||||
|
<th scope="col" role="columnheader" id="t-name" aria-sort="none">Name</th>
|
||||||
|
<th scope="col" role="columnheader" id="t-users" aria-sort="none" class="u-align--right">Users</th>
|
||||||
|
<th scope="col" role="columnheader" id="t-units" aria-sort="none" class="u-align--right">Units</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<accesspoint-table-item/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.component('accesspoint-table-item', {
|
||||||
|
template: `<tr role="row">
|
||||||
|
<td role="rowheader" aria-label="Name">Grape</td>
|
||||||
|
<td role="gridcell" aria-label="Users" class="u-align--right">8</td>
|
||||||
|
<td role="gridcell" aria-label="Units" class="u-align--right">19</td>
|
||||||
|
</tr>`
|
||||||
|
})
|
Loading…
Reference in New Issue