mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-18 12:32:13 +00:00
cmd/k8s-operator: validate that tailscale.com/tailnet-ip annotation value is a valid IP
Fixes #13836 Signed-off-by: Nick Kirby <nrkirb@gmail.com>
This commit is contained in:
parent
e815ae0ec4
commit
6ab39b7bcd
@ -432,6 +432,148 @@ func TestTailnetTargetIPAnnotation(t *testing.T) {
|
|||||||
expectMissing[corev1.Secret](t, fc, "operator-ns", fullName)
|
expectMissing[corev1.Secret](t, fc, "operator-ns", fullName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTailnetTargetIPAnnotation_IPCouldNotBeParsed(t *testing.T) {
|
||||||
|
fc := fake.NewFakeClient()
|
||||||
|
ft := &fakeTSClient{}
|
||||||
|
zl, err := zap.NewDevelopment()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
clock := tstest.NewClock(tstest.ClockOpts{})
|
||||||
|
sr := &ServiceReconciler{
|
||||||
|
Client: fc,
|
||||||
|
ssr: &tailscaleSTSReconciler{
|
||||||
|
Client: fc,
|
||||||
|
tsClient: ft,
|
||||||
|
defaultTags: []string{"tag:k8s"},
|
||||||
|
operatorNamespace: "operator-ns",
|
||||||
|
proxyImage: "tailscale/tailscale",
|
||||||
|
},
|
||||||
|
logger: zl.Sugar(),
|
||||||
|
clock: clock,
|
||||||
|
recorder: record.NewFakeRecorder(100),
|
||||||
|
}
|
||||||
|
tailnetTargetIP := "invalid-ip"
|
||||||
|
mustCreate(t, fc, &corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test",
|
||||||
|
Namespace: "default",
|
||||||
|
|
||||||
|
UID: types.UID("1234-UID"),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
AnnotationTailnetTargetIP: tailnetTargetIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
ClusterIP: "10.20.30.40",
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
LoadBalancerClass: ptr.To("tailscale"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expectReconciled(t, sr, "default", "test")
|
||||||
|
|
||||||
|
t0 := conditionTime(clock)
|
||||||
|
|
||||||
|
want := &corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test",
|
||||||
|
Namespace: "default",
|
||||||
|
UID: types.UID("1234-UID"),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
AnnotationTailnetTargetIP: tailnetTargetIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
ClusterIP: "10.20.30.40",
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
LoadBalancerClass: ptr.To("tailscale"),
|
||||||
|
},
|
||||||
|
Status: corev1.ServiceStatus{
|
||||||
|
Conditions: []metav1.Condition{{
|
||||||
|
Type: string(tsapi.ProxyReady),
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
LastTransitionTime: t0,
|
||||||
|
Reason: reasonProxyInvalid,
|
||||||
|
Message: `unable to provision proxy resources: invalid Service: invalid value of annotation tailscale.com/tailnet-ip: "invalid-ip" could not be parsed as a valid IP Address, error: ParseAddr("invalid-ip"): unable to parse IP`,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectEqual(t, fc, want, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTailnetTargetIPAnnotation_InvalidIP(t *testing.T) {
|
||||||
|
fc := fake.NewFakeClient()
|
||||||
|
ft := &fakeTSClient{}
|
||||||
|
zl, err := zap.NewDevelopment()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
clock := tstest.NewClock(tstest.ClockOpts{})
|
||||||
|
sr := &ServiceReconciler{
|
||||||
|
Client: fc,
|
||||||
|
ssr: &tailscaleSTSReconciler{
|
||||||
|
Client: fc,
|
||||||
|
tsClient: ft,
|
||||||
|
defaultTags: []string{"tag:k8s"},
|
||||||
|
operatorNamespace: "operator-ns",
|
||||||
|
proxyImage: "tailscale/tailscale",
|
||||||
|
},
|
||||||
|
logger: zl.Sugar(),
|
||||||
|
clock: clock,
|
||||||
|
recorder: record.NewFakeRecorder(100),
|
||||||
|
}
|
||||||
|
tailnetTargetIP := "999.999.999.999"
|
||||||
|
mustCreate(t, fc, &corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test",
|
||||||
|
Namespace: "default",
|
||||||
|
|
||||||
|
UID: types.UID("1234-UID"),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
AnnotationTailnetTargetIP: tailnetTargetIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
ClusterIP: "10.20.30.40",
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
LoadBalancerClass: ptr.To("tailscale"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expectReconciled(t, sr, "default", "test")
|
||||||
|
|
||||||
|
t0 := conditionTime(clock)
|
||||||
|
|
||||||
|
want := &corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test",
|
||||||
|
Namespace: "default",
|
||||||
|
UID: types.UID("1234-UID"),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
AnnotationTailnetTargetIP: tailnetTargetIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
ClusterIP: "10.20.30.40",
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
LoadBalancerClass: ptr.To("tailscale"),
|
||||||
|
},
|
||||||
|
Status: corev1.ServiceStatus{
|
||||||
|
Conditions: []metav1.Condition{{
|
||||||
|
Type: string(tsapi.ProxyReady),
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
LastTransitionTime: t0,
|
||||||
|
Reason: reasonProxyInvalid,
|
||||||
|
Message: `unable to provision proxy resources: invalid Service: invalid value of annotation tailscale.com/tailnet-ip: "999.999.999.999" could not be parsed as a valid IP Address, error: ParseAddr("999.999.999.999"): IPv4 field has value >255`,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectEqual(t, fc, want, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAnnotations(t *testing.T) {
|
func TestAnnotations(t *testing.T) {
|
||||||
fc := fake.NewFakeClient()
|
fc := fake.NewFakeClient()
|
||||||
ft := &fakeTSClient{}
|
ft := &fakeTSClient{}
|
||||||
|
@ -358,9 +358,14 @@ func validateService(svc *corev1.Service) []string {
|
|||||||
violations = append(violations, fmt.Sprintf("invalid value of annotation %s: %q does not appear to be a valid MagicDNS name", AnnotationTailnetTargetFQDN, fqdn))
|
violations = append(violations, fmt.Sprintf("invalid value of annotation %s: %q does not appear to be a valid MagicDNS name", AnnotationTailnetTargetFQDN, fqdn))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ipStr := svc.Annotations[AnnotationTailnetTargetIP]; ipStr != "" {
|
||||||
// TODO(irbekrm): validate that tailscale.com/tailnet-ip annotation is a
|
ip, err := netip.ParseAddr(ipStr)
|
||||||
// valid IP address (tailscale/tailscale#13671).
|
if err != nil {
|
||||||
|
violations = append(violations, fmt.Sprintf("invalid value of annotation %s: %q could not be parsed as a valid IP Address, error: %s", AnnotationTailnetTargetIP, ipStr, err))
|
||||||
|
} else if !ip.IsValid() {
|
||||||
|
violations = append(violations, fmt.Sprintf("parsed IP address in annotation %s: %q is not valid", AnnotationTailnetTargetIP, ipStr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
svcName := nameForService(svc)
|
svcName := nameForService(svc)
|
||||||
if err := dnsname.ValidLabel(svcName); err != nil {
|
if err := dnsname.ValidLabel(svcName); err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user