diff options
| -rw-r--r-- | cmd/appsluggr/main.go | 165 | ||||
| -rw-r--r-- | cmd/dnsd/Dockerfile | 11 | ||||
| -rw-r--r-- | cmd/dnsd/README.md | 88 | ||||
| -rw-r--r-- | cmd/dnsd/dnsd.conf | 7 | ||||
| -rw-r--r-- | cmd/dnsd/dnsd.service | 12 | ||||
| -rw-r--r-- | cmd/dnsd/main.go | 169 | ||||
| -rw-r--r-- | cmd/genua/main.go | 20 | ||||
| -rw-r--r-- | cmd/minipaas/main.go | 21 | ||||
| -rw-r--r-- | cmd/thumber/in/XjScp8a.png | bin | 292689 -> 0 bytes | |||
| -rw-r--r-- | cmd/thumber/in/lB4ICS3.png | bin | 369886 -> 0 bytes | |||
| -rw-r--r-- | cmd/thumber/in/oaw79y9.jpg | bin | 67119 -> 0 bytes | |||
| -rw-r--r-- | cmd/thumber/main.go | 58 | ||||
| -rw-r--r-- | cmd/tlstestd/main.go | 31 | ||||
| -rw-r--r-- | cmd/tor2web/main.go | 58 | ||||
| -rw-r--r-- | cmd/within.website/build.go | 58 | ||||
| -rw-r--r-- | cmd/xedn/build.go | 10 | ||||
| -rw-r--r-- | docs/man1/appsluggr.1 | 113 | ||||
| -rw-r--r-- | internal/appsluggr/appsluggr.go | 112 | ||||
| -rw-r--r-- | tor/tor.go | 88 |
19 files changed, 118 insertions, 903 deletions
diff --git a/cmd/appsluggr/main.go b/cmd/appsluggr/main.go deleted file mode 100644 index 9a90796..0000000 --- a/cmd/appsluggr/main.go +++ /dev/null @@ -1,165 +0,0 @@ -// Command appsluggr packages a standard web and worker slug for Heroku/Dokku. -// -// This hard assumes that the binaries put into the slug will work on the target hardware. -package main - -import ( - "archive/tar" - "compress/gzip" - "flag" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "strings" - - "github.com/otiai10/copy" - "golang.org/x/mod/sumdb/dirhash" - "within.website/x/internal" -) - -var ( - web = flag.String("web", "", "path to binary for web process") - webScale = flag.Int("web-scale", 1, "default scale for web process if defined") - worker = flag.String("worker", "", "path to binary for worker process") - workerScale = flag.Int("worker-scale", 1, "default scale for worker process if defined") - fname = flag.String("fname", "slug.tar.gz", "slug name") -) - -func main() { - internal.HandleStartup() - - fout, err := os.Create(*fname) - if err != nil { - log.Fatal(err) - } - defer fout.Close() - - gzw := gzip.NewWriter(fout) - defer gzw.Close() - - tw := tar.NewWriter(gzw) - defer tw.Close() - - dir, err := ioutil.TempDir("", "appsluggr") - if err != nil { - log.Fatal(err) - } - - defer os.RemoveAll(dir) // clean up - - os.MkdirAll(filepath.Join(dir, "bin"), 0777) - var procfile, scalefile string - - copy.Copy("translations", filepath.Join(dir, "translations")) - if *web != "" { - procfile += "web: /app/bin/web\n" - scalefile += fmt.Sprintf("web=%d", *webScale) - Copy(*web, filepath.Join(dir, "bin", "web")) - - } - if *worker != "" { - procfile += "worker: /app/bin/worker\n" - scalefile += fmt.Sprintf("worker=%d", *workerScale) - Copy(*worker, filepath.Join(dir, "bin", "worker")) - } - - os.MkdirAll(filepath.Join(dir, ".config"), 0777) - - err = ioutil.WriteFile(filepath.Join(dir, ".buildpacks"), []byte("https://github.com/ryandotsmith/null-buildpack"), 0666) - if err != nil { - log.Fatal(err) - } - err = ioutil.WriteFile(filepath.Join(dir, "DOKKU_SCALE"), []byte(scalefile), 0666) - if err != nil { - log.Fatal(err) - } - err = ioutil.WriteFile(filepath.Join(dir, "Procfile"), []byte(procfile), 0666) - if err != nil { - log.Fatal(err) - } - - hash, err := dirhash.HashDir(dir, os.Args[0], dirhash.Hash1) - if err != nil { - log.Fatal(err) - } - - log.Printf("hash: %s", hash) - gzw.Comment = hash - - filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error { - // return on any error - if err != nil { - log.Printf("got error on %s: %v", file, err) - return err - } - - if fi.IsDir() { - return nil // not a file. ignore. - } - - // create a new dir/file header - header, err := tar.FileInfoHeader(fi, fi.Name()) - if err != nil { - return err - } - - // update the name to correctly reflect the desired destination when untaring - header.Name = strings.TrimPrefix(strings.Replace(file, dir, "", -1), string(filepath.Separator)) - - // write the header - if err := tw.WriteHeader(header); err != nil { - return err - } - - // return on non-regular files (thanks to [kumo](https://medium.com/@komuw/just-like-you-did-fbdd7df829d3) for this suggested update) - if !fi.Mode().IsRegular() { - return nil - } - - // open files for taring - f, err := os.Open(file) - if err != nil { - return err - } - - // copy file data into tar writer - if _, err := io.Copy(tw, f); err != nil { - return err - } - - // manually close here after each file operation; defering would cause each file close - // to wait until all operations have completed. - f.Close() - - return nil - }) -} - -// Copy the src file to dst. Any existing file will be overwritten and will not -// copy file attributes. -func Copy(src, dst string) error { - in, err := os.Open(src) - if err != nil { - return err - } - defer in.Close() - st, err := in.Stat() - if err != nil { - return err - } - - out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, st.Mode()) - if err != nil { - return err - } - defer out.Close() - - _, err = io.Copy(out, in) - if err != nil { - return err - } - return out.Close() -} diff --git a/cmd/dnsd/Dockerfile b/cmd/dnsd/Dockerfile deleted file mode 100644 index 7cd22bf..0000000 --- a/cmd/dnsd/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -ARG X_VERSION - -FROM xena/xperimental:$X_VERSION as build -FROM xena/alpine - -ENV PORT 53 -ENV FORWARD_SERVER 1.1.1.1:53 -EXPOSE 53/udp - -COPY --from=build /usr/local/bin/dnsd /usr/local/bin/dnsd -CMD /usr/local/bin/dnsd diff --git a/cmd/dnsd/README.md b/cmd/dnsd/README.md deleted file mode 100644 index cb3921c..0000000 --- a/cmd/dnsd/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# dnsd - -A custom [DNS](https://en.wikipedia.org/wiki/Domain_Name_System) server for my network. DNS zone files are dynamically downloaded on -startup and are continuously monitored for changes. When the DNS zone is changed, -the service reloads it. - -I primarily use this to give myself a limited form of piHole DNS adblocking, as -well as serving my [home network services](https://home.cetacean.club). - -This is related to my [WireGuard Site to Site VPN](https://christine.website/blog/site-to-site-wireguard-part-1-2019-04-02) -project. - -## How to Configure `dnsd` - -`dnsd` relies on [RFC 1035](https://tools.ietf.org/html/rfc1035) zone files. This -is a file that looks roughly like this: - -```rfc1035 -$TTL 60 -$ORIGIN pele. -@ IN SOA oho.pele. some@email.address. ( - 2019040601 ; serial number YYYYMMDDNN - 28800 ; Refresh - 7200 ; Retry - 864000 ; Expire - 60 ; Minimum DNS TTL - ) - IN NS oho.pele. - -oho IN A 10.55.0.1 -1.0.55.10.in-addr.arpa. IN PTR oho.pele. - -;; apps -prometheus IN CNAME oho.pele. -grafana IN CNAME oho.pele. -``` - -Put this file in a publicly available place and then set its URL as a -`-zone-file` in the command line configuration. This file will be monitored -every minute for changes (via the proxy of the ETag of the HTTP responses). - -If you need to change the DNS forwarding server, set the value of the environment -variable `FORWARD_SERVER` or the command line flag `-forward-server`. - -## Installation - -### Docker - -```console -$ export DNSD_VERSION=v1.0.3 -$ docker run --name dnsd -p 53:53/udp -dit --restart always xena/dnsd:$DNSD_VERSION \ - dnsd -zone-url https://domain.hostname.tld/path/to/your.zone \ - -zone-url https://domain.hostname.tld/path/to/adblock.zone \ - -forward-server 1.1.1.1:53 -``` - -### From Git with systemd - -```console -$ go get -u -v github.com/Xe/x/cmd/dnsd@latest -$ GOBIN=$(pwd) go install github.com/Xe/x/cmd/dnsd -$ sudo cp dnsd /usr/local/bin/dnsd -<edit dnsd.service as needed> -$ sudo cp dnsd.service /etc/systemd/system/dnsd.service -$ sudo systemctl daemon-reload -$ sudo systemctl start dnsd -$ sudo systemctl status dnsd -$ sudo systemctl enable dnsd -``` - -## Testing - -```console -$ dig @127.0.0.1 google.com -$ dig @127.0.0.1 oho.pele -``` - -## Support - -If you need help with this, please [contact](https://christine.website/contact) me. -This is fairly simplistic software. If you need anything more, I'd suggest using -[CoreDNS](https://coredns.io) or similar. - -If you like this software, please consider donating on [Patreon](https://www.patreon.com/cadey) -or [Ko-Fi](https://www.ko-fi.com/christinedodrill). I use this software daily on my personal -network to service most of my devices. - -Thanks and be well. diff --git a/cmd/dnsd/dnsd.conf b/cmd/dnsd/dnsd.conf deleted file mode 100644 index e490d13..0000000 --- a/cmd/dnsd/dnsd.conf +++ /dev/null @@ -1,7 +0,0 @@ -port 5900 -forward-server 1.1.1.1:53 - -zone-url ( - https://xena.greedo.xeserv.us/files/akua.zone - https://xena.greedo.xeserv.us/files/adblock.zone -)
\ No newline at end of file diff --git a/cmd/dnsd/dnsd.service b/cmd/dnsd/dnsd.service deleted file mode 100644 index 73add3d..0000000 --- a/cmd/dnsd/dnsd.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=dnsd - -[Service] -Environment=PORT=53 -Environment=FORWARD_SERVER=1.1.1.1:53 -ExecStart=/usr/local/bin/dnsd -zone-file https://host.domain.tld/path/to/your.zone -zone-file https://host.domain.tld/path/to/adblock.zone -Restart=always -RestartSec=1s - -[Install] -WantedBy=multi-user.target diff --git a/cmd/dnsd/main.go b/cmd/dnsd/main.go deleted file mode 100644 index c84a54b..0000000 --- a/cmd/dnsd/main.go +++ /dev/null @@ -1,169 +0,0 @@ -// Command dnsd is a simple DNS server for my network. It has O(n) lookup times. -package main - -import ( - "bufio" - "flag" - "log" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "github.com/miekg/dns" - "github.com/mmikulicic/stringlist" - "within.website/x/internal" -) - -var ( - port = flag.String("port", "53", "UDP port to listen on for DNS") - server = flag.String("forward-server", "1.1.1.1:53", "forward DNS server") - - zoneURLs = stringlist.Flag("zone-url", "DNS zonefiles to load") -) - -var ( - defaultZoneURLS = []string{ - "https://xena.greedo.xeserv.us/files/akua.zone", - "https://xena.greedo.xeserv.us/files/adblock.zone", - } -) - -func monitorURLs(urls []string) { - etags := make(map[string]string) - - t := time.NewTicker(time.Minute) - defer t.Stop() - - for { - select { - case <-t.C: - for _, urli := range urls { - resp, err := http.Get(urli) - if err != nil { - panic(err) - } - - et := resp.Header.Get("ETag") - - ot, ok := etags[urli] - if !ok { - log.Printf("stored %s:%s", urli, et) - etags[urli] = et - } - if ok && et != ot { - log.Fatalf("url %s has new etag %s and wanted old etag %s", urli, et, ot) - } - } - } - } -} - -func main() { - internal.HandleStartup() - - if len(*zoneURLs) == 0 { - *zoneURLs = defaultZoneURLS - } - - go monitorURLs(*zoneURLs) - - for _, zurl := range *zoneURLs { - log.Printf("conf: -zone-url=%s", zurl) - } - log.Printf("conf: -port=%s", *port) - log.Printf("conf: -forward-server=%s", *server) - - rrs := []dns.RR{} - ns := []dns.RR{} - - for _, zurl := range *zoneURLs { - resp, err := http.Get(zurl) - if err != nil { - panic(err) - } - - reader := bufio.NewReaderSize(resp.Body, 2048) - - var i int - zp := dns.NewZoneParser(reader, "", zurl) - for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { - rrs = append(rrs, rr) - - if rr.Header().Rrtype == dns.TypeNS { - ns = append(ns, rr) - } - - i++ - } - - if zp.Err() != nil { - panic(zp.Err()) - } - - resp.Body.Close() - - log.Printf("%s: %d records", zurl, i) - } - - dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) { - m := new(dns.Msg) - m.SetReply(r) - m.Authoritative = true - - for _, q := range r.Question { - answers := []dns.RR{} - for _, rr := range rrs { - rh := rr.Header() - - if rh.Rrtype == dns.TypeCNAME && q.Name == rh.Name { - answers = append(answers, rr) - - for _, a := range resolver("127.0.0.1:"+*port, rr.(*dns.CNAME).Target, q.Qtype) { - answers = append(answers, a) - } - } - - if q.Name == rh.Name && q.Qtype == rh.Rrtype && q.Qclass == rh.Class { - answers = append(answers, rr) - } - } - if len(answers) == 0 && *server != "" { - for _, a := range resolver(*server, q.Name, q.Qtype) { - answers = append(answers, a) - } - } else { - m.Ns = ns - } - for _, a := range answers { - m.Answer = append(m.Answer, a) - } - } - w.WriteMsg(m) - }) - - go func() { - srv := &dns.Server{Addr: ":" + *port, Net: "udp"} - if err := srv.ListenAndServe(); err != nil { - log.Fatalf("Failed to set udp listener %s\n", err.Error()) - } - }() - - sig := make(chan os.Signal) - signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) - s := <-sig - log.Fatalf("Signal (%v) received, stopping\n", s) -} - -func resolver(server, fqdn string, r_type uint16) []dns.RR { - m1 := new(dns.Msg) - m1.Id = dns.Id() - m1.SetQuestion(fqdn, r_type) - - in, err := dns.Exchange(m1, server) - if err == nil { - return in.Answer - } - return []dns.RR{} -} diff --git a/cmd/genua/main.go b/cmd/genua/main.go deleted file mode 100644 index 1ff8516..0000000 --- a/cmd/genua/main.go +++ /dev/null @@ -1,20 +0,0 @@ -// Command genua generates an example user agent. -package main - -import ( - "flag" - "fmt" - "log" - - "within.website/x/web/useragent" -) - -func main() { - flag.Parse() - - if flag.NArg() != 2 { - log.Fatal("usage: genua <prefix> <infoURL>") - } - - fmt.Println(useragent.GenUserAgent(flag.Arg(0), flag.Arg(1))) -} diff --git a/cmd/minipaas/main.go b/cmd/minipaas/main.go deleted file mode 100644 index 41ca72a..0000000 --- a/cmd/minipaas/main.go +++ /dev/null @@ -1,21 +0,0 @@ -// Command minipaas is a simple client for minipaas.xeserv.us. This is not useful without access to that server. -package main - -import ( - "flag" - "log" - "strings" - - "within.website/x/internal" - "within.website/x/internal/minipaas" -) - -func main() { - flag.Parse() - internal.HandleStartup() - - err := minipaas.Exec(strings.Join(flag.Args(), " ")) - if err != nil { - log.Fatal(err) - } -} diff --git a/cmd/thumber/in/XjScp8a.png b/cmd/thumber/in/XjScp8a.png Binary files differdeleted file mode 100644 index a9ec6df..0000000 --- a/cmd/thumber/in/XjScp8a.png +++ /dev/null diff --git a/cmd/thumber/in/lB4ICS3.png b/cmd/thumber/in/lB4ICS3.png Binary files differdeleted file mode 100644 index 850bf28..0000000 --- a/cmd/thumber/in/lB4ICS3.png +++ /dev/null diff --git a/cmd/thumber/in/oaw79y9.jpg b/cmd/thumber/in/oaw79y9.jpg Binary files differdeleted file mode 100644 index 2ab4cb9..0000000 --- a/cmd/thumber/in/oaw79y9.jpg +++ /dev/null diff --git a/cmd/thumber/main.go b/cmd/thumber/main.go deleted file mode 100644 index bd67130..0000000 --- a/cmd/thumber/main.go +++ /dev/null @@ -1,58 +0,0 @@ -// Command thumber creates 256x256 thumbnails for a folder/pile of them. -package main - -import ( - "flag" - "log" - "os" - "path/filepath" - "strings" - - "github.com/disintegration/imaging" - "within.website/x/internal" -) - -var ( - dirToWalk = flag.String("walkdir", "./in", "directory to walk and generate thumbnails for") -) - -func main() { - internal.HandleStartup() - - err := filepath.Walk(*dirToWalk, makeThumbnail) - if err != nil { - log.Fatal(err) - } -} - -func makeThumbnail(fname string, info os.FileInfo, err error) error { - if info.IsDir() { - return nil - } - - if strings.HasSuffix(fname, ".thumb.png") { - return nil - } - - if strings.HasSuffix(fname, ".html") { - return nil - } - - _, err = os.Stat("thumbs/" + filepath.Base(fname) + ".thumb.png") - if err == nil { - log.Printf("skipping %s", fname) - return nil - } - - log.Printf("Starting to open %s", fname) - - img, err := imaging.Open(fname) - if err != nil { - return err - } - - croppedImage := imaging.Thumbnail(img, 256, 256, imaging.Lanczos) - err = imaging.Save(croppedImage, "thumbs/"+filepath.Base(fname)+".thumb.png") - - return err -} diff --git a/cmd/tlstestd/main.go b/cmd/tlstestd/main.go deleted file mode 100644 index 8b1489e..0000000 --- a/cmd/tlstestd/main.go +++ /dev/null @@ -1,31 +0,0 @@ -// Command tlstestd loads the given TLS cert/key and listens to the given port over HTTPS. -package main - -import ( - "flag" - "log" - "net/http" - - "within.website/x/internal" -) - -var ( - cert = flag.String("cert", "cert.pem", "TLS cert file") - key = flag.String("key", "key.pem", "TLS key") - port = flag.String("port", "2848", "TCP port to listen on") -) - -func helloServer(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Content-Type", "text/plain") - w.Write([]byte("Your TLS connection works...or you accepted an invalid cert :)")) -} - -func main() { - internal.HandleStartup() - - http.HandleFunc("/", helloServer) - err := http.ListenAndServeTLS(":"+*port, *cert, *key, nil) - if err != nil { - log.Fatal("ListenAndServe: ", err) - } -} diff --git a/cmd/tor2web/main.go b/cmd/tor2web/main.go deleted file mode 100644 index b6a03e6..0000000 --- a/cmd/tor2web/main.go +++ /dev/null @@ -1,58 +0,0 @@ -// Command tor2web is a simple caching proxy to tor onion sites. -package main - -import ( - "context" - "flag" - "log" - "net/http" - "net/http/httputil" - - "github.com/birkelund/boltdbcache" - "github.com/gregjones/httpcache" - "golang.org/x/net/proxy" - "within.website/ln" - "within.website/ln/opname" -) - -var ( - dbLoc = flag.String("db-loc", "./cache.db", "cache location on disk (boltdb)") - torSocksAddr = flag.String("tor-socks-addr", "127.0.0.1:9050", "tor socks address") - httpPort = flag.String("port", "80", "HTTP port") - httpsPort = flag.String("https-port", "443", "HTTPS port") - tlsCert = flag.String("tls-cert", "/etc/within/star.onion.cert.pem", "tls cert location on disk") - tlsKey = flag.String("tls-key", "/etc/within/star.onion.key.pem", "tls key location on disk") -) - -func main() { - ctx := opname.With(context.Background(), "main") - // Create a socks5 dialer - dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct) - if err != nil { - log.Fatal(err) - } - - // Setup HTTP transport - tr := &http.Transport{ - Dial: dialer.Dial, - } - - c, err := boltdbcache.New(*dbLoc, boltdbcache.WithBucketName("darkweb")) - if err != nil { - ln.FatalErr(ctx, err) - } - - ttr := httpcache.NewTransport(c) - ttr.Transport = tr - - rp := &httputil.ReverseProxy{ - Transport: ttr, - Director: func(r *http.Request) { - r.URL.Scheme = "http" - r.URL.Host = r.Host - }, - } - - go ln.FatalErr(ctx, http.ListenAndServe(":"+*httpPort, rp)) - ln.FatalErr(ctx, http.ListenAndServeTLS(":"+*httpsPort, *tlsCert, *tlsKey, rp)) -} diff --git a/cmd/within.website/build.go b/cmd/within.website/build.go deleted file mode 100644 index b6dfb4e..0000000 --- a/cmd/within.website/build.go +++ /dev/null @@ -1,58 +0,0 @@ -//+build ignore - -// Builds and deploys the application to minipaas. -package main - -import ( - "context" - "flag" - "log" - "net/http" - "os" - - "github.com/shurcooL/vfsgen" - "within.website/x/internal" - "within.website/x/internal/kahless" - "within.website/x/internal/yeet" -) - -var ( - genVFS = flag.Bool("gen-vfs", true, "if true, generate VFS") - deploy = flag.Bool("deploy", true, "if true, deploy to minipaas via kahless") -) - -func main() { - internal.HandleStartup() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - if *genVFS { - err := vfsgen.Generate(http.Dir("./static"), vfsgen.Options{ - PackageName: "main", - BuildTags: "!dev", - VariableName: "assets", - }) - if err != nil { - log.Fatalln(err) - } - } - - if *deploy { - env := append(os.Environ(), []string{"CGO_ENABLED=0", "GOOS=linux"}...) - yeet.ShouldWork(ctx, env, yeet.WD, "go", "build", "-o=web") - yeet.ShouldWork(ctx, env, yeet.WD, "appsluggr", "-web=web") - fin, err := os.Open("slug.tar.gz") - if err != nil { - log.Fatal(err) - } - defer fin.Close() - - fname := "within.website-" + yeet.DateTag + ".tar.gz" - pubURL, err := kahless.CopyFile(fname, fin) - if err != nil { - log.Fatal(err) - } - - log.Println(pubURL) - } -} diff --git a/cmd/xedn/build.go b/cmd/xedn/build.go index d84e89e..38f6335 100644 --- a/cmd/xedn/build.go +++ b/cmd/xedn/build.go @@ -1,6 +1,6 @@ -//+build ignore +//go:build ignore -// Builds and deploys the application to minipaas. +// Builds and deploys the application to fly.io. package main import ( @@ -8,6 +8,7 @@ import ( "os" "within.website/x/internal" + "within.website/x/internal/appsluggr" "within.website/x/internal/yeet" ) @@ -19,7 +20,8 @@ func main() { env := append(os.Environ(), []string{"CGO_ENABLED=0", "GOOS=linux"}...) yeet.ShouldWork(ctx, env, yeet.WD, "go", "build", "-v", "-o=web") - yeet.ShouldWork(ctx, env, yeet.WD, "appsluggr", "-web=web") - os.Remove("web") + appsluggr.Must("./web", "./slug.tar.gz") + os.Remove("./web") yeet.ShouldWork(ctx, env, yeet.WD, "flyctl", "deploy", "--now") + os.Remove("./slug.tar.gz") } diff --git a/docs/man1/appsluggr.1 b/docs/man1/appsluggr.1 deleted file mode 100644 index a0f3881..0000000 --- a/docs/man1/appsluggr.1 +++ /dev/null @@ -1,113 +0,0 @@ -.Dd December 09, 2018 -.Dt APPSLUGGR 1 URM - - -.Sh NAME -.Nm appsluggr -.Nd appsluggr packages a precompiled binary application as a Heroku style slug for use with Dokku. - - -.Sh SYNOPSIS -.Nm - -.Op Fl fname -.Op Fl license -.Op Fl web -.Op Fl web-scale -.Op Fl worker -.Op Fl worker-scale - - -.Sh DESCRIPTION -.Nm -is a small tool to package -.Li GOOS=linux GOARCH=amd64 -binaries for consumption on -.Li hyperlink: http://dokku.viewdocs.io/dokku/ Dokku -. - -.Bl -tag -width " " -offset indent -compact - -.It Fl fname -The filename to write the resulting slug to. - -The default value for this is -.Li slug.tar.gz -. - -.It Fl license -If set, the tool will show its software license details and then exit. - -.It Fl web -The path to the binary for the web process. - -One of -.Fl web -or -.Fl worker -must be set. - -.It Fl web-scale -The default scale for web process if defined. - -The default value for this is 1. - -.It Fl worker -The path to the binary for the worker process. -One of -.Fl web -or -.Fl worker -must be set. - -.It Fl worker-scale -The default scale for the worker process if defined. - -The default value for this is 1 - -.El - - -.Sh EXAMPLES - -.Li appsluggr - -.Li appsluggr -web web - -.Li appsluggr -worker ilo-sona - -.Li appsluggr -fname foo.tar.gz -web web -worker worker -web-scale 4 -worker-scale 16 - - -.Sh IMPLEMENTATION NOTES - -.Nm -when used with -.Lk hyperlink: http://dokku.viewdocs.io/dokku/ Dokku -requires the use of the -.Lk hyperlink: https://github.com/ryandotsmith/null-buildpack Null Buildpack -as follows: - -.Li $ dokku config:set $APP_NAME BUILDPACK_URL=https://github.com/ryandotsmith/null-buildpack - -Or - -.Li $ ssh dokku@host config:set <see above> - - -.Sh DIAGNOSTICS - -.Ex -std appsluggr - - -.Sh SEE ALSO - -.Bl -bullet - -.It -.Lk hyperlink: http://dokku.viewdocs.io/dokku/ Dokku - -.It -.Lk hyperlink: https://github.com/ryandotsmith/null-buildpack Null Buildpack - -.El diff --git a/internal/appsluggr/appsluggr.go b/internal/appsluggr/appsluggr.go new file mode 100644 index 0000000..7a884ae --- /dev/null +++ b/internal/appsluggr/appsluggr.go @@ -0,0 +1,112 @@ +// Package appsluggr packages a given binary into a Heroku-compatible slug. +package appsluggr + +import ( + "archive/tar" + "compress/gzip" + "io" + "log" + "os" + "path/filepath" + "strings" + + "github.com/otiai10/copy" + "github.com/rogpeppe/go-internal/dirhash" +) + +// Must calls Pack and panics on an error. +// +// This is useful for using appsluggr from yeet scripts. +func Must(fname, outFname string) { + if err := Pack(fname, outFname); err != nil { + log.Panicf("error packing %s into %s: %v", fname, outFname, err) + } +} + +// Pack takes a statically linked binary and packs it into a Heroku-compatible slug image +// at outFname. +func Pack(fname string, outFname string) error { + dir, err := os.MkdirTemp("", "appsluggr") + if err != nil { + return err + } + defer os.RemoveAll(dir) + + fout, err := os.Create(outFname) + if err != nil { + return err + } + defer fout.Close() + + gzw := gzip.NewWriter(fout) + defer gzw.Close() + + tw := tar.NewWriter(gzw) + defer tw.Close() + + os.MkdirAll(filepath.Join(dir, "bin"), 0777) + + if err := copy.Copy(fname, filepath.Join(dir, "bin", "main")); err != nil { + return err + } + + if err := os.WriteFile(filepath.Join(dir, "Procfile"), []byte("web: /app/bin/main\n"), 0777); err != nil { + return err + } + + hash, err := dirhash.HashDir(dir, os.Args[0], dirhash.Hash1) + if err != nil { + return err + } + + gzw.Comment = hash + log.Printf("hash: %s", hash) + + return filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error { + // return on any error + if err != nil { + log.Printf("got error on %s: %v", file, err) + return err + } + + if fi.IsDir() { + return nil // not a file. ignore. + } + + // create a new dir/file header + header, err := tar.FileInfoHeader(fi, fi.Name()) + if err != nil { + return err + } + + // update the name to correctly reflect the desired destination when untaring + header.Name = strings.TrimPrefix(strings.Replace(file, dir, "", -1), string(filepath.Separator)) + + // write the header + if err := tw.WriteHeader(header); err != nil { + return err + } + + // return on non-regular files (thanks to [kumo](https://medium.com/@komuw/just-like-you-did-fbdd7df829d3) for this suggested update) + if !fi.Mode().IsRegular() { + return nil + } + + // open files for taring + f, err := os.Open(file) + if err != nil { + return err + } + + // copy file data into tar writer + if _, err := io.Copy(tw, f); err != nil { + return err + } + + // manually close here after each file operation; defering would cause each file close + // to wait until all operations have completed. + f.Close() + + return nil + }) +} diff --git a/tor/tor.go b/tor/tor.go deleted file mode 100644 index a7ad373..0000000 --- a/tor/tor.go +++ /dev/null @@ -1,88 +0,0 @@ -// Package tor manages and automates starting a child tor process for exposing TCP services into onionland. -package tor - -import ( - "crypto/rsa" - "fmt" - "log" - "strconv" - "time" - - "github.com/Yawning/bulb" - "github.com/phayes/freeport" - "github.com/sycamoreone/orc/tor" -) - -// Config is a wrapper struct for tor configuration. -type Config struct { - DataDir string - HashedControlPassword string - ClearPassword string - Timeout time.Duration -} - -// StartTor starts a new instance of tor or doesn't with the reason why. -func StartTor(cfg Config) (*Tor, error) { - tc := tor.NewConfig() - tc.Set("DataDirectory", cfg.DataDir) - tc.Set("HashedControlPassword", cfg.HashedControlPassword) - tc.Set("SocksPort", "0") - - port, err := freeport.GetFreePort() - if err != nil { - return nil, err - } - - tc.Set("ControlPort", fmt.Sprint(port)) - - tc.Timeout = cfg.Timeout - - tcmd, err := tor.NewCmd(tc) - if err != nil { - return nil, err - } - - err = tcmd.Start() - if err != nil { - return nil, err - } - - log.Println("tor started, sleeping for a few seconds for it to settle...") - time.Sleep(5 * time.Second) - - bc, err := bulb.Dial("tcp", "127.0.0.1:"+strconv.Itoa(port)) - if err != nil { - return nil, err - } - - err = bc.Authenticate(cfg.ClearPassword) - if err != nil { - return nil, err - } - - t := &Tor{ - tc: tc, - tcmd: tcmd, - bc: bc, - } - - return t, nil -} - -// Tor is a higher level wrapper to a child tor process -type Tor struct { - tc *tor.Config - tcmd *tor.Cmd - bc *bulb.Conn -} - -// AddOnion adds an onion service to this machine with the given private key -// (can be nil for an auto-generated key), virtual onion port and TCP destunation. -func (t *Tor) AddOnion(pKey *rsa.PrivateKey, virtPort uint16, destination string) (*bulb.OnionInfo, error) { - return t.bc.AddOnion([]bulb.OnionPortSpec{ - { - VirtPort: virtPort, - Target: destination, - }, - }, pKey, true) -} |
