Skip to content

Commit

Permalink
Merge branch 'tailscale:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Asutorufa authored Nov 16, 2023
2 parents 58114a5 + 2ff54f9 commit 07fd542
Show file tree
Hide file tree
Showing 151 changed files with 7,693 additions and 1,976 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/checklocks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: [ ubuntu-latest ]
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Build checklocks
run: ./tool/go build -o /tmp/checklocks gvisor.dev/gvisor/tools/checklocks/cmd/checklocks
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/kubemanifests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: "Kubernetes manifests"
on:
pull_request:
paths:
- './cmd/k8s-operator/'
- '.github/workflows/kubemanifests.yaml'

# Cancel workflow run if there is a newer push to the same PR for which it is
# running
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
testchart:
runs-on: [ ubuntu-latest ]
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Build and lint Helm chart
run: |
eval `./tool/go run ./cmd/mkversion`
./tool/helm package --app-version="${VERSION_SHORT}" --version=${VERSION_SHORT} './cmd/k8s-operator/deploy/chart'
./tool/helm lint "tailscale-operator-${VERSION_SHORT}.tgz"
51 changes: 51 additions & 0 deletions .github/workflows/update-webclient-prebuilt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: update-webclient-prebuilt

on:
# manually triggered
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
update-webclient-prebuilt:
runs-on: ubuntu-latest

steps:
- name: Check out code
uses: actions/checkout@v4

- name: Run go get
run: |
GOPROXY=direct ./tool/go get github.com/tailscale/web-client-prebuilt
./tool/go mod tidy
- name: Get access token
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 # v1.8.0
id: generate-token
with:
# TODO(will): this should use the code updater app rather than licensing.
# It has the same permissions, so not a big deal, but still.
app_id: ${{ secrets.LICENSING_APP_ID }}
installation_id: ${{ secrets.LICENSING_APP_INSTALLATION_ID }}
private_key: ${{ secrets.LICENSING_APP_PRIVATE_KEY }}

- name: Send pull request
id: pull-request
uses: peter-evans/create-pull-request@284f54f989303d2699d373481a0cfa13ad5a6666 #v5.0.1
with:
token: ${{ steps.generate-token.outputs.token }}
author: OSS Updater <noreply+oss-updater@tailscale.com>
committer: OSS Updater <noreply+oss-updater@tailscale.com>
branch: actions/update-webclient-prebuilt
commit-message: "go.mod: update web-client-prebuilt module"
title: "go.mod: update web-client-prebuilt module"
body: Triggered by ${{ github.repository }}@${{ github.sha }}
signoff: true
delete-branch: true
reviewers: ${{ github.triggering_actor }}

- name: Summary
if: ${{ steps.pull-request.outputs.pull-request-number }}
run: echo "${{ steps.pull-request.outputs.pull-request-operation}} ${{ steps.pull-request.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.53.0
1.55.0
221 changes: 221 additions & 0 deletions appc/appconnector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

// Package appc implements App Connectors.
// An AppConnector provides DNS domain oriented routing of traffic. An App
// Connector becomes a DNS server for a peer, authoritative for the set of
// configured domains. DNS resolution of the target domain triggers dynamic
// publication of routes to ensure that traffic to the domain is routed through
// the App Connector.
package appc

import (
"net/netip"
"slices"
"strings"
"sync"

xmaps "golang.org/x/exp/maps"
"golang.org/x/net/dns/dnsmessage"
"tailscale.com/types/logger"
"tailscale.com/types/views"
"tailscale.com/util/dnsname"
)

// RouteAdvertiser is an interface that allows the AppConnector to advertise
// newly discovered routes that need to be served through the AppConnector.
type RouteAdvertiser interface {
// AdvertiseRoute adds a new route advertisement if the route is not already
// being advertised.
AdvertiseRoute(netip.Prefix) error
}

// AppConnector is an implementation of an AppConnector that performs
// its function as a subsystem inside of a tailscale node. At the control plane
// side App Connector routing is configured in terms of domains rather than IP
// addresses.
// The AppConnectors responsibility inside tailscaled is to apply the routing
// and domain configuration as supplied in the map response.
// DNS requests for configured domains are observed. If the domains resolve to
// routes not yet served by the AppConnector the local node configuration is
// updated to advertise the new route.
type AppConnector struct {
logf logger.Logf
routeAdvertiser RouteAdvertiser

// mu guards the fields that follow
mu sync.Mutex
// domains is a map of lower case domain names with no trailing dot, to a
// list of resolved IP addresses.
domains map[string][]netip.Addr

// wildcards is the list of domain strings that match subdomains.
wildcards []string
}

// NewAppConnector creates a new AppConnector.
func NewAppConnector(logf logger.Logf, routeAdvertiser RouteAdvertiser) *AppConnector {
return &AppConnector{
logf: logger.WithPrefix(logf, "appc: "),
routeAdvertiser: routeAdvertiser,
}
}

// UpdateDomains replaces the current set of configured domains with the
// supplied set of domains. Domains must not contain a trailing dot, and should
// be lower case. If the domain contains a leading '*' label it matches all
// subdomains of a domain.
func (e *AppConnector) UpdateDomains(domains []string) {
e.mu.Lock()
defer e.mu.Unlock()

var oldDomains map[string][]netip.Addr
oldDomains, e.domains = e.domains, make(map[string][]netip.Addr, len(domains))
e.wildcards = e.wildcards[:0]
for _, d := range domains {
d = strings.ToLower(d)
if len(d) == 0 {
continue
}
if strings.HasPrefix(d, "*.") {
e.wildcards = append(e.wildcards, d[2:])
continue
}
e.domains[d] = oldDomains[d]
delete(oldDomains, d)
}

// Ensure that still-live wildcards addresses are preserved as well.
for d, addrs := range oldDomains {
for _, wc := range e.wildcards {
if dnsname.HasSuffix(d, wc) {
e.domains[d] = addrs
break
}
}
}
e.logf("handling domains: %v and wildcards: %v", xmaps.Keys(e.domains), e.wildcards)
}

// Domains returns the currently configured domain list.
func (e *AppConnector) Domains() views.Slice[string] {
e.mu.Lock()
defer e.mu.Unlock()

return views.SliceOf(xmaps.Keys(e.domains))
}

// DomainRoutes returns a map of domains to resolved IP
// addresses.
func (e *AppConnector) DomainRoutes() map[string][]netip.Addr {
e.mu.Lock()
defer e.mu.Unlock()

drCopy := make(map[string][]netip.Addr)
for k, v := range e.domains {
drCopy[k] = append(drCopy[k], v...)
}

return drCopy
}

// ObserveDNSResponse is a callback invoked by the DNS resolver when a DNS
// response is being returned over the PeerAPI. The response is parsed and
// matched against the configured domains, if matched the routeAdvertiser is
// advised to advertise the discovered route.
func (e *AppConnector) ObserveDNSResponse(res []byte) {
var p dnsmessage.Parser
if _, err := p.Start(res); err != nil {
return
}
if err := p.SkipAllQuestions(); err != nil {
return
}

for {
h, err := p.AnswerHeader()
if err == dnsmessage.ErrSectionDone {
break
}
if err != nil {
return
}

if h.Class != dnsmessage.ClassINET {
if err := p.SkipAnswer(); err != nil {
return
}
continue
}
if h.Type != dnsmessage.TypeA && h.Type != dnsmessage.TypeAAAA {
if err := p.SkipAnswer(); err != nil {
return
}
continue
}

domain := h.Name.String()
if len(domain) == 0 {
return
}
domain = strings.TrimSuffix(domain, ".")
domain = strings.ToLower(domain)
e.logf("[v2] observed DNS response for %s", domain)

e.mu.Lock()
addrs, ok := e.domains[domain]
// match wildcard domains
if !ok {
for _, wc := range e.wildcards {
if dnsname.HasSuffix(domain, wc) {
e.domains[domain] = nil
ok = true
break
}
}
}
e.mu.Unlock()

if !ok {
if err := p.SkipAnswer(); err != nil {
return
}
continue
}

var addr netip.Addr
switch h.Type {
case dnsmessage.TypeA:
r, err := p.AResource()
if err != nil {
return
}
addr = netip.AddrFrom4(r.A)
case dnsmessage.TypeAAAA:
r, err := p.AAAAResource()
if err != nil {
return
}
addr = netip.AddrFrom16(r.AAAA)
default:
if err := p.SkipAnswer(); err != nil {
return
}
continue
}
if slices.Contains(addrs, addr) {
continue
}
// TODO(raggi): check for existing prefixes
if err := e.routeAdvertiser.AdvertiseRoute(netip.PrefixFrom(addr, addr.BitLen())); err != nil {
e.logf("failed to advertise route for %v: %v", addr, err)
continue
}
e.logf("[v2] advertised route for %v: %v", domain, addr)

e.mu.Lock()
e.domains[domain] = append(addrs, addr)
e.mu.Unlock()
}

}
Loading

0 comments on commit 07fd542

Please sign in to comment.