feat: Add read-only flag to FTP server
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -144,6 +144,7 @@ func main() {
|
||||
)
|
||||
},
|
||||
false,
|
||||
false,
|
||||
|
||||
func(hdr *config.Header) {
|
||||
l.Trace("Header transform", hdr)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user