github.com/cilium/cilium@v1.16.2/pkg/identity/identity.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package identity 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "net" 10 "strconv" 11 12 "github.com/cilium/cilium/pkg/labels" 13 "github.com/cilium/cilium/pkg/option" 14 ) 15 16 const ( 17 NodeLocalIdentityType = "node_local" 18 ReservedIdentityType = "reserved" 19 ClusterLocalIdentityType = "cluster_local" 20 WellKnownIdentityType = "well_known" 21 RemoteNodeIdentityType = "remote_node" 22 ) 23 24 // Identity is the representation of the security context for a particular set of 25 // labels. 26 type Identity struct { 27 // Identity's ID. 28 ID NumericIdentity `json:"id"` 29 // Set of labels that belong to this Identity. 30 Labels labels.Labels `json:"labels"` 31 32 // LabelArray contains the same labels as Labels in a form of a list, used 33 // for faster lookup. 34 LabelArray labels.LabelArray `json:"-"` 35 36 // CIDRLabel is the primary identity label when the identity represents 37 // a CIDR. The Labels field will consist of all matching prefixes, e.g. 38 // 10.0.0.0/8 39 // 10.0.0.0/7 40 // 10.0.0.0/6 41 // [...] 42 // reserved:world 43 // 44 // The CIDRLabel field will only contain 10.0.0.0/8 45 CIDRLabel labels.Labels `json:"-"` 46 47 // ReferenceCount counts the number of references pointing to this 48 // identity. This field is used by the owning cache of the identity. 49 ReferenceCount int `json:"-"` 50 } 51 52 // IPIdentityPair is a pairing of an IP and the security identity to which that 53 // IP corresponds. May include an optional Mask which, if present, denotes that 54 // the IP represents a CIDR with the specified Mask. 55 // 56 // WARNING - STABLE API 57 // This structure is written as JSON to the key-value store. Do NOT modify this 58 // structure in ways which are not JSON forward compatible. 59 type IPIdentityPair struct { 60 IP net.IP `json:"IP"` 61 Mask net.IPMask `json:"Mask"` 62 HostIP net.IP `json:"HostIP"` 63 ID NumericIdentity `json:"ID"` 64 Key uint8 `json:"Key"` 65 Metadata string `json:"Metadata"` 66 K8sNamespace string `json:"K8sNamespace,omitempty"` 67 K8sPodName string `json:"K8sPodName,omitempty"` 68 NamedPorts []NamedPort `json:"NamedPorts,omitempty"` 69 } 70 71 type IdentityMap map[NumericIdentity]labels.LabelArray 72 73 // GetKeyName returns the kvstore key to be used for the IPIdentityPair 74 func (pair *IPIdentityPair) GetKeyName() string { return pair.PrefixString() } 75 76 // Marshal returns the IPIdentityPair object as JSON byte slice 77 func (pair *IPIdentityPair) Marshal() ([]byte, error) { return json.Marshal(pair) } 78 79 // Unmarshal parses the JSON byte slice and updates the IPIdentityPair receiver 80 func (pair *IPIdentityPair) Unmarshal(key string, data []byte) error { 81 newPair := IPIdentityPair{} 82 if err := json.Unmarshal(data, &newPair); err != nil { 83 return err 84 } 85 86 if got := newPair.GetKeyName(); got != key { 87 return fmt.Errorf("IP address does not match key: expected %s, got %s", key, got) 88 } 89 90 *pair = newPair 91 return nil 92 } 93 94 // NamedPort is a mapping from a port name to a port number and protocol. 95 // 96 // WARNING - STABLE API 97 // This structure is written as JSON to the key-value store. Do NOT modify this 98 // structure in ways which are not JSON forward compatible. 99 type NamedPort struct { 100 Name string `json:"Name"` 101 Port uint16 `json:"Port"` 102 Protocol string `json:"Protocol"` 103 } 104 105 // Sanitize takes a partially initialized Identity (for example, deserialized 106 // from json) and reconstitutes the full object from what has been restored. 107 func (id *Identity) Sanitize() { 108 if id.Labels != nil { 109 id.LabelArray = id.Labels.LabelArray() 110 } 111 } 112 113 // StringID returns the identity identifier as string 114 func (id *Identity) StringID() string { 115 return id.ID.StringID() 116 } 117 118 // StringID returns the identity identifier as string 119 func (id *Identity) String() string { 120 return id.ID.StringID() 121 } 122 123 // IsReserved returns whether the identity represents a reserved identity 124 // (true), or not (false). 125 func (id *Identity) IsReserved() bool { 126 return LookupReservedIdentity(id.ID) != nil 127 } 128 129 // IsFixed returns whether the identity represents a fixed identity 130 // (true), or not (false). 131 func (id *Identity) IsFixed() bool { 132 return LookupReservedIdentity(id.ID) != nil && 133 (id.ID == ReservedIdentityHost || id.ID == ReservedIdentityHealth || 134 IsUserReservedIdentity(id.ID)) 135 } 136 137 // IsWellKnown returns whether the identity represents a well known identity 138 // (true), or not (false). 139 func (id *Identity) IsWellKnown() bool { 140 return WellKnown.lookupByNumericIdentity(id.ID) != nil 141 } 142 143 // IsWellKnownIdentity returns true if the identity represents a well-known 144 // identity, false otherwise. 145 func IsWellKnownIdentity(id NumericIdentity) bool { 146 return WellKnown.lookupByNumericIdentity(id) != nil 147 } 148 149 // NewIdentityFromLabelArray creates a new identity 150 func NewIdentityFromLabelArray(id NumericIdentity, lblArray labels.LabelArray) *Identity { 151 var lbls labels.Labels 152 153 if lblArray != nil { 154 lbls = lblArray.Labels() 155 } 156 return &Identity{ID: id, Labels: lbls, LabelArray: lblArray} 157 } 158 159 // NewIdentity creates a new identity 160 func NewIdentity(id NumericIdentity, lbls labels.Labels) *Identity { 161 var lblArray labels.LabelArray 162 163 if lbls != nil { 164 lblArray = lbls.LabelArray() 165 } 166 return &Identity{ID: id, Labels: lbls, LabelArray: lblArray} 167 } 168 169 // IsHost determines whether the IP in the pair represents a host (true) or a 170 // CIDR prefix (false) 171 func (pair *IPIdentityPair) IsHost() bool { 172 return pair.Mask == nil 173 } 174 175 // PrefixString returns the IPIdentityPair's IP as either a host IP in the 176 // format w.x.y.z if 'host' is true, or as a prefix in the format the w.x.y.z/N 177 // if 'host' is false. 178 func (pair *IPIdentityPair) PrefixString() string { 179 ipstr := pair.IP.String() 180 181 if pair.IsHost() { 182 return ipstr 183 } 184 185 ones, _ := pair.Mask.Size() 186 return ipstr + "/" + strconv.Itoa(ones) 187 } 188 189 // RequiresGlobalIdentity returns true if the label combination requires a 190 // global identity 191 func RequiresGlobalIdentity(lbls labels.Labels) bool { 192 return ScopeForLabels(lbls) == IdentityScopeGlobal 193 } 194 195 // ScopeForLabels returns the identity scope to be used for the label set. 196 // If all labels are either CIDR or reserved, then returns the CIDR scope. 197 // Note: This assumes the caller has already called LookupReservedIdentityByLabels; 198 // it does not handle that case. 199 func ScopeForLabels(lbls labels.Labels) NumericIdentity { 200 scope := IdentityScopeGlobal 201 202 // If this is a remote node, return the remote node scope. 203 // Note that this is not reachable when policy-cidr-selects-nodes is false or 204 // when enable-node-selector-labels is false, since 205 // callers will already have gotten a value from LookupReservedIdentityByLabels. 206 if lbls.Has(labels.LabelRemoteNode[labels.IDNameRemoteNode]) { 207 return IdentityScopeRemoteNode 208 } 209 210 for _, label := range lbls { 211 switch label.Source { 212 case labels.LabelSourceCIDR, labels.LabelSourceFQDN, labels.LabelSourceReserved: 213 scope = IdentityScopeLocal 214 default: 215 return IdentityScopeGlobal 216 } 217 } 218 219 return scope 220 } 221 222 // AddUserDefinedNumericIdentitySet adds all key-value pairs from the given map 223 // to the map of user defined numeric identities and reserved identities. 224 // The key-value pairs should map a numeric identity to a valid label. 225 // Is not safe for concurrent use. 226 func AddUserDefinedNumericIdentitySet(m map[string]string) error { 227 // Validate first 228 for k := range m { 229 ni, err := ParseNumericIdentity(k) 230 if err != nil { 231 return err 232 } 233 if !IsUserReservedIdentity(ni) { 234 return ErrNotUserIdentity 235 } 236 } 237 for k, lbl := range m { 238 ni, _ := ParseNumericIdentity(k) 239 AddUserDefinedNumericIdentity(ni, lbl) 240 AddReservedIdentity(ni, lbl) 241 } 242 return nil 243 } 244 245 // LookupReservedIdentityByLabels looks up a reserved identity by its labels and 246 // returns it if found. Returns nil if not found. 247 func LookupReservedIdentityByLabels(lbls labels.Labels) *Identity { 248 if identity := WellKnown.LookupByLabels(lbls); identity != nil { 249 return identity 250 } 251 252 // Check if a fixed identity exists. 253 if lbl, exists := lbls[labels.LabelKeyFixedIdentity]; exists { 254 // If the set of labels contain a fixed identity then and exists in 255 // the map of reserved IDs then return the identity of that reserved ID. 256 id := GetReservedID(lbl.Value) 257 if id != IdentityUnknown && IsUserReservedIdentity(id) { 258 return LookupReservedIdentity(id) 259 } 260 // If a fixed identity was not found then we return nil to avoid 261 // falling to a reserved identity. 262 return nil 263 } 264 265 // If there is no reserved label, return nil. 266 if !lbls.IsReserved() { 267 return nil 268 } 269 270 var nid NumericIdentity 271 if lbls.Has(labels.LabelHost[labels.IDNameHost]) { 272 nid = ReservedIdentityHost 273 } else if lbls.Has(labels.LabelRemoteNode[labels.IDNameRemoteNode]) { 274 // If selecting remote-nodes via CIDR policies is allowed, then 275 // they no longer have a reserved identity. 276 if option.Config.PolicyCIDRMatchesNodes() { 277 return nil 278 } 279 // If selecting remote-nodes via node labels is allowed, then 280 // they no longer have a reserved identity and are using 281 // IdentityScopeRemoteNode. 282 if option.Config.PerNodeLabelsEnabled() { 283 return nil 284 } 285 nid = ReservedIdentityRemoteNode 286 if lbls.Has(labels.LabelKubeAPIServer[labels.IDNameKubeAPIServer]) { 287 // If there's a kube-apiserver label, then we know this is 288 // kube-apiserver reserved ID, so change it as such. 289 // Only traffic from non-kube-apiserver nodes should be 290 // considered as remote-node. 291 nid = ReservedIdentityKubeAPIServer 292 } 293 } 294 295 if nid != IdentityUnknown { 296 return NewIdentity(nid, lbls) 297 } 298 299 // We have handled all the cases where multiple labels can be present. 300 // So, we make sure the set of labels only contains a single label and 301 // that label is of the reserved type. This is to prevent users from 302 // adding cilium-reserved labels into the workloads. 303 if len(lbls) != 1 { 304 return nil 305 } 306 307 nid = GetReservedID(lbls.ToSlice()[0].Key) 308 if nid != IdentityUnknown && !IsUserReservedIdentity(nid) { 309 return LookupReservedIdentity(nid) 310 } 311 return nil 312 } 313 314 // IdentityAllocationIsLocal returns true if a call to AllocateIdentity with 315 // the given labels would not require accessing the KV store to allocate the 316 // identity. 317 // Currently, this function returns true only if the labels are those of a 318 // reserved identity, i.e. if the slice contains a single reserved 319 // "reserved:*" label. 320 func IdentityAllocationIsLocal(lbls labels.Labels) bool { 321 // If there is only one label with the "reserved" source and a well-known 322 // key, the well-known identity for it can be allocated locally. 323 return LookupReservedIdentityByLabels(lbls) != nil 324 }