From 7bb32e7c170134869e848fabfd71be9c8edc0228 Mon Sep 17 00:00:00 2001 From: delorean Date: Sat, 30 Sep 2023 18:06:22 -0500 Subject: [PATCH] initial --- README.md | 0 config.toml | 6 ++ go.mod | 20 +++++ go.sum | 29 +++++++ main.go | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ www/fist.ico | Bin 0 -> 20207 bytes www/index.html | 155 +++++++++++++++++++++++++++++++++++++ 7 files changed, 416 insertions(+) create mode 100644 README.md create mode 100644 config.toml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 www/fist.ico create mode 100644 www/index.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..a19e287 --- /dev/null +++ b/config.toml @@ -0,0 +1,6 @@ +webroot = "www" +lport = "5000" +vhost = "hardfiles.org" +dbfile = "dbfile.db" +filelen = 6 +folder = "files" \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ebc1c5d --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module hardfiles + +go 1.21.0 + +require ( + github.com/boltdb/bolt v1.3.1 + github.com/gabriel-vasile/mimetype v1.4.2 + github.com/gorilla/mux v1.8.0 + github.com/landlock-lsm/go-landlock v0.0.0-20230607164353-b03374193cb2 + github.com/rs/zerolog v1.31.0 +) + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.12.0 // indirect + kernel.org/pub/linux/libs/security/libcap/psx v1.2.66 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dbebc3a --- /dev/null +++ b/go.sum @@ -0,0 +1,29 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/landlock-lsm/go-landlock v0.0.0-20230607164353-b03374193cb2 h1:urvYVFXXrgiZWCYCQ6LWEE98QdU4Mvd5zwXuTllWMTg= +github.com/landlock-lsm/go-landlock v0.0.0-20230607164353-b03374193cb2/go.mod h1:D25+lEYNcoxH7SgM/VOvWNrF3ZNAfRdydDuVbQBe6yE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.66 h1:ikIhPzfkSSAEwBOU+2DWhoF+xnGUhvlMTfQjBVhvzQY= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.66/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= diff --git a/main.go b/main.go new file mode 100644 index 0000000..5060b2f --- /dev/null +++ b/main.go @@ -0,0 +1,206 @@ +package main + +import ( + "io" + "math/rand" + "net/http" + "os" + "strconv" + "time" + + "github.com/BurntSushi/toml" + "github.com/boltdb/bolt" + "github.com/gabriel-vasile/mimetype" + "github.com/gorilla/mux" + "github.com/landlock-lsm/go-landlock/landlock" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +var ( + db *bolt.DB + conf Config +) + +type Config struct { + Webroot string `toml:"webroot"` + LPort string `toml:"lport"` + VHost string `toml:"vhost"` + DBFile string `toml:"dbfile"` + FileLen int `toml:"filelen"` + FileFolder string `toml:"folder"` +} + +func LoadConf() { + if _, err := toml.DecodeFile("config.toml", &conf); err != nil { + log.Fatal().Err(err).Msg("unable to parse config.toml") + } +} + +func NameGen() string { + const chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789" + ll := len(chars) + b := make([]byte, conf.FileLen) + rand.Read(b) // generates len(b) random bytes + for i := int64(0); i < int64(conf.FileLen); i++ { + b[i] = chars[int(b[i])%ll] + } + return string(b) +} + +func CheckFile(name string) bool { // false if doesn't exist, true if exists + tfd, err := os.Open(conf.FileFolder + "/" + name) + if err != nil { + return false + } + tfd.Close() + return true +} + +func UploadHandler(w http.ResponseWriter, r *http.Request) { + // expiry sanitize + twentyfour := int64(86400) + + file, _, err := r.FormFile("file") + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + defer file.Close() + + mtype, err := mimetype.DetectReader(file) + if err != nil { + w.Write([]byte("error detecting the mime type of your file\n")) + return + } + file.Seek(0, 0) + + // generate + check name + var name string + for { + id := NameGen() + name = id + mtype.Extension() + if !CheckFile(name) { + break + } + } + + err = db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte("expiry")) + err := b.Put([]byte(name), []byte(strconv.FormatInt(time.Now().Unix()+twentyfour, 10))) + return err + }) + if err != nil { + log.Error().Err(err).Msg("Failed to put expiry") + } + + log.Info().Int64("expiry", twentyfour).Msg("Writing new file") + + f, err := os.OpenFile(conf.FileFolder+"/"+name, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + log.Error().Err(err).Msg("Error opening a file for write") + w.WriteHeader(http.StatusInternalServerError) // change to json + return + } + defer f.Close() + + io.Copy(f, file) + + w.Write([]byte("https://" + conf.VHost + "/uploads/" + name)) +} + +func Cull() { + for { + removed := 0 + db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte("expiry")) + c := b.Cursor() + for k, v := c.First(); k != nil; k, v = c.Next() { + eol, err := strconv.ParseInt(string(v), 10, 64) + if err != nil { + log.Error().Err(err).Bytes("k", k).Bytes("v", v).Msg("expiration time could not be parsed") + continue + } + if time.Now().After(time.Unix(eol, 0)) { + os.Remove(conf.FileFolder + "/" + string(k)) + removed += 1 + c.Delete() + } + } + return nil + }) + if removed >= 1 { + log.Info().Int("amount", removed).Msg("expired") + } + time.Sleep(5 * time.Second) + } +} + +func main() { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + LoadConf() + + err := landlock.V2.BestEffort().RestrictPaths( + landlock.RWDirs("./"+conf.FileFolder), + landlock.RWFiles(conf.DBFile), + landlock.RWFiles(conf.Webroot+"/index.html"), + ) + + if err != nil { + log.Warn().Err(err).Msg("could not landlock") + } + + _, err = os.Open("/etc/passwd") + if err == nil { + log.Warn().Msg("landlock failed, could open /etc/passwd") + } else { + log.Info().Err(err).Msg("Landlocked") + } + + db, err = bolt.Open(conf.DBFile, 0600, nil) + if err != nil { + log.Fatal().Err(err).Msg("unable to open database file") + } + db.Update(func(tx *bolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte("expiry")) + if err != nil { + log.Fatal().Err(err).Msg("error creating expiry bucket") + return err + } + return nil + }) + + r := mux.NewRouter() + r.HandleFunc("/", UploadHandler).Methods("POST") + r.HandleFunc("/uploads/{name}", func(w http.ResponseWriter, r *http.Request) { // upload hits + vars := mux.Vars(r) + if !CheckFile(vars["name"]) { + w.WriteHeader(http.StatusNotFound) + } else { + http.ServeFile(w, r, conf.FileFolder+"/"+vars["name"]) + } + }).Methods("GET") + r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, conf.Webroot+"/index.html") + }).Methods("GET") + http.Handle("/", r) + + go Cull() + + serv := &http.Server{ + Addr: ":" + conf.LPort, + Handler: r, + ErrorLog: nil, + //ReadTimeout: 20 * time.Second, + //WriteTimeout: 20 * time.Second, + IdleTimeout: 20 * time.Second, + } + + log.Info().Err(err).Msg("listening...") + + if err := serv.ListenAndServe(); err != nil { + log.Fatal().Err(err).Msg("error starting server") + } + + db.Close() +} diff --git a/www/fist.ico b/www/fist.ico new file mode 100644 index 0000000000000000000000000000000000000000..ad0892d9823d6d18790a9ed08d6e39d68c8dcbd1 GIT binary patch literal 20207 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 literal 0 HcmV?d00001 diff --git a/www/index.html b/www/index.html new file mode 100644 index 0000000..e720718 --- /dev/null +++ b/www/index.html @@ -0,0 +1,155 @@ + + + + + + + + + + HARDFILES + + + +
+ +

HARDFILES

+

curl -F file=@example.png https://hardfiles.org/

+
+
+
+
Browse
+
No file chosen...
+ +
+ +
+
+

⚠️ Uploads are erased after 24 hours

+
+ + + + +