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

[WIP] Shifud #549

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions Shifud/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# syntax=docker/dockerfile:1

FROM golang:1.19.2-alpine

WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

# Install required packages
RUN apk add --no-cache gcc musl-dev libc-dev

COPY . .

RUN for f in plugins/*.go; do go build -buildmode=plugin -o "${f%.go}.so" "$f"; done

RUN go build -o main main.go

CMD ["./main"]
82 changes: 82 additions & 0 deletions Shifud/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Shifud
## How do I run it locally
```bash
for f in plugins/*.go; do go build -buildmode=plugin -o "${f%.go}.so" "$f"; done
go run main.go
```

## Running in docker
```bash
docker build -t shifud .
docker run -p 8080:8080 shifud
```

## Shifud Plugin Development Guide
Shifud is a flexible network scanning tool with an extensible plugin system. This guide will help you create and integrate a new plugin into the Shifud system.

## Understanding the Plugin System

Shifud uses Go's `plugin` package to dynamically load plugins at runtime. Plugins are compiled as shared libraries (`.so` files) and loaded from the `plugins` folder. Each plugin must implement the `ScannerPlugin` interface, which is defined in the `shifud.go` file:

```go
type ScannerPlugin interface {
Scan() ([]DeviceConfig, error)
}
```

## Creating a New Plugin
1. ** folder.** Name the file based on your plugin's functionality, e.g., `http_scanner.go`.
2. ** interface.** For example:

```go
package plugins

import (
"example.com/Shifud/shifud"
"fmt"
)

type HttpScanner struct{}

func (s *HttpScanner) Scan() ([]shifud.DeviceConfig, error) {
fmt.Println("Scanning with HTTP scanner...")
// Add your plugin logic here
return nil, nil
}

var ScannerPlugin HttpScanner
```


** function.** This is where you'll add the core functionality of your plugin. The `Scan()` function should return a slice of `DeviceConfig` objects and an error if something goes wrong.
## Compiling and Loading the Plugin
**Compile the plugin as a shared library.** Use the following command to compile your plugin:

```bash
go build -buildmode=plugin -o plugins/*.so plugins/*.go
```

You can also build all plugins yourself with the following command:
```bash
for f in plugins/*.go; do go build -buildmode=plugin -o "${f%.go}.so" "$f"; done
```



Replace `your_plugin_name` with the appropriate name for your plugin.
**file.** The `LoadPlugins` function in the `shifud.go` file handles the loading of plugins. It is called in the `main.go` file as follows:

```go
err := shifud.LoadPlugins(scanner, "plugins")
if err != nil {
fmt.Println("Error loading plugins:", err)
os.Exit(1)
}
```



With these steps, your new plugin will be loaded and used by Shifud at runtime. You can create additional plugins by following the same process, and Shifud will automatically load and use them.
## Testing Your Plugin

After creating and loading your plugin, run the `main.go` file to test your plugin's functionality. If everything is set up correctly, you should see the output of your plugin's `Scan()` function when Shifud runs.
1 change: 1 addition & 0 deletions Shifud/devices.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
null
12 changes: 12 additions & 0 deletions Shifud/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module example.com/Shifud

go 1.17

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.8.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
20 changes: 20 additions & 0 deletions Shifud/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
43 changes: 43 additions & 0 deletions Shifud/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"example.com/Shifud/shifud"
"fmt"
_ "net"
"os"
"sigs.k8s.io/yaml"
)

func main() {
fmt.Println("Shifud online...")

// Create a scanner
scanner := &shifud.Scanner{}

// Load plugins from the plugins folder
err := shifud.LoadPlugins(scanner, "plugins")
if err != nil {
fmt.Println("Error loading plugins:", err)
os.Exit(1)
}

// Scan devices with all plugins
devices, err := scanner.Scan()
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}

// Export the configuration settings to a YAML file
output, err := yaml.Marshal(devices)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}

err = os.WriteFile("devices.yml", output, 0644)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
}
40 changes: 40 additions & 0 deletions Shifud/plugincommon/basewebscanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package plugincommon

import (
"example.com/Shifud/shifud"
"fmt"
"log"
"sync"
"time"
)

type ProtocolHandler func(ip string, port int, timeout time.Duration) bool

func WebScanner(startPort, endPort int, timeout time.Duration, handler ProtocolHandler) []shifud.DeviceConfig {
var devices []shifud.DeviceConfig
var wg sync.WaitGroup

log.Println("Launching our little scanner minions! 🚀")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use pkg/logger/log.go to print shifu log

for i := 1; i <= 254; i++ {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

254 to be a const?

ip := fmt.Sprintf("192.168.1.%d", i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make 192.168.1. as a const
BTW, one question, would the network always start with 192.168.1?

wg.Add(1)

go func(ip string) {
defer wg.Done()

for port := startPort; port <= endPort; port++ {
if handler(ip, port, timeout) {
devices = append(devices, shifud.DeviceConfig{
IP: ip,
Port: port,
})
log.Printf("Minion found a device at %s:%d! High five! 🙌\n", ip, port)
}
}
}(ip)
}

wg.Wait()
log.Println("All minions have returned! Time to count our treasures! 💎")
return devices
}
42 changes: 42 additions & 0 deletions Shifud/plugins/httpscanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"example.com/Shifud/plugincommon"
"example.com/Shifud/shifud"
"log"
"net"
"net/http"
"strconv"
"time"
)

type HttpScanner struct{}

func (s *HttpScanner) Scan() ([]shifud.DeviceConfig, error) {
startPort := 80
endPort := 10000
timeout := 5 * time.Second
handler := httpHandler
log.Println("Scanning for HTTP devices in the wild... Let's see what we find!")
devices := plugincommon.WebScanner(startPort, endPort, timeout, handler)
log.Printf("Finished scanning HTTP devices! Found %d devices. Time to party! 🥳\n", len(devices))
return devices, nil
}

func httpHandler(ip string, port int, timeout time.Duration) bool {
address := ip + ":" + strconv.Itoa(port)
conn, err := net.DialTimeout("tcp", address, timeout)
if err == nil {
defer conn.Close()

// Check if it's an HTTP server
_, err := http.Get("http://" + address)
if err == nil {
log.Printf("HTTP server spotted at %s! 🎯\n", address)
return true
}
}
return false
}

var ScannerPlugin HttpScanner
34 changes: 34 additions & 0 deletions Shifud/plugins/httpsscanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"crypto/tls"
"example.com/Shifud/plugincommon"
"example.com/Shifud/shifud"
"net"
"strconv"
"time"
)

type HttpsScanner struct{}

func (s *HttpsScanner) Scan() ([]shifud.DeviceConfig, error) {
startPort := 80
endPort := 10000
timeout := 5 * time.Second
handler := httpsHandler
devices := plugincommon.WebScanner(startPort, endPort, timeout, handler)
return devices, nil
}

func httpsHandler(ip string, port int, timeout time.Duration) bool {
address := ip + ":" + strconv.Itoa(port)
dialer := &net.Dialer{Timeout: timeout}
conn, err := tls.DialWithDialer(dialer, "tcp", address, &tls.Config{InsecureSkipVerify: true})
if err == nil {
defer conn.Close()
return true
}
return false
}

var ScannerPlugin HttpsScanner
Loading