mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-07 22:24:42 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47a09aa177 | |||
| 2456b65ce2 | |||
| 292686a19a | |||
| 5ec62ca732 | |||
| a79df0d2dd | |||
| cc8fdbfede |
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use think\Db;
|
use think\Db;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
$ws = isset($_GET['ws']) ? (string)$_GET['ws'] : (isset($_GET['amp;ws']) ? (string)$_GET['amp;ws'] : '');
|
$ws = isset($_GET['ws']) ? (string)$_GET['ws'] : (isset($_GET['amp;ws']) ? (string)$_GET['amp;ws'] : '');
|
||||||
$protocol = isset($_GET['protocol']) ? (string)$_GET['protocol'] : (isset($_GET['amp;protocol']) ? (string)$_GET['amp;protocol'] : '');
|
$protocol = isset($_GET['protocol']) ? (string)$_GET['protocol'] : (isset($_GET['amp;protocol']) ? (string)$_GET['amp;protocol'] : '');
|
||||||
$container = isset($_GET['container']) ? (string)$_GET['container'] : (isset($_GET['amp;container']) ? (string)$_GET['amp;container'] : '');
|
$container = isset($_GET['container']) ? (string)$_GET['container'] : (isset($_GET['amp;container']) ? (string)$_GET['amp;container'] : '');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<style>
|
<style>
|
||||||
.clicd-fw-panel {
|
.clicd-fw-panel {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #1f2937
|
color: #1f2937
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<style>
|
<style>
|
||||||
.clicd-info{font-size:14px;color:#1f2937;background:#f6f8fb;padding:14px;border-radius:6px;max-width:100%;overflow:hidden}
|
.clicd-info{font-size:14px;color:#1f2937;background:#f6f8fb;padding:14px;border-radius:6px;max-width:100%;overflow:hidden}
|
||||||
.clicd-info *{box-sizing:border-box}
|
.clicd-info *{box-sizing:border-box}
|
||||||
.clicd-head{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-bottom:12px}
|
.clicd-head{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-bottom:12px}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<style>
|
<style>
|
||||||
.clicd-nat-panel{font-size:14px;color:#1f2937}
|
.clicd-nat-panel{font-size:14px;color:#1f2937}
|
||||||
.clicd-nat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:16px}
|
.clicd-nat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:16px}
|
||||||
.clicd-nat-card{border:1px solid #e5e7eb;border-radius:6px;padding:12px;background:#fff}
|
.clicd-nat-card{border:1px solid #e5e7eb;border-radius:6px;padding:12px;background:#fff}
|
||||||
|
|||||||
@@ -1017,12 +1017,99 @@ func findSeccompProfile() (string, error) {
|
|||||||
"/etc/lxc/common.seccomp",
|
"/etc/lxc/common.seccomp",
|
||||||
} {
|
} {
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
return path, nil
|
return ensureCVE202643499SeccompProfile(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", errors.New("required LXC seccomp profile not found")
|
return "", errors.New("required LXC seccomp profile not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clicdSeccompProfileDir = "/var/lib/clicd/security/seccomp"
|
||||||
|
const clicdCVE202643499SeccompProfile = clicdSeccompProfileDir + "/lxc-cve-2026-43499.profile"
|
||||||
|
|
||||||
|
var cve202643499FutexSeccompRules = []string{
|
||||||
|
"# clicd managed: mitigate CVE-2026-43499 from LXC guests by blocking PI futex operations",
|
||||||
|
"futex errno 1 [1,0x6,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0x7,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0x8,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0xb,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0xc,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0xd,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureCVE202643499SeccompProfile(basePath string) (string, error) {
|
||||||
|
data, err := os.ReadFile(basePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read LXC seccomp profile: %v", err)
|
||||||
|
}
|
||||||
|
content := string(data)
|
||||||
|
if !isLXCVDenylistSeccompProfile(content) {
|
||||||
|
return "", fmt.Errorf("LXC seccomp profile %s is not a v2 denylist profile; cannot apply CVE-2026-43499 futex mitigation safely", basePath)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(clicdSeccompProfileDir, 0755); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create CLICD seccomp directory: %v", err)
|
||||||
|
}
|
||||||
|
hardened := appendMissingSeccompRules(content, cve202643499FutexSeccompRules)
|
||||||
|
if err := os.WriteFile(clicdCVE202643499SeccompProfile, []byte(hardened), 0644); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to write CLICD seccomp profile: %v", err)
|
||||||
|
}
|
||||||
|
return clicdCVE202643499SeccompProfile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLXCVDenylistSeccompProfile(content string) bool {
|
||||||
|
lines := nonCommentSeccompLines(content)
|
||||||
|
return len(lines) >= 2 && lines[0] == "2" && isSeccompDenylistPolicy(lines[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSeccompDenylistPolicy(line string) bool {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fields[0] == "denylist" || fields[0] == "blacklist"
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendMissingSeccompRules(content string, rules []string) string {
|
||||||
|
trimmed := strings.TrimRight(content, "\r\n")
|
||||||
|
existing := map[string]bool{}
|
||||||
|
for _, line := range strings.Split(trimmed, "\n") {
|
||||||
|
line = strings.TrimSpace(stripSeccompLineComment(line))
|
||||||
|
if line != "" {
|
||||||
|
existing[line] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString(trimmed)
|
||||||
|
for _, rule := range rules {
|
||||||
|
key := strings.TrimSpace(stripSeccompLineComment(rule))
|
||||||
|
if key != "" && existing[key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
builder.WriteString("\n")
|
||||||
|
builder.WriteString(rule)
|
||||||
|
}
|
||||||
|
builder.WriteString("\n")
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonCommentSeccompLines(content string) []string {
|
||||||
|
lines := make([]string, 0)
|
||||||
|
for _, line := range strings.Split(content, "\n") {
|
||||||
|
line = strings.TrimSpace(stripSeccompLineComment(line))
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripSeccompLineComment(line string) string {
|
||||||
|
if idx := strings.Index(line, "#"); idx >= 0 {
|
||||||
|
return line[:idx]
|
||||||
|
}
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
|
||||||
func findAppArmorProfile() (string, error) {
|
func findAppArmorProfile() (string, error) {
|
||||||
data, err := os.ReadFile("/sys/kernel/security/apparmor/profiles")
|
data, err := os.ReadFile("/sys/kernel/security/apparmor/profiles")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -88,3 +88,48 @@ func TestSafeRootfsPathRejectsSiblingPrefix(t *testing.T) {
|
|||||||
t.Fatalf("safeRootfsPath returned %v, want unsafe rootfs path error", err)
|
t.Fatalf("safeRootfsPath returned %v, want unsafe rootfs path error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsLXCVDenylistSeccompProfile(t *testing.T) {
|
||||||
|
tests := []string{`
|
||||||
|
# base profile
|
||||||
|
2
|
||||||
|
denylist
|
||||||
|
[all]
|
||||||
|
open_by_handle_at errno 1
|
||||||
|
`, `
|
||||||
|
2
|
||||||
|
blacklist allow
|
||||||
|
[all]
|
||||||
|
open_by_handle_at errno 1
|
||||||
|
`}
|
||||||
|
|
||||||
|
for _, profile := range tests {
|
||||||
|
if !isLXCVDenylistSeccompProfile(profile) {
|
||||||
|
t.Fatalf("expected v2 denylist profile for\n%s", profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isLXCVDenylistSeccompProfile("1\nallowlist\n1\n") {
|
||||||
|
t.Fatal("did not expect v1 allowlist profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendMissingSeccompRulesAddsFutexMitigationOnce(t *testing.T) {
|
||||||
|
base := "2\ndenylist\n[all]\nopen_by_handle_at errno 1\n"
|
||||||
|
|
||||||
|
once := appendMissingSeccompRules(base, cve202643499FutexSeccompRules)
|
||||||
|
twice := appendMissingSeccompRules(once, cve202643499FutexSeccompRules)
|
||||||
|
|
||||||
|
for _, want := range []string{
|
||||||
|
"futex errno 1 [1,0x6,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0xb,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0xc,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
"futex errno 1 [1,0xd,SCMP_CMP_MASKED_EQ,0x7f]",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(once, want) {
|
||||||
|
t.Fatalf("missing seccomp rule %q in\n%s", want, once)
|
||||||
|
}
|
||||||
|
if strings.Count(twice, want) != 1 {
|
||||||
|
t.Fatalf("rule %q duplicated in\n%s", want, twice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version = "1.1.20"
|
Version = "1.1.21"
|
||||||
Repo = "MengMengCode/CLICD"
|
Repo = "MengMengCode/CLICD"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "clicd-frontend",
|
"name": "clicd-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.1.20",
|
"version": "1.1.21",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export default function Login() {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-center text-xs text-gray-400 mt-6">CLICD v1.1.20</p>
|
<p className="text-center text-xs text-gray-400 mt-6">CLICD v1.1.21</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user