Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kmesh enable ipsec #1030

Merged
merged 9 commits into from
Jan 10, 2025
Merged

Conversation

bitcoffeeiux
Copy link
Contributor

What type of PR is this?

/kind feature

What this PR does / why we need it:

Which issue(s) this PR fixes:
Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?:


@@ -20,16 +20,20 @@ package bpf2go
// go run github.com/cilium/ebpf/cmd/bpf2go --help
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshCgroupSock ../ads/cgroup_sock.c -- -I../ads/include -I../../include -I../../../api/v2-c -DCGROUP_SOCK_MANAGE -DKERNEL_VERSION_HIGHER_5_13_0=1
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshCgroupSockWorkload ../workload/cgroup_sock.c -- -I../workload/include -I../../include -I../probes -DKERNEL_VERSION_HIGHER_5_13_0=1
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshSockops ../ads/sockops.c -- -I../ads/include -I../../include -I../../../api/v2-c -DKERNEL_VERSION_HIGHER_5_13_0=1
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshTracePoint ../ads/tracepoint.c -- -I../ads/include -I../../include -DKERNEL_VERSION_HIGHER_5_13_0=1
//not go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshSockops ../ads/sockops.c -- -I../ads/include -I../../include -I../../../api/v2-c -DKERNEL_VERSION_HIGHER_5_13_0=1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no modification, it will be cleaned up later

__type(value, struct nodeinfo);
__uint(map_flags, BPF_F_NO_PREALLOC);
__uint(max_entries, MAP_SIZE_OF_NODEINFO);
} map_of_nodeinfo SEC(".maps");
Copy link
Collaborator

@LiZhenCheng9527 LiZhenCheng9527 Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map name need a prefix: km_

defer file.Close()

is.ipSecLoadLock.Lock()
defer is.ipSecLoadLock.Unlock()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to unlock after line 108 is executed.
No need to defer

log.Infof("start watching file %s", IpSecKeyFile)

var timerC <-chan time.Time
for {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I end the loop?

ipsecKeyBase := ipsecController.ipsecKey.GetIPSecKey()
ipsecController.kmeshNodeInfo.Spec.Spi = ipsecKeyBase.Spi

myNodeName := os.Getenv("NODE_NAME")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: localNodeName? or onle nodeName

kniKey := lpm_key{
trie_key: uint32(prefix),
}
ip, _ := netip.ParseAddr(cidr[0])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this here to parse the cidr to get the IP? If so, see net.ParseCIDR

}

func (ic *ipsecController) isMine(name string) bool {
myNodeName := os.Getenv("NODE_NAME")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe can use a global variable to store nodeName

log.Errorf("failed to create kmesh node info to k8s: %v", err)
return
}
tmpUpdate, err := ic.kniClient.Get(context.TODO(), ic.kmeshNodeInfo.Name, metav1.GetOptions{})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it take a while to wait for the kmeshNode to be created?

ic.ipsecKey.StartWatch(ipsecUpdateChan)

go func() {
for {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to end this loop?

@@ -39,6 +41,21 @@ func GetK8sclient() (kubernetes.Interface, error) {
return clientset, nil
}

func GetKmeshNodeInfoClient() (kni_versioned.Interface, error) {
var clientset kni_versioned.Interface
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

void *begin = (void *)(long)(ctx->data);
void *end = (void *)(long)(ctx->data_end);

// eth header
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a similar parse function (https://github.com/kmesh-net/kmesh/blob/main/bpf/kmesh/workload/xdp.c#L71), can it be reused or abstracted into a tool function.

}

if link, err = netlink.LinkByIndex(ifIndex); err != nil {
return fmt.Errorf("failed ot link valid interface, %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("failed ot link valid interface, %v", err)
return fmt.Errorf("failed to link valid interface, %v", err)

}

if err = utils.AttchTCProgram(link, tc, utils.TC_DIR_INGRESS); err != nil {
return fmt.Errorf("failed ot attach tc program, %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("failed ot attach tc program, %v", err)
return fmt.Errorf("failed to attach tc program, %v", err)

@YaoZengzeng
Copy link
Member

@bitcoffeeiux it would be better to upload the design doc together for easier review :)

@LiZhenCheng9527
Copy link
Collaborator

@bitcoffeeiux Any update on this PR?

@bitcoffeeiux bitcoffeeiux force-pushed the kmesh-develop-push branch 2 times, most recently from aaaeb32 to 010c40b Compare December 5, 2024 08:16
@kmesh-bot kmesh-bot added size/L and removed size/XXL labels Dec 13, 2024
@kmesh-bot kmesh-bot added size/XXL and removed size/L labels Dec 16, 2024
__type(value, struct nodeinfo);
__uint(map_flags, BPF_F_NO_PREALLOC);
__uint(max_entries, MAP_SIZE_OF_NODEINFO);
} map_of_nodeinfo SEC(".maps");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} map_of_nodeinfo SEC(".maps");
} km_nodeinfo SEC(".maps");

Or rename the map in config.h.

return TC_ACT_OK;
}
nodeid = nodeinfo->nodeid;
ctx->mark = (nodeid << 16) + 0x00d0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this 0x00d0 and is it possible to add a comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that the packet needs to be decrypted by xfrm.

if (!nodeinfo) {
return TC_ACT_OK;
}
ctx->mark = ((nodeinfo->nodeid) << 16) + ((nodeinfo->spi) << 8) + 0x00e0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which one?


Kmesh使能ipsec时,需要精细化控制ipsec数据加密行为,这其中要求Kmesh具有节点之间的信息同步机制。当前主要场景基于云原生业务场景,信息同步机制基于K8s集群api-server构建,依赖Kmesh自定义结构体来完成数据存储。

CRD数据结构定义如下:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The meaning of each field in crd can be illustrated from the contents of type.go


### 4.3 Kmesh IPsec通信路径

![](./pics/ipsec_traffic_path.png)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The image can be centred

"reflect"
"syscall"

"github.com/cilium/ebpf"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

golint error

@@ -194,6 +255,11 @@ func CmdAdd(args *skel.CmdArgs) error {
log.Error(err)
return err
}

if err := enableTcEgress(args); err != nil {
err = fmt.Errorf("failed to set tc to dev %v, err is %v", args.IfName, err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what tc means. can be stated in error info

return NULL;
}
key.trie_key.prefixlen = 128;
IP6_COPY(key.ip.ip6, ip6);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some place, we use bpf_memcpy

}

func (c *encryptionConfig) AttachFlags(cmd *cobra.Command) {
cmd.PersistentFlags().BoolVar(&c.EnableIPsec, "enable-ipsec", false, "enable ipsec in daemon process")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: enable ipsec in service to service communication

@@ -11,3 +11,6 @@ rules:
- apiGroups: ["apps"]
resources: ["daemonsets"]
verbs: ["get"]
- apiGroups: ["kmesh.net"]
resources: ["kmeshnodeinfos"]
verbs: ["get", "create", "update", "patch", "list", "watch"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

patch, update, which one do you use?

}

getVethPeerLinkNum := func(netns.NetNS) error {
if err = utils.ExecuteWithRedirect("ethtool", ethtoolArgs, &output); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use go code

Spi int8 `json:"spi"`
NicIPs []string `json:"nicIP"`
BootID string `json:"bootid"`
Cirds []string `json:"cirds"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some comments for the above fields

myNode *v1.Node
}

func NewIPsecController(k8sClientSet kubernetes.Interface) (*IpsecController, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func NewIPsecController(k8sClientSet kubernetes.Interface) (*IpsecController, error) {
func NewIPSecController(k8sClientSet kubernetes.Interface) (*IpsecController, error) {

Comment on lines 21 to 38
func (s *Sum) Write(input []byte) (n int, err error) {
var sum uint64 = 0
for pos, i := range input {
si := uint32(i)
switch pos % 4 {
case 0:
case 1:
si = si << 8
case 2:
si = si << 16
case 3:
si = si << 24
}
sum += uint64(si)
}
for sum&0xffffffffffff0000 != 0 {
sum = sum&0xffff + sum>>16
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you want to do, just remind that golang lib has hash function

pkg/utils/tc.go Outdated
return nil
}

func DetchTCProgram(link netlink.Link, tc *ebpf.Program) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not called anywhere


kube::codegen::gen_client \
--with-watch \
--output-dir "${SCRIPT_ROOT}/pkg/kube/exnodeinfo" \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you use exnodeinfo

}

func (ic *IpsecController) handleAllKmeshNodeInfo() bool {
kmeshNodeInfoList, err := ic.kniClient.List(context.TODO(), metav1.ListOptions{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Donot use this list, since you have created a informer, use its List()

if ic.isMine(node.Name) {
continue
}
if err = ic.handleOtherNodeInfo(&node); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if err = ic.handleOtherNodeInfo(&node); err != nil {
if err = ic.handleNodeInfo(&node); err != nil {

}

if item.action == ActionAdd || item.action == ActionUpdate {
kniNodeInfo, err := ic.kniClient.Get(context.TODO(), item.name, metav1.GetOptions{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

donot use this one, use thae Get method from informer

}
}

func (ic *IpsecController) processNextItem() bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function cannot return false, otherwise the Run( will exit)


其他节点:

- node节点新增以后并将自己的kmeshnodeinfo上传api-server后,说明当前node节点上ipsec规则已准备好,当前kmesh-daemon仅需将对应node的state、policy、map更新即可
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen when a new node has already send traffic to the dst node, before the dst node setup the following policy ready

if strings.Compare(line[0], "peer_ifindex") != 0 {
continue
}
if ifIndex, err = strconv.Atoi(strings.Trim(line[1], " ")); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of trim, you can split ": " or just " "

}

if ifIndex == 0 {
return fmt.Errorf("can not found valid if index")
Copy link
Member

@hzxuzhonghu hzxuzhonghu Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("can not found valid if index")
return fmt.Errorf("can not find valid peer interface index")

@@ -194,6 +255,11 @@ func CmdAdd(args *skel.CmdArgs) error {
log.Error(err)
return err
}

if err := enableTcEgress(args); err != nil {
err = fmt.Errorf("failed to set tc to dev %v, err is %v", args.IfName, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err = fmt.Errorf("failed to set tc to dev %v, err is %v", args.IfName, err)
err = fmt.Errorf("failed to link tc to dev %v, err is %v", args.IfName, err)

if (!nodeinfo) {
return TC_ACT_OK;
}
ctx->mark = ((nodeinfo->nodeid) << 16) + 0x00e0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: ((nodeinfo->nodeid) << 16) | 0x00e0;

Use: "secret",
Short: "Use secrets to generate secret configuration data for IPsec",
Example: `# Use secrets to generate secret configuration data for IPsec:
kmeshctl secret aeadKey aeadLength, only support rfc4106(gcm(aes))`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide complete usage guide

if err == nil {
return
}
_, err = clientset.Kube().CoreV1().Secrets(utils.KmeshNamespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. can you only call this when .Get return non err

@@ -9,5 +9,8 @@ rules:
resources: ["pods","services","namespaces","nodes"]
verbs: ["get", "update", "patch", "list", "watch"]
- apiGroups: ["apps"]
resources: ["daemonsets"]
resources: ["daemonsets", "secret"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you donot reply on secret get, but use filewatch

}
for _, rawLocalAddr := range c.kmeshNodeInfo.Spec.Addresses {
localAddr := net.ParseIP(rawLocalAddr)
if addr.IP.Equal(localAddr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this check mean

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mount the tc program only when NodeInternalIP exists on the NIC.

}

for _, addr := range node.Spec.Addresses {
for _, cidr := range c.ipsecHandler.nodeInfos[addr].cidrs {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do not make node name as the key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use the name of the node as the key. In the current processing process, such as creating state rules, the iphandler processes information based on IP addresses and uses IP addresses to classify information.

return err
}

_, remoteCIDR, err := net.ParseCIDR("0.0.0.0/0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a const, we can define one

IpSecKeyFile = "./kmesh-ipsec/ipSec"
)

type ipSecInfo struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite similar as KmeshNodeInfo, can you use it

@bitcoffeeiux bitcoffeeiux force-pushed the kmesh-develop-push branch 2 times, most recently from d32dd79 to 93a5a6a Compare January 8, 2025 14:53
Kmesh introduce IPsec encryption to encrypt communicatioon between nodes,
which is critical to maintaining communication security. This submission
includes come preliminary design of ipsec and outlines the basic data
path for Kmesh using IPsec.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
We have added two new eBPF tc programs to mark traffic data.
`tc_mark_encrypt` runs on the NIC of the pod. Currently, the veth
NIC is supported. When the veth node receives a data packet, tc
programs determines whether the traffic needs to be encrypted based
oon the target information. If the traffic needs to be encrypted, tc
add l marker to the packet and sends it to the IPsec of the host for
processing.
`tc_mark_decrypt` runs on the NIC of a node. After receiving an IPsec
packet, mark the decrypted IPsec packet and determine whether to send
the packet to the backend for processing or directly discarded by IPsec.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
kmesh node info is an CRD. This resource is used to transmit perr node
information between kmesh nodes, including node names and IPsec information,
to construct an IPsec encryption system.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
This patch is used to load the tc program and close the tc program.
Note that the attach and detach operations are not included in this
patch. The attach and detach operations will be implemented in the
subsequent commit operations.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
This PR is used to add a controller for IPsec. This controller is
used to enable IPsec when the Kmesh is started, stop IPsec when the
Kmesh ends, and manage IPsec configuration data. When a node in a
cluster changes, the node information is sent to api-server, and
information about other nodes is obtained and synchronized to the
IPsec.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
The tc eBPF processing logic is added to the cni. When the pod is
started, the corresponding tc program is mounted in the egress
direction of the veth network nic of the cni tc. The tc program
adds encrypted labels to the traffic on the traffic egress. After
the traffic reaches the host, the traffic is encrypted by IPsec.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
When the management tag is added, the Kmesh adds the tc enabling
logic for the existing pod. When the management tag is removed,
the Kmesh adds the disabling logic.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
kmeshctl secret used to generate IPsec config to K8s.

Signed-off-by: bitcoffee <liuxin350@huawei.com>
make gen

Signed-off-by: bitcoffee <liuxin350@huawei.com>
hzxuzhonghu
hzxuzhonghu previously approved these changes Jan 9, 2025
@hzxuzhonghu
Copy link
Member

/lgtm
/approve

@kmesh-bot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: hzxuzhonghu

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kmesh-bot kmesh-bot merged commit 4601e6a into kmesh-net:main Jan 10, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants