Skip to content

Commit

Permalink
refactor(transparent-proxy): allow config to be initialized (#10207)
Browse files Browse the repository at this point in the history
- Introduced `InitializedConfig` structure

  `InitializedConfig` extends the `Config` struct by adding fields that require
  additional logic to retrieve their values. These values typically involve
  interacting with the system or external resources.

  An `InitializedConfig` can contain nested configuration structs. If these
  nested structs share field names with the `Config` struct, they should embed
  the corresponding field from `Config` during initialization. I.e.

  ```go
  type Redirect struct {
    // NamePrefix is a prefix which will be used go generate chains name
    NamePrefix string
    Inbound    TrafficFlow
    Outbound   TrafficFlow
    DNS        DNS
    VNet       VNet
  }

  type InitializedRedirect struct {
    Redirect
    DNS InitializedDNS
  }

  func (c Redirect) Initialize() (InitializedRedirect, error) {
    var err error

    initialized := InitializedRedirect{Redirect: c}

    // .DNS
    initialized.DNS, err = c.DNS.Initialize()
    ...

    return initialized, nil
  }
  ```

  I moved obtaining DNS servers from `/etc/resolv.conf` and getting loopback
  network interface name to initialization methods for appropriate sub-configs.

- Combined static variables in `builder_restore.go`

  By combining them all together at top it makes it easier to read.

- Moved iptables/ip6tables consts to consts package

  As we have a package for consts let's use it everywhere

- Got rid of unnecessary `IPTables` struct

  This structure was unnecessary as it was only used in
  `BuildIPTablesForRestore`, which also was using only its one method
  `BuildForRestore`. I moved logic of this method directly inside
  `BuildIPTablesForRestore`, which removes unnecessary complexity.

Signed-off-by: Bart Smykla <bartek@smykla.com>
Signed-off-by: Ilya Lobkov <ilya.lobkov@konghq.com>
  • Loading branch information
bartsmykla authored and lobkovilya committed May 15, 2024
1 parent 2863d8c commit 3b87136
Show file tree
Hide file tree
Showing 25 changed files with 362 additions and 254 deletions.
7 changes: 6 additions & 1 deletion app/cni/pkg/cni/injector_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,19 @@ func Inject(netns string, logger logr.Logger, intermediateConfig *IntermediateCo
return err
}

initializedConfig, err := cfg.Initialize()
if err != nil {
return errors.Wrap(err, "failed to initialize config")
}

namespace, err := ns.GetNS(netns)
if err != nil {
return errors.Wrap(err, "failed to open namespace")
}
defer namespace.Close()

return namespace.Do(func(_ ns.NetNS) error {
if _, err := transparentproxy.Setup(context.Background(), *cfg); err != nil {
if _, err := transparentproxy.Setup(context.Background(), initializedConfig); err != nil {
return err
}

Expand Down
7 changes: 6 additions & 1 deletion app/kumactl/cmd/install/install_transparent_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,12 @@ runuser -u kuma-dp -- \
return errors.Wrap(err, "failed to setup transparent proxy")
}

output, err := transparentproxy.Setup(cmd.Context(), cfg)
initializedConfig, err := cfg.Initialize()
if err != nil {
return errors.Wrap(err, "failed to initialize config")
}

output, err := transparentproxy.Setup(cmd.Context(), initializedConfig)
if err != nil {
return errors.Wrap(err, "failed to setup transparent proxy")
}
Expand Down
7 changes: 6 additions & 1 deletion app/kumactl/cmd/uninstall/uninstall_transparent_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ func newUninstallTransparentProxy() *cobra.Command {
return errors.Errorf("transparent proxy will work only on Linux OSes")
}

output, err := transparentproxy.Cleanup(cfg)
initializedConfig, err := cfg.Initialize()
if err != nil {
return errors.Wrap(err, "failed to initialize config")
}

output, err := transparentproxy.Cleanup(initializedConfig)
if err != nil {
return errors.Wrap(err, "transparent proxy cleanup failed")
}
Expand Down
117 changes: 112 additions & 5 deletions pkg/transparentproxy/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package config
import (
"fmt"
"io"
"net"
"os"
"os/exec"
"time"

"github.com/miekg/dns"
"github.com/pkg/errors"

"github.com/kumahq/kuma/pkg/util/pointer"
)

Expand Down Expand Up @@ -52,6 +56,41 @@ type DNS struct {
ResolvConfigPath string
}

type InitializedDNS struct {
DNS
ServersIPv4 []string
ServersIPv6 []string
}

// Initialize initializes the ServersIPv4 and ServersIPv6 fields by parsing
// the nameservers from the file specified in the ResolvConfigPath field of
// the input DNS struct.
func (c DNS) Initialize() (InitializedDNS, error) {
initialized := InitializedDNS{DNS: c}

// We don't have to get DNS servers if DNS traffic shouldn't be redirected,
// or if we want to capture all DNS traffic
if !c.Enabled || c.CaptureAll {
return initialized, nil
}

dnsConfig, err := dns.ClientConfigFromFile(c.ResolvConfigPath)
if err != nil {
return initialized, errors.Errorf("unable to read file %s: %s", c.ResolvConfigPath, err)
}

for _, address := range dnsConfig.Servers {
parsed := net.ParseIP(address)
if parsed.To4() != nil {
initialized.ServersIPv4 = append(initialized.ServersIPv4, address)
} else {
initialized.ServersIPv6 = append(initialized.ServersIPv6, address)
}
}

return initialized, nil
}

type VNet struct {
Networks []string
}
Expand All @@ -65,6 +104,25 @@ type Redirect struct {
VNet VNet
}

type InitializedRedirect struct {
Redirect
DNS InitializedDNS
}

func (c Redirect) Initialize() (InitializedRedirect, error) {
var err error

initialized := InitializedRedirect{Redirect: c}

// .DNS
initialized.DNS, err = c.DNS.Initialize()
if err != nil {
return initialized, errors.Wrap(err, "unable to initialize .DNS")
}

return initialized, nil
}

type Chain struct {
Name string
}
Expand Down Expand Up @@ -139,39 +197,55 @@ type Config struct {
StoreFirewalld bool
}

// InitializedConfig extends the Config struct by adding fields that require
// additional logic to retrieve their values. These values typically involve
// interacting with the system or external resources.
type InitializedConfig struct {
Config
// Redirect is an InitializedRedirect struct containing the initialized
// redirection configuration. If DNS redirection is enabled this includes
// the DNS servers retrieved from the specified resolv.conf file
// (/etc/resolv.conf by default)
Redirect InitializedRedirect
// LoopbackInterfaceName represents the name of the loopback interface which
// will be used to construct outbound iptable rules for outbound (i.e.
// -A KUMA_MESH_OUTBOUND -s 127.0.0.6/32 -o lo -j RETURN)
LoopbackInterfaceName string
}

// ShouldDropInvalidPackets is just a convenience function which can be used in
// iptables conditional command generations instead of inlining anonymous functions
// i.e. AddRuleIf(ShouldDropInvalidPackets, Match(...), Jump(Drop()))
func (c Config) ShouldDropInvalidPackets() bool {
func (c InitializedConfig) ShouldDropInvalidPackets() bool {
return c.DropInvalidPackets
}

// ShouldRedirectDNS is just a convenience function which can be used in
// iptables conditional command generations instead of inlining anonymous functions
// i.e. AddRuleIf(ShouldRedirectDNS, Match(...), Jump(Drop()))
func (c Config) ShouldRedirectDNS() bool {
func (c InitializedConfig) ShouldRedirectDNS() bool {
return c.Redirect.DNS.Enabled
}

// ShouldFallbackDNSToUpstreamChain is just a convenience function which can be used in
// iptables conditional command generations instead of inlining anonymous functions
// i.e. AddRuleIf(ShouldFallbackDNSToUpstreamChain, Match(...), Jump(Drop()))
func (c Config) ShouldFallbackDNSToUpstreamChain() bool {
func (c InitializedConfig) ShouldFallbackDNSToUpstreamChain() bool {
return c.Redirect.DNS.UpstreamTargetChain != ""
}

// ShouldCaptureAllDNS is just a convenience function which can be used in
// iptables conditional command generations instead of inlining anonymous functions
// i.e. AddRuleIf(ShouldCaptureAllDNS, Match(...), Jump(Drop()))
func (c Config) ShouldCaptureAllDNS() bool {
func (c InitializedConfig) ShouldCaptureAllDNS() bool {
return c.Redirect.DNS.CaptureAll
}

// ShouldConntrackZoneSplit is a function which will check if DNS redirection and
// conntrack zone splitting settings are enabled (return false if not), and then
// will verify if there is conntrack iptables extension available to apply
// the DNS conntrack zone splitting iptables rules
func (c Config) ShouldConntrackZoneSplit(iptablesExecutable string) bool {
func (c InitializedConfig) ShouldConntrackZoneSplit(iptablesExecutable string) bool {
if !c.Redirect.DNS.Enabled || !c.Redirect.DNS.ConntrackZoneSplit {
return false
}
Expand All @@ -196,6 +270,39 @@ func (c Config) ShouldConntrackZoneSplit(iptablesExecutable string) bool {
return true
}

func getLoopbackInterfaceName() (string, error) {
interfaces, err := net.Interfaces()
if err != nil {
return "", errors.Wrap(err, "unable to list network interfaces")
}

for _, iface := range interfaces {
if iface.Flags&net.FlagLoopback != 0 {
return iface.Name, nil
}
}

return "", errors.New("loopback interface not found")
}

func (c Config) Initialize() (InitializedConfig, error) {
var err error

initialized := InitializedConfig{Config: c}

initialized.Redirect, err = c.Redirect.Initialize()
if err != nil {
return initialized, errors.Wrap(err, "unable to initialize Redirect configuration")
}

initialized.LoopbackInterfaceName, err = getLoopbackInterfaceName()
if err != nil {
return initialized, errors.Wrap(err, "unable to initialize LoopbackInterfaceName")
}

return initialized, nil
}

func DefaultConfig() Config {
return Config{
Owner: Owner{UID: "5678"},
Expand Down
8 changes: 4 additions & 4 deletions pkg/transparentproxy/ebpf/cleanup.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func buildOptionSet(rawOptions string) map[string]struct{} {
return options
}

func CleanPathsRelativeToBPFFS(paths ...string) func(cfg config.Config) error {
return func(cfg config.Config) error {
func CleanPathsRelativeToBPFFS(paths ...string) func(cfg config.InitializedConfig) error {
return func(cfg config.InitializedConfig) error {
for _, p := range paths {
if err := os.RemoveAll(path.Join(cfg.Ebpf.BPFFSPath, p)); err != nil {
return fmt.Errorf(
Expand All @@ -38,7 +38,7 @@ func CleanPathsRelativeToBPFFS(paths ...string) func(cfg config.Config) error {
}
}

func UnloadEbpfPrograms(programs []*Program, cfg config.Config) (string, error) {
func UnloadEbpfPrograms(programs []*Program, cfg config.InitializedConfig) (string, error) {
if os.Getuid() != 0 {
return "", fmt.Errorf("root user in required for this process or container")
}
Expand Down Expand Up @@ -78,6 +78,6 @@ func UnloadEbpfPrograms(programs []*Program, cfg config.Config) (string, error)
return "", nil
}

func Cleanup(cfg config.Config) (string, error) {
func Cleanup(cfg config.InitializedConfig) (string, error) {
return UnloadEbpfPrograms(programs, cfg)
}
2 changes: 1 addition & 1 deletion pkg/transparentproxy/ebpf/cleanup_fallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import (
"github.com/kumahq/kuma/pkg/transparentproxy/config"
)

func Cleanup(config.Config) (string, error) {
func Cleanup(config.InitializedConfig) (string, error) {
return "", fmt.Errorf("ebpf is currently supported only on linux")
}
12 changes: 6 additions & 6 deletions pkg/transparentproxy/ebpf/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var programs = []*Program{
{
Name: "mb_connect",
Flags: func(
cfg config.Config,
cfg config.InitializedConfig,
cgroup string,
bpffs string,
) ([]string, error) {
Expand All @@ -76,7 +76,7 @@ var programs = []*Program{
{
Name: "mb_sockops",
Flags: func(
cfg config.Config,
cfg config.InitializedConfig,
cgroup string,
bpffs string,
) ([]string, error) {
Expand Down Expand Up @@ -105,7 +105,7 @@ var programs = []*Program{
{
Name: "mb_sendmsg",
Flags: func(
cfg config.Config,
cfg config.InitializedConfig,
cgroup string,
bpffs string,
) ([]string, error) {
Expand All @@ -124,7 +124,7 @@ var programs = []*Program{
{
Name: "mb_recvmsg",
Flags: func(
cfg config.Config,
cfg config.InitializedConfig,
cgroup string,
bpffs string,
) ([]string, error) {
Expand All @@ -150,7 +150,7 @@ var programs = []*Program{
{
Name: "mb_tc",
Flags: func(
cfg config.Config,
cfg config.InitializedConfig,
cgroup string,
bpffs string,
) ([]string, error) {
Expand Down Expand Up @@ -207,7 +207,7 @@ func ipStrToPtr(ipstr string) (unsafe.Pointer, error) {
return unsafe.Pointer(&ip[0]), nil
}

func LoadAndAttachEbpfPrograms(programs []*Program, cfg config.Config) error {
func LoadAndAttachEbpfPrograms(programs []*Program, cfg config.InitializedConfig) error {
var errs []string

cgroup, err := getCgroupPath(cfg)
Expand Down
14 changes: 7 additions & 7 deletions pkg/transparentproxy/ebpf/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ const (
)

type FlagGenerator = func(
cfg config.Config,
cfg config.InitializedConfig,
cgroup string,
bpffs string,
) ([]string, error)

type Program struct {
Name string
Flags FlagGenerator
Cleanup func(cfg config.Config) error
Cleanup func(cfg config.InitializedConfig) error
}

func (p Program) LoadAndAttach(cfg config.Config, programs embed.FS, cgroup string, bpffs string) error {
func (p Program) LoadAndAttach(cfg config.InitializedConfig, programs embed.FS, cgroup string, bpffs string) error {
programBytes, err := programs.ReadFile(p.Name)
if err != nil {
return fmt.Errorf("reading ebpf program bytes failed: %s", err)
Expand Down Expand Up @@ -122,7 +122,7 @@ func initBPFFSMaybe(fsPath string) error {
return nil
}

func getCgroupPath(cfg config.Config) (string, error) {
func getCgroupPath(cfg config.InitializedConfig) (string, error) {
cgroupPath := cfg.Ebpf.CgroupPath

if cgroupPath != "" {
Expand Down Expand Up @@ -177,7 +177,7 @@ func getCgroupPath(cfg config.Config) (string, error) {
return mounts[0].Mountpoint, nil
}

func getBpffsPath(cfg config.Config) (string, error) {
func getBpffsPath(cfg config.InitializedConfig) (string, error) {
bpffsPath := cfg.Ebpf.BPFFSPath

if bpffsPath != "" {
Expand Down Expand Up @@ -238,7 +238,7 @@ func getBpffsPath(cfg config.Config) (string, error) {
}

func Flags(flags map[string]string) FlagGenerator {
return func(cfg config.Config, _ string, bpffs string) ([]string, error) {
return func(cfg config.InitializedConfig, _ string, bpffs string) ([]string, error) {
f := map[string]string{
"--bpffs": bpffs,
}
Expand All @@ -256,7 +256,7 @@ func Flags(flags map[string]string) FlagGenerator {
}

func CgroupFlags(
cfg config.Config,
cfg config.InitializedConfig,
cgroup string,
bpffs string,
) ([]string, error) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/transparentproxy/ebpf/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func GetFileInode(path string) (uint64, error) {
return stat.Ino, nil
}

func Setup(cfg config.Config) (string, error) {
func Setup(cfg config.InitializedConfig) (string, error) {
if os.Getuid() != 0 {
return "", fmt.Errorf("root user in required for this process or container")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/transparentproxy/ebpf/setup_fallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import (
"github.com/kumahq/kuma/pkg/transparentproxy/config"
)

func Setup(config.Config) (string, error) {
func Setup(config.InitializedConfig) (string, error) {
return "", fmt.Errorf("ebpf is currently supported only on linux")
}
Loading

0 comments on commit 3b87136

Please sign in to comment.