fix(firewall): guard IPv6 forwarding against RA disruption (#13882)

This commit is contained in:
ssongliu
2026-09-21 17:12:17 +08:00
committed by GitHub
parent 3a5371652e
commit 19bb823b05
20 changed files with 320 additions and 63 deletions
+6 -5
View File
@@ -49,11 +49,12 @@ type FirewallBackendOption struct {
}
type FirewallBackendFamilyStatus struct {
Available bool `json:"available"`
Initialized bool `json:"initialized"`
Bound bool `json:"bound"`
Reason string `json:"reason,omitempty"`
ForwardPolicy string `json:"forwardPolicy,omitempty"`
Available bool `json:"available"`
Initialized bool `json:"initialized"`
Bound bool `json:"bound"`
Reason string `json:"reason,omitempty"`
ForwardPolicy string `json:"forwardPolicy,omitempty"`
RAInterfaces []string `json:"raInterfaces,omitempty"`
}
type FirewallBackendGroup struct {
+15 -10
View File
@@ -2413,18 +2413,23 @@ func firewallDeleteChange(current filter.ObservedRule, desired filter.DesiredRul
func loadForwardingFirewallOverview(manager forwarding.Adapter) (dto.FirewallSubsystemStatus, error) {
ipv4, ipv4Err := loadForwardingFamilyInfo(manager, manager.Name(), constant.FirewallFamilyIPv4)
ipv6, ipv6Err := loadForwardingFamilyInfo(manager, manager.Name(), constant.FirewallFamilyIPv6)
initialized, bound, statusErr := ipv4.Initialized, ipv4.Bound, ipv4Err
if manager.Name() == constant.FirewallProviderNftables {
if statusErr == nil && initialized && bound {
initialized, bound, statusErr = ipv6.Initialized, ipv6.Bound, ipv6Err
}
} else {
statusErr = errors.Join(statusErr, ipv6Err)
if ipv6.Available {
initialized, bound = initialized && ipv6.Initialized, bound && ipv6.Bound
if ipv6.Available {
interfaces, err := forwarding.IPv6RAInterfaces(os.ReadFile)
var pathError *os.PathError
switch {
case errors.As(err, &pathError) && errors.Is(err, os.ErrNotExist) && pathError.Path == "/proc/net/if_inet6":
ipv6.Available, ipv6.Initialized, ipv6.Bound = false, false, false
case err != nil:
ipv6.Reason = "ipv6_ra_check_failed"
ipv6.Bound = false
case len(interfaces) > 0:
ipv6.Reason, ipv6.RAInterfaces = "ipv6_ra_required", interfaces
ipv6.Bound = false
case !ipv6.Bound:
ipv6.Reason = "ipv6_forwarding_not_enabled"
}
}
return dto.FirewallSubsystemStatus{IsInit: initialized, IsBind: bound, IPv4: ipv4, IPv6: ipv6}, statusErr
return dto.FirewallSubsystemStatus{IsInit: ipv4.Initialized || ipv6.Initialized, IsBind: ipv4.Bound || ipv6.Bound, IPv4: ipv4, IPv6: ipv6}, errors.Join(ipv4Err, ipv6Err)
}
func loadForwardingFamilyInfo(manager forwarding.Adapter, backend, family string) (dto.FirewallBackendFamilyStatus, error) {
+14
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"os"
"strconv"
"strings"
"sync"
@@ -459,6 +460,19 @@ func (s *ForwardingService) operateRules(ctx context.Context, request dto.Forwar
}
return nil
}
for _, rule := range groups[1].rules {
if rule.Family != forwarding.FamilyIPv6 {
continue
}
interfaces, err := forwarding.IPv6RAInterfaces(os.ReadFile)
if err != nil {
return fmt.Errorf("check IPv6 Router Advertisement: %w", err)
}
if len(interfaces) > 0 {
return fmt.Errorf("IPv6 forwarding blocked: interfaces %s may depend on RA/SLAAC with accept_ra=1; persist accept_ra=2 on interfaces that require RA before retrying", strings.Join(interfaces, ", "))
}
break
}
}
var client forwarding.Adapter
var failures []error
+39 -21
View File
@@ -90,7 +90,6 @@ func (systemIptablesBackend) LoadIPv6RulesFromFile(table, chain, fileName string
type forwardingSystem interface {
ReadFile(name string) ([]byte, error)
WriteFile(name string, data []byte, perm os.FileMode) error
RunWithOptionalSudo(name string, args ...string) error
}
type defaultForwardingSystem struct{}
@@ -103,10 +102,6 @@ func (defaultForwardingSystem) WriteFile(name string, data []byte, perm os.FileM
return cmd.WriteFileWithOptionalSudo(name, data, perm)
}
func (defaultForwardingSystem) RunWithOptionalSudo(name string, args ...string) error {
return cmd.NewCommandMgr().RunWithOptionalSudo(name, args...)
}
type Iptables struct {
provider string
backend iptablesBackend
@@ -151,15 +146,33 @@ func (l *Iptables) ReplaceRules(rules []Rule) error {
if err != nil {
return err
}
if normalized.Family == FamilyIPv6 && !l.backend.IPv6Available() {
return fmt.Errorf("ip6tables command family is unavailable")
}
byFamily[normalized.Family] = append(byFamily[normalized.Family], normalized)
}
var failures []error
for _, family := range []string{FamilyIPv4, FamilyIPv6} {
if family == FamilyIPv6 && !l.backend.IPv6Available() {
if len(byFamily[family]) > 0 {
failures = append(failures, fmt.Errorf("ip6tables command family is unavailable"))
}
continue
}
if family == FamilyIPv6 {
if len(byFamily[family]) > 0 {
if err := ensureForwardingSysctls(l.system, true); err != nil {
failures = append(failures, err)
continue
}
} else {
initialized, _, err := l.familyInitStatus(family)
if err != nil {
failures = append(failures, err)
continue
}
if !initialized {
continue
}
}
}
if err := l.batchEnsureChains(family); err != nil {
return err
}
@@ -171,7 +184,7 @@ func (l *Iptables) ReplaceRules(rules []Rule) error {
return fmt.Errorf("restore %s forwarding rules: %w", family, err)
}
}
return nil
return errors.Join(failures...)
}
func (l *Iptables) CreateRules(ctx context.Context, rules []Rule) error {
@@ -186,6 +199,17 @@ func (l *Iptables) CreateRules(ctx context.Context, rules []Rule) error {
if family == "" {
family = FamilyIPv4
}
if family == FamilyIPv6 {
if !l.backend.IPv6Available() {
return fmt.Errorf("ip6tables command family is unavailable")
}
if err := ensureForwardingSysctls(l.system, true); err != nil {
return err
}
if err := l.batchEnsureChains(family); err != nil {
return err
}
}
return l.backend.Restore(ctx, family, script)
}
@@ -287,19 +311,10 @@ func isRemoteTarget(family, target string) bool {
}
func (l *Iptables) Enable() error {
if err := ensureForwardingSysctls(l.system, l.backend.IPv6Available()); err != nil {
if err := ensureForwardingSysctls(l.system, false); err != nil {
return err
}
for _, family := range []string{FamilyIPv4, FamilyIPv6} {
if family == FamilyIPv6 && !l.backend.IPv6Available() {
continue
}
if err := l.batchEnsureChains(family); err != nil {
return err
}
}
return nil
return l.batchEnsureChains(FamilyIPv4)
}
func (l *Iptables) batchEnsureChains(family string) error {
@@ -506,7 +521,7 @@ func (l *Iptables) InitStatus() (bool, bool, error) {
if err != nil {
return false, false, err
}
return ipv4Init && ipv6Init, ipv4Bind && ipv6Bind, nil
return ipv4Init || ipv6Init, ipv4Bind || ipv6Bind, nil
}
func (l *Iptables) familyInitStatus(family string) (bool, bool, error) {
@@ -519,6 +534,9 @@ func (l *Iptables) familyInitStatus(family string) (bool, bool, error) {
list = l.backend.RunIPv6WithStd
}
data, err := l.system.ReadFile(sysctlPath)
if family == FamilyIPv6 && errors.Is(err, os.ErrNotExist) {
return false, false, nil
}
if err != nil {
return false, false, fmt.Errorf("read %s forwarding status: %w", label, err)
}
+64 -18
View File
@@ -48,14 +48,45 @@ func (n *Nftables) List() ([]Rule, error) {
}
func (n *Nftables) ReplaceRules(rules []Rule) error {
if err := ensureNftForwardTables(); err != nil {
return fmt.Errorf("initialize nftables forwarding table: %w", err)
byFamily := map[string][]Rule{FamilyIPv4: nil, FamilyIPv6: nil}
for _, rule := range rules {
normalized, err := NormalizeRule(rule)
if err != nil {
return err
}
byFamily[normalized.Family] = append(byFamily[normalized.Family], normalized)
}
commands, err := rebuildNftForwardCommands(rules)
if err != nil {
return err
var failures []error
for _, family := range []string{FamilyIPv4, FamilyIPv6} {
if family == FamilyIPv6 {
if len(byFamily[family]) > 0 {
if err := ensureForwardingSysctls(n.system, true); err != nil {
failures = append(failures, err)
continue
}
} else {
_, exists, err := nftables_helper.ReadTable(nftRun, nftTableFamily(family), nftForwardTable)
if err != nil {
failures = append(failures, err)
continue
}
if !exists {
continue
}
}
}
if err := ensureNftForwardTables(family); err != nil {
return fmt.Errorf("initialize nftables forwarding table: %w", err)
}
commands, err := rebuildNftForwardCommands(byFamily[family], family)
if err != nil {
return err
}
if err := nftRunCommands(context.Background(), commands); err != nil {
return err
}
}
return nftRunCommands(context.Background(), commands)
return errors.Join(failures...)
}
func (n *Nftables) CreateRules(ctx context.Context, rules []Rule) error {
@@ -66,6 +97,17 @@ func (n *Nftables) CreateRules(ctx context.Context, rules []Rule) error {
if err != nil {
return err
}
for _, rule := range rules {
if strings.EqualFold(strings.TrimSpace(rule.Family), FamilyIPv6) {
if err := ensureForwardingSysctls(n.system, true); err != nil {
return err
}
if err := ensureNftForwardTables(FamilyIPv6); err != nil {
return err
}
break
}
}
return nftRunCommands(ctx, commands)
}
@@ -113,10 +155,10 @@ func (n *Nftables) DeleteRules(ctx context.Context, rules []Rule) error {
}
func (n *Nftables) Enable() error {
if err := ensureForwardingSysctls(n.system, true); err != nil {
if err := ensureForwardingSysctls(n.system, false); err != nil {
return err
}
if err := ensureNftForwardTables(); err != nil {
if err := ensureNftForwardTables(FamilyIPv4); err != nil {
return fmt.Errorf("initialize nftables forwarding table: %w", err)
}
return nil
@@ -144,13 +186,16 @@ func (n *Nftables) Cleanup() error {
}
func (n *Nftables) InitStatus() (bool, bool, error) {
var anyInitialized, anyBound bool
for _, family := range []string{FamilyIPv4, FamilyIPv6} {
initialized, bound, err := n.FamilyStatus(family)
if err != nil || !initialized || !bound {
return initialized, bound, err
if err != nil {
return false, false, err
}
anyInitialized = anyInitialized || initialized
anyBound = anyBound || bound
}
return true, true, nil
return anyInitialized, anyBound, nil
}
func (n *Nftables) FamilyStatus(family string) (bool, bool, error) {
@@ -159,6 +204,9 @@ func (n *Nftables) FamilyStatus(family string) (bool, bool, error) {
sysctlPath = "/proc/sys/net/ipv6/conf/all/forwarding"
}
data, err := n.system.ReadFile(sysctlPath)
if family == FamilyIPv6 && errors.Is(err, os.ErrNotExist) {
return false, false, nil
}
if err != nil {
return false, false, fmt.Errorf("read %s forwarding status: %w", family, err)
}
@@ -197,9 +245,9 @@ func (n *Nftables) Replay() error {
return nftRunCommand("-f", file)
}
func ensureNftForwardTables() error {
func ensureNftForwardTables(families ...string) error {
commands := make([][]string, 0, 8)
for _, family := range []string{FamilyIPv4, FamilyIPv6} {
for _, family := range families {
tableFamily := nftTableFamily(family)
output, tableExists, err := nftables_helper.ReadTable(nftRun, tableFamily, nftForwardTable)
if err != nil {
@@ -232,12 +280,10 @@ func ensureNftForwardTables() error {
return nftRunCommands(context.Background(), commands)
}
func rebuildNftForwardCommands(rules []Rule) ([][]string, error) {
func rebuildNftForwardCommands(rules []Rule, family string) ([][]string, error) {
commands := make([][]string, 0, 6+len(rules)*4)
for _, family := range []string{FamilyIPv4, FamilyIPv6} {
for _, chain := range []string{ChainPreRouting, ChainPostRouting, ChainForward} {
commands = append(commands, []string{"flush", "chain", nftTableFamily(family), nftForwardTable, "NFT_" + chain})
}
for _, chain := range []string{ChainPreRouting, ChainPostRouting, ChainForward} {
commands = append(commands, []string{"flush", "chain", nftTableFamily(family), nftForwardTable, "NFT_" + chain})
}
additions, err := createNftForwardCommands(rules)
return append(commands, additions...), err
+96 -3
View File
@@ -4,12 +4,24 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/agent/constant"
)
func ensureForwardingSysctls(system forwardingSystem, withIPv6 bool) error {
if withIPv6 {
interfaces, err := IPv6RAInterfaces(system.ReadFile)
if err != nil {
return fmt.Errorf("check IPv6 Router Advertisement: %w", err)
}
if len(interfaces) > 0 {
return fmt.Errorf("IPv6 forwarding blocked: interfaces %s may depend on RA/SLAAC with accept_ra=1; persist accept_ra=2 on interfaces that require RA before retrying", strings.Join(interfaces, ", "))
}
}
paths := []string{"/proc/sys/net/ipv4/ip_forward"}
if withIPv6 {
paths = append(paths, "/proc/sys/net/ipv6/conf/all/forwarding")
@@ -27,12 +39,93 @@ func ensureForwardingSysctls(system forwardingSystem, withIPv6 bool) error {
if err := system.WriteFile("/etc/sysctl.conf", []byte(content), constant.FilePerm); err != nil {
return fmt.Errorf("failed to persist IP forwarding: %w", err)
}
if err := system.RunWithOptionalSudo("sysctl", "-p"); err != nil {
return fmt.Errorf("failed to apply IP forwarding: %w", err)
}
return nil
}
func IPv6RAInterfaces(readFile func(string) ([]byte, error)) ([]string, error) {
addresses, err := readFile("/proc/net/if_inet6")
if err != nil {
return nil, err
}
const permanentAddress = 0x80
globalAddresses := make(map[string]bool)
raInterfaces := make(map[string]bool)
for _, line := range strings.Split(string(addresses), "\n") {
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
if len(fields) != 6 {
return nil, fmt.Errorf("invalid IPv6 address entry: %q", line)
}
name := fields[5]
if name == "lo" {
continue
}
flags, err := strconv.ParseUint(fields[4], 16, 32)
if err != nil {
return nil, err
}
if _, exists := globalAddresses[name]; !exists {
globalAddresses[name] = false
}
if fields[3] == "00" {
globalAddresses[name] = true
if flags&permanentAddress == 0 {
raInterfaces[name] = true
}
}
}
routes, err := readFile("/proc/net/ipv6_route")
if err != nil {
return nil, err
}
const raRouteFlags = 0x00010000 | 0x00800000
for _, line := range strings.Split(string(routes), "\n") {
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
if len(fields) != 10 {
return nil, fmt.Errorf("invalid IPv6 route entry: %q", line)
}
flags, err := strconv.ParseUint(fields[8], 16, 32)
if err != nil {
return nil, err
}
if flags&raRouteFlags != 0 {
raInterfaces[fields[9]] = true
}
}
for name, hasGlobalAddress := range globalAddresses {
if !hasGlobalAddress {
raInterfaces[name] = true
}
}
var interfaces []string
for name := range raInterfaces {
if name == "lo" {
continue
}
if name == "." || name == ".." || filepath.Base(name) != name {
return nil, fmt.Errorf("invalid IPv6 interface %q", name)
}
value, err := readFile("/proc/sys/net/ipv6/conf/" + name + "/accept_ra")
if err != nil {
return nil, err
}
switch strings.TrimSpace(string(value)) {
case "1":
interfaces = append(interfaces, name)
case "0", "2":
default:
return nil, fmt.Errorf("invalid accept_ra value for %s", name)
}
}
sort.Strings(interfaces)
return interfaces, nil
}
func enableForwardingSysctls(content string, withIPv6 bool) string {
lines := strings.Split(strings.TrimRight(content, "\n"), "\n")
wanted := map[string]string{"net.ipv4.ip_forward": "net.ipv4.ip_forward = 1"}
+1
View File
@@ -23,6 +23,7 @@ export namespace Firewall {
bound: boolean;
reason?: string;
forwardPolicy?: 'ACCEPT' | 'DROP';
raInterfaces?: string[];
}
export interface BackendGroup {
selected: string;
+6
View File
@@ -4294,6 +4294,12 @@ const message = {
dockerInputNotProtected:
'Host INPUT rules do not directly protect this Docker published port. Click to open Container Port Guard.',
notInitialized: 'Not initialized',
ipv6RARisk:
'IPv6 forwarding is blocked: interfaces {0} have accept_ra=1 and may rely on RA/SLAAC. Review the network configuration, persist accept_ra=2 on interfaces that need RA, then retry.',
ipv6RACheckFailed:
'Cannot check IPv6 RA configuration. Enabling IPv6 forwarding is blocked. Check the system network configuration and retry.',
ipv6ForwardingOnDemand:
'IPv6 forwarding is not enabled. It will be checked and enabled when IPv6 rules are added or restored.',
familyUnsupported: 'The system does not support {0}',
familyChainIssue: '{0} · {1} chain · {2}',
dockerGuardUnbindConfirm:
+6
View File
@@ -4341,6 +4341,12 @@ const message = {
dockerInputNotProtected:
'Las reglas INPUT del host no protegen directamente este puerto publicado por Docker. Haz clic para abrir la protección de puertos de contenedores.',
notInitialized: 'No inicializado',
ipv6RARisk:
'El reenvío IPv6 está bloqueado: las interfaces {0} tienen accept_ra=1 y pueden depender de RA/SLAAC. Revise la configuración de red, configure accept_ra=2 de forma persistente en las interfaces que necesitan RA y vuelva a intentarlo.',
ipv6RACheckFailed:
'No se puede comprobar la configuración RA de IPv6. Se ha bloqueado la activación del reenvío IPv6. Revise la configuración de red y vuelva a intentarlo.',
ipv6ForwardingOnDemand:
'El reenvío IPv6 no está habilitado. Se comprobará y habilitará al añadir o restaurar reglas IPv6.',
familyUnsupported: 'El sistema no admite {0}',
familyChainIssue: '{0} · cadena {1} · {2}',
dockerGuardUnbindConfirm:
+5
View File
@@ -4251,6 +4251,11 @@ const message = {
dockerInputNotProtected:
'قوانین INPUT میزبان مستقیماً از این پورت منتشرشده Docker محافظت نمی‌کنند. برای باز کردن محافظت از پورت کانتینر کلیک کنید.',
notInitialized: 'راه‌اندازی نشده',
ipv6RARisk:
'هدایت IPv6 مسدود شد: رابط‌های {0} دارای accept_ra=1 هستند و ممکن است به RA/SLAAC وابسته باشند. تنظیمات شبکه را بررسی کنید، برای رابط‌های نیازمند RA مقدار accept_ra=2 را به‌صورت پایدار تنظیم کنید و دوباره تلاش کنید.',
ipv6RACheckFailed:
'بررسی تنظیمات RA در IPv6 ممکن نیست. فعال‌سازی هدایت IPv6 مسدود شده است. تنظیمات شبکه سیستم را بررسی کرده و دوباره تلاش کنید.',
ipv6ForwardingOnDemand: 'هدایت IPv6 فعال نیست. هنگام افزودن یا بازیابی قوانین IPv6 بررسی و فعال خواهد شد.',
familyUnsupported: 'سیستم از {0} پشتیبانی نمی‌کند',
familyChainIssue: '{0} · زنجیره {1} · {2}',
dockerGuardUnbindConfirm:
+5
View File
@@ -4276,6 +4276,11 @@ const message = {
dockerInputNotProtected:
'ホストの INPUT ルールではこの Docker 公開ポートを直接保護できませんクリックしてコンテナポート保護を開きます',
notInitialized: '未初期化',
ipv6RARisk:
'IPv6 転送がブロックされていますインターフェース {0} accept_ra=1 RA/SLAAC に依存している可能性がありますネットワーク設定を確認しRA が必要なインターフェースで accept_ra=2 を永続的に設定してから再試行してください',
ipv6RACheckFailed:
'IPv6 RA 設定を確認できないためIPv6 転送の有効化をブロックしましたシステムのネットワーク設定を確認して再試行してください',
ipv6ForwardingOnDemand: 'IPv6 転送は無効ですIPv6 ルールの追加または復元時に確認して有効化します',
familyUnsupported: 'システムは {0} をサポートしていません',
familyChainIssue: '{0} · {1} チェーン · {2}',
dockerGuardUnbindConfirm:
+6
View File
@@ -4199,6 +4199,12 @@ const message = {
dockerInputNotProtected:
'호스트 INPUT 규칙은 Docker 게시 포트를 직접 보호하지 않습니다. 클릭하여 컨테이너 포트 보호를 여세요.',
notInitialized: '초기화되지 않음',
ipv6RARisk:
'IPv6 전달이 차단되었습니다. 인터페이스 {0} accept_ra=1 설정으로 인해 RA/SLAAC에 의존할 있습니다. 네트워크 설정을 확인하고 RA가 필요한 인터페이스에 accept_ra=2 영구 설정한 다시 시도하세요.',
ipv6RACheckFailed:
'IPv6 RA 설정을 확인할 없어 IPv6 전달 활성화를 차단했습니다. 시스템 네트워크 설정을 확인한 다시 시도하세요.',
ipv6ForwardingOnDemand:
'IPv6 전달이 활성화되지 않았습니다. IPv6 규칙을 추가하거나 복원할 확인 활성화됩니다.',
familyUnsupported: '시스템에서 {0} 지원하지 않습니다',
familyChainIssue: '{0} · {1} 체인 · {2}',
dockerGuardUnbindConfirm:
+5
View File
@@ -4163,6 +4163,11 @@ const message = {
dockerInputNotProtected:
'ກົດ INPUT ຂອງໂຮສບໍ່ໄດ້ປ້ອງກັນພອດ Docker ນີ້ໂດຍກົງ. ຄລິກເພື່ອເປີດການປ້ອງກັນພອດຄອນເທນເນີ.',
notInitialized: 'ຍັງບໍ່ໄດ້ເລີ່ມຕົ້ນ',
ipv6RARisk:
'ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກ: ອິນເຕີເຟດ {0} ມີ accept_ra=1 ແລະອາດອາໄສ RA/SLAAC. ກວດສອບການຕັ້ງຄ່າເຄືອຂ່າຍ, ຕັ້ງ accept_ra=2 ແບບຖາວອນໃຫ້ອິນເຕີເຟດທີ່ຕ້ອງການ RA ແລ້ວລອງໃໝ່.',
ipv6RACheckFailed:
'ບໍ່ສາມາດກວດສອບການຕັ້ງຄ່າ IPv6 RA ໄດ້. ການເປີດໃຊ້ການສົ່ງຕໍ່ IPv6 ຖືກບລັອກ. ກວດສອບການຕັ້ງຄ່າເຄືອຂ່າຍແລ້ວລອງໃໝ່.',
ipv6ForwardingOnDemand: 'ຍັງບໍ່ໄດ້ເປີດໃຊ້ການສົ່ງຕໍ່ IPv6. ລະບົບຈະກວດສອບແລະເປີດໃຊ້ເມື່ອເພີ່ມຫຼືກູ້ຄືນກົດ IPv6.',
familyUnsupported: 'ລະບົບບໍ່ຮອງຮັບ {0}',
familyChainIssue: '{0} · chain {1} · {2}',
dockerGuardUnbindConfirm:
+6
View File
@@ -4363,6 +4363,12 @@ const message = {
dockerInputNotProtected:
'Peraturan INPUT hos tidak melindungi port terbitan Docker ini secara langsung. Klik untuk membuka perlindungan port bekas.',
notInitialized: 'Belum dimulakan',
ipv6RARisk:
'Pemajuan IPv6 disekat: antara muka {0} mempunyai accept_ra=1 dan mungkin bergantung pada RA/SLAAC. Semak konfigurasi rangkaian, tetapkan accept_ra=2 secara kekal pada antara muka yang memerlukan RA, kemudian cuba lagi.',
ipv6RACheckFailed:
'Konfigurasi RA IPv6 tidak dapat diperiksa. Pengaktifan pemajuan IPv6 disekat. Semak konfigurasi rangkaian sistem dan cuba lagi.',
ipv6ForwardingOnDemand:
'Pemajuan IPv6 belum diaktifkan. Ia akan diperiksa dan diaktifkan apabila peraturan IPv6 ditambah atau dipulihkan.',
familyUnsupported: 'Sistem tidak menyokong {0}',
familyChainIssue: '{0} · rantaian {1} · {2}',
dockerGuardUnbindConfirm:
+6
View File
@@ -4380,6 +4380,12 @@ const message = {
dockerInputNotProtected:
'As regras INPUT do host não protegem diretamente esta porta publicada pelo Docker. Clique para abrir a proteção de portas de contêineres.',
notInitialized: 'Não inicializado',
ipv6RARisk:
'O encaminhamento IPv6 foi bloqueado: as interfaces {0} têm accept_ra=1 e podem depender de RA/SLAAC. Verifique a configuração de rede, configure accept_ra=2 de forma persistente nas interfaces que precisam de RA e tente novamente.',
ipv6RACheckFailed:
'Não foi possível verificar a configuração RA do IPv6. A ativação do encaminhamento IPv6 foi bloqueada. Verifique a configuração de rede e tente novamente.',
ipv6ForwardingOnDemand:
'O encaminhamento IPv6 não está ativado. Ele será verificado e ativado ao adicionar ou restaurar regras IPv6.',
familyUnsupported: 'O sistema não oferece suporte a {0}',
familyChainIssue: '{0} · cadeia {1} · {2}',
dockerGuardUnbindConfirm:
+6
View File
@@ -4348,6 +4348,12 @@ const message = {
dockerInputNotProtected:
'Правила INPUT хоста не защищают этот опубликованный Docker-порт напрямую. Нажмите, чтобы открыть защиту портов контейнеров.',
notInitialized: 'Не инициализировано',
ipv6RARisk:
'Пересылка IPv6 заблокирована: интерфейсы {0} имеют accept_ra=1 и могут зависеть от RA/SLAAC. Проверьте настройки сети, сохраните accept_ra=2 для интерфейсов, которым нужен RA, и повторите попытку.',
ipv6RACheckFailed:
'Не удалось проверить настройки RA для IPv6. Включение пересылки IPv6 заблокировано. Проверьте настройки сети и повторите попытку.',
ipv6ForwardingOnDemand:
'Пересылка IPv6 не включена. Проверка и включение выполняются при добавлении или восстановлении правил IPv6.',
familyUnsupported: 'Система не поддерживает {0}',
familyChainIssue: '{0} · цепочка {1} · {2}',
dockerGuardUnbindConfirm:
+6
View File
@@ -4366,6 +4366,12 @@ const message = {
dockerInputNotProtected:
'Ana makine INPUT kuralları bu Docker yayımlanmış portunu doğrudan korumaz. Konteyner portu korumasını açmak için tıklayın.',
notInitialized: 'Başlatılmadı',
ipv6RARisk:
'IPv6 yönlendirme engellendi: {0} arayüzlerinde accept_ra=1 ayarlı ve bu arayüzler RA/SLAAC kullanıyor olabilir. yapılandırmasını kontrol edin, RA gereken arayüzlerde accept_ra=2 ayarını kalıcı olarak uygulayın ve yeniden deneyin.',
ipv6RACheckFailed:
'IPv6 RA yapılandırması kontrol edilemedi. IPv6 yönlendirmeyi etkinleştirme engellendi. Sistem yapılandırmasını kontrol edip yeniden deneyin.',
ipv6ForwardingOnDemand:
'IPv6 yönlendirme etkin değil. IPv6 kuralları eklenirken veya geri yüklenirken kontrol edilip etkinleştirilecektir.',
familyUnsupported: 'Sistem {0} desteğini sunmuyor',
familyChainIssue: '{0} · {1} zinciri · {2}',
dockerGuardUnbindConfirm:
+4
View File
@@ -4004,6 +4004,10 @@ const message = {
dockerInputUseHostFirewall: '此連接埠需要透過主機防火牆設定存取規則請前往主機防火牆進行設定',
dockerInputNotProtected: '主機 INPUT 規則無法直接保護此 Docker 發佈連接埠點擊前往容器連接埠防護',
notInitialized: '未初始化',
ipv6RARisk:
'IPv6 轉送已阻止網路介面 {0} accept_ra=1可能依賴 RA/SLAAC請確認網路設定為需要接收 RA 的介面持久化設定 accept_ra=2 後重試',
ipv6RACheckFailed: '無法檢查 IPv6 RA 設定已阻止啟用 IPv6 轉送請檢查系統網路設定後重試',
ipv6ForwardingOnDemand: 'IPv6 轉送尚未啟用將在新增或還原 IPv6 規則時檢查並啟用',
familyUnsupported: '系統不支援 {0}',
familyChainIssue: '{0} · {1} · {2}',
dockerGuardUnbindConfirm:
+4
View File
@@ -4052,6 +4052,10 @@ const message = {
dockerInputUseHostFirewall: '该端口需要通过主机防火墙设置访问规则请前往主机防火墙进行配置',
dockerInputNotProtected: '宿主机 INPUT 规则不能直接保护此 Docker 发布端口点击前往容器端口防护',
notInitialized: '未初始化',
ipv6RARisk:
'IPv6 转发已阻止网卡 {0} accept_ra=1可能依赖 RA/SLAAC请确认网络配置为需要接收 RA 的网卡持久化设置 accept_ra=2 后重试',
ipv6RACheckFailed: '无法检查 IPv6 RA 配置已阻止启用 IPv6 转发请检查系统网络配置后重试',
ipv6ForwardingOnDemand: 'IPv6 转发尚未启用将在添加或恢复 IPv6 规则时检查并启用',
familyUnsupported: '系统不支持 {0}',
familyChainIssue: '{0} · {1} · {2}',
dockerGuardUnbindConfirm: '解绑后所有容器端口将暂时恢复 Docker 默认访问方式现有防护设置会保留是否继续',
@@ -285,15 +285,17 @@ const allAvailableFamiliesInitialized = computed(
() => availableFamilies.value.length > 0 && availableFamilies.value.every((item) => item.status.initialized),
);
const anyFamilyBound = computed(() => availableFamilies.value.some((item) => item.status.bound));
interface FamilyIssue {
interface FamilyIssue extends Firewall.BackendFamilyStatus {
family: 'IPv4' | 'IPv6';
available: boolean;
initialized: boolean;
bound: boolean;
}
const managedChainName = computed(() => (props.currentTab === 'forward' ? '1PANEL_FORWARD' : '1PANEL_BASIC'));
const familyIssues = computed<FamilyIssue[]>(() => {
if (!isDirectManaged.value || !anyFamilyBound.value) return [];
if (!isDirectManaged.value) return [];
if (isDirectForward.value) {
if (familyStatuses.value.every((item) => item.status.bound && !item.status.reason)) return [];
return familyStatuses.value.map((item) => ({ family: item.family, ...item.status }));
}
if (!anyFamilyBound.value) return [];
return familyStatuses.value
.filter((item) => !item.status.available || !item.status.initialized || !item.status.bound)
.map((item) => ({
@@ -303,9 +305,21 @@ const familyIssues = computed<FamilyIssue[]>(() => {
bound: item.status.bound,
}));
});
const retryableFamilyIssues = computed(() => familyIssues.value.filter((item) => item.available));
const retryableFamilyIssues = computed(() =>
familyIssues.value.filter(
(item) =>
item.available &&
!item.bound &&
(!item.reason || (isDirectForward.value && item.reason === 'ipv6_forwarding_not_enabled')),
),
);
const familyIssueText = (issue: FamilyIssue) => {
if (!issue.available) return i18n.global.t('firewall.familyUnsupported', [issue.family]);
if (issue.reason === 'ipv6_ra_required') {
return i18n.global.t('firewall.ipv6RARisk', [issue.raInterfaces?.join(', ') || '-']);
}
if (issue.reason === 'ipv6_ra_check_failed') return i18n.global.t('firewall.ipv6RACheckFailed');
if (issue.reason === 'ipv6_forwarding_not_enabled') return i18n.global.t('firewall.ipv6ForwardingOnDemand');
const status = i18n.global.t(
!issue.initialized ? 'firewall.notInitialized' : issue.bound ? 'commons.status.bound' : 'commons.status.unbind',
);