Add custom image handling and access policy management

- Implement tests for custom KVM and LXC image creation, ensuring invalid sources and architecture mismatches are rejected.
- Introduce access policy management in CLI, allowing configuration of allowed sources and trusted proxies.
- Add NAT network configuration with validation for RFC1918 compliance and subnet parsing.
- Create panel access policy management, including normalization and evaluation of access decisions based on client IPs and forwarded headers.
- Develop middleware for enforcing access policies in the server, returning appropriate responses for allowed and denied requests.
- Enhance custom image downloading and validation, ensuring integrity and security of downloaded root filesystem archives.
- Include comprehensive tests for all new functionalities to ensure reliability and correctness.
This commit is contained in:
MengMengCode
2026-07-26 04:04:45 +08:00
parent 8283b88ded
commit 38debab1aa
49 changed files with 4504 additions and 246 deletions
+89 -26
View File
@@ -503,15 +503,30 @@ func (m *Manager) CreateContainer(cfg ContainerConfig) error {
lxcName, id, tmpl.Distro, tmpl.Release, tmpl.Arch)
cfg.ReportProgress("rootfs", "下载模板并创建基础文件系统")
args := []string{"-n", lxcName, "-t", "download", "--",
"-d", tmpl.Distro, "-r", tmpl.Release, "-a", tmpl.Arch}
if tmpl.Variant != "" {
args = append(args, "--variant", tmpl.Variant)
}
cmd := exec.Command("lxc-create", args...)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("lxc-create failed: %v, output: %s", err, string(output))
if tmpl.Custom {
output, err := exec.Command("lxc-create", "-n", lxcName, "-t", "none").CombinedOutput()
if err != nil {
return fmt.Errorf("lxc-create failed for custom rootfs: %v, output: %s", err, string(output))
}
if err := m.configureCustomLXCBase(lxcName, tmpl); err != nil {
_ = m.cleanupContainerStorage(lxcName)
return err
}
if err := ExtractCustomRootfs(tmpl.ID, filepath.Join(containerDir, "rootfs")); err != nil {
_ = m.cleanupContainerStorage(lxcName)
return err
}
} else {
args := []string{"-n", lxcName, "-t", "download", "--",
"-d", tmpl.Distro, "-r", tmpl.Release, "-a", tmpl.Arch}
if tmpl.Variant != "" {
args = append(args, "--variant", tmpl.Variant)
}
cmd := exec.Command("lxc-create", args...)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("lxc-create failed: %v, output: %s", err, string(output))
}
}
cfg.ReportProgress("storage", "复制容器数据到存储磁盘")
@@ -676,6 +691,41 @@ func (m *Manager) CreateContainer(cfg ContainerConfig) error {
return nil
}
func (m *Manager) configureCustomLXCBase(lxcName string, tmpl *Template) error {
rootfsPath, err := m.safeRootfsPath(filepath.Join(m.LxcPath, lxcName, "rootfs"))
if err != nil {
return fmt.Errorf("invalid custom LXC rootfs path: %v", err)
}
configFile := filepath.Join(filepath.Dir(rootfsPath), "config")
data, err := os.ReadFile(configFile)
if err != nil {
return fmt.Errorf("failed to read custom LXC base config: %v", err)
}
if _, err := os.Stat("/usr/share/lxc/config/common.conf"); err != nil {
return fmt.Errorf("LXC common configuration is unavailable: %v", err)
}
arch := "linux64"
switch strings.ToLower(strings.TrimSpace(tmpl.Arch)) {
case "amd64", "x86_64", "arm64", "aarch64":
default:
return fmt.Errorf("unsupported custom LXC architecture: %s", tmpl.Arch)
}
base := []string{
"# CLICD custom rootfs base configuration",
"lxc.include = /usr/share/lxc/config/common.conf",
"lxc.arch = " + arch,
"lxc.rootfs.path = dir:" + rootfsPath,
"lxc.uts.name = " + lxcName,
"",
}
if err := os.WriteFile(configFile, []byte(strings.Join(base, "\n")+string(data)), 0644); err != nil {
return fmt.Errorf("failed to write custom LXC base config: %v", err)
}
return nil
}
func (m *Manager) preconfigureNetwork(rootfsPath string, cfg ContainerConfig) {
templateID := cfg.TemplateID
osRelease := ""
@@ -1667,6 +1717,9 @@ func appArmorProfileForTemplate(templateID string) (string, error) {
func systemdTemplateNeedsUnconfinedAppArmor(templateID string) bool {
id := strings.ToLower(strings.TrimSpace(templateID))
if template := FindTemplate(templateID); template != nil {
id += " " + strings.ToLower(template.Distro+" "+template.Release)
}
if id == "" || strings.Contains(id, "alpine") {
return false
}
@@ -2564,7 +2617,7 @@ if [ -L /etc/resolv.conf ] 2>/dev/null; then
fi
# Also try resolvectl for systemd-resolved setups
if command -v resolvectl >/dev/null 2>&1; then
resolvectl dns eth0 10.0.3.1 2>/dev/null || true
resolvectl dns eth0 __CLICD_LXC_GATEWAY__ 2>/dev/null || true
resolvectl dns eth0 8.8.8.8 2>/dev/null || true
resolvectl domain eth0 '~.' 2>/dev/null || true
fi
@@ -2572,7 +2625,7 @@ fi
# Avoid the trap where systemd stub resolver puts "nameserver 127.0.0.53"
# but doesn't actually resolve anything.
if ! grep -q '^nameserver [1-9]' /etc/resolv.conf 2>/dev/null; then
echo "nameserver 10.0.3.1" > /etc/resolv.conf
echo "nameserver __CLICD_LXC_GATEWAY__" > /etc/resolv.conf
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
fi
export DEBIAN_FRONTEND=noninteractive
@@ -2709,6 +2762,7 @@ ensure_sshd_runtime_dir
}
`
script = strings.ReplaceAll(script, "__CLICD_PUBKEY_AUTH__", pubkeyValue)
script = strings.ReplaceAll(script, "__CLICD_LXC_GATEWAY__", config.LXCNATNetwork().Gateway)
if !startService {
return script
}
@@ -3308,23 +3362,32 @@ func (m *Manager) replaceRootfsFromTemplate(lxcName string, tmpl *Template) erro
}
defer m.cleanupTemporaryContainer(tmpName)
args := []string{
"-n", tmpName,
"-t", "download",
"--",
"-d", tmpl.Distro,
"-r", tmpl.Release,
"-a", tmpl.Arch,
}
if tmpl.Variant != "" {
args = append(args, "--variant", tmpl.Variant)
}
output, err := exec.Command("lxc-create", args...).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to download replacement rootfs: %v, output: %s", err, string(output))
tmpRootfs := filepath.Join(tmpDir, "rootfs")
if tmpl.Custom {
if err := os.MkdirAll(tmpRootfs, 0755); err != nil {
return err
}
if err := ExtractCustomRootfs(tmpl.ID, tmpRootfs); err != nil {
return err
}
} else {
args := []string{
"-n", tmpName,
"-t", "download",
"--",
"-d", tmpl.Distro,
"-r", tmpl.Release,
"-a", tmpl.Arch,
}
if tmpl.Variant != "" {
args = append(args, "--variant", tmpl.Variant)
}
output, err := exec.Command("lxc-create", args...).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to download replacement rootfs: %v, output: %s", err, string(output))
}
}
tmpRootfs := filepath.Join(tmpDir, "rootfs")
if !rootfsHasInit(tmpRootfs) {
return fmt.Errorf("downloaded replacement rootfs is invalid: init not found")
}