From 20b0eee9fa661ab8a5b7aba113f49d5297e210ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 7 Jul 2025 17:07:28 +0800 Subject: [PATCH 1/2] Add nftables rules for firewalld --- go.mod | 14 +- go.sum | 28 +-- redirect_linux.go | 1 + redirect_nftables.go | 11 +- redirect_nftables_rules_firewalld.go | 253 +++++++++++++++++++++++++++ 5 files changed, 286 insertions(+), 21 deletions(-) create mode 100644 redirect_nftables_rules_firewalld.go diff --git a/go.mod b/go.mod index 0bb96e75..2d662af4 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,27 @@ module github.com/sagernet/sing-tun -go 1.20 +go 1.21 require ( github.com/go-ole/go-ole v1.3.0 + github.com/godbus/dbus/v5 v5.1.0 github.com/google/btree v1.1.3 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/nftables v0.3.0-beta.4 + github.com/sagernet/nftables v0.3.0-mod.1 github.com/sagernet/sing v0.6.0-beta.2 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 - golang.org/x/net v0.26.0 - golang.org/x/sys v0.26.0 + golang.org/x/net v0.33.0 + golang.org/x/sys v0.28.0 ) require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/josharian/native v1.1.0 // indirect - github.com/mdlayher/netlink v1.7.2 // indirect - github.com/mdlayher/socket v0.4.1 // indirect + github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect + github.com/mdlayher/socket v0.5.0 // indirect github.com/vishvananda/netns v0.0.4 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/time v0.7.0 // indirect diff --git a/go.sum b/go.sum index e2ea32a3..e51c30d5 100644 --- a/go.sum +++ b/go.sum @@ -1,43 +1,47 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg= +github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o= +github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= +github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs= github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= -github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= +github.com/sagernet/nftables v0.3.0-mod.1 h1:OMe+qoEAx8EipYAQbD2FI5erVvKmTS9+cYhdpg+vezY= +github.com/sagernet/nftables v0.3.0-mod.1/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ= github.com/sagernet/sing v0.6.0-beta.2 h1:Dcutp3kxrsZes9q3oTiHQhYYjQvDn5rwp1OI9fDLYwQ= github.com/sagernet/sing v0.6.0-beta.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/redirect_linux.go b/redirect_linux.go index 5441bc10..2201fcb6 100644 --- a/redirect_linux.go +++ b/redirect_linux.go @@ -41,6 +41,7 @@ type autoRedirect struct { suPath string routeAddressSet *[]*netipx.IPSet routeExcludeAddressSet *[]*netipx.IPSet + firewalldListener *firewalldListener } func NewAutoRedirect(options AutoRedirectOptions) (AutoRedirect, error) { diff --git a/redirect_nftables.go b/redirect_nftables.go index b2d54f93..096248f0 100644 --- a/redirect_nftables.go +++ b/redirect_nftables.go @@ -10,6 +10,7 @@ import ( "github.com/sagernet/nftables/expr" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/control" + E "github.com/sagernet/sing/common/exceptions" "golang.org/x/exp/slices" "golang.org/x/sys/unix" @@ -235,7 +236,12 @@ func (r *autoRedirect) setupNFTables() error { err = r.configureOpenWRTFirewall4(nft, false) if err != nil { - return err + return E.Cause(err, "configure OpenWRT firewall") + } + + err = r.configureFirewalld(nft, false) + if err != nil { + return E.Cause(err, "configure firewalld") } err = nft.Flush() @@ -308,7 +314,8 @@ func (r *autoRedirect) cleanupNFTables() { Name: r.tableName, Family: nftables.TableFamilyINet, }) - common.Must(r.configureOpenWRTFirewall4(nft, true)) + _ = r.configureOpenWRTFirewall4(nft, true) + _ = r.configureFirewalld(nft, true) _ = nft.Flush() _ = nft.CloseLasting() } diff --git a/redirect_nftables_rules_firewalld.go b/redirect_nftables_rules_firewalld.go new file mode 100644 index 00000000..294a95d1 --- /dev/null +++ b/redirect_nftables_rules_firewalld.go @@ -0,0 +1,253 @@ +//go:build linux + +package tun + +import ( + "time" + + "github.com/sagernet/nftables" + "github.com/sagernet/nftables/expr" + E "github.com/sagernet/sing/common/exceptions" + + "github.com/godbus/dbus/v5" + "golang.org/x/exp/slices" +) + +const ( + NFT_TABLE_F_DORMANT = 0x1 + NFT_TABLE_F_OWNER = 0x2 + NFT_TABLE_F_PERSIST = 0x4 +) + +const ( + firewalldInterface = "org.fedoraproject.FirewallD1" + firewalldPath = "/org/fedoraproject/FirewallD1" +) + +func (r *autoRedirect) configureFirewalld(nft *nftables.Conn, cleanup bool) error { + if cleanup { + if r.firewalldListener == nil { + return nil + } + r.firewalldListener.Close() + return r.configureFirewalldRules(nft, false) + } + conn, err := dbus.SystemBus() + if err != nil { + return nil + } + err = conn.Object(firewalldInterface, firewalldPath).Call("org.freedesktop.DBus.Peer.Ping", 0).Err + if err != nil { + return nil + } + err = r.configureFirewalldRules(nft, false) + if err != nil { + r.logger.Warn(E.Cause(err, "configure firewalld rules")) + return nil + } + err = conn.BusObject().AddMatchSignal(firewalldInterface, "Reloaded").Err + if err != nil { + return E.Cause(err, "configure firewalld reload listener") + } + err = conn.BusObject().AddMatchSignal( + "org.freedesktop.DBus", + "NameOwnerChanged", + dbus.WithMatchSender("org.freedesktop.DBus"), + dbus.WithMatchArg(0, firewalldInterface), + ).Err + if err != nil { + return E.Cause(err, "configure firewalld restart listener") + } + signal := make(chan *dbus.Signal, 1) + conn.Signal(signal) + listener := &firewalldListener{ + autoRedirect: r, + conn: conn, + signal: signal, + done: make(chan struct{}), + } + go listener.loopReload() + r.firewalldListener = listener + return nil +} + +type firewalldListener struct { + *autoRedirect + conn *dbus.Conn + signal chan *dbus.Signal + done chan struct{} +} + +func (l *firewalldListener) loopReload() { + for { + select { + case <-l.done: + return + case signal := <-l.signal: + var restarted bool + if signal.Name == "org.freedesktop.DBus.NameOwnerChanged" { + if len(signal.Body) != 3 || signal.Body[2].(string) == "" { + continue + } else { + restarted = true + } + } + err := l.configureFirewalldRulesOnce(restarted) + if err != nil { + l.logger.Error(E.Cause(err, "reconfigure firewalld rules")) + } + } + } +} + +func (l *firewalldListener) configureFirewalldRulesOnce(restarted bool) error { + nft, err := nftables.New() + if err != nil { + return err + } + defer nft.CloseLasting() + if restarted { + for i := 0; i < 10; i++ { + _, err = nft.ListTableOfFamily("firewalld", nftables.TableFamilyINet) + if err == nil { + break + } + time.Sleep(10 * time.Millisecond) + } + } + err = l.configureFirewalldRules(nft, false) + if err != nil { + return err + } + return nft.Flush() +} + +func (l *firewalldListener) Close() { + select { + case <-l.done: + return + default: + close(l.done) + l.conn.Close() + } +} + +func (r *autoRedirect) configureFirewalldRules(nft *nftables.Conn, cleanup bool) error { + tableFirewalld, err := nft.ListTableOfFamily("firewalld", nftables.TableFamilyINet) + if err != nil { + return err + } + if tableFirewalld.Flags&NFT_TABLE_F_OWNER != 0 { + var conn *dbus.Conn + conn, err = dbus.SystemBus() + if err != nil { + return E.Cause(err, "connect to system bus") + } + err = conn.Object(firewalldInterface, firewalldPath+"/config").SetProperty(firewalldInterface+".config.NftablesTableOwner", dbus.MakeVariant("no")) + if err != nil { + return E.Cause(err, "take owner of firewalld table") + } + err = conn.Object(firewalldInterface, firewalldPath).Call( + firewalldInterface+".reload", 0).Err + if err != nil { + return E.Cause(err, "reload firewalld") + } + tableFirewalld, err = nft.ListTableOfFamily("firewalld", nftables.TableFamilyINet) + if err != nil { + return E.Cause(err, "check reloaded firewalld table") + } + if tableFirewalld.Flags&NFT_TABLE_F_OWNER != 0 { + return E.New("unable to take owner of firewalld table") + } + } + for _, chainName := range []string{"filter_INPUT", "filter_FORWARD"} { + var rules []*nftables.Rule + rules, err = nft.GetRules(tableFirewalld, &nftables.Chain{ + Name: chainName, + }) + if err != nil { + return err + } + for _, rule := range rules { + if len(rule.Exprs) != 4 { + continue + } + exprMeta, isMeta := rule.Exprs[0].(*expr.Meta) + if !isMeta { + continue + } + if exprMeta.Key != expr.MetaKeyIIFNAME && exprMeta.Key != expr.MetaKeyOIFNAME { + continue + } + exprCmp, isCmp := rule.Exprs[1].(*expr.Cmp) + if !isCmp { + continue + } + if !slices.Equal(exprCmp.Data, nftablesIfname(r.tunOptions.Name)) { + continue + } + if cleanup { + err = nft.DelRule(rule) + if err != nil { + return err + } + } else { + return nil + } + } + } + if !cleanup { + ruleIif := &nftables.Rule{ + Table: tableFirewalld, + Exprs: []expr.Any{ + &expr.Meta{ + Key: expr.MetaKeyIIFNAME, + Register: 1, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: nftablesIfname(r.tunOptions.Name), + }, + &expr.Counter{}, + &expr.Verdict{ + Kind: expr.VerdictAccept, + }, + }, + } + ruleOif := &nftables.Rule{ + Table: tableFirewalld, + Exprs: []expr.Any{ + &expr.Meta{ + Key: expr.MetaKeyOIFNAME, + Register: 1, + }, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: nftablesIfname(r.tunOptions.Name), + }, + &expr.Counter{}, + &expr.Verdict{ + Kind: expr.VerdictAccept, + }, + }, + } + chainForward := &nftables.Chain{ + Name: "filter_FORWARD", + } + ruleIif.Chain = chainForward + ruleOif.Chain = chainForward + nft.InsertRule(ruleOif) + nft.InsertRule(ruleIif) + chainInput := &nftables.Chain{ + Name: "filter_INPUT", + } + ruleIif.Chain = chainInput + ruleOif.Chain = chainInput + nft.InsertRule(ruleOif) + nft.InsertRule(ruleIif) + return nil + } + return nil +} From b2e2674d73e5da542d51dcc08f1c680081cd494d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 7 Jul 2025 17:47:32 +0800 Subject: [PATCH 2/2] Update dependencies --- go.mod | 8 ++++---- go.sum | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 2d662af4..f50929bf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/sagernet/sing-tun -go 1.21 +go 1.23.0 require ( github.com/go-ole/go-ole v1.3.0 @@ -10,11 +10,11 @@ require ( github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/nftables v0.3.0-mod.1 - github.com/sagernet/sing v0.6.0-beta.2 + github.com/sagernet/sing v0.6.11 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 - golang.org/x/net v0.33.0 - golang.org/x/sys v0.28.0 + golang.org/x/net v0.41.0 + golang.org/x/sys v0.33.0 ) require ( diff --git a/go.sum b/go.sum index e51c30d5..075c157a 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-mod.1 h1:OMe+qoEAx8EipYAQbD2FI5erVvKmTS9+cYhdpg+vezY= github.com/sagernet/nftables v0.3.0-mod.1/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ= -github.com/sagernet/sing v0.6.0-beta.2 h1:Dcutp3kxrsZes9q3oTiHQhYYjQvDn5rwp1OI9fDLYwQ= -github.com/sagernet/sing v0.6.0-beta.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.6.11 h1:BXYwLYw2srH1Kdp1vlZgrK+1lAan5i2UdZ4lPWqEgUU= +github.com/sagernet/sing v0.6.11/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= @@ -34,13 +34,13 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=