feat: Add read-only flag to FTP server

This commit is contained in:
Felix Pojtinger
2022-01-05 03:50:37 +01:00
parent f856f880e5
commit e8a164ca61
4 changed files with 86 additions and 21 deletions

View File

@@ -37,6 +37,7 @@ const (
signatureRecipientFlag = "signature-recipient"
cacheWriteFlag = "cache-write-type"
readOnlyFlag = "read-only"
)
var (
@@ -197,6 +198,7 @@ var serveFTPCmd = &cobra.Command{
)
},
true, // FTP needs read permission for `STOR` command even if O_WRONLY is set
viper.GetBool(readOnlyFlag),
func(hdr *config.Header) {
jsonLogger.Trace("Header transform", hdr)
@@ -313,6 +315,7 @@ func init() {
serveFTPCmd.PersistentFlags().StringP(cacheWriteFlag, "q", config.WriteCacheTypeFile, fmt.Sprintf("Write cache to use (default %v, available are %v)", config.WriteCacheTypeFile, config.KnownWriteCacheTypes))
serveFTPCmd.PersistentFlags().DurationP(cacheDurationFlag, "u", time.Hour, "Duration until cache is invalidated")
serveFTPCmd.PersistentFlags().StringP(cacheDirFlag, "w", cacheDir, "Directory to use if dir cache is enabled")
serveFTPCmd.PersistentFlags().BoolP(readOnlyFlag, "j", false, "Block all write operations")
viper.AutomaticEnv()

View File

@@ -131,6 +131,7 @@ var serveHTTPCmd = &cobra.Command{
"", // We never write
nil, // We never write
false, // We never write
true, // We never write
func(hdr *config.Header) {
jsonLogger.Trace("Header transform", hdr)

View File

@@ -144,6 +144,7 @@ func main() {
)
},
false,
false,
func(hdr *config.Header) {
l.Trace("Header transform", hdr)

View File

@@ -30,6 +30,7 @@ type STFS struct {
compressionLevel string
getFileBuffer func() (cache.WriteCache, func() error, error)
readOnly bool
ignoreReadWritePermissions bool
ioLock sync.Mutex
@@ -47,6 +48,7 @@ func NewSTFS(
compressionLevel string,
getFileBuffer func() (cache.WriteCache, func() error, error),
ignorePermissionFlags bool,
readOnly bool,
onHeader func(hdr *config.Header),
log logging.StructuredLogger,
@@ -60,6 +62,7 @@ func NewSTFS(
compressionLevel: compressionLevel,
getFileBuffer: getFileBuffer,
ignoreReadWritePermissions: ignorePermissionFlags,
readOnly: readOnly,
onHeader: onHeader,
log: log,
@@ -82,6 +85,10 @@ func (f *STFS) Create(name string) (afero.File, error) {
"name": name,
})
if f.readOnly {
return nil, os.ErrPermission
}
return f.OpenFile(name, os.O_CREATE|os.O_RDWR, 0666)
}
@@ -91,6 +98,10 @@ func (f *STFS) mknodeWithoutLocking(dir bool, name string, perm os.FileMode, ove
"perm": perm,
})
if f.readOnly {
return os.ErrPermission
}
usr, err := user.Current()
if err != nil {
return err
@@ -171,6 +182,10 @@ func (f *STFS) MkdirRoot(name string, perm os.FileMode) error {
"perm": perm,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -192,6 +207,10 @@ func (f *STFS) Mkdir(name string, perm os.FileMode) error {
"perm": perm,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -204,6 +223,10 @@ func (f *STFS) MkdirAll(path string, perm os.FileMode) error {
"perm": perm,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -244,30 +267,36 @@ func (f *STFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, er
defer f.ioLock.Unlock()
flags := &ifs.FileFlags{}
if flag&os.O_RDONLY != 0 {
flags.Read = true
}
if f.readOnly {
if flag&os.O_RDONLY != 0 || flag&os.O_RDWR != 0 || f.ignoreReadWritePermissions {
flags.Read = true
}
} else {
if flag&os.O_RDONLY != 0 {
flags.Read = true
}
if flag&os.O_WRONLY != 0 {
flags.Write = true
}
if flag&os.O_WRONLY != 0 {
flags.Write = true
}
if flag&os.O_RDWR != 0 {
flags.Read = true
flags.Write = true
}
if flag&os.O_RDWR != 0 {
flags.Read = true
flags.Write = true
}
if f.ignoreReadWritePermissions {
flags.Read = true
flags.Write = true
}
if f.ignoreReadWritePermissions {
flags.Read = true
flags.Write = true
}
if flag&os.O_APPEND != 0 {
flags.Append = true
}
if flag&os.O_APPEND != 0 {
flags.Append = true
}
if flag&os.O_TRUNC != 0 {
flags.Truncate = true
if flag&os.O_TRUNC != 0 {
flags.Truncate = true
}
}
hdr, err := inventory.Stat(
@@ -280,7 +309,7 @@ func (f *STFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, er
)
if err != nil {
if err == sql.ErrNoRows {
if flag&os.O_CREATE != 0 && flag&os.O_EXCL == 0 {
if !f.readOnly && flag&os.O_CREATE != 0 && flag&os.O_EXCL == 0 {
if err := f.mknodeWithoutLocking(false, name, perm, false, ""); err != nil {
return nil, err
}
@@ -299,7 +328,6 @@ func (f *STFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, er
} else {
return nil, os.ErrNotExist
}
} else {
return nil, err
}
@@ -332,6 +360,10 @@ func (f *STFS) Remove(name string) error {
"name": name,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -343,6 +375,10 @@ func (f *STFS) RemoveAll(path string) error {
"path": path,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -355,6 +391,10 @@ func (f *STFS) Rename(oldname, newname string) error {
"newname": newname,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -389,6 +429,10 @@ func (f *STFS) Stat(name string) (os.FileInfo, error) {
}
func (f *STFS) updateMetadata(hdr *tar.Header) error {
if f.readOnly {
return os.ErrPermission
}
done := false
if _, err := f.writeOps.Update(
func() (config.FileConfig, error) {
@@ -420,6 +464,10 @@ func (f *STFS) Chmod(name string, mode os.FileMode) error {
"name": mode,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -451,6 +499,10 @@ func (f *STFS) Chown(name string, uid, gid int) error {
"gid": gid,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -483,6 +535,10 @@ func (f *STFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
"mtime": mtime,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()
@@ -549,6 +605,10 @@ func (f *STFS) SymlinkIfPossible(oldname, newname string) error {
"newname": newname,
})
if f.readOnly {
return os.ErrPermission
}
f.ioLock.Lock()
defer f.ioLock.Unlock()