feat: Add read-only flag to FTP server
This commit is contained in:
@@ -37,6 +37,7 @@ const (
|
|||||||
signatureRecipientFlag = "signature-recipient"
|
signatureRecipientFlag = "signature-recipient"
|
||||||
|
|
||||||
cacheWriteFlag = "cache-write-type"
|
cacheWriteFlag = "cache-write-type"
|
||||||
|
readOnlyFlag = "read-only"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -197,6 +198,7 @@ var serveFTPCmd = &cobra.Command{
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
true, // FTP needs read permission for `STOR` command even if O_WRONLY is set
|
true, // FTP needs read permission for `STOR` command even if O_WRONLY is set
|
||||||
|
viper.GetBool(readOnlyFlag),
|
||||||
|
|
||||||
func(hdr *config.Header) {
|
func(hdr *config.Header) {
|
||||||
jsonLogger.Trace("Header transform", hdr)
|
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().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().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().StringP(cacheDirFlag, "w", cacheDir, "Directory to use if dir cache is enabled")
|
||||||
|
serveFTPCmd.PersistentFlags().BoolP(readOnlyFlag, "j", false, "Block all write operations")
|
||||||
|
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ var serveHTTPCmd = &cobra.Command{
|
|||||||
"", // We never write
|
"", // We never write
|
||||||
nil, // We never write
|
nil, // We never write
|
||||||
false, // We never write
|
false, // We never write
|
||||||
|
true, // We never write
|
||||||
|
|
||||||
func(hdr *config.Header) {
|
func(hdr *config.Header) {
|
||||||
jsonLogger.Trace("Header transform", hdr)
|
jsonLogger.Trace("Header transform", hdr)
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ func main() {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
|
|
||||||
func(hdr *config.Header) {
|
func(hdr *config.Header) {
|
||||||
l.Trace("Header transform", hdr)
|
l.Trace("Header transform", hdr)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type STFS struct {
|
|||||||
|
|
||||||
compressionLevel string
|
compressionLevel string
|
||||||
getFileBuffer func() (cache.WriteCache, func() error, error)
|
getFileBuffer func() (cache.WriteCache, func() error, error)
|
||||||
|
readOnly bool
|
||||||
ignoreReadWritePermissions bool
|
ignoreReadWritePermissions bool
|
||||||
|
|
||||||
ioLock sync.Mutex
|
ioLock sync.Mutex
|
||||||
@@ -47,6 +48,7 @@ func NewSTFS(
|
|||||||
compressionLevel string,
|
compressionLevel string,
|
||||||
getFileBuffer func() (cache.WriteCache, func() error, error),
|
getFileBuffer func() (cache.WriteCache, func() error, error),
|
||||||
ignorePermissionFlags bool,
|
ignorePermissionFlags bool,
|
||||||
|
readOnly bool,
|
||||||
|
|
||||||
onHeader func(hdr *config.Header),
|
onHeader func(hdr *config.Header),
|
||||||
log logging.StructuredLogger,
|
log logging.StructuredLogger,
|
||||||
@@ -60,6 +62,7 @@ func NewSTFS(
|
|||||||
compressionLevel: compressionLevel,
|
compressionLevel: compressionLevel,
|
||||||
getFileBuffer: getFileBuffer,
|
getFileBuffer: getFileBuffer,
|
||||||
ignoreReadWritePermissions: ignorePermissionFlags,
|
ignoreReadWritePermissions: ignorePermissionFlags,
|
||||||
|
readOnly: readOnly,
|
||||||
|
|
||||||
onHeader: onHeader,
|
onHeader: onHeader,
|
||||||
log: log,
|
log: log,
|
||||||
@@ -82,6 +85,10 @@ func (f *STFS) Create(name string) (afero.File, error) {
|
|||||||
"name": name,
|
"name": name,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return nil, os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
return f.OpenFile(name, os.O_CREATE|os.O_RDWR, 0666)
|
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,
|
"perm": perm,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
usr, err := user.Current()
|
usr, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -171,6 +182,10 @@ func (f *STFS) MkdirRoot(name string, perm os.FileMode) error {
|
|||||||
"perm": perm,
|
"perm": perm,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
@@ -192,6 +207,10 @@ func (f *STFS) Mkdir(name string, perm os.FileMode) error {
|
|||||||
"perm": perm,
|
"perm": perm,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
@@ -204,6 +223,10 @@ func (f *STFS) MkdirAll(path string, perm os.FileMode) error {
|
|||||||
"perm": perm,
|
"perm": perm,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
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()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
flags := &ifs.FileFlags{}
|
flags := &ifs.FileFlags{}
|
||||||
if flag&os.O_RDONLY != 0 {
|
if f.readOnly {
|
||||||
flags.Read = true
|
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 {
|
if flag&os.O_WRONLY != 0 {
|
||||||
flags.Write = true
|
flags.Write = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if flag&os.O_RDWR != 0 {
|
if flag&os.O_RDWR != 0 {
|
||||||
flags.Read = true
|
flags.Read = true
|
||||||
flags.Write = true
|
flags.Write = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.ignoreReadWritePermissions {
|
if f.ignoreReadWritePermissions {
|
||||||
flags.Read = true
|
flags.Read = true
|
||||||
flags.Write = true
|
flags.Write = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if flag&os.O_APPEND != 0 {
|
if flag&os.O_APPEND != 0 {
|
||||||
flags.Append = true
|
flags.Append = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if flag&os.O_TRUNC != 0 {
|
if flag&os.O_TRUNC != 0 {
|
||||||
flags.Truncate = true
|
flags.Truncate = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr, err := inventory.Stat(
|
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 != nil {
|
||||||
if err == sql.ErrNoRows {
|
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 {
|
if err := f.mknodeWithoutLocking(false, name, perm, false, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -299,7 +328,6 @@ func (f *STFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, er
|
|||||||
} else {
|
} else {
|
||||||
return nil, os.ErrNotExist
|
return nil, os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -332,6 +360,10 @@ func (f *STFS) Remove(name string) error {
|
|||||||
"name": name,
|
"name": name,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
@@ -343,6 +375,10 @@ func (f *STFS) RemoveAll(path string) error {
|
|||||||
"path": path,
|
"path": path,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
@@ -355,6 +391,10 @@ func (f *STFS) Rename(oldname, newname string) error {
|
|||||||
"newname": newname,
|
"newname": newname,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
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 {
|
func (f *STFS) updateMetadata(hdr *tar.Header) error {
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
done := false
|
done := false
|
||||||
if _, err := f.writeOps.Update(
|
if _, err := f.writeOps.Update(
|
||||||
func() (config.FileConfig, error) {
|
func() (config.FileConfig, error) {
|
||||||
@@ -420,6 +464,10 @@ func (f *STFS) Chmod(name string, mode os.FileMode) error {
|
|||||||
"name": mode,
|
"name": mode,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
@@ -451,6 +499,10 @@ func (f *STFS) Chown(name string, uid, gid int) error {
|
|||||||
"gid": gid,
|
"gid": gid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
@@ -483,6 +535,10 @@ func (f *STFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|||||||
"mtime": mtime,
|
"mtime": mtime,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
@@ -549,6 +605,10 @@ func (f *STFS) SymlinkIfPossible(oldname, newname string) error {
|
|||||||
"newname": newname,
|
"newname": newname,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if f.readOnly {
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
f.ioLock.Lock()
|
f.ioLock.Lock()
|
||||||
defer f.ioLock.Unlock()
|
defer f.ioLock.Unlock()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user