收紧运行目录与管理员本地文件读取边界

This commit is contained in:
smail 2026-09-22 03:26:01 +08:00
parent f732a255ce
commit 2dd5156018
5 changed files with 98 additions and 6 deletions

View File

@ -16,7 +16,15 @@ func MigrateLegacySSLStorage(root string) error {
if !runtimepath.DeploymentLayout(root) {
return nil
}
data, err := os.ReadFile(filepath.Join(root, "config", "config.json"))
configRoot, err := os.OpenRoot(filepath.Join(root, "config"))
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
data, err := configRoot.ReadFile("config.json")
_ = configRoot.Close()
if os.IsNotExist(err) {
return nil
}

View File

@ -148,7 +148,15 @@ func pruneManagedSSLLocked(root string, protected []SSLConfig) error {
}
}
// 磁盘引用也必须保留,避免其他管理器实例或已提交未发布的候选被删除。
data, err := os.ReadFile(filepath.Join(root, "config", "ssl.json"))
configRoot, err := os.OpenRoot(filepath.Join(root, "config"))
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
data, err := configRoot.ReadFile("ssl.json")
_ = configRoot.Close()
if err == nil {
var section SSLSectionConfig
if err := ParseStrict(data, &section); err != nil {

View File

@ -120,7 +120,12 @@ func findStartCaller() (callerIdentity, error) {
if err != nil || (executable != "/usr/bin/systemctl" && executable != "/bin/systemctl") {
continue
}
data, err := os.ReadFile(filepath.Join(base, "cmdline"))
procRoot, err := os.OpenRoot(base)
if err != nil {
continue
}
data, err := procRoot.ReadFile("cmdline")
_ = procRoot.Close()
if err != nil {
continue
}
@ -132,7 +137,12 @@ func findStartCaller() (callerIdentity, error) {
if !allowed {
return callerIdentity{}, ErrUnauthorizedPeer
}
status, err := os.ReadFile(filepath.Join(base, "status"))
procRoot, err = os.OpenRoot(base)
if err != nil {
return callerIdentity{}, ErrUnauthorizedPeer
}
status, err := procRoot.ReadFile("status")
_ = procRoot.Close()
if err != nil || !rootProcessStatus(string(status)) {
return callerIdentity{}, ErrUnauthorizedPeer
}

View File

@ -36,6 +36,59 @@ func ReadRegularFile(path string, maxSize int64) ([]byte, error) {
return data, nil
}
func OpenRegularFile(path string, flags int, perm os.FileMode) (*os.File, error) {
if strings.TrimSpace(path) == "" {
return nil, ErrUnsafeRegularFile
}
absolute, err := filepath.Abs(filepath.Clean(path))
if err != nil {
return nil, ErrUnsafeRegularFile
}
root, err := os.OpenRoot(filepath.Dir(absolute))
if err != nil {
return nil, ErrUnsafeRegularFile
}
defer root.Close()
name := filepath.Base(absolute)
info, err := root.Lstat(name)
if err != nil && !os.IsNotExist(err) {
return nil, ErrUnsafeRegularFile
}
if err == nil && (info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular()) {
return nil, ErrUnsafeRegularFile
}
file, err := root.OpenFile(name, flags, perm)
if err != nil {
return nil, err
}
opened, err := file.Stat()
if err != nil || !opened.Mode().IsRegular() {
_ = file.Close()
return nil, ErrUnsafeRegularFile
}
return file, nil
}
func CreateExclusiveFile(path string, perm os.FileMode) (*os.File, error) {
return OpenRegularFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, perm)
}
func WriteRegularFile(path string, data []byte, perm os.FileMode) error {
if strings.TrimSpace(path) == "" {
return ErrUnsafeRegularFile
}
absolute, err := filepath.Abs(filepath.Clean(path))
if err != nil {
return ErrUnsafeRegularFile
}
root, err := os.OpenRoot(filepath.Dir(absolute))
if err != nil {
return ErrUnsafeRegularFile
}
defer root.Close()
return root.WriteFile(filepath.Base(absolute), data, perm)
}
func ControlledRelativePath(base, value string) (string, error) {
if strings.TrimSpace(value) == "" {
return "", errors.New("path is required")

View File

@ -180,7 +180,15 @@ func CheckReady(root string) error {
return ErrMigrationRequired
}
// 已初始化部署缺失账号库或加密密钥时必须报错,不能按首次安装处理。
raw, err := os.ReadFile(filepath.Join(root, "config", "config.json"))
configRoot, err := os.OpenRoot(filepath.Join(root, "config"))
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
raw, err := configRoot.ReadFile("config.json")
_ = configRoot.Close()
if os.IsNotExist(err) {
return nil
}
@ -279,7 +287,12 @@ func migrate(root string, rename func(string, string) error) ([]Move, error) {
return nil, err
}
lockPath := filepath.Join(directory, migrationLock)
lock, err := os.OpenFile(lockPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
lockRoot, err := os.OpenRoot(directory)
if err != nil {
return nil, err
}
lock, err := lockRoot.OpenFile(migrationLock, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
_ = lockRoot.Close()
if err != nil {
return nil, errors.New("无法取得迁移锁;请确认没有其他迁移进程,并人工核对中断现场")
}