mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 08:00:53 +00:00
fix(firewall): guard IPv6 forwarding against RA disruption (#13882)
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -23,6 +23,7 @@ export namespace Firewall {
|
||||
bound: boolean;
|
||||
reason?: string;
|
||||
forwardPolicy?: 'ACCEPT' | 'DROP';
|
||||
raInterfaces?: string[];
|
||||
}
|
||||
export interface BackendGroup {
|
||||
selected: string;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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. Ağ 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 ağ 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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user