mirror of https://github.com/tailscale/tailscale/
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
3.5 KiB
Go
104 lines
3.5 KiB
Go
1 month ago
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
// Package egressservices contains shared types for exposing tailnet services to
|
||
|
// cluster workloads.
|
||
|
// These are split into a separate package for consumption of
|
||
|
// non-Kubernetes shared libraries and binaries. Be mindful of not increasing
|
||
|
// dependency size for those consumers when adding anything new here.
|
||
|
package egressservices
|
||
|
|
||
|
import (
|
||
|
"encoding"
|
||
|
"fmt"
|
||
|
"net/netip"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// KeyEgressServices is name of the proxy state Secret field that contains the
|
||
|
// currently applied egress proxy config.
|
||
|
const KeyEgressServices = "egress-services"
|
||
|
|
||
|
// Configs contains the desired configuration for egress services keyed by
|
||
|
// service name.
|
||
|
type Configs map[string]Config
|
||
|
|
||
|
// Config is an egress service configuration.
|
||
|
// TODO(irbekrm): version this?
|
||
|
type Config struct {
|
||
|
// TailnetTarget is the target to which cluster traffic for this service
|
||
|
// should be proxied.
|
||
|
TailnetTarget TailnetTarget `json:"tailnetTarget"`
|
||
|
// Ports contains mappings for ports that can be accessed on the tailnet target.
|
||
|
Ports map[PortMap]struct{} `json:"ports"`
|
||
|
}
|
||
|
|
||
|
// TailnetTarget is the tailnet target to which traffic for the egress service
|
||
|
// should be proxied. Exactly one of IP or FQDN should be set.
|
||
|
type TailnetTarget struct {
|
||
|
// IP is the tailnet IP of the target.
|
||
|
IP string `json:"ip"`
|
||
|
// FQDN is the full tailnet FQDN of the target.
|
||
|
FQDN string `json:"fqdn"`
|
||
|
}
|
||
|
|
||
|
// PorMap is a mapping between match port on which proxy receives cluster
|
||
|
// traffic and target port where traffic received on match port should be
|
||
|
// fowardded to.
|
||
|
type PortMap struct {
|
||
|
Protocol string `json:"protocol"`
|
||
|
MatchPort uint16 `json:"matchPort"`
|
||
|
TargetPort uint16 `json:"targetPort"`
|
||
|
}
|
||
|
|
||
|
// PortMap is used as a Config.Ports map key. Config needs to be serialized/deserialized to/from JSON. JSON only
|
||
|
// supports string map keys, so we need to implement TextMarshaler/TextUnmarshaler to convert PortMap to string and
|
||
|
// back.
|
||
|
var _ encoding.TextMarshaler = PortMap{}
|
||
|
var _ encoding.TextUnmarshaler = &PortMap{}
|
||
|
|
||
|
func (pm *PortMap) UnmarshalText(t []byte) error {
|
||
|
tt := string(t)
|
||
|
ss := strings.Split(tt, ":")
|
||
|
if len(ss) != 3 {
|
||
|
return fmt.Errorf("error unmarshalling portmap from JSON, wants a portmap in form <protocol>:<matchPort>:<targetPor>, got %q", tt)
|
||
|
}
|
||
|
(*pm).Protocol = ss[0]
|
||
|
matchPort, err := strconv.ParseUint(ss[1], 10, 16)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error converting match port %q to uint16: %w", ss[1], err)
|
||
|
}
|
||
|
(*pm).MatchPort = uint16(matchPort)
|
||
|
targetPort, err := strconv.ParseUint(ss[2], 10, 16)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error converting target port %q to uint16: %w", ss[2], err)
|
||
|
}
|
||
|
(*pm).TargetPort = uint16(targetPort)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (pm PortMap) MarshalText() ([]byte, error) {
|
||
|
s := fmt.Sprintf("%s:%d:%d", pm.Protocol, pm.MatchPort, pm.TargetPort)
|
||
|
return []byte(s), nil
|
||
|
}
|
||
|
|
||
|
// Status represents the currently configured firewall rules for all egress
|
||
|
// services for a proxy identified by the PodIP.
|
||
|
type Status struct {
|
||
|
PodIP string `json:"podIP"`
|
||
|
// All egress service status keyed by service name.
|
||
|
Services map[string]*ServiceStatus `json:"services"`
|
||
|
}
|
||
|
|
||
|
// ServiceStatus is the currently configured firewall rules for an egress
|
||
|
// service.
|
||
|
type ServiceStatus struct {
|
||
|
Ports map[PortMap]struct{} `json:"ports"`
|
||
|
// TailnetTargetIPs are the tailnet target IPs that were used to
|
||
|
// configure these firewall rules. For a TailnetTarget with IP set, this
|
||
|
// is the same as IP.
|
||
|
TailnetTargetIPs []netip.Addr `json:"tailnetTargetIPs"`
|
||
|
TailnetTarget TailnetTarget `json:"tailnetTarget"`
|
||
|
}
|