From c2acb3aacdecb8e196c176791545d283f02181b3 Mon Sep 17 00:00:00 2001 From: Kevin Hellemun <17928966+OGKevin@users.noreply.github.com> Date: Sat, 22 May 2021 11:52:13 +0200 Subject: [PATCH] feature: add support for base dir BaseDir allows one to serve files that are stored on the host machine. --- README.md | 8 ++++++++ httpd/handler.go | 20 ++++++++++++++++---- manifest/io.go | 33 +++++++++++++++++++++++++++++++-- manifest/schema.go | 5 +++++ tftpd/handler.go | 43 +++++++++++++++++++++++++++++++++++++++---- 5 files changed, 99 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 01212f9..ccfd918 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,14 @@ mounts: # When true, the proxy path defined above gets a suffix to the Path prefix appended to it. proxyAppendSuffix: true + - path: /subdir + # When true, all paths starting with this prefix use this mount. + pathIsPrefix: true + # Provides a path on the host to find the files. + # So that BasePath: /tftpboot path: /subdir and client requests: /tfptboot/subdir/file.x the path on the host + # becomes /tfptboot/subdir/file.x + baseDir: /tftpboot + - path: /install.ipxe # The templating context provides access to: .LocalIP, .RemoteIP, .HttpBaseUrl and .Manifest. # Sprig functions are available: masterminds.github.io/sprig diff --git a/httpd/handler.go b/httpd/handler.go index b363fff..8b9360e 100644 --- a/httpd/handler.go +++ b/httpd/handler.go @@ -3,17 +3,19 @@ package httpd import ( "bytes" _ "embed" - mfest "github.com/DSpeichert/netbootd/manifest" - "github.com/DSpeichert/netbootd/static" - "github.com/Masterminds/sprig" "io" "net" "net/http" "net/http/httputil" "net/url" + "os" "strings" "text/template" "time" + + mfest "github.com/DSpeichert/netbootd/manifest" + "github.com/DSpeichert/netbootd/static" + "github.com/Masterminds/sprig" ) type Handler struct { @@ -129,8 +131,18 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } rp.ServeHTTP(w, r) return + } else if mount.BaseDir != "" { + f, err := os.Open(mount.BaseDir + r.URL.Path) + if err != nil { + h.server.logger.Error(). + Err(err). + Msg("Could not get file from BaseDir") + http.Error(w, err.Error(), http.StatusInternalServerError) + } + http.ServeContent(w, r, r.URL.Path, time.Time{}, f) + return } else { - // mount has neither .Path nor .Proxy defined + // mount has neither .Path, .Proxy nor .BaseDir defined h.server.logger.Error(). Str("path", r.RequestURI). Str("client", raddr.String()). diff --git a/manifest/io.go b/manifest/io.go index ccc2c7e..6846425 100644 --- a/manifest/io.go +++ b/manifest/io.go @@ -2,12 +2,20 @@ package manifest import ( "encoding/json" + "fmt" + "path/filepath" + "strings" + "gopkg.in/yaml.v2" ) func ManifestFromJson(content []byte) (manifest Manifest, err error) { err = json.Unmarshal(content, &manifest) - return + if err != nil { + return manifest, err + } + + return manifest, manifest.Validate() } func (m *Manifest) ToJson() ([]byte, error) { @@ -16,7 +24,28 @@ func (m *Manifest) ToJson() ([]byte, error) { func ManifestFromYaml(content []byte) (manifest Manifest, err error) { err = yaml.Unmarshal(content, &manifest) - return + if err != nil { + return manifest, err + } + + return manifest, manifest.Validate() +} + +func (m Manifest) Validate() error { + for i, mount := range m.Mounts { + if mount.BaseDir != "" { + if !filepath.IsAbs(mount.BaseDir) { + return fmt.Errorf("BaseDir needs to be absolute path") + } + if !strings.HasSuffix(mount.BaseDir, "/") { + mount.BaseDir = mount.BaseDir + "/" + } + + m.Mounts[i] = mount + } + } + + return nil } func (m *Manifest) ToYaml() ([]byte, error) { diff --git a/manifest/schema.go b/manifest/schema.go index 62b1e3a..c24aa55 100644 --- a/manifest/schema.go +++ b/manifest/schema.go @@ -47,6 +47,11 @@ type Mount struct { // Provides content template (passed through template/text) to serve. // Mutually exclusive with Proxy option. Content string + + // Provides a path on the host to find the files. + // So that BasePath: /tftpboot path: /subdir and client requests: /tfptboot/subdir/file.x the path on the host + // becomes /tfptboot/subdir/file.x + BaseDir string `yaml:"baseDir"` } func (m Mount) ProxyDirector() (func(req *http.Request), error) { diff --git a/tftpd/handler.go b/tftpd/handler.go index 1e1f4ac..e88c666 100644 --- a/tftpd/handler.go +++ b/tftpd/handler.go @@ -5,15 +5,17 @@ import ( _ "embed" "errors" "fmt" - mfest "github.com/DSpeichert/netbootd/manifest" - "github.com/DSpeichert/netbootd/static" - "github.com/Masterminds/sprig" - "github.com/pin/tftp" "io" "net/http" "net/url" + "os" "strings" "text/template" + + mfest "github.com/DSpeichert/netbootd/manifest" + "github.com/DSpeichert/netbootd/static" + "github.com/Masterminds/sprig" + "github.com/pin/tftp" ) func (server *Server) tftpReadHandler(filename string, rf io.ReaderFrom) error { @@ -156,6 +158,39 @@ func (server *Server) tftpReadHandler(filename string, rf io.ReaderFrom) error { return err } + server.logger.Info(). + Err(err). + Str("path", filename). + Str("client", raddr.IP.String()). + Int64("sent", n). + Msg("transfer finished") + } else if mount.BaseDir != "" { + f, err := os.Open(mount.BaseDir + filename) + if err != nil { + server.logger.Error(). + Err(err). + Msg("Could not get file from BaseDir") + + return err + } + + stat, err := f.Stat() + if err != nil { + server.logger.Error(). + Err(err). + Msg("Could not get file host file stats") + return err + } + + rf.(tftp.OutgoingTransfer).SetSize(int64(stat.Size())) + + n, err := rf.ReadFrom(f) + if err != nil { + server.logger.Error(). + Msgf("ReadFrom failed: %v", err) + return err + } + server.logger.Info(). Err(err). Str("path", filename).