Skip to content

Commit

Permalink
Introduce a discontinued mode
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbakker committed Dec 20, 2023
1 parent 3050d7f commit 992121d
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 97 deletions.
191 changes: 104 additions & 87 deletions cmd/log4shell-tools-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
flagHTTPAddr = flag.String("http-addr", "127.0.0.1:8001", "listening address for the HTTP server")
flagHTTPAddrExternal = flag.String("http-addr-external", "127.0.0.1:8001", "address where the HTTP server can be reached externally")
flagTestTimeout = flag.Int("test-timeout", 30, "test timeout in minutes")
flagDiscontinued = flag.Bool("discontinued", false, "whether this service has been discontinued")
testTimeout = time.Duration(*flagTestTimeout)

className = "Log4Shell"
Expand All @@ -56,6 +57,7 @@ type IndexModel struct {
DNSZone string
ActiveTests int64
Error string
Discontinued bool
}

type StatsCache struct {
Expand Down Expand Up @@ -97,66 +99,72 @@ func main() {
flag.Parse()
testTimeout = time.Minute * time.Duration(*flagTestTimeout)

log.WithField("uri", *flagStorage).Info("Opening storage backend")
var err error
store, err = storage.NewBackend(*flagStorage)
if err != nil {
log.WithError(err).Fatal("Unable to open storage backend")
}
defer store.Close()
statsCache = &StatsCache{store: store}

go func() {
for {
<-time.After(1 * time.Minute)

deleted, err := store.PruneTestResults(context.Background())
if err != nil {
log.WithError(err).Error("Unable to delete old test results")
} else {
log.WithField("count", deleted).Info("Deleted old test results")
}
if !*flagDiscontinued {
log.WithField("uri", *flagStorage).Info("Opening storage backend")
var err error
store, err = storage.NewBackend(*flagStorage)
if err != nil {
log.WithError(err).Fatal("Unable to open storage backend")
}
}()
defer store.Close()
statsCache = &StatsCache{store: store}

ldapServer := NewLDAPServer(store)
go func() {
log.WithFields(log.Fields{
"addr": *flagLDAPAddr,
"addr_ext": *flagLDAPAddrExternal,
}).Info("Starting LDAP server")

if err := ldapServer.ListenAndServe(*flagLDAPAddr); err != nil {
log.WithError(err).Fatal("Unable to run LDAP server")
}
}()

if *flagDNSEnable {
dnsServer := NewDNSServer(store, DNSServerOpts{
Addr: *flagDNSAddr,
Zone: *flagDNSZone,
A: *flagDNSA,
AAAA: *flagDNSAAAA,
})
go func() {
for {
<-time.After(1 * time.Minute)

deleted, err := store.PruneTestResults(context.Background())
if err != nil {
log.WithError(err).Error("Unable to delete old test results")
} else {
log.WithField("count", deleted).Info("Deleted old test results")
}
}
}()

ldapServer := NewLDAPServer(store)
go func() {
log.WithFields(log.Fields{
"addr": *flagDNSAddr,
}).Info("Starting DNS server")
"addr": *flagLDAPAddr,
"addr_ext": *flagLDAPAddrExternal,
}).Info("Starting LDAP server")

if err := dnsServer.ListenAndServe(); err != nil {
log.WithError(err).Fatal("Unable to run DNS server")
if err := ldapServer.ListenAndServe(*flagLDAPAddr); err != nil {
log.WithError(err).Fatal("Unable to run LDAP server")
}
}()

if *flagDNSEnable {
dnsServer := NewDNSServer(store, DNSServerOpts{
Addr: *flagDNSAddr,
Zone: *flagDNSZone,
A: *flagDNSA,
AAAA: *flagDNSAAAA,
})

go func() {
log.WithFields(log.Fields{
"addr": *flagDNSAddr,
}).Info("Starting DNS server")

if err := dnsServer.ListenAndServe(); err != nil {
log.WithError(err).Fatal("Unable to run DNS server")
}
}()
}
} else {
log.Warn("Running in discontinued mode")
}

promHandler := promhttp.Handler()
router := httprouter.New()
router.GET("/", handleIndex)
router.GET("/metrics", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
promHandler.ServeHTTP(w, r)
})
router.GET(fmt.Sprintf("/api/tests/:uuid/payload/%s.class", className), handleTestPayloadDownload)
if !*flagDiscontinued {
promHandler := promhttp.Handler()
router.GET("/metrics", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
promHandler.ServeHTTP(w, r)
})
router.GET(fmt.Sprintf("/api/tests/:uuid/payload/%s.class", className), handleTestPayloadDownload)
}

log.WithFields(log.Fields{
"addr": *flagHTTPAddr,
Expand All @@ -169,64 +177,73 @@ func main() {
}

func handleIndex(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var activeTests int64
if !*flagDiscontinued {
activeTests = statsCache.getActiveTests(r.Context())
}
model := IndexModel{
Context: r.Context(),
ActiveTests: statsCache.getActiveTests(r.Context()),
ActiveTests: activeTests,
AddrLDAP: *flagLDAPAddr,
AddrLDAPExternal: *flagLDAPAddrExternal,
DNSEnabled: *flagDNSEnable,
DNSZone: *flagDNSZone,
Discontinued: *flagDiscontinued,
}
ctxLog := log.WithFields(log.Fields{
"server": "http",
"addr": getRemoteAddr(r),
"req": r.URL.Path,
})

idString := r.URL.Query().Get("uuid")
if idString != "" {
var err error
if model.UUID, err = uuid.Parse(idString); err != nil {
ctxLog.WithField("id", idString).WithError(err).Error("Unable to parse UUID")
writeHttpError(w, http.StatusBadRequest)
return
}
ctxLog = ctxLog.WithField("test", model.UUID)
if !*flagDiscontinued {
idString := r.URL.Query().Get("uuid")
if idString != "" {
var err error
if model.UUID, err = uuid.Parse(idString); err != nil {
ctxLog.WithField("id", idString).WithError(err).Error("Unable to parse UUID")
writeHttpError(w, http.StatusBadRequest)
return
}
ctxLog = ctxLog.WithField("test", model.UUID)

model.Test, err = store.Test(r.Context(), model.UUID)
if err != nil {
ctxLog.WithError(err).Error("Unable to lookup test in storage")
writeHttpError(w, http.StatusInternalServerError)
return
}
if model.Test == nil {
if r.URL.Query().Get("terms") != "y" {
model.Error = "You cannot continue before agreeing to only testing on machines that you have permission to test on."
} else {
ctxLog.Info("Inserting new test")

if err := store.InsertTest(r.Context(), model.UUID); err != nil {
ctxLog.WithError(err).Error("Unable to insert new test")
writeHttpError(w, http.StatusInternalServerError)
return
}
if model.Test, err = store.Test(r.Context(), model.UUID); err != nil {
ctxLog.WithError(err).Error("Unable to lookup test in storage")
writeHttpError(w, http.StatusInternalServerError)
return
}
if model.Test == nil {
ctxLog.Error("Unable to obtain test right after insert")
writeHttpError(w, http.StatusInternalServerError)
return
model.Test, err = store.Test(r.Context(), model.UUID)
if err != nil {
ctxLog.WithError(err).Error("Unable to lookup test in storage")
writeHttpError(w, http.StatusInternalServerError)
return
}
if model.Test == nil {
if r.URL.Query().Get("terms") != "y" {
model.Error = "You cannot continue before agreeing to only testing on machines that you have permission to test on."
} else {
ctxLog.Info("Inserting new test")

if err := store.InsertTest(r.Context(), model.UUID); err != nil {
ctxLog.WithError(err).Error("Unable to insert new test")
writeHttpError(w, http.StatusInternalServerError)
return
}
if model.Test, err = store.Test(r.Context(), model.UUID); err != nil {
ctxLog.WithError(err).Error("Unable to lookup test in storage")
writeHttpError(w, http.StatusInternalServerError)
return
}
if model.Test == nil {
ctxLog.Error("Unable to obtain test right after insert")
writeHttpError(w, http.StatusInternalServerError)
return
}

counterTestsCreated.Inc()
}

counterTestsCreated.Inc()
}
} else {
model.New = true
model.UUID = uuid.New()
}
} else {
model.New = true
model.UUID = uuid.New()
}

var buf bytes.Buffer
Expand Down
21 changes: 11 additions & 10 deletions cmd/log4shell-tools-server/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ <h1 class="mb-0"><a class="text-decoration-none" href="/">Log4Shell Vulnerabilit
{{ if gt .ActiveTests 0 }}
<span class="fs-6 text-muted">({{ .ActiveTests }} tests currently active)</span>
{{ end }}
<div class="alert alert-danger mt-3" role="alert">
<b>Is it time to shut down log4shell.tools?</b>
It has been 2 years since Log4Shell was discovered and patched.
Since this service costs money to run, I'm considering shutting it
down. Usage of this service has been decreasing, but it's still
being used around ~35 times every day. Is this tool still
indispensable to you? Please
<a href="mailto:contact@alexbakker.me">let me know</a>.
</div>
{{ if .Discontinued }}
<div class="alert alert-danger mt-3" role="alert">
<b>2023-12-20: Discontinued</b>
<div>
This service has been discontinued. If you still depend on it,
consider self-hosting: <a
href="https://github.com/alexbakker/log4shell-tools">https://github.com/alexbakker/log4shell-tools</a>.
</div>
</div>
{{ end }}
<p class="mt-3">
This tool allows you to run a test to check whether one of your
applications is affected by the recent vulnerabilities in log4j:
Expand Down Expand Up @@ -100,7 +101,7 @@ <h1 class="mb-0"><a class="text-decoration-none" href="/">Log4Shell Vulnerabilit
I'm testing a device that I personally own, or a device for which I have permission from the owner to run this test
</label>
</div>
<input class="btn btn-primary" type="submit" value="Start" {{ if .Test }}disabled{{ end }}>
<input class="btn btn-primary" type="submit" value="Start" {{ if or .Test .Discontinued }}disabled{{ end }}>
</form>
</div>
</div>
Expand Down

0 comments on commit 992121d

Please sign in to comment.