Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 37 additions & 18 deletions cmd/cilium-agent-proxy/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"io"
"net"
"net/http"
"slices"
"strconv"
"strings"
)

const socketPath = "/var/run/cilium/cilium.sock"
Expand Down Expand Up @@ -43,30 +45,47 @@ func handleEndpoint(w http.ResponseWriter, r *http.Request) {
renderJSON(w, r.URL.Path, buf.Bytes(), http.StatusOK)
}

func handleIdentity(w http.ResponseWriter, r *http.Request) {
param := r.URL.Path[len("/v1/identity/"):]
if len(param) == 0 {
renderError(w, r.URL.Path, "failed to read identity", http.StatusBadRequest)
return
}

// Convert to number to avoid parameter injection
identity, err := strconv.Atoi(param)
func handleCIDRIdentites(w http.ResponseWriter, r *http.Request) {
url := "http://localhost/v1/identity"
resp, err := socketClient.Get(url)
if err != nil {
renderError(w, r.URL.Path, "failed to read identity", http.StatusBadRequest)
renderError(w, r.URL.Path, "failed to call Cilium API", http.StatusInternalServerError)
return
}

url := fmt.Sprintf("http://localhost/v1/identity/%d", identity)
resp, err := socketClient.Get(url)
// https://github.com/cilium/cilium/blob/main/api/v1/models/identity.go
type Identity struct {
ID int64 `json:"id,omitempty"`
Labels []string `json:"labels,omitempty"`
}
var ids []Identity
{
data, err := io.ReadAll(resp.Body)
if err != nil {
renderError(w, r.URL.Path, "failed to read data", http.StatusInternalServerError)
return
}
if err := json.Unmarshal(data, &ids); err != nil {
renderError(w, r.URL.Path, "failed to unmarshal result", http.StatusInternalServerError)
return
}
}
ids = slices.DeleteFunc(ids, func(i Identity) bool {
// https://docs.cilium.io/en/stable/internals/security-identities/
if (1<<24) <= i.ID && i.ID < (1<<25) {
return !slices.ContainsFunc(i.Labels, func(l string) bool {
return strings.HasPrefix(l, "cidr:")
})
}
return true
})

data, err := json.Marshal(ids)
if err != nil {
renderError(w, r.URL.Path, "failed to call Cilium API", http.StatusInternalServerError)
renderError(w, r.URL.Path, "failed to marshal result", http.StatusInternalServerError)
return
}

buf := new(bytes.Buffer)
io.Copy(buf, resp.Body)
renderJSON(w, r.URL.Path, buf.Bytes(), http.StatusOK)
renderJSON(w, r.URL.Path, data, http.StatusOK)
}

func handlePolicy(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -145,7 +164,7 @@ func subMain() error {
}

http.HandleFunc("/v1/endpoint/", handleEndpoint)
http.HandleFunc("/v1/identity/", handleIdentity)
http.HandleFunc("/cidr-identities", handleCIDRIdentites)
http.HandleFunc("/policy/", handlePolicy)
http.HandleFunc("/version", handleVersion)

Expand Down
109 changes: 77 additions & 32 deletions cmd/npv/app/helper_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ import (
"net"
"net/http"
"slices"
"strconv"
"strings"
"sync"

"github.com/cilium/cilium/api/v1/client/policy"
"github.com/cilium/cilium/api/v1/models"
"github.com/cilium/cilium/pkg/client"
"github.com/cilium/cilium/pkg/identity"
"github.com/cilium/cilium/pkg/ip"
Expand All @@ -27,12 +26,17 @@ import (
"k8s.io/client-go/kubernetes"
)

type cachedIdentity struct {
identity *models.Identity
cidr *net.IPNet
}

type proxyClient struct {
*client.Client

node string
endpointURL string
cachedLocalIdentity map[uint32]*policy.GetIdentityIDOK
node string
endpointURL string
cachedCIDRIdentities map[uint32]*cachedIdentity
}

var (
Expand Down Expand Up @@ -105,34 +109,11 @@ func createCiliumClient(ctx context.Context, stderr io.Writer, c *kubernetes.Cli
return proxy, nil
}

func (c *proxyClient) queryLocalIdentity(ctx context.Context, id uint32) (*policy.GetIdentityIDOK, error) {
if c.cachedLocalIdentity == nil {
c.cachedLocalIdentity = make(map[uint32]*policy.GetIdentityIDOK)
}

if _, ok := c.cachedLocalIdentity[id]; !ok {
// If the identity is in the local scope, it is only valid on the reporting node.
params := policy.GetIdentityIDParams{
Context: ctx,
ID: strconv.FormatInt(int64(id), 10),
}
response, err := c.Policy.GetIdentityID(&params)
switch err {
case nil:
c.cachedLocalIdentity[id] = response
default:
err = fmt.Errorf("failed to get identity: %w", err)
}
return response, err
}
return c.cachedLocalIdentity[id], nil
}

func (c *proxyClient) testAgentVersion(ctx context.Context, stderr io.Writer) error {
url := c.endpointURL + "/version"
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("failed to request version: %w", err)
return fmt.Errorf("failed to request /version: %w", err)
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
Expand All @@ -156,6 +137,70 @@ func (c *proxyClient) testAgentVersion(ctx context.Context, stderr io.Writer) er
return nil
}

func (c *proxyClient) fetchCIDRIdentities() error {
if c.cachedCIDRIdentities == nil {
url := c.endpointURL + "/cidr-identities"
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("failed to request /cidr-identities: %w", err)
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read /cidr-identities: %w", err)
}

var m []models.Identity
if err := json.Unmarshal(data, &m); err != nil {
return fmt.Errorf("failed to unmarshal /cidr-identities: %w", err)
}

c.cachedCIDRIdentities = make(map[uint32]*cachedIdentity)
for _, id := range m {
lbls := labels.NewLabelsFromModel(id.Labels)
cidrModel := lbls.GetFromSource(labels.LabelSourceCIDR).GetPrintableModel()
if len(cidrModel) != 1 {
return fmt.Errorf("unexpected CIDR label for identity %d", id.ID)
}
parts := strings.Split(cidrModel[0], ":")
if len(parts) != 2 {
return fmt.Errorf("failed to parse CIDR label for identity %d", id.ID)
}
_, cidr, err := net.ParseCIDR(parts[1])
if err != nil {
return fmt.Errorf("failed to parse CIDR for identity %d", id.ID)
}

c.cachedCIDRIdentities[uint32(id.ID)] = &cachedIdentity{
identity: &id,
cidr: cidr,
}
}
}
return nil
}

func (c *proxyClient) listCIDRIdentity() ([]uint32, error) {
if err := c.fetchCIDRIdentities(); err != nil {
return nil, err
}
// for k, v := range c.cachedCIDRIdentities {

// }
return nil, nil
}

func (c *proxyClient) getCIDRIdentity(ctx context.Context, id uint32) (*models.Identity, error) {
if err := c.fetchCIDRIdentities(); err != nil {
return nil, err
}

value, ok := c.cachedCIDRIdentities[id]
if !ok {
return nil, fmt.Errorf("failed to found CIDR identity for %d", id)
}
return value.identity, nil
}

// For the meanings of the flags, see:
// https://github.com/cilium/cilium/blob/v1.16.12/bpf/lib/common.h#L396
type policyEntry struct {
Expand Down Expand Up @@ -289,16 +334,16 @@ func makeCIDRFilter(ingress, egress bool, incl []*net.IPNet, excl []*net.IPNet)
}

// Retrieve identity information
response, err := client.queryLocalIdentity(ctx, p.Key.Identity)
cidrID, err := client.getCIDRIdentity(ctx, p.Key.Identity)
if err != nil {
return false, err
}
if !slices.Contains(response.Payload.Labels, "reserved:world") {
if !slices.Contains(cidrID.Labels, "reserved:world") {
return false, nil
}

// Compute leaf CIDR of the identity
lbls := labels.NewLabelsFromModel(response.Payload.Labels)
lbls := labels.NewLabelsFromModel(cidrID.Labels)
cidrModel := lbls.GetFromSource(labels.LabelSourceCIDR).GetPrintableModel()
if len(cidrModel) != 1 {
return false, errors.New("internal error")
Expand Down
8 changes: 4 additions & 4 deletions cmd/npv/app/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,12 @@ func runInspectOnPod(ctx context.Context, stderr io.Writer, clientset *kubernete
if idObj.IsReservedIdentity() {
entry.Example = "reserved:" + idObj.String()
} else if idObj.HasLocalScope() {
response, err := client.queryLocalIdentity(ctx, p.Key.Identity)
cidrID, err := client.getCIDRIdentity(ctx, p.Key.Identity)
if err != nil {
return nil, err
}
if slices.Contains(response.Payload.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(response.Payload.Labels)
if slices.Contains(cidrID.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(cidrID.Labels)
cidrModel := lbls.GetFromSource(labels.LabelSourceCIDR).GetPrintableModel()
if len(cidrModel) == 1 {
entry.Example = cidrModel[0]
Expand Down Expand Up @@ -215,7 +215,7 @@ func runInspect(ctx context.Context, stdout, stderr io.Writer, name string) erro
func(pod *corev1.Pod) []inspectEntry {
result, err := runInspectOnPod(ctx, stderr, clientset, dynamicClient, filter, pod)
if err != nil {
fmt.Fprintf(stderr, "* %v\n", err)
fmt.Fprintf(stderr, "Warning: %v\n", err)
return nil
}
return result
Expand Down
2 changes: 1 addition & 1 deletion cmd/npv/app/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func runList(ctx context.Context, stdout, stderr io.Writer, name string) error {
func(pod *corev1.Pod) map[derivedFromEntry]any {
policy, err := runListOnPod(ctx, stderr, clientset, dynamicClient, pod)
if err != nil {
fmt.Fprintf(stderr, "* %v\n", err)
fmt.Fprintf(stderr, "Warning: %v\n", err)
return nil
}
return policy
Expand Down
12 changes: 6 additions & 6 deletions cmd/npv/app/reach.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@ func runReach(ctx context.Context, stdout, stderr io.Writer) error {
if idObj.IsReservedIdentity() {
entry.Example = "reserved:" + idObj.String()
} else if idObj.HasLocalScope() {
response, err := client.queryLocalIdentity(ctx, p.Key.Identity)
cidrID, err := client.getCIDRIdentity(ctx, p.Key.Identity)
if err != nil {
return err
}
if slices.Contains(response.Payload.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(response.Payload.Labels)
if slices.Contains(cidrID.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(cidrID.Labels)
cidrModel := lbls.GetFromSource(labels.LabelSourceCIDR).GetPrintableModel()
if len(cidrModel) == 1 {
entry.Example = cidrModel[0]
Expand Down Expand Up @@ -249,12 +249,12 @@ func runReach(ctx context.Context, stdout, stderr io.Writer) error {
if idObj.IsReservedIdentity() {
entry.Example = "reserved:" + idObj.String()
} else if idObj.HasLocalScope() {
response, err := client.queryLocalIdentity(ctx, p.Key.Identity)
cidrID, err := client.getCIDRIdentity(ctx, p.Key.Identity)
if err != nil {
return err
}
if slices.Contains(response.Payload.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(response.Payload.Labels)
if slices.Contains(cidrID.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(cidrID.Labels)
cidrModel := lbls.GetFromSource(labels.LabelSourceCIDR).GetPrintableModel()
if len(cidrModel) == 1 {
entry.Example = cidrModel[0]
Expand Down
2 changes: 1 addition & 1 deletion cmd/npv/app/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func runSummary(ctx context.Context, stdout, stderr io.Writer) error {
func(pod *corev1.Pod) []summaryEntry {
entry, err := runSummaryOnPod(ctx, clientset, dynamicClient, pod)
if err != nil {
fmt.Fprintf(stderr, "* %v\n", err)
fmt.Fprintf(stderr, "Warning: %v\n", err)
return nil
}
return []summaryEntry{entry}
Expand Down
8 changes: 4 additions & 4 deletions cmd/npv/app/traffic.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,12 @@ func runTrafficOnPod(ctx context.Context, stderr io.Writer, clientset *kubernete
if idObj.IsReservedIdentity() {
example = "reserved:" + idObj.String()
} else if idObj.HasLocalScope() {
response, err := client.queryLocalIdentity(ctx, p.Key.Identity)
cidrID, err := client.getCIDRIdentity(ctx, p.Key.Identity)
if err != nil {
return nil, err
}
if slices.Contains(response.Payload.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(response.Payload.Labels)
if slices.Contains(cidrID.Labels, "reserved:world") {
lbls := labels.NewLabelsFromModel(cidrID.Labels)
cidrModel := lbls.GetFromSource(labels.LabelSourceCIDR).GetPrintableModel()
if len(cidrModel) == 1 {
// Cilium allocates different identity for a CIDR between nodes, so we cannot use it as a key.
Expand Down Expand Up @@ -223,7 +223,7 @@ func runTraffic(ctx context.Context, stdout, stderr io.Writer, name string) erro
func(pod *corev1.Pod) map[trafficKey]*trafficValue {
result, err := runTrafficOnPod(ctx, stderr, clientset, dynamicClient, filter, pod)
if err != nil {
fmt.Fprintf(stderr, "* %v\n", err)
fmt.Fprintf(stderr, "Warning: %v\n", err)
return nil
}
return result
Expand Down
5 changes: 3 additions & 2 deletions e2e/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ HELM := helm --content-cache $(CACHE_DIR)/helm/content

# Avoid using SHA here
# https://github.com/kubernetes-sigs/kind/issues/4028
CILIUM_IMAGE := quay.io/cilium/cilium:v1.16.19
CILIUM_CHART := oci://quay.io/cilium/charts/cilium:1.16.19@sha256:7496a278401c221a30d023df4e40c4e97c3c203645b337d332da5f9e887acf06
CILIUM_IMAGE := quay.io/cilium/cilium:v1.17.15
CILIUM_CHART := oci://quay.io/cilium/charts/cilium:1.17.15@sha256:464dbdb023f9d2fd291efbe2adcf76681e36cc7144ed5fe983058b25cb23a4a5

DEPLOYMENT_REPLICAS ?= 1

Expand Down Expand Up @@ -89,6 +89,7 @@ install-test-pod:
$(MAKE) --no-print-directory DEPLOYMENT_REPLICAS=2 run-test-pod-l3-ingress-explicit-allow-all
$(MAKE) --no-print-directory wait-for-workloads

kubectl apply -f testdata/policy/cidr-group.yaml
kubectl apply -f testdata/policy/l3.yaml
kubectl apply -f testdata/policy/l4.yaml

Expand Down
Loading
Loading