diff --git a/Dockerfile b/Dockerfile index 0fc866c..f48b425 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,17 @@ # Taken from https://github.com/chemidy/smallest-secured-golang-docker-image FROM golang@sha256:244a736db4a1d2611d257e7403c729663ce2eb08d4628868f9d9ef2735496659 as builder + +# Install git + SSL ca certificates. +# Git is required for fetching the dependencies. +# Ca-certificates is required to call HTTPS endpoints. RUN apk update && apk add --no-cache git ca-certificates tzdata && update-ca-certificates +# Create appuser ENV USER=appuser ENV UID=10001 +# See https://stackoverflow.com/a/55757473/12429735 RUN adduser \ --disabled-password \ --gecos "" \ @@ -13,17 +19,36 @@ RUN adduser \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ - "${USER}" && \ - mkdir /build && \ - chown -R "${USER}":"${USER}" /build -WORKDIR /build + "${USER}" +WORKDIR $GOPATH/src/mypackage/myapp/ COPY . . -RUN go get -d -v && \ - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + +# Fetch dependencies. +RUN go get -d -v + +# Build the binary +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags='-w -s -extldflags "-static"' -a \ - -o shomon . -USER appuser:appuser -ENTRYPOINT [ "./shomon" ] + -o /go/bin/shomon . +############################ +# STEP 2 build a small image +############################ +FROM scratch + +# Import from builder. +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /etc/passwd /etc/passwd +COPY --from=builder /etc/group /etc/group + +# Copy our static executable +COPY --from=builder /go/bin/shomon /go/bin/shomon + +# Use an unprivileged user. +USER appuser:appuser +WORKDIR /go/bin/ +# Run the shomon binary. +ENTRYPOINT ["/go/bin/shomon"] diff --git a/pkg/logwrapper/logwrapper.go b/pkg/logwrapper/logwrapper.go index feeef4b..f23bc6b 100644 --- a/pkg/logwrapper/logwrapper.go +++ b/pkg/logwrapper/logwrapper.go @@ -1,7 +1,6 @@ package logwrapper import ( - "io" "os" "github.com/sirupsen/logrus" @@ -12,22 +11,15 @@ type StandardLogger struct { *logrus.Logger } +// Logger : Globally shared logging instance +var Logger = NewLogger() + // NewLogger initializes the standard logger func NewLogger() *StandardLogger { var baseLogger = logrus.New() var standardLogger = &StandardLogger{baseLogger} + standardLogger.SetLevel(logrus.InfoLevel) - standardLogger.Formatter = &logrus.JSONFormatter{} - standardLogger.SetReportCaller(true) - standardLogger.SetLevel(logrus.DebugLevel) - - file, err := os.OpenFile("shodanmonitor.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - standardLogger.Fatal(err) - return nil - } - - mw := io.MultiWriter(os.Stdout, file) - standardLogger.SetOutput(mw) + standardLogger.SetOutput(os.Stdout) return standardLogger } diff --git a/pkg/shodan/shodan.go b/pkg/shodan/shodan.go index 08e2afd..8865d05 100644 --- a/pkg/shodan/shodan.go +++ b/pkg/shodan/shodan.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "net/http" + "os" conf "github.com/KaanSK/shomon/pkg/conf" lw "github.com/KaanSK/shomon/pkg/logwrapper" @@ -18,7 +19,6 @@ import ( var ( errShomonServiceStop = errors.New("listener service stopped") - logger = lw.NewLogger() ) func parseResponse(destination interface{}, body io.Reader) error { @@ -40,13 +40,15 @@ func handleAlertStream(ch chan *HostData) { }() resp, err := http.Get("https://stream.shodan.io/shodan/alert?key=" + conf.Config.ShodanKey) if err != nil { - logger.Error(err) - return + lw.Logger.Error(err) } if resp.StatusCode != http.StatusOK { err = GetErrorFromResponse(resp) resp.Body.Close() - logger.Fatal(err) + lw.Logger.Error(err) + if err.Error() == "No alerts specified" || err.Error() == "Invalid API key" { + os.Exit(1) + } } reader := bufio.NewReader(resp.Body) @@ -65,7 +67,7 @@ func handleAlertStream(ch chan *HostData) { if err := parseResponse(banner, bytes.NewBuffer(chunk)); err != nil { resp.Body.Close() - logger.Error(err) + lw.Logger.Error(err) break } @@ -78,7 +80,7 @@ func ListenAlerts() error { ch := make(chan *HostData) go handleAlertStream(ch) - logger.Info("listening process initiated") + lw.Logger.Info("listening process initiated") for { banner, ok := <-ch @@ -99,13 +101,13 @@ func ListenAlerts() error { hash := md5.Sum([]byte(foundService)) hiveAlert.SourceRef = hex.EncodeToString(hash[:]) - logger.Info("triggered alarm for: " + hiveAlert.SourceRef) + lw.Logger.Info("triggered alarm for: " + hiveAlert.SourceRef) err := thehive.CreateAlert(hiveAlert) if err != nil { return err } - logger.Info("created alert for " + hiveAlert.SourceRef) + lw.Logger.Info("created alert for " + hiveAlert.SourceRef) } return errShomonServiceStop } diff --git a/pkg/thehive/alert.go b/pkg/thehive/alert.go index e3e56e7..e0376b1 100644 --- a/pkg/thehive/alert.go +++ b/pkg/thehive/alert.go @@ -9,11 +9,8 @@ import ( "time" conf "github.com/KaanSK/shomon/pkg/conf" - lw "github.com/KaanSK/shomon/pkg/logwrapper" ) -var logger = lw.NewLogger() - // HiveAlert : Alert structure type HiveAlert struct { CaseTemplate string `json:"caseTemplate,omitempty"` diff --git a/shomon.go b/shomon.go index 10cf7b3..d2f00ab 100644 --- a/shomon.go +++ b/shomon.go @@ -7,34 +7,38 @@ import ( lw "github.com/KaanSK/shomon/pkg/logwrapper" "github.com/KaanSK/shomon/pkg/shodan" "github.com/jessevdk/go-flags" + "github.com/sirupsen/logrus" ) -var logger = lw.NewLogger() - func init() { parser := flags.NewParser(&conf.Config, flags.Default) _, err := parser.Parse() if err != nil { os.Exit(1) } + if conf.Config.Verbose { + lw.Logger.Formatter = &logrus.JSONFormatter{} + lw.Logger.SetReportCaller(true) + lw.Logger.SetLevel(logrus.DebugLevel) + } } func neverExit() { defer func() { if err := recover(); err != nil { - logger.Error(err) + lw.Logger.Error(err) go neverExit() } }() err := shodan.ListenAlerts() if err != nil { - logger.Error(err) + lw.Logger.Error(err) go neverExit() } } func main() { - logger.Debug("main process started") + lw.Logger.Info("main process started") go neverExit() select {} }