* cmd/containerboot,cmd/k8s-operator/deploy/manifests: optionally forward cluster traffic via ingress proxy.
If a tailscale Ingress has tailscale.com/experimental-forward-cluster-traffic-via-ingress annotation, configure the associated ingress proxy to have its tailscale serve proxy to listen on Pod's IP address. This ensures that cluster traffic too can be forwarded via this proxy to the ingress backend(s).
In containerboot, if EXPERIMENTAL_PROXY_CLUSTER_TRAFFIC_VIA_INGRESS is set to true
and the node is Kubernetes operator ingress proxy configured via Ingress,
make sure that traffic from within the cluster can be proxied to the ingress target.
Updates tailscale/tailscale#10499
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Do not provision resources for a tailscale Ingress that has no valid backends.
Updates tailscale/tailscale#10910
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Also perform minor cleanups on the ctxkey package itself.
Provide guidance on when to use ctxkey.Key[T] over ctxkey.New.
Also, allow for interface kinds because the value wrapping trick
also happens to fix edge cases with interfaces in Go.
Updates #cleanup
Signed-off-by: Joe Tsai <joetsai@digital-static.net>
To reduce the likelihood of breaking users,
if we implement stricter Exact path type matching in the future.
Updates tailscale/tailscale#10730
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
So that users have predictable label values to use when configuring network policies.
Updates tailscale/tailscale#10854
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
* cmd/k8s-operator/deploy: deploy a Tailscale IngressClass resource.
Some Ingress validating webhooks reject Ingresses with
.spec.ingressClassName for which there is no matching IngressClass.
Additionally, validate that the expected IngressClass is present,
when parsing a tailscale `Ingress`.
We currently do not utilize the IngressClass,
however we might in the future at which point
we might start requiring that the right class
for this controller instance actually exists.
Updates tailscale/tailscale#10820
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Co-authored-by: Anton Tolchanov <anton@tailscale.com>
The configuration knob (that defaulted to Connector being disabled)
was added largely because the Connector CRD had to be installed in a separate step.
Now when the CRD has been added to both chart and static manifest, we can have it on by default.
Updates tailscale/tailscale#10878
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
cmd/k8s-operator: fix base truncating for extra long Service names
StatefulSet names for ingress/egress proxies are calculated
using Kubernetes name generator and the parent resource name
as a base.
The name generator also cuts the base, but has a higher max cap.
This commit fixes a bug where, if we get a shortened base back
from the generator, we cut off too little as the base that we
have cut will be passed into the generator again, which will
then itself cut less because the base is shorter- so we end up
with a too long name again.
Updates tailscale/tailscale#10807
Co-authored-by: Maisem Ali <maisem@tailscale.com>
Signed-off-by: Irbe Krumina <irbekrm@gmail.com>
cmd/k8s-operator: add CRD to chart and static manifest
Add functionality to insert CRD to chart at package time.
Insert CRD to static manifests as this is where they are currently consumed from.
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
cmd/k8s-operator/deploy/crds,k8s-operator/apis/v1alpha1: allow to define an exit node via Connector CR.
Make it possible to define an exit node to be deployed to a Kubernetes cluster
via Connector Custom resource.
Also changes to Connector API so that one Connector corresponds
to one Tailnet node that can be either a subnet router or an exit
node or both.
The Kubernetes operator parses Connector custom resource and,
if .spec.isExitNode is set, configures that Tailscale node deployed
for that connector as an exit node.
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Co-authored-by: Anton Tolchanov <anton@tailscale.com>
* k8s-operator,cmd/k8s-operator,Makefile,scripts,.github/workflows: add Connector kube CRD.
Connector CRD allows users to configure the Tailscale Kubernetes operator
to deploy a subnet router to expose cluster CIDRs or
other CIDRs available from within the cluster
to their tailnet.
Also adds various CRD related machinery to
generate CRD YAML, deep copy implementations etc.
Engineers will now have to run
'make kube-generate-all` after changing kube files
to ensure that all generated files are up to date.
* cmd/k8s-operator,k8s-operator: reconcile Connector resources
Reconcile Connector resources, create/delete subnetrouter resources in response to changes to Connector(s).
Connector reconciler will not be started unless
ENABLE_CONNECTOR env var is set to true.
This means that users who don't want to use the alpha
Connector custom resource don't have to install the Connector
CRD to their cluster.
For users who do want to use it the flow is:
- install the CRD
- install the operator (via Helm chart or using static manifests).
For Helm users set .values.enableConnector to true, for static
manifest users, set ENABLE_CONNECTOR to true in the static manifest.
Updates tailscale/tailscale#502
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
* cmd/k8s-operator: generate static manifests from Helm charts
This is done to ensure that there is a single source of truth
for the operator kube manifests.
Also adds linux node selector to the static manifests as
this was added as a default to the Helm chart.
Static manifests can now be generated by running
`go generate tailscale.com/cmd/k8s-operator`.
Updates tailscale/tailscale#9222
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
* cmd/containerboot: proxy traffic to tailnet target defined by FQDN
Add a new Service annotation tailscale.com/tailnet-fqdn that
users can use to specify a tailnet target for which
an egress proxy should be deployed in the cluster.
Updates tailscale/tailscale#10280
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Kubernetes can generate StatefulSet names that are too long and result in invalid Pod revision hash label values.
Calculate whether a StatefulSet name generated for a Service or Ingress
will be too long and if so, truncate it.
Updates tailscale/tailscale#10284
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Updates tailscale/tailscale#9222
plain k8s-operator should have hostinfo.App set to 'k8s-operator', operator with proxy should have it set to 'k8s-operator-proxy'. In proxy mode, we were setting the type after it had already been set to 'k8s-operator'
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
API v1 is compatible with helm v2 and v2 is not.
However, helm v2 (the Tiller deployment mechanism) was deprecated in 2020
and no-one should be using it anymore.
This PR also adds a CI lint test for helm chart
Updates tailscale/tailscale#9222
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
Users should delete proxies by deleting or modifying the k8s cluster resources
that they used to tell the operator to create they proxy. With this flow,
the tailscale operator will delete the associated device from the control.
However, in some cases users might have already deleted the device from the control manually.
Updates tailscale/tailscale#9773
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
* cmd/k8s-operator: users can configure operator to set firewall mode for proxies
Users can now pass PROXY_FIREWALL_MODE={nftables,auto,iptables} to operator to make it create ingress/egress proxies with that firewall mode
Also makes sure that if an invalid firewall mode gets configured, the operator will not start provisioning proxy resources, but will instead log an error and write an error event to the related Service.
Updates tailscale/tailscale#9310
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
We were too strict and required the user not specify the host field at all
in the ingress rules, but that degrades compatibility with existing helm charts.
Relax the constraint so that rule.Host can either be empty, or match the tls.Host[0]
value exactly.
Fixes#9548
Signed-off-by: Maisem Ali <maisem@tailscale.com>
Replace the deprecated var with the one in docs to avoid confusion.
Introduced in 335a5aaf9a.
Updates #8317Fixes#9764
Signed-off-by: Maisem Ali <maisem@tailscale.com>
Ensure that when there is an event on a Tailscale managed Ingress or Service child resource, the right parent type gets reconciled
Updates tailscale/tailscale#502
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
It was tailscale.com/ts-tailnet-target-ip, which was pretty
redundant. Change it to tailscale.com/tailnet-ip.
Updates #502
Signed-off-by: Maisem Ali <maisem@tailscale.com>
This adds a new RawMessage type backed by string instead of the
json.RawMessage which is backed by []byte. The byte slice makes
the generated views be a lot more defensive than the need to be
which we can get around by using a string instead.
Updates #cleanup
Signed-off-by: Maisem Ali <maisem@tailscale.com>
The kube-apiserver proxy in the operator would only run in
auth proxy mode but thats not always desirable. There are
situations where the proxy should just be a transparent
proxy and not inject auth headers, so do that using a new
env var APISERVER_PROXY and deprecate the AUTH_PROXY env.
THe new env var has three options `false`, `true` and `noauth`.
Updates #8317
Signed-off-by: Maisem Ali <maisem@tailscale.com>
Instead of confusing users, emit an event that explicitly tells the
user that HTTPS is disabled on the tailnet and that ingress may not
work until they enable it.
Updates #9141
Signed-off-by: Maisem Ali <maisem@tailscale.com>
Ensures that Statefulset reconciler config has only one of Cluster target IP or tailnet target IP.
Adds a test case for containerboot egress proxy mode.
Updates tailscale/tailscale#8184
Signed-off-by: irbekrm <irbekrm@gmail.com>
First part of work for the functionality that allows users to create an egress
proxy to access Tailnet services from within Kubernetes cluster workloads.
This PR allows creating an egress proxy that can access Tailscale services over HTTP only.
Updates tailscale/tailscale#8184
Signed-off-by: irbekrm <irbekrm@gmail.com>
Co-authored-by: Maisem Ali <maisem@tailscale.com>
Co-authored-by: Rhea Ghosh <rhea@tailscale.com>
We would call Update on the secret, but that was racey and would occasionaly
fail. Instead use patch whenever we can.
Fixes errors like
```
boot: 2023/08/29 01:03:53 failed to set serve config: sending serve config: updating config: writing ServeConfig to StateStore: Operation cannot be fulfilled on secrets "ts-webdav-kfrzv-0": the object has been modified; please apply your changes to the latest version and try again
{"level":"error","ts":"2023-08-29T01:03:48Z","msg":"Reconciler error","controller":"ingress","controllerGroup":"networking.k8s.io","controllerKind":"Ingress","Ingress":{"name":"webdav","namespace":"default"},"namespace":"default","name":"webdav","reconcileID":"96f5cfed-7782-4834-9b75-b0950fd563ed","error":"failed to provision: failed to create or get API key secret: Operation cannot be fulfilled on secrets \"ts-webdav-kfrzv-0\": the object has been modified; please apply your changes to the latest version and try again","stacktrace":"sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).reconcileHandler\n\tsigs.k8s.io/controller-runtime@v0.15.0/pkg/internal/controller/controller.go:324\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).processNextWorkItem\n\tsigs.k8s.io/controller-runtime@v0.15.0/pkg/internal/controller/controller.go:265\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).Start.func2.2\n\tsigs.k8s.io/controller-runtime@v0.15.0/pkg/internal/controller/controller.go:226"}
```
Updates #502
Updates #7895
Signed-off-by: Maisem Ali <maisem@tailscale.com>
This was added in 3451b89e5f, but
resulted in the v6 Tailscale address being added to status when
when the forwarding only happened on the v4 address.
Updates #502
Signed-off-by: Maisem Ali <maisem@tailscale.com>
The new ingress reconcile raises events on failure, but I forgot to
add the updated permission.
Updates #502
Signed-off-by: Maisem Ali <maisem@tailscale.com>
Previously, the operator would only monitor Services and create
a Tailscale StatefulSet which acted as a L3 proxy which proxied
traffic inbound to the Tailscale IP onto the services ClusterIP.
This extends that functionality to also monitor Ingress resources
where the `ingressClassName=tailscale` and similarly creates a
Tailscale StatefulSet, acting as a L7 proxy instead.
Users can override the desired hostname by setting:
```
- tls
hosts:
- "foo"
```
Hostnames specified under `rules` are ignored as we only create a single
host. This is emitted as an event for users to see.
Fixes#7895
Signed-off-by: Maisem Ali <maisem@tailscale.com>
Previously users would have to unexpose/expose the service in order to
change Hostname/TargetIP. This now applies those changes by causing a
StatefulSet rollout now that a61a9ab087 is in.
Updates #502
Signed-off-by: Maisem Ali <maisem@tailscale.com>
I'm not saying it works, but it compiles.
Updates #5794
Change-Id: I2f3c99732e67fe57a05edb25b758d083417f083e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
I forgot to move the defer out of the func, so the tsnet.Server
immediately closed after starting.
Updates #502
Signed-off-by: Maisem Ali <maisem@tailscale.com>
It was jumbled doing a lot of things, this breaks it up into
the svc reconciliation and the tailscale sts reconciliation.
Prep for future commit.
Updates #502
Signed-off-by: Maisem Ali <maisem@tailscale.com>
Previously we would use the Impersonate-Group header to pass through
tags to the k8s api server. However, we would do nothing for non-tagged
nodes. Now that we have a way to specify these via peerCaps respect those
and send down groups for non-tagged nodes as well.
For tagged nodes, it defaults to sending down the tags as groups to retain
legacy behavior if there are no caps set. Otherwise, the tags are omitted.
Updates #5055
Signed-off-by: Maisem Ali <maisem@tailscale.com>
The client/tailscale is a stable-ish API we try not to break. Revert
the Client.CreateKey method as it was and add a new
CreateKeyWithExpiry method to do the new thing. And document the
expiry field and enforce that the time.Duration can't be between in
range greater than 0 and less than a second.
Updates #7143
Updates #8124 (reverts it, effectively)
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Adds a parameter for create key that allows a number of seconds
(less than 90) to be specified for new keys.
Fixes https://github.com/tailscale/tailscale/issues/7965
Signed-off-by: Matthew Brown <matthew@bargrove.com>
getSingleObject can return `nil, nil`, getDeviceInfo was not handling
that case which resulted in panics.
Fixes#7303
Signed-off-by: Maisem Ali <maisem@tailscale.com>
This is a follow-up to #7905 that adds two more linters and fixes the corresponding findings. As per the previous PR, this only flags things that are "obviously" wrong, and fixes the issues found.
Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I8739bdb7bc4f75666a7385a7a26d56ec13741b7c
Kubernetes uses SPDY/3.1 which is incompatible with HTTP/2, disable it
in the transport and server.
Fixes#7645Fixes#7646
Signed-off-by: Maisem Ali <maisem@tailscale.com>
We were not handling tags at all, pass them through as Impersonate-Group headers.
And use the FQDN for tagged nodes as Impersonate-User.
Updates #5055
Signed-off-by: Maisem Ali <maisem@tailscale.com>
"Device Authorization" was recently renamed to "Device Approval"
on the control side. This change updates the k8s operator to match.
Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
This allows us to differentiate between the various tsnet apps that
we have like `golinks` and `k8s-operator`.
Signed-off-by: Maisem Ali <maisem@tailscale.com>
There is no stable release yet, and for alpha we want people on the
unstable build while we iterate.
Updates #502
Signed-off-by: David Anderson <danderson@tailscale.com>
This updates all source files to use a new standard header for copyright
and license declaration. Notably, copyright no longer includes a date,
and we now use the standard SPDX-License-Identifier header.
This commit was done almost entirely mechanically with perl, and then
some minimal manual fixes.
Updates #6865
Signed-off-by: Will Norris <will@tailscale.com>
The dependency injection functionality has been deprecated a while back
and it'll be removed in the 0.15 release of Controller Runtime. This
changeset sets the Client after creating the Manager, instead of using
InjectClient.
Signed-off-by: Vince Prignano <vince@prigna.com>
The operator creates a fair bit of internal cluster state to manage proxying,
dumping it all in the default namespace is handy for development but rude
for production.
Updates #502
Signed-off-by: David Anderson <danderson@tailscale.com>
We used to need to do timed requeues in a few places in the reconcile logic,
and the easiest way to do that was to plumb reconcile.Result return values
around. But now we're purely event-driven, so the only thing we care about
is whether or not an error occurred.
Incidentally also fix a very minor bug where headless services would get
completely ignored, rather than reconciled into the correct state. This
shouldn't matter in practice because you can't transition from a headful
to a headless service without a deletion, but for consistency let's avoid
having a path that takes no definite action if a service of interest does
exist.
Updates #502.
Signed-off-by: David Anderson <danderson@tailscale.com>
Previously, we had to do blind timed requeues while waiting for
the tailscale hostname, because we looked up the hostname through
the API. But now the proxy container image writes back its hostname
to the k8s secret, so we get an event-triggered reconcile automatically
when the time is right.
Updates #502
Signed-off-by: David Anderson <danderson@tailscale.com>
As is convention in the k8s world, use zap for structured logging. For
development, OPERATOR_LOGGING=dev switches to a more human-readable output
than JSON.
Updates #502
Signed-off-by: David Anderson <danderson@tailscale.com>
Our reconcile loop gets triggered again when the StatefulSet object
finally disappears (in addition to when its deletion starts, as indicated
by DeletionTimestamp != 0). So, we don't need to queue additional
reconciliations to proceed with the remainder of the cleanup, that
happens organically.
Signed-off-by: David Anderson <danderson@tailscale.com>
Tests cover configuring a proxy through an annotation rather than a
LoadBalancerClass, and converting between those two modes at runtime.
Updates #502.
Signed-off-by: David Anderson <danderson@tailscale.com>
For other test cases, the operator is going to produce similar generated
objects in several codepaths, and those objects are large. Move them out
to helpers so that the main test code stays a bit more intelligible.
The top-level Service that we start and end with remains in the main test
body, because its shape at the start and end is one of the main things that
varies a lot between test cases.
Updates #502.
Signed-off-by: David Anderson <danderson@tailscale.com>
The test verifies one of the successful reconcile paths, where
a client requests an exposed service via a LoadBalancer class.
Updates #502.
Signed-off-by: David Anderson <danderson@tailscale.com>
Also introduces an intermediary interface for the tailscale client, in
preparation for operator tests that fake out the Tailscale API interaction.
Updates #502.
Signed-off-by: David Anderson <danderson@tailscale.com>
This was initially developed in a separate repo, but for build/release
reasons and because go module management limits the damage of importing
k8s things now, moving it into this repo.
At time of commit, the operator enables exposing services over tailscale,
with the 'tailscale' loadBalancerClass. It also currently requires an
unreleased feature to access the Tailscale API, so is not usable yet.
Updates #502.
Signed-off-by: David Anderson <danderson@tailscale.com>