├── cmd ├── teleroute │ ├── .gitignore │ ├── Dockerfile │ ├── config.json │ ├── DEVELOPING.md │ └── go.mod ├── telepresence │ └── main.go ├── traffic │ └── cmd │ │ ├── manager │ │ ├── test │ │ │ └── testdata │ │ │ │ ├── mechanisms.yaml │ │ │ │ └── clients.yaml │ │ ├── state │ │ │ └── data.go │ │ ├── cluster │ │ │ └── id.go │ │ ├── managerutil │ │ │ └── ctx.go │ │ └── health.go │ │ ├── agentinit │ │ └── agent_init_windows.go │ │ └── agent │ │ └── fwd │ │ └── udp_test.go └── cobraparser │ ├── go.mod │ ├── README.md │ └── main.go ├── pkg ├── vif │ ├── testdata │ │ └── router │ │ │ └── .gitignore │ ├── device_unix.go │ └── logging.go ├── client │ ├── cli │ │ ├── cmd │ │ │ ├── testdata │ │ │ │ ├── zipDir │ │ │ │ │ ├── file1.log │ │ │ │ │ ├── file2.log │ │ │ │ │ └── diff_name.log │ │ │ │ ├── testLogDir │ │ │ │ │ ├── cli.log │ │ │ │ │ ├── connector-20210916T130356.log │ │ │ │ │ ├── echo-auto-inject-6496f77cbd-n86nc │ │ │ │ │ └── traffic-manager-5c69859f94-g4ntj │ │ │ │ └── license │ │ │ ├── compose.go │ │ │ ├── mcp_test.go │ │ │ ├── intercept.go │ │ │ ├── replace.go │ │ │ ├── wiretap.go │ │ │ ├── ingest.go │ │ │ ├── curl.go │ │ │ ├── quit.go │ │ │ └── list_namespaces.go │ │ ├── flags │ │ │ ├── zerovalue.go │ │ │ ├── context.go │ │ │ ├── addunique.go │ │ │ ├── map.go │ │ │ └── deprecation.go │ │ ├── ann │ │ │ └── annotations.go │ │ ├── mount │ │ │ ├── prepare_unix.go │ │ │ └── prepare_windows.go │ │ ├── env │ │ │ └── flags.go │ │ └── progress │ │ │ ├── quiet.go │ │ │ └── noop.go │ ├── config_darwin.go │ ├── config_linux.go │ ├── logging │ │ ├── initcontext_windows_test.go │ │ ├── stat_linux_test.go │ │ ├── initcontext_unix_test.go │ │ ├── rotatingfile_unix.go │ │ ├── dup_windows.go │ │ ├── dup_unix.go │ │ ├── stat.go │ │ └── rotatingfile_windows.go │ ├── envconfig_unix.go │ ├── envconfig_windows.go │ ├── remotefs │ │ ├── fuseftp_external.go │ │ ├── fuseftp.go │ │ ├── mounter.go │ │ ├── fuseftp_docker.go │ │ └── fuseftp_embedded.go │ ├── docker │ │ ├── teleroute │ │ │ ├── server.go │ │ │ └── server_other.go │ │ ├── volume_test.go │ │ └── daemon_test.go │ ├── userd │ │ ├── trafficmgr │ │ │ ├── context.go │ │ │ └── config.go │ │ └── service.go │ ├── config_unix.go │ ├── cmd_error.go │ ├── k8s │ │ └── cani.go │ ├── bwcompat │ │ └── clusterinfo.go │ ├── config_util.go │ ├── install_id.go │ ├── stream_error.go │ └── ensured_state.go ├── k8sapi │ ├── clientconfigprovider.go │ ├── namespaceid.go │ ├── interface.go │ └── kind.go ├── proc │ ├── wsl_other.go │ ├── docker_other.go │ ├── wsl_linux.go │ └── docker_linux.go ├── tunnel │ ├── synthetic.go │ ├── probe.go │ ├── server_stream.go │ └── provider.go ├── slice │ ├── strings.go │ ├── csv.go │ └── contains.go ├── dpipe │ ├── dpipe_windows.go │ ├── dpipe_unix.go │ └── testdata │ │ └── echo │ │ └── echo.go ├── pprof │ └── pprof.go ├── authenticator │ ├── config.go │ ├── exec.go │ └── exec_test.go ├── ioutil │ ├── tempname.go │ ├── writeallto.go │ ├── safename.go │ └── free_ports.go ├── iputil │ ├── normalize.go │ ├── join.go │ └── parse.go ├── dos │ ├── package.go │ └── exe.go ├── log │ ├── base_logger.go │ └── discard.go ├── filelocation │ ├── osfile_darwin.go │ ├── osfile_linux.go │ └── osfile_windows.go ├── maps │ └── xmap.go ├── agentmap │ ├── capsbase26.go │ └── capsbase26_test.go ├── workload │ └── util.go ├── grpc │ ├── client │ │ └── connect.go │ └── server │ │ └── context.go ├── routing │ └── routing_test.go ├── informer │ └── factory.go ├── matcher │ └── header_stringer.go └── dnsproxy │ └── rpc.go ├── docs ├── redirects.yml ├── variables.yml ├── images │ ├── logo.png │ ├── bugfix.png │ ├── change.png │ ├── feature.png │ ├── security.png │ ├── tracing.png │ ├── vpn-dns.png │ ├── vpn-vnat.jpg │ ├── tunnelblick.png │ ├── vpn-routing.jpg │ ├── split-tunnel.png │ ├── vpn-k8s-config.jpg │ ├── vpn-with-tele.jpg │ ├── trad-inner-dev-loop.png │ ├── container-inner-dev-loop.png │ ├── docker-header-containers.png │ ├── secondary-no-intercept.png │ ├── secondary-normal-intercept.png │ └── secondary-container-intercept.png ├── common │ └── quantity.md ├── licenses.md ├── community.md ├── reference │ └── cli │ │ ├── telepresence_version.md │ │ ├── telepresence_curl.md │ │ ├── telepresence_docker-run.md │ │ ├── telepresence_helm_version.md │ │ ├── telepresence_leave.md │ │ ├── telepresence_quit.md │ │ ├── telepresence_serve.md │ │ ├── telepresence_config_view.md │ │ ├── telepresence_uninstall.md │ │ ├── telepresence_status.md │ │ ├── telepresence_mcp_start.md │ │ ├── telepresence_mcp_tools.md │ │ ├── telepresence_mcp_claude_list.md │ │ ├── telepresence_mcp_cursor_list.md │ │ ├── telepresence_mcp_vscode_list.md │ │ ├── telepresence_config.md │ │ ├── telepresence_mcp_stream.md │ │ └── telepresence_mcp_claude_disable.md └── CONTRIBUTING.md ├── integration_test ├── testdata │ ├── udp-echo │ │ ├── go.mod │ │ └── Dockerfile │ ├── cli-container │ │ └── Dockerfile │ ├── k8s │ │ ├── client_sa.yaml │ │ ├── echo-manual-inject-svc.yaml │ │ ├── memory-constraints.yaml │ │ ├── disruption-budget.goyaml │ │ ├── pvc.goyaml │ │ ├── secret-reader-rbac.goyaml │ │ ├── echo-manual-inject-deploy.yaml │ │ ├── client_rancher.goyaml │ │ ├── echo-no-svc.yaml │ │ ├── echo-no-containerport.yaml │ │ ├── pv.goyaml │ │ ├── pol-none.yaml │ │ ├── echo-one.yaml │ │ ├── client_experiment.yaml │ │ ├── echo-easy.yaml │ │ ├── rs-echo.yaml │ │ ├── ss-echo.yaml │ │ ├── echo-no-svc-ann.yaml │ │ ├── echo-same-target-port.yaml │ │ ├── echo-w-subdomain.yaml │ │ ├── namespaces.yaml │ │ ├── echo-headless.yaml │ │ ├── pol-enabled.yaml │ │ ├── echo-double-one-unnamed.yaml │ │ ├── pol-disabled.yaml │ │ ├── echo-auto-inject.yaml │ │ ├── echo-w-hostalias.goyaml │ │ ├── echo-min.yaml │ │ └── echo-no-vols.yaml │ ├── telepresence-1.9.9.tgz │ ├── echo-server │ │ ├── go.mod │ │ ├── go.sum │ │ └── Dockerfile │ ├── routing-values.yaml │ ├── scripts │ │ ├── veth-down.sh │ │ └── veth-up.sh │ └── count-server │ │ └── main.go ├── itest │ ├── rm_as_root_unix.go │ ├── timed.go │ ├── rm_as_root_windows.go │ ├── logdir.go │ ├── assertions.go │ ├── single_service.go │ ├── suite.go │ └── tempdir.go ├── integration_test.go ├── single_service_test.go ├── config_test.go ├── svcdomain_test.go ├── list_watch_test.go ├── loglevel_test.go └── subdomain_test.go ├── .gitattributes ├── packaging ├── sidebar.png ├── artifacthub-repo.yml ├── bundle.wxs.in ├── helmpackage.go └── install-telepresence.ps1 ├── test-infra ├── aws-okd │ └── dns │ │ └── .gitignore └── aws-vpn │ ├── .gitignore │ └── provider.tf ├── CODEOWNERS ├── tools └── src │ ├── gosimports │ ├── pin.go │ └── go.mod │ ├── protoc-gen-go │ ├── pin.go │ ├── go.mod │ └── go.sum │ ├── protoc-gen-go-grpc │ ├── pin.go │ └── go.mod │ ├── go-mkopensource │ └── pin.go │ ├── y2j │ ├── go.mod │ └── main.go │ ├── relnotesgen │ ├── go.mod │ ├── main.go │ └── relnotes │ │ ├── relnotes.gomdx │ │ └── relnotes.gomd │ └── test-report │ └── go.mod ├── k8s ├── ext-example.yaml ├── rs-echo-svc2.yaml ├── agent-injector-rbac.yaml ├── private-reg-proxy.yaml ├── local-echo-easy.yaml ├── local-echo-next.yaml ├── dnsutils-headless.yaml ├── echo-sc.yaml ├── manager.yaml ├── apitest.yaml └── echo-auto-headless.yaml ├── .dockerignore ├── .protolint.yaml ├── examples ├── compose │ ├── proxy-voting.override.yaml │ ├── proxy-web.override.yaml │ └── replace.override.yaml └── docker │ └── iam-authenticator │ └── Dockerfile ├── CODE-OF-CONDUCT.md ├── charts └── telepresence-oss │ ├── templates │ ├── issuer.yaml │ ├── trafficManagerRbac │ │ ├── service-account.yaml │ │ └── webhook-secret.yaml │ ├── certificate.yaml │ ├── trafficManager-configmap.yaml │ ├── tests │ │ └── test-connection.yaml │ ├── NOTES.txt │ └── clientRbac │ │ └── cluster-scope.yaml │ ├── .helmignore │ └── Chart.yaml ├── rpc ├── go.mod ├── authenticator │ └── authenticator.proto ├── common │ └── version.proto └── agent │ └── agent.proto ├── .editorconfig ├── DEPENDENCY_LICENSES.md ├── MEETING_SCHEDULE.md ├── .github ├── pull_request_template.md ├── workflows │ ├── go-dependency-submission.yaml │ ├── release-teleroute.yaml │ └── stale.yaml ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── Feature_request.md └── actions │ └── upload-logs │ └── action.yaml ├── .gitignore ├── SECURITY.md └── .mailmap /cmd/teleroute/.gitignore: -------------------------------------------------------------------------------- 1 | rpc/ -------------------------------------------------------------------------------- /pkg/vif/testdata/router/.gitignore: -------------------------------------------------------------------------------- 1 | router 2 | -------------------------------------------------------------------------------- /docs/redirects.yml: -------------------------------------------------------------------------------- 1 | - {from: "", to: "quick-start"} 2 | -------------------------------------------------------------------------------- /docs/variables.yml: -------------------------------------------------------------------------------- 1 | version: "2.26.0" 2 | dlVersion: "v2.26.0" 3 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/zipDir/file1.log: -------------------------------------------------------------------------------- 1 | logs from file1.log 2 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/zipDir/file2.log: -------------------------------------------------------------------------------- 1 | logs from file2.log 2 | -------------------------------------------------------------------------------- /integration_test/testdata/udp-echo/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/zipDir/diff_name.log: -------------------------------------------------------------------------------- 1 | logs from diff_name.log 2 | -------------------------------------------------------------------------------- /pkg/client/config_darwin.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | const defaultAddHostGateway = false 4 | -------------------------------------------------------------------------------- /pkg/client/config_linux.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | const defaultAddHostGateway = true 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Always check out with LF so that golangci-lint doesn't break 2 | *.go text eol=lf -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/bugfix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/bugfix.png -------------------------------------------------------------------------------- /docs/images/change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/change.png -------------------------------------------------------------------------------- /packaging/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/packaging/sidebar.png -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/cli.log: -------------------------------------------------------------------------------- 1 | 2022-02-12 15:02:29.6927 info Logging at this level "debug" -------------------------------------------------------------------------------- /test-infra/aws-okd/dns/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | .terraform* 3 | terraform.tfvars 4 | terraform.tfstate 5 | -------------------------------------------------------------------------------- /docs/images/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/feature.png -------------------------------------------------------------------------------- /docs/images/security.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/security.png -------------------------------------------------------------------------------- /docs/images/tracing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/tracing.png -------------------------------------------------------------------------------- /docs/images/vpn-dns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/vpn-dns.png -------------------------------------------------------------------------------- /docs/images/vpn-vnat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/vpn-vnat.jpg -------------------------------------------------------------------------------- /test-infra/aws-vpn/.gitignore: -------------------------------------------------------------------------------- 1 | certs/* 2 | *.tfstate 3 | *.backup 4 | *.tfvars 5 | .terraform 6 | .terraform.* 7 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default codeowners for the telepresenceio/telepresence repository 2 | * @telepresenceio/telepresence 3 | -------------------------------------------------------------------------------- /docs/images/tunnelblick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/tunnelblick.png -------------------------------------------------------------------------------- /docs/images/vpn-routing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/vpn-routing.jpg -------------------------------------------------------------------------------- /docs/images/split-tunnel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/split-tunnel.png -------------------------------------------------------------------------------- /docs/images/vpn-k8s-config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/vpn-k8s-config.jpg -------------------------------------------------------------------------------- /docs/images/vpn-with-tele.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/vpn-with-tele.jpg -------------------------------------------------------------------------------- /docs/images/trad-inner-dev-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/trad-inner-dev-loop.png -------------------------------------------------------------------------------- /docs/images/container-inner-dev-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/container-inner-dev-loop.png -------------------------------------------------------------------------------- /docs/images/docker-header-containers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/docker-header-containers.png -------------------------------------------------------------------------------- /docs/images/secondary-no-intercept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/secondary-no-intercept.png -------------------------------------------------------------------------------- /integration_test/testdata/cli-container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM telepresence 2 | 3 | RUN apk add --no-cache bash curl 4 | 5 | ENTRYPOINT ["/bin/bash"] -------------------------------------------------------------------------------- /integration_test/testdata/k8s/client_sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: telepresence-test-developer 5 | -------------------------------------------------------------------------------- /pkg/client/logging/initcontext_windows_test.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | func dupStd() (func(), error) { 4 | return func() {}, nil 5 | } 6 | -------------------------------------------------------------------------------- /docs/images/secondary-normal-intercept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/secondary-normal-intercept.png -------------------------------------------------------------------------------- /docs/images/secondary-container-intercept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/docs/images/secondary-container-intercept.png -------------------------------------------------------------------------------- /tools/src/gosimports/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "github.com/rinchsan/gosimports/cmd/gosimports" 7 | -------------------------------------------------------------------------------- /packaging/artifacthub-repo.yml: -------------------------------------------------------------------------------- 1 | repositoryID: 37605942-af09-4135-bd75-5241c570ad76 2 | owners: 3 | - name: Thomas Hallgren 4 | email: thomas@tada.se 5 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "google.golang.org/protobuf/cmd/protoc-gen-go" 7 | -------------------------------------------------------------------------------- /integration_test/testdata/telepresence-1.9.9.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/HEAD/integration_test/testdata/telepresence-1.9.9.tgz -------------------------------------------------------------------------------- /tools/src/protoc-gen-go-grpc/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "google.golang.org/grpc/cmd/protoc-gen-go-grpc" 7 | -------------------------------------------------------------------------------- /integration_test/testdata/echo-server/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | golang.org/x/sync v0.18.0 7 | golang.org/x/sys v0.38.0 8 | ) 9 | -------------------------------------------------------------------------------- /k8s/ext-example.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: ext-example 6 | spec: 7 | type: ExternalName 8 | externalName: example.com 9 | -------------------------------------------------------------------------------- /tools/src/go-mkopensource/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "github.com/telepresenceio/go-mkopensource/cmd/go-mkopensource" 7 | -------------------------------------------------------------------------------- /pkg/client/envconfig_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package client 4 | 5 | type OSSpecificEnv struct { 6 | Shell string `env:"SHELL, parser=nonempty-string,default=/bin/bash"` 7 | } 8 | -------------------------------------------------------------------------------- /pkg/client/envconfig_windows.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | type OSSpecificEnv struct { 4 | Shell string `env:"ComSpec, parser=nonempty-string,default=C:\\WINDOWS\\system32\\cmd.exe"` 5 | } 6 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/license: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 2 | -------------------------------------------------------------------------------- /tools/src/y2j/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/tools/src/y2j 2 | 3 | go 1.24 4 | 5 | require sigs.k8s.io/yaml v1.6.0 6 | 7 | require go.yaml.in/yaml/v2 v2.4.3 // indirect 8 | -------------------------------------------------------------------------------- /pkg/k8sapi/clientconfigprovider.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import "k8s.io/client-go/tools/clientcmd" 4 | 5 | type ClientConfigProvider interface { 6 | ClientConfig() (clientcmd.ClientConfig, error) 7 | } 8 | -------------------------------------------------------------------------------- /pkg/proc/wsl_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package proc 4 | 5 | // RunningInWSL returns true if the current process is a Linux running under WSL. 6 | func RunningInWSL() bool { 7 | return false 8 | } 9 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.23 4 | 5 | toolchain go1.24.4 6 | 7 | require google.golang.org/protobuf v1.36.10 8 | 9 | require github.com/google/go-cmp v0.7.0 // indirect 10 | -------------------------------------------------------------------------------- /pkg/tunnel/synthetic.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "net/netip" 5 | ) 6 | 7 | type SyntheticIPResolver interface { 8 | Resolve(netip.Addr) (netip.Addr, error) 9 | ResolveName(netip.Addr) string 10 | } 11 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .circleci/ 2 | .idea/ 3 | .git/ 4 | build-aux/ 5 | build-output/ 6 | !build-output/version.txt 7 | !build-output/helm-version.txt 8 | integration_test/ 9 | k8s/ 10 | packaging/ 11 | test-infra/ 12 | tools/ 13 | -------------------------------------------------------------------------------- /.protolint.yaml: -------------------------------------------------------------------------------- 1 | lint: 2 | # Which rules to enable/disable. 3 | rules: 4 | remove: 5 | - ENUM_FIELD_NAMES_PREFIX 6 | # Settings for those rules. 7 | rules_option: 8 | max_line_length: 9 | max_chars: 120 10 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go-grpc/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.23 4 | 5 | toolchain go1.24.4 6 | 7 | require google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 8 | 9 | require google.golang.org/protobuf v1.36.10 // indirect 10 | -------------------------------------------------------------------------------- /examples/compose/proxy-voting.override.yaml: -------------------------------------------------------------------------------- 1 | x-tele: 2 | connections: 3 | - namespace: emojivoto 4 | mounts: 5 | - volumePattern: .* 6 | policy: local 7 | services: 8 | voting: 9 | x-tele: 10 | type: proxy 11 | -------------------------------------------------------------------------------- /pkg/slice/strings.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | import "fmt" 4 | 5 | func AsStrings[T fmt.Stringer](vs []T) []string { 6 | ss := make([]string, len(vs)) 7 | for i, v := range vs { 8 | ss[i] = v.String() 9 | } 10 | return ss 11 | } 12 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-manual-inject-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: "manual-inject" 5 | spec: 6 | type: ClusterIP 7 | selector: 8 | app: manual-inject 9 | ports: 10 | - port: 80 11 | -------------------------------------------------------------------------------- /test-infra/aws-vpn/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws", 5 | version = "~> 3.20" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | -------------------------------------------------------------------------------- /cmd/telepresence/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli" 8 | ) 9 | 10 | func main() { 11 | cli.Main(cli.InitContext(context.Background()), os.Args[1:]) 12 | } 13 | -------------------------------------------------------------------------------- /examples/compose/proxy-web.override.yaml: -------------------------------------------------------------------------------- 1 | x-tele: 2 | connections: 3 | - namespace: emojivoto 4 | mounts: 5 | - volumePattern: .* 6 | policy: local 7 | services: 8 | web: 9 | x-tele: 10 | type: proxy 11 | ports: 12 | - 8080:80 -------------------------------------------------------------------------------- /integration_test/testdata/k8s/memory-constraints.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: LimitRange 3 | metadata: 4 | name: mem-min-max-test 5 | spec: 6 | limits: 7 | - max: 8 | memory: 100Mi 9 | min: 10 | memory: 20Mi 11 | type: Container 12 | 13 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/tools/src/relnotesgen 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/blang/semver/v4 v4.0.0 7 | sigs.k8s.io/yaml v1.6.0 8 | ) 9 | 10 | require go.yaml.in/yaml/v2 v2.4.3 // indirect 11 | -------------------------------------------------------------------------------- /tools/src/gosimports/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.24.0 4 | 5 | require github.com/rinchsan/gosimports v0.3.8 6 | 7 | require ( 8 | golang.org/x/mod v0.29.0 // indirect 9 | golang.org/x/sys v0.38.0 // indirect 10 | golang.org/x/tools v0.38.0 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). 4 | 5 | Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io) 6 | in order to report violations of the Code of Conduct. -------------------------------------------------------------------------------- /examples/compose/replace.override.yaml: -------------------------------------------------------------------------------- 1 | x-tele: 2 | connections: 3 | - namespace: emojivoto 4 | services: 5 | emoji: 6 | x-tele: 7 | type: replace 8 | voting: 9 | x-tele: 10 | type: replace 11 | vote-bot: 12 | profiles: 13 | - notEnabled 14 | -------------------------------------------------------------------------------- /pkg/client/remotefs/fuseftp_external.go: -------------------------------------------------------------------------------- 1 | //go:build external_fuseftp && !docker 2 | 3 | package remotefs 4 | 5 | import ( 6 | "context" 7 | "os/exec" 8 | ) 9 | 10 | func getFuseFTPServer(_ context.Context, exe string) (string, error) { 11 | return exec.LookPath(exe) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/proc/docker_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package proc 4 | 5 | // RunningInContainer returns true if the current process runs from inside a docker container. 6 | func RunningInContainer() bool { 7 | return false 8 | } 9 | 10 | func SetRunningInContainer(_ bool) { 11 | } 12 | -------------------------------------------------------------------------------- /integration_test/testdata/routing-values.yaml: -------------------------------------------------------------------------------- 1 | client: 2 | routing: 3 | # add the following subnets to the client's virtual network interface 4 | # array of strings 5 | alsoProxySubnets: ["11.11.11.11/32", "12.12.12.12/32"] 6 | neverProxySubnets: ["13.11.11.11/32", "14.12.12.12/32"] -------------------------------------------------------------------------------- /pkg/client/cli/flags/zerovalue.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | // IsZeroValue returns true if the given string represents a well-known zero value. 4 | func IsZeroValue(str string) bool { 5 | switch str { 6 | case "", "false", "", "[]", "0": 7 | return true 8 | } 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /integration_test/testdata/udp-echo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine3.15 AS builder 2 | 3 | WORKDIR /udp-echo 4 | COPY go.mod . 5 | COPY main.go . 6 | RUN go build -o udp-echo . 7 | 8 | FROM alpine:3.15 9 | COPY --from=builder /udp-echo/udp-echo /usr/local/bin/ 10 | ENTRYPOINT ["/usr/local/bin/udp-echo"] 11 | -------------------------------------------------------------------------------- /pkg/vif/device_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package vif 4 | 5 | import ( 6 | "context" 7 | "net/netip" 8 | ) 9 | 10 | func (d *device) setDNS(context.Context, string, netip.AddrPort, []string) (err error) { 11 | // DNS is configured by other means than through the actual device 12 | return nil 13 | } 14 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/test/testdata/mechanisms.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | tcp: 3 | name: tcp 4 | product: oss 5 | version: "1" 6 | http: 7 | name: http 8 | product: plus 9 | version: "1" 10 | httpv2: 11 | name: http 12 | product: plus 13 | version: "2" 14 | grpc: 15 | name: grpc 16 | product: plus 17 | version: "1" 18 | -------------------------------------------------------------------------------- /pkg/client/docker/teleroute/server.go: -------------------------------------------------------------------------------- 1 | package teleroute 2 | 3 | import "net/netip" 4 | 5 | type Server interface { 6 | // DaemonAddresses returns the daemon's IP on the connected teleroute network. It will return an 7 | // Invalid address if the container hasn't been connected yet. 8 | DaemonAddresses() []netip.Addr 9 | } 10 | -------------------------------------------------------------------------------- /docs/common/quantity.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Quantity 3 | --- 4 | Quantity is measured in bytes. You can express it as a plain integer or as a fixed-point number using E, G, M, or K. You can also use the power-of-two equivalents: Gi, Mi, Ki. For example, the following represent roughly the same value: 5 | ``` 6 | 128974848, 129e6, 129M, 123Mi 7 | ``` -------------------------------------------------------------------------------- /pkg/client/remotefs/fuseftp.go: -------------------------------------------------------------------------------- 1 | package remotefs 2 | 3 | import ( 4 | "context" 5 | _ "embed" 6 | 7 | "github.com/telepresenceio/go-fuseftp/rpc" 8 | ) 9 | 10 | type FuseFTPManager interface { 11 | LinkedFTP() bool 12 | DeferInit(ctx context.Context) error 13 | GetFuseFTPClient(ctx context.Context) rpc.FuseFTPClient 14 | } 15 | -------------------------------------------------------------------------------- /integration_test/itest/rm_as_root_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package itest 4 | 5 | //nolint:depguard // don't care about output or contexts 6 | import ( 7 | "context" 8 | "os/exec" 9 | ) 10 | 11 | func rmAsRoot(ctx context.Context, file string) error { 12 | return exec.Command("sudo", "-n", "rm", "-f", file).Run() 13 | } 14 | -------------------------------------------------------------------------------- /pkg/client/logging/stat_linux_test.go: -------------------------------------------------------------------------------- 1 | package logging_test 2 | 3 | import ( 4 | "errors" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | var stat unix.Statx_t 11 | err := unix.Statx(-1, "/", 0, unix.STATX_BTIME, &stat) 12 | if err != nil && errors.Is(err, unix.ENOSYS) { 13 | osHasBTime = false 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /integration_test/testdata/echo-server/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= 2 | golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 3 | golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 4 | golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 5 | -------------------------------------------------------------------------------- /pkg/dpipe/dpipe_windows.go: -------------------------------------------------------------------------------- 1 | package dpipe 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/exec" //nolint:depguard // We want no logging and no soft-context signal handling 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/proc" 9 | ) 10 | 11 | func killProcess(ctx context.Context, cmd *exec.Cmd) { 12 | proc.KillProcessGroup(ctx, cmd, os.Kill) 13 | } 14 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/state/data.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | rpc "github.com/telepresenceio/telepresence/rpc/v2/manager" 5 | ) 6 | 7 | func agentHasMechanism(agent *rpc.AgentInfo, mechName string) bool { 8 | for _, mechanism := range agent.Mechanisms { 9 | if mechanism.Name == mechName { 10 | return true 11 | } 12 | } 13 | 14 | return false 15 | } 16 | -------------------------------------------------------------------------------- /pkg/pprof/pprof.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | _ "net/http/pprof" 8 | 9 | "github.com/datawire/dlib/dhttp" 10 | ) 11 | 12 | func PprofServer(ctx context.Context, port uint16) error { 13 | sc := dhttp.ServerConfig{Handler: http.DefaultServeMux} 14 | return sc.ListenAndServe(ctx, fmt.Sprintf("localhost:%d", port)) 15 | } 16 | -------------------------------------------------------------------------------- /tools/src/y2j/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "os" 7 | 8 | "sigs.k8s.io/yaml" 9 | ) 10 | 11 | func main() { 12 | data, err := io.ReadAll(os.Stdin) 13 | if err == nil { 14 | data, err = yaml.YAMLToJSON(data) 15 | if err == nil { 16 | _, err = os.Stdout.Write(data) 17 | } 18 | } 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/issuer.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (eq .Values.agentInjector.certificate.method "certmanager") .Values.agentInjector.enabled }} 2 | apiVersion: cert-manager.io/v1 3 | kind: {{ .Values.agentInjector.certificate.certmanager.issuerRef.kind }} 4 | metadata: 5 | name: {{ .Values.agentInjector.certificate.certmanager.issuerRef.name }} 6 | spec: 7 | selfSigned: {} 8 | {{- end }} 9 | -------------------------------------------------------------------------------- /pkg/authenticator/config.go: -------------------------------------------------------------------------------- 1 | package authenticator 2 | 3 | import "k8s.io/client-go/tools/clientcmd" 4 | 5 | func LoadKubeConfig(kubeConfig string) clientcmd.ClientConfig { 6 | loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 7 | loadingRules.ExplicitPath = kubeConfig 8 | 9 | return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) 10 | } 11 | -------------------------------------------------------------------------------- /pkg/ioutil/tempname.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import "os" 4 | 5 | // CreateTempName creates a new temporary file using os.CreateTemp, removes it, and then returns its name. 6 | func CreateTempName(dir, pattern string) (string, error) { 7 | f, err := os.CreateTemp(dir, pattern) 8 | if err != nil { 9 | return "", err 10 | } 11 | path := f.Name() 12 | _ = f.Close() 13 | _ = os.Remove(path) 14 | return path, nil 15 | } 16 | -------------------------------------------------------------------------------- /pkg/iputil/normalize.go: -------------------------------------------------------------------------------- 1 | package iputil 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | ) 7 | 8 | func IsIpV6Addr(ipAddStr string) bool { 9 | return strings.Count(ipAddStr, ":") >= 2 10 | } 11 | 12 | // Normalize returns the four byte version of an IPv4, even if it 13 | // was expressed as a 16 byte IP. 14 | func Normalize(ip net.IP) net.IP { 15 | if ip4 := ip.To4(); ip4 != nil { 16 | ip = ip4 17 | } 18 | return ip 19 | } 20 | -------------------------------------------------------------------------------- /rpc/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/rpc/v2 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | google.golang.org/grpc v1.76.0 7 | google.golang.org/protobuf v1.36.10 8 | ) 9 | 10 | require ( 11 | golang.org/x/net v0.46.0 // indirect 12 | golang.org/x/sys v0.38.0 // indirect 13 | golang.org/x/text v0.30.0 // indirect 14 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251110190251-83f479183930 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/trafficManagerRbac/service-account.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.managerRbac.create }} 2 | {{- /* This file contains the serviceAccount used for the traffic-manager deployment. */}} 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | name: traffic-manager 7 | namespace: {{ include "traffic-manager.namespace" $ }} 8 | labels: 9 | {{- include "telepresence.labels" $ | nindent 4 }} 10 | 11 | {{- end }} 12 | -------------------------------------------------------------------------------- /pkg/client/userd/trafficmgr/context.go: -------------------------------------------------------------------------------- 1 | package trafficmgr 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type sessionKey struct{} 8 | 9 | func withSession(ctx context.Context, session *session) context.Context { 10 | return context.WithValue(ctx, sessionKey{}, session) 11 | } 12 | 13 | func getSession(ctx context.Context) *session { 14 | if s, ok := ctx.Value(sessionKey{}).(*session); ok { 15 | return s 16 | } 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.go] 10 | indent_style = tab 11 | ; mimic gofmt's go/printer.printer.funcBody().maxSize 12 | max_line_length = 100 13 | ; mimic gofmt's cmd/gofmt.tabWidth 14 | tab_width = 8 15 | 16 | [{go.mod,go.sum}] 17 | indent_style = tab 18 | 19 | [*.proto] 20 | indent_style = space 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /integration_test/integration_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 8 | ) 9 | 10 | func Test_Integration(t *testing.T) { 11 | ossRoot, err := filepath.Abs(".") 12 | if err != nil { 13 | t.Fatalf("unable to get absolute path of .oss: %v", err) 14 | } 15 | itest.RunTests(itest.TestContext(t, ossRoot, filepath.Dir(ossRoot))) 16 | } 17 | -------------------------------------------------------------------------------- /integration_test/testdata/scripts/veth-down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | ip link set vm2 down 3 | ip link set brm down 4 | 5 | for var in "$@" 6 | do 7 | ip addr del "$(echo "$var" | sed -r 's/\.0$/.1/')" dev brm 8 | ip addr del "$(echo "$var" | sed -r 's/\.0$/.2/')" dev vm2 9 | done 10 | 11 | ip link del brm type bridge 12 | ip link set dev tapm down 13 | ip tuntap del tapm mode tap 14 | ip link set dev vm1 down 15 | ip link del dev vm1 type veth peer name vm2 16 | -------------------------------------------------------------------------------- /pkg/client/cli/ann/annotations.go: -------------------------------------------------------------------------------- 1 | package ann 2 | 3 | // -- Annotation keys 4 | 5 | const ( 6 | UserDaemon = "userD" 7 | Session = "session" 8 | VersionCheck = "versionCheck" 9 | UpdateCheckFormat = "updateCheckFormat" 10 | Progress = "progress" 11 | ) 12 | 13 | // -- Annotation values 14 | 15 | const ( 16 | Optional = "optional" 17 | Required = "required" 18 | Tel2 = "https://%s/download/tel2/%s/%s/stable.txt" 19 | ) 20 | -------------------------------------------------------------------------------- /pkg/client/logging/initcontext_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package logging 4 | 5 | import ( 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func dupStd() (func(), error) { 10 | stdoutFd, err := unix.Dup(1) 11 | if err != nil { 12 | return nil, err 13 | } 14 | stderrFd, err := unix.Dup(2) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return func() { 19 | _ = unix.Dup2(stdoutFd, 1) 20 | _ = unix.Dup2(stderrFd, 2) 21 | }, nil 22 | } 23 | -------------------------------------------------------------------------------- /pkg/slice/csv.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | import ( 4 | "bytes" 5 | "encoding/csv" 6 | "strings" 7 | ) 8 | 9 | // AsCSV returns the string slice encoded by a csv.NewWriter. 10 | func AsCSV(vs []string) string { 11 | b := &bytes.Buffer{} 12 | w := csv.NewWriter(b) 13 | if err := w.Write(vs); err != nil { 14 | // The underlying bytes.Buffer should never error. 15 | panic(err) 16 | } 17 | w.Flush() 18 | return strings.TrimSuffix(b.String(), "\n") 19 | } 20 | -------------------------------------------------------------------------------- /pkg/client/userd/trafficmgr/config.go: -------------------------------------------------------------------------------- 1 | package trafficmgr 2 | 3 | import ( 4 | "github.com/telepresenceio/telepresence/v2/pkg/client" 5 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 6 | ) 7 | 8 | func (s *session) GetConfig() (*client.SessionConfig, error) { 9 | return &client.SessionConfig{ 10 | ClientFile: client.GetConfigFile(s), 11 | LogDirectory: filelocation.AppUserLogDir(s), 12 | Config: client.GetConfig(s), 13 | }, nil 14 | } 15 | -------------------------------------------------------------------------------- /k8s/rs-echo-svc2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use a second service here that appoints the same port in the replicaset to 3 | # make things a bit more complex for the discovery mechanism, and also force 4 | # use of the --service flag when intercepting 5 | apiVersion: v1 6 | kind: Service 7 | metadata: 8 | name: rs-echo-canary 9 | spec: 10 | type: ClusterIP 11 | selector: 12 | service: rs-echo 13 | ports: 14 | - name: http 15 | port: 80 16 | targetPort: 8080 17 | -------------------------------------------------------------------------------- /charts/telepresence-oss/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /pkg/client/config_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package client 4 | 5 | import "net/netip" 6 | 7 | // defaultVirtualSubnet A randomly chosen class E subnet. 8 | var defaultVirtualSubnet = netip.MustParsePrefix("246.246.0.0/16") //nolint:gochecknoglobals // constant 9 | 10 | type OSSpecificConfig struct{} 11 | 12 | func GetDefaultOSSpecificConfig() OSSpecificConfig { 13 | return OSSpecificConfig{} 14 | } 15 | 16 | func (c *OSSpecificConfig) Merge(o *OSSpecificConfig) { 17 | } 18 | -------------------------------------------------------------------------------- /integration_test/testdata/count-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "sync/atomic" 8 | ) 9 | 10 | func main() { 11 | counter := int64(0) 12 | http.HandleFunc("/count", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, atomic.LoadInt64(&counter)) }) 13 | 14 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 15 | atomic.AddInt64(&counter, 1) 16 | }) 17 | log.Fatal(http.ListenAndServe(":8080", nil)) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/dos/package.go: -------------------------------------------------------------------------------- 1 | // Package dos contains an abstraction of some functions in the go os package. When used in code, it allows those 2 | // functions to be mocked in unit tests. 3 | // In general, the functions are implemented using an interface which is then stored in the context. The functions 4 | // are then called using dos instead of os, and with an additional first context argument. E.g. 5 | // 6 | // ctx := dos.WithFS(ctx, mockFS) 7 | // f, err := dos.Open(ctx, "/etc/resolv.conf") 8 | package dos 9 | -------------------------------------------------------------------------------- /docs/licenses.md: -------------------------------------------------------------------------------- 1 | Telepresence CLI incorporates Free and Open Source software under the following licenses: 2 | 3 | * [2-clause BSD license](https://opensource.org/licenses/BSD-2-Clause) 4 | * [3-clause BSD license](https://opensource.org/licenses/BSD-3-Clause) 5 | * [Apache License 2.0](https://opensource.org/licenses/Apache-2.0) 6 | * [ISC license](https://opensource.org/licenses/ISC) 7 | * [MIT license](https://opensource.org/licenses/MIT) 8 | * [Mozilla Public License 2.0](https://opensource.org/licenses/MPL-2.0) 9 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/disruption-budget.goyaml: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/v2/integration_test/itest.DisruptionBudget*/ -}}--- 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: {{.Name}} 6 | spec: 7 | {{- if gt .MinAvailable 0 }} 8 | minAvailable: {{ .MinAvailable }} 9 | {{- end }} 10 | {{- if gt .MaxUnavailable 0 }} 11 | maxUnavailable: {{ .MaxUnavailable }} 12 | {{- end }} 13 | selector: 14 | matchLabels: 15 | budget: {{.Name}} 16 | -------------------------------------------------------------------------------- /DEPENDENCY_LICENSES.md: -------------------------------------------------------------------------------- 1 | Telepresence CLI incorporates Free and Open Source software under the following licenses: 2 | 3 | * [2-clause BSD license](https://opensource.org/licenses/BSD-2-Clause) 4 | * [3-clause BSD license](https://opensource.org/licenses/BSD-3-Clause) 5 | * [Apache License 2.0](https://opensource.org/licenses/Apache-2.0) 6 | * [ISC license](https://opensource.org/licenses/ISC) 7 | * [MIT license](https://opensource.org/licenses/MIT) 8 | * [Mozilla Public License 2.0](https://opensource.org/licenses/MPL-2.0) 9 | -------------------------------------------------------------------------------- /pkg/iputil/join.go: -------------------------------------------------------------------------------- 1 | package iputil 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | ) 7 | 8 | func JoinIpPort(ip net.IP, port uint16) string { 9 | ps := strconv.Itoa(int(port)) 10 | if ip4 := ip.To4(); ip4 != nil { 11 | return ip4.String() + ":" + ps 12 | } 13 | if ip16 := ip.To16(); ip16 != nil { 14 | return "[" + ip16.String() + "]:" + ps 15 | } 16 | return ":" + ps 17 | } 18 | 19 | func JoinHostPort(host string, port uint16) string { 20 | return net.JoinHostPort(host, strconv.Itoa(int(port))) 21 | } 22 | -------------------------------------------------------------------------------- /MEETING_SCHEDULE.md: -------------------------------------------------------------------------------- 1 | # Community Meeting Schedule 2 | 3 | ## Monthly Contributors Meeting 4 | 5 | The Telepresence Contributors Meeting is held on the first Wednesday of every month at 11am Eastern. The focus of this meeting is discussion of technical issues related to development of Telepresence. 6 | 7 | New contributors are always welcome! Check out our [contributor's guide](CONTRIBUTING.md) to learn how you can help make Telepresence better. 8 | 9 | **Zoom Meeting Link**: https://us02web.zoom.us/j/6297823847 10 | -------------------------------------------------------------------------------- /integration_test/testdata/scripts/veth-up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | ip link add dev vm1 type veth peer name vm2 3 | ip link set dev vm1 up 4 | ip tuntap add tapm mode tap 5 | ip link set dev tapm up 6 | ip link add brm type bridge 7 | 8 | ip link set tapm master brm 9 | ip link set vm1 master brm 10 | 11 | for var in "$@" 12 | do 13 | ip addr add "$(echo "$var" | sed -r 's/\.0$/.1/')" dev brm 14 | ip addr add "$(echo "$var" | sed -r 's/\.0$/.2/')" dev vm2 15 | done 16 | 17 | ip link set brm up 18 | ip link set vm2 up -------------------------------------------------------------------------------- /pkg/client/cli/cmd/compose.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/docker/compose" 7 | ) 8 | 9 | func composeCmd() *cobra.Command { 10 | cmd := &cobra.Command{ 11 | Use: "compose command [flags]", 12 | Short: "Define and run multi-container applications with Telepresence and Docker", 13 | TraverseChildren: true, 14 | } 15 | cmd.AddCommand(compose.GenerateSubCommands(cmd)...) 16 | return cmd 17 | } 18 | -------------------------------------------------------------------------------- /pkg/client/logging/rotatingfile_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package logging 4 | 5 | import ( 6 | "time" 7 | 8 | "golang.org/x/term" 9 | ) 10 | 11 | // restoreCTimeAfterRename is a noop on unixes since the renamed file retains the creation time of the source. 12 | func restoreCTimeAfterRename(_ string, _ time.Time) error { 13 | return nil 14 | } 15 | 16 | // IsTerminal returns whether the given file descriptor is a terminal. 17 | var IsTerminal = term.IsTerminal //nolint:gochecknoglobals // os specific func replacement 18 | -------------------------------------------------------------------------------- /docs/community.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Community 3 | hide_table_of_contents: true 4 | --- 5 | 6 | # Community 7 | 8 | ## Contributor's guide 9 | Please review our [contributor's guide](https://github.com/telepresenceio/telepresence/blob/release/v2/CONTRIBUTING.md) 10 | on GitHub to learn how you can help make Telepresence better. 11 | 12 | ## Meetings 13 | Check out our community [meeting schedule](https://github.com/telepresenceio/telepresence/blob/release/v2/MEETING_SCHEDULE.md) for opportunities to interact with Telepresence developers. 14 | -------------------------------------------------------------------------------- /pkg/tunnel/probe.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | type CounterProbe struct { 8 | name string 9 | value uint64 10 | } 11 | 12 | func NewCounterProbe(name string) *CounterProbe { 13 | return &CounterProbe{name: name} 14 | } 15 | 16 | func (p *CounterProbe) Increment(v uint64) { 17 | atomic.AddUint64(&p.value, v) 18 | } 19 | 20 | func (p *CounterProbe) GetName() string { 21 | return p.name 22 | } 23 | 24 | func (p *CounterProbe) GetValue() uint64 { 25 | return atomic.LoadUint64(&p.value) 26 | } 27 | -------------------------------------------------------------------------------- /rpc/authenticator/authenticator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package telepresence.authenticator; 4 | 5 | option go_package = "github.com/telepresenceio/telepresence/rpc/v2/authenticator"; 6 | 7 | service Authenticator { 8 | rpc GetContextExecCredentials(GetContextExecCredentialsRequest) returns (GetContextExecCredentialsResponse); 9 | } 10 | 11 | message GetContextExecCredentialsRequest { 12 | string context_name = 1; 13 | } 14 | 15 | message GetContextExecCredentialsResponse { 16 | bytes raw_credentials = 1; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /pkg/client/cmd_error.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os/exec" 7 | ) 8 | 9 | // RunError checks if the given err is a *exit.ExitError, and if so, extracts 10 | // Stderr and the ExitCode from it. 11 | func RunError(err error) error { 12 | var ee *exec.ExitError 13 | if errors.As(err, &ee) { 14 | if len(ee.Stderr) > 0 { 15 | err = fmt.Errorf("%s, exit code %d", string(ee.Stderr), ee.ExitCode()) 16 | } else { 17 | err = fmt.Errorf("exit code %d", ee.ExitCode()) 18 | } 19 | } 20 | return err 21 | } 22 | -------------------------------------------------------------------------------- /cmd/teleroute/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM golang:alpine AS builder 2 | 3 | RUN apk add --no-cache --virtual .build-deps gcc libc-dev 4 | WORKDIR /docker-network-teleroute 5 | COPY . . 6 | ARG TARGETOS 7 | ARG TARGETARCH 8 | RUN \ 9 | --mount=type=cache,target=/root/.cache/go-build \ 10 | --mount=type=cache,target=/go/pkg/mod \ 11 | GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /go/bin/docker-network-teleroute 12 | 13 | FROM alpine 14 | RUN apk add --no-cache bash 15 | COPY --from=builder /go/bin/docker-network-teleroute /bin/ 16 | -------------------------------------------------------------------------------- /pkg/client/cli/flags/context.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/spf13/pflag" 7 | ) 8 | 9 | type flagSetsKey struct{} 10 | 11 | func WithFlagSets(ctx context.Context, flagSets ...*pflag.FlagSet) context.Context { 12 | if len(flagSets) > 0 { 13 | ctx = context.WithValue(ctx, flagSetsKey{}, flagSets) 14 | } 15 | return ctx 16 | } 17 | 18 | func GetFlagSets(ctx context.Context) []*pflag.FlagSet { 19 | if fs, ok := ctx.Value(flagSetsKey{}).([]*pflag.FlagSet); ok { 20 | return fs 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/agentinit/agent_init_windows.go: -------------------------------------------------------------------------------- 1 | package agentinit 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | // This file needs to exist because agent_init.go imports iptables, which obviously doesn't exist on windows. 9 | // It really doesn't matter, since there's no support for windows-based containers of any sort, but it'll fail the build. 10 | 11 | // Main is the main function for the agent init container. 12 | func Main(ctx context.Context, args ...string) error { 13 | return fmt.Errorf("windows-based init agent is not a thing") 14 | } 15 | -------------------------------------------------------------------------------- /integration_test/single_service_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 5 | ) 6 | 7 | type singleServiceSuite struct { 8 | itest.Suite 9 | itest.SingleService 10 | } 11 | 12 | func (s *singleServiceSuite) SuiteName() string { 13 | return "SingleService" 14 | } 15 | 16 | func init() { 17 | itest.AddSingleServiceSuite("", "echo", func(h itest.SingleService) itest.TestingSuite { 18 | return &singleServiceSuite{Suite: itest.Suite{Harness: h}, SingleService: h} 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/docker/teleroute/server_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package teleroute 4 | 5 | import ( 6 | "net/netip" 7 | 8 | "github.com/datawire/dlib/dgroup" 9 | "github.com/telepresenceio/telepresence/v2/pkg/vif" 10 | ) 11 | 12 | // StartServer returns nil because the teleroute server is only relevant in a container, 13 | // and hence, only relevant in linux. 14 | func StartServer(g *dgroup.Group, tap *vif.TunnelingDevice, routesCh <-chan []netip.Prefix, teleroutePort uint16) (Server, error) { 15 | for range routesCh { 16 | } 17 | return nil, nil 18 | } 19 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/cluster/id.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "context" 5 | 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | v1 "k8s.io/client-go/kubernetes/typed/core/v1" 8 | ) 9 | 10 | var GetInstallIDFunc = GetNamespaceID //nolint:gochecknoglobals // extension point 11 | 12 | func GetNamespaceID(ctx context.Context, client v1.CoreV1Interface, namespace string) (string, error) { 13 | ns, err := client.Namespaces().Get(ctx, namespace, metav1.GetOptions{}) 14 | if err == nil { 15 | return string(ns.GetUID()), nil 16 | } 17 | return "", err 18 | } 19 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pvc.goyaml: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/v2/integration_test/itest.PersistentVolumeClaim*/ -}} 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ .Name }} 6 | labels: 7 | purpose: tp-cli-testing 8 | {{- with .Annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | accessModes: 14 | - ReadWriteMany 15 | resources: 16 | requests: 17 | storage: 0.1Gi 18 | {{- with .StorageClassName }} 19 | storageClassName: {{ . }} 20 | {{- end }} 21 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/secret-reader-rbac.goyaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: secret-reader 6 | rules: 7 | - apiGroups: [""] 8 | resources: ["secrets"] 9 | verbs: ["get", "watch", "list"] 10 | --- 11 | apiVersion: rbac.authorization.k8s.io/v1 12 | kind: RoleBinding 13 | metadata: 14 | name: read-secrets 15 | subjects: 16 | - kind: ServiceAccount 17 | name: {{ .Name }} 18 | apiGroup: "" 19 | roleRef: 20 | kind: Role 21 | name: secret-reader 22 | apiGroup: rbac.authorization.k8s.io 23 | -------------------------------------------------------------------------------- /pkg/proc/wsl_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package proc 4 | 5 | import ( 6 | "os" 7 | "strings" 8 | ) 9 | 10 | var runningInWSL bool //nolint:gochecknoglobals // this is a constant 11 | 12 | func init() { 13 | pv, err := os.ReadFile("/proc/version") 14 | if err == nil { 15 | sv := strings.ToLower(string(pv)) 16 | runningInWSL = strings.Contains(sv, "microsoft") && strings.Contains(sv, "wsl") 17 | } 18 | } 19 | 20 | // RunningInWSL returns true if the current process is a Linux running under WSL. 21 | func RunningInWSL() bool { 22 | return runningInWSL 23 | } 24 | -------------------------------------------------------------------------------- /integration_test/itest/timed.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // TimedRun gives the given function maxDuration time to finish, and returns a timeout error if it doesn't. Otherwise, 9 | // it returns the error from the function. 10 | func TimedRun(ctx context.Context, maxDuration time.Duration, function func(ctx2 context.Context) error) error { 11 | ctx, cancel := context.WithTimeout(ctx, maxDuration) 12 | defer cancel() 13 | select { 14 | case <-ctx.Done(): 15 | return ctx.Err() 16 | default: 17 | return function(ctx) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/mcp_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/njayp/ophis/test" 7 | ) 8 | 9 | func TestMCP(t *testing.T) { 10 | ctx := WithSubCommands(t.Context()) 11 | cmd := Telepresence(ctx, nil) 12 | tools := test.GetTools(t, cmd) 13 | test.ToolNames(t, tools, 14 | "telepresence_quit", 15 | "telepresence_status", 16 | "telepresence_connect", 17 | "telepresence_intercept", 18 | "telepresence_ingest", 19 | "telepresence_leave", 20 | "telepresence_list", 21 | "telepresence_wiretap", 22 | "telepresence_replace", 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /integration_test/config_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 8 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 9 | ) 10 | 11 | func (s *notConnectedSuite) Test_EmptyConfigFile() { 12 | ctx := s.Context() 13 | cfgDir := itest.TempDir(ctx) 14 | ctx = filelocation.WithAppUserConfigDir(ctx, cfgDir) 15 | f, err := os.Create(filepath.Join(cfgDir, "config.yml")) 16 | s.Require().NoError(err) 17 | f.Close() 18 | s.TelepresenceConnect(ctx) 19 | itest.TelepresenceQuitOk(ctx) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/cli/flags/addunique.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/pflag" 7 | ) 8 | 9 | func AddUnique(dst *pflag.FlagSet, src *pflag.FlagSet) (err error) { 10 | src.VisitAll(func(f *pflag.Flag) { 11 | if err != nil { 12 | return 13 | } 14 | if dst.Lookup(f.Name) != nil { 15 | err = fmt.Errorf("flag %q from flagset %q is already present in flagset %q", f.Name, src.Name(), dst.Name()) 16 | return 17 | } 18 | if f.Shorthand != "" && dst.ShorthandLookup(f.Shorthand) != nil { 19 | f.Shorthand = "" 20 | } 21 | dst.AddFlag(f) 22 | }) 23 | return err 24 | } 25 | -------------------------------------------------------------------------------- /pkg/proc/docker_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package proc 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | var runningInContainer bool //nolint:gochecknoglobals // this is a constant 10 | 11 | func init() { 12 | _, err := os.Stat("/.dockerenv") 13 | runningInContainer = err == nil 14 | } 15 | 16 | // RunningInContainer returns true if the current process runs from inside a docker container. 17 | func RunningInContainer() bool { 18 | return runningInContainer 19 | } 20 | 21 | // SetRunningInContainer forces the runningInContainer state. 22 | func SetRunningInContainer(flag bool) { 23 | runningInContainer = flag 24 | } 25 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-manual-inject-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: "manual-inject" 5 | labels: 6 | app: manual-inject 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: manual-inject 12 | template: 13 | metadata: 14 | labels: 15 | app: manual-inject 16 | spec: 17 | containers: 18 | - name: echo-container 19 | image: ghcr.io/telepresenceio/echo-server:latest 20 | imagePullPolicy: IfNotPresent 21 | ports: 22 | - containerPort: 80 23 | resources: {} 24 | -------------------------------------------------------------------------------- /pkg/client/remotefs/mounter.go: -------------------------------------------------------------------------------- 1 | package remotefs 2 | 3 | import ( 4 | "context" 5 | "net/netip" 6 | ) 7 | 8 | // A Mounter is responsible for mounting a remote filesystem in a local directory or drive letter. 9 | type Mounter interface { 10 | // Start mounts the remote directory given by mountPoint on the local directory or drive letter 11 | // given ty clientMountPoint. The podIP and port is the address to the remote FTP or SFTP server. 12 | // The id is just used for logging purposes. 13 | Start(ctx context.Context, workload, container, clientMountPoint, mountPoint string, podAddrPort netip.AddrPort, ro bool) error 14 | } 15 | -------------------------------------------------------------------------------- /pkg/client/k8s/cani.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/api/authorization/v1" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/k8sapi" 9 | ) 10 | 11 | // CanPortForward answers the question if this client has the RBAC permissions necessary 12 | // to perform a port-forward to the connected namespace. 13 | func CanPortForward(ctx context.Context, namespace string) bool { 14 | ok, err := k8sapi.CanI(ctx, &v1.ResourceAttributes{ 15 | Verb: "create", 16 | Resource: "pods", 17 | Subresource: "portforward", 18 | Namespace: namespace, 19 | }) 20 | return err == nil && ok 21 | } 22 | -------------------------------------------------------------------------------- /pkg/log/base_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/sirupsen/logrus" 7 | 8 | "github.com/datawire/dlib/dlog" 9 | ) 10 | 11 | func MakeBaseLogger(ctx context.Context, logLevel string) context.Context { 12 | logrusLogger := logrus.StandardLogger() 13 | logrusFormatter := NewFormatter("2006-01-02 15:04:05.0000") 14 | logrusLogger.SetFormatter(logrusFormatter) 15 | 16 | SetLogrusLevel(logrusLogger, logLevel, false) 17 | 18 | logger := dlog.WrapLogrus(logrusLogger) 19 | dlog.SetFallbackLogger(logger) 20 | ctx = dlog.WithLogger(ctx, logger) 21 | return WithLevelSetter(ctx, logrusLogger) 22 | } 23 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/connector-20210916T130356.log: -------------------------------------------------------------------------------- 1 | 2021-09-16 13:03:47.7084 info Logging at this level "info" 2 | 2021-09-16 13:03:47.7451 info --- 3 | 2021-09-16 13:03:47.7451 info Telepresence Connector v2.4.3-98-g0585bbd1-1631811285 (api v3) starting... 4 | 2021-09-16 13:03:47.7452 info PID is 42128 5 | 2021-09-16 13:03:47.7452 info 6 | 2021-09-16 13:03:47.7453 info connector/server-grpc : gRPC server started 7 | 2021-09-16 13:03:54.6001 info connector/background-systema:shutdown_logger : shutting down (gracefully)... 8 | 2021-09-16 13:03:54.6002 info connector:shutdown_logger : shutting down (gracefully)... 9 | -------------------------------------------------------------------------------- /tools/src/test-report/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | github.com/fatih/color v1.18.0 7 | github.com/vbauerster/mpb/v8 v8.11.2 8 | ) 9 | 10 | require ( 11 | github.com/VividCortex/ewma v1.2.0 // indirect 12 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect 13 | github.com/clipperhouse/stringish v0.1.1 // indirect 14 | github.com/clipperhouse/uax29/v2 v2.3.0 // indirect 15 | github.com/mattn/go-colorable v0.1.14 // indirect 16 | github.com/mattn/go-isatty v0.0.20 // indirect 17 | github.com/mattn/go-runewidth v0.0.19 // indirect 18 | golang.org/x/sys v0.38.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/certificate.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (eq .Values.agentInjector.certificate.method "certmanager") .Values.agentInjector.enabled }} 2 | apiVersion: cert-manager.io/v1 3 | kind: Certificate 4 | metadata: 5 | name: {{ .Values.agentInjector.secret.name }} 6 | spec: 7 | secretName: {{ .Values.agentInjector.secret.name }} 8 | dnsNames: 9 | - {{ (printf "%s.%s" .Values.agentInjector.name .Release.Namespace ) }} 10 | - {{ (printf "%s.%s.svc" .Values.agentInjector.name .Release.Namespace ) }} 11 | {{- with .Values.agentInjector.certificate.certmanager }} 12 | {{- toYaml . | nindent 2 }} 13 | {{- end }} 14 | {{- end }} 15 | -------------------------------------------------------------------------------- /pkg/dpipe/dpipe_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package dpipe 4 | 5 | import ( 6 | "context" 7 | "os" 8 | "os/exec" //nolint:depguard // We want no logging and no soft-context signal handling 9 | "time" 10 | 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | func killProcess(_ context.Context, cmd *exec.Cmd) { 15 | // A process is sometimes not terminated gracefully by the SIGTERM, so we give 16 | // it a second to succeed and then kill it forcefully. 17 | time.AfterFunc(time.Second, func() { 18 | if cmd.ProcessState == nil { 19 | _ = cmd.Process.Signal(unix.SIGKILL) 20 | } 21 | }) 22 | _ = cmd.Process.Signal(os.Interrupt) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/client/logging/dup_windows.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "os" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | func dupStdOut(file *os.File) error { 10 | if err := windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, windows.Handle(file.Fd())); err != nil { 11 | return err 12 | } 13 | os.Stdout = file 14 | return nil 15 | } 16 | 17 | func dupStdErr(file *os.File) error { 18 | // https://stackoverflow.com/questions/34772012/capturing-panic-in-golang/34772516 19 | if err := windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(file.Fd())); err != nil { 20 | return err 21 | } 22 | os.Stderr = file 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/ioutil/writeallto.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import "io" 4 | 5 | // WriterTos implements something that can be represented as a list of io.WriterTo. 6 | type WriterTos interface { 7 | WriterTos() []io.WriterTo 8 | } 9 | 10 | // WriteAllTo calls WriteTo on all elements of the WriterTo slice and 11 | // returns the total number of bytes written. 12 | func WriteAllTo(out io.Writer, wts ...io.WriterTo) (tn int64, err error) { 13 | for _, wt := range wts { 14 | if wt == nil { 15 | continue 16 | } 17 | var n int64 18 | if n, err = wt.WriteTo(out); err != nil { 19 | return tn, err 20 | } 21 | tn += n 22 | } 23 | return tn, nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/k8sapi/namespaceid.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | // GetNamespaceID returns the uuid for a given namespace. If there is an error, it still 10 | // returns a usable ID along with the error. 11 | func GetNamespaceID(ctx context.Context, namespace string) (clusterID string, err error) { 12 | ns, err := GetK8sInterface(ctx).CoreV1().Namespaces().Get(ctx, namespace, v1.GetOptions{}) 13 | if err != nil { 14 | // But still return a usable ID if there's an error. 15 | return "00000000-0000-0000-0000-000000000000", err 16 | } 17 | return string(ns.GetUID()), nil 18 | } 19 | -------------------------------------------------------------------------------- /cmd/cobraparser/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/cmd/cobraparser 2 | 3 | go 1.24 4 | 5 | require ( 6 | github.com/docker/docker v28.5.2+incompatible 7 | github.com/spf13/pflag v1.0.10 8 | ) 9 | 10 | require ( 11 | github.com/docker/go-connections v0.6.0 // indirect 12 | github.com/docker/go-units v0.5.0 // indirect 13 | github.com/gogo/protobuf v1.3.2 // indirect 14 | github.com/moby/docker-image-spec v1.3.1 // indirect 15 | github.com/opencontainers/go-digest v1.0.0 // indirect 16 | github.com/opencontainers/image-spec v1.1.1 // indirect 17 | github.com/pkg/errors v0.9.1 // indirect 18 | gotest.tools/v3 v3.5.2 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/echo-auto-inject-6496f77cbd-n86nc: -------------------------------------------------------------------------------- 1 | time="2021-09-16 17:07:04.8111" level=info msg="Logging at this level \"info\"" 2 | time="2021-09-16 17:07:04.8112" level=info msg="Traffic Agent v2.4.2 [pid:1]" 3 | time="2021-09-16 17:07:04.8112" level=info msg="{Name:echo-auto-inject Namespace:default PodIP:10.42.0.25 AgentPort:9900 AppMounts:/tel_app_mounts AppPort:8080 ManagerHost:traffic-manager.ambassador ManagerPort:8081}" 4 | time="2021-09-16 17:07:04.8257" level=info msg="new agent secrets mount path: /var/run/secrets/kubernetes.io" 5 | time="2021-09-16 17:07:04.8298" level=info msg="Connected to Manager v2.4.3-98-g0585bbd1-1631811285" THREAD=/client 6 | -------------------------------------------------------------------------------- /pkg/dpipe/testdata/echo/echo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | var dest string 11 | flag.StringVar(&dest, "d", "1", "Destination of output. Legal values are 1 (stdout), 2 (stderr) or a file name") 12 | flag.Parse() 13 | 14 | var out *os.File 15 | switch dest { 16 | case "1": 17 | out = os.Stdout 18 | case "2": 19 | out = os.Stderr 20 | default: 21 | var err error 22 | if out, err = os.Create(dest); err != nil { 23 | fmt.Fprintln(os.Stderr, err.Error()) 24 | os.Exit(1) 25 | } 26 | defer out.Close() 27 | } 28 | for _, s := range flag.Args() { 29 | fmt.Fprintln(out, s) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/filelocation/osfile_darwin.go: -------------------------------------------------------------------------------- 1 | package filelocation 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func userHomeDir() string { 10 | if v := os.Getenv("HOME"); v != "" { 11 | return v 12 | } 13 | panic("$HOME is not defined") 14 | } 15 | 16 | func userCacheDir(ctx context.Context) string { 17 | return filepath.Join(UserHomeDir(ctx), "Library", "Caches") 18 | } 19 | 20 | func userConfigDir(ctx context.Context) string { 21 | return filepath.Join(UserHomeDir(ctx), "Library", "Application Support") 22 | } 23 | 24 | func appUserLogDir(ctx context.Context) string { 25 | return filepath.Join(UserHomeDir(ctx), "Library", "Logs", appName) 26 | } 27 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "flag" 6 | "log" 7 | 8 | "github.com/telepresenceio/telepresence/tools/src/relnotesgen/relnotes" 9 | ) 10 | 11 | func main() { 12 | var input string 13 | var mdx bool 14 | var variables bool 15 | flag.StringVar(&input, "input", "", "input file") 16 | flag.BoolVar(&mdx, "mdx", false, "generate mdx") 17 | flag.BoolVar(&variables, "variables", false, "generate mdx") 18 | flag.Parse() 19 | var err error 20 | if variables { 21 | err = relnotes.MakeVariables(input) 22 | } else { 23 | err = relnotes.MakeReleaseNotes(input, mdx) 24 | } 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/maps/xmap.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/puzpuzpuz/xsync/v4" 7 | ) 8 | 9 | // GC removes entries from the provided xsync.Map at regular intervals based on a provided condition and stops when the done channel is closed. 10 | func GC[K comparable, V any](m *xsync.Map[K, V], interval time.Duration, done <-chan struct{}, deleteWhen func(K, V) bool) { 11 | ticker := time.NewTicker(interval) 12 | for { 13 | select { 14 | case <-done: 15 | ticker.Stop() 16 | return 17 | case <-ticker.C: 18 | m.Range(func(k K, v V) bool { 19 | if deleteWhen(k, v) { 20 | m.Delete(k) 21 | } 22 | return true 23 | }) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /k8s/agent-injector-rbac.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: traffic-manager 6 | namespace: ambassador 7 | --- 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRole 10 | metadata: 11 | name: traffic-manager 12 | rules: 13 | - apiGroups: 14 | - "" 15 | resources: ["services"] 16 | verbs: ["get", "list"] 17 | --- 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: ClusterRoleBinding 20 | metadata: 21 | name: traffic-manager 22 | subjects: 23 | - kind: ServiceAccount 24 | name: traffic-manager 25 | namespace: ambassador 26 | roleRef: 27 | kind: ClusterRole 28 | name: traffic-manager 29 | apiGroup: rbac.authorization.k8s.io 30 | -------------------------------------------------------------------------------- /pkg/k8sapi/interface.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import ( 4 | "k8s.io/client-go/kubernetes" 5 | 6 | argoRollouts "github.com/datawire/argo-rollouts-go-client/pkg/client/clientset/versioned" 7 | typedArgoRollouts "github.com/datawire/argo-rollouts-go-client/pkg/client/clientset/versioned/typed/rollouts/v1alpha1" 8 | ) 9 | 10 | type JoinedClientSetInterface interface { 11 | kubernetes.Interface 12 | argoRollouts.Interface 13 | } 14 | 15 | type joinedClientSetInterface struct { 16 | kubernetes.Interface 17 | ari argoRollouts.Interface 18 | } 19 | 20 | func (j joinedClientSetInterface) ArgoprojV1alpha1() typedArgoRollouts.ArgoprojV1alpha1Interface { 21 | return j.ari.ArgoprojV1alpha1() 22 | } 23 | -------------------------------------------------------------------------------- /cmd/teleroute/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Docker network plugin for Telepresence", 3 | "documentation": "https://docs.docker.com/engine/extend/plugins/", 4 | "entrypoint": [ 5 | "/bin/docker-network-teleroute" 6 | ], 7 | "env": [ 8 | { 9 | "name": "DEBUG", 10 | "settable": [ 11 | "value" 12 | ], 13 | "value": "false" 14 | } 15 | ], 16 | "interface": { 17 | "socket": "teleroute.sock", 18 | "types": [ 19 | "docker.networkdriver/1.0" 20 | ] 21 | }, 22 | "pidhost": true, 23 | "linux": { 24 | "capabilities": [ 25 | "CAP_NET_ADMIN" 26 | ] 27 | }, 28 | "network": { 29 | "type": "host" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/client/cli/mount/prepare_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package mount 4 | 5 | import ( 6 | "context" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/telepresenceio/telepresence/v2/pkg/client" 11 | ) 12 | 13 | func prepare(ctx context.Context, cwd string, mountPoint string) (string, error) { 14 | if mountPoint == "" { 15 | return os.MkdirTemp(client.GetConfig(ctx).Intercept().MountsRoot, "telfs-") 16 | } 17 | 18 | // filepath.Abs uses os.Getwd but we need the working dir of the cli 19 | if !filepath.IsAbs(mountPoint) { 20 | mountPoint = filepath.Join(cwd, mountPoint) 21 | mountPoint = filepath.Clean(mountPoint) 22 | } 23 | 24 | return mountPoint, os.MkdirAll(mountPoint, 0o700) 25 | } 26 | -------------------------------------------------------------------------------- /integration_test/svcdomain_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "time" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | ) 11 | 12 | func (s *connectedSuite) Test_SvcDomain() { 13 | c := s.Context() 14 | s.ApplyEchoService(c, "echo", 8080) 15 | defer s.DeleteSvcAndWorkload(c, "deploy", "echo") 16 | 17 | host := fmt.Sprintf("echo.%s.svc", s.AppNamespace()) 18 | s.Eventuallyf(func() bool { 19 | c, cancel := context.WithTimeout(c, 1800*time.Millisecond) 20 | defer cancel() 21 | dlog.Info(c, "LookupHost("+host+")") 22 | _, err := net.DefaultResolver.LookupHost(c, host) 23 | return err == nil 24 | }, 10*time.Second, 2*time.Second, "%s did not resolve", host) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/client/cli/flags/map.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/slice" 7 | ) 8 | 9 | // Map returns a map of the flags that has been modified in the given FlagSet. 10 | func Map(flags *pflag.FlagSet) map[string]string { 11 | if flags == nil { 12 | return nil 13 | } 14 | flagMap := make(map[string]string, flags.NFlag()) 15 | flags.VisitAll(func(flag *pflag.Flag) { 16 | if flag.Changed { 17 | var v string 18 | if sv, ok := flag.Value.(pflag.SliceValue); ok { 19 | v = slice.AsCSV(sv.GetSlice()) 20 | } else { 21 | v = flag.Value.String() 22 | } 23 | flagMap[flag.Name] = v 24 | } 25 | }) 26 | return flagMap 27 | } 28 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/client_rancher.goyaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: ClusterRole 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | metadata: 5 | name: rancher-inspect 6 | labels: 7 | purpose: tp-cli-testing 8 | rules: 9 | - apiGroups: [""] 10 | resources: ["nodes"] 11 | verbs: ["get", "list"] 12 | 13 | --- 14 | apiVersion: rbac.authorization.k8s.io/v1 15 | kind: ClusterRoleBinding 16 | metadata: 17 | name: rancher-inspect-{{ .ManagerNamespace }} 18 | labels: 19 | purpose: tp-cli-testing 20 | subjects: 21 | - kind: ServiceAccount 22 | name: telepresence-test-developer 23 | namespace: {{ .ManagerNamespace }} 24 | roleRef: 25 | apiGroup: rbac.authorization.k8s.io 26 | name: rancher-inspect 27 | kind: ClusterRole 28 | -------------------------------------------------------------------------------- /k8s/private-reg-proxy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: private-registry-proxy 5 | labels: 6 | app: private-registry-proxy 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: private-registry-proxy 11 | template: 12 | metadata: 13 | labels: 14 | app: private-registry-proxy 15 | spec: 16 | containers: 17 | - name: tcp-proxy 18 | image: quay.io/bentoml/proxy-to-service:v2 19 | args: 20 | - tcp 21 | - "5000" 22 | - docker-registry.default.svc.cluster.local 23 | ports: 24 | - containerPort: 5000 25 | hostPort: 5000 26 | name: tcp 27 | protocol: TCP 28 | -------------------------------------------------------------------------------- /rpc/common/version.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package telepresence.common; 3 | 4 | option go_package = "github.com/telepresenceio/telepresence/rpc/v2/common"; 5 | 6 | // VersionInfo is the type that both `telepresence daemon` (the super-user 7 | // daemon) and `telepresence conector` (the normal-user daemon) use 8 | // when reporting their version to the user-facing CLI. 9 | message VersionInfo { 10 | int32 api_version = 1; 11 | 12 | // Version is a "vSEMVER" string of the product version number. 13 | string version = 2; 14 | 15 | // Executable is the path to the executable for the process. 16 | string executable = 3; 17 | 18 | // Name of the process (Client, User Daemon, Root Daemon, Traffic Manager) 19 | string name = 4; 20 | } 21 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/test/testdata/clients.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | alice: 3 | name: alice@squirtle.bigcorp.com 4 | namespace: default 5 | install_id: install_id_for_alice 6 | product: telepresence 7 | version: "2.14.0" 8 | bob: 9 | name: bob@bulbasaur.bigcorp.com 10 | namespace: default 11 | install_id: install_id_for_bob 12 | product: telepresence 13 | version: "2.14.0" 14 | cameron: 15 | name: cameron@charmander.bigcorp.com 16 | namespace: default 17 | install_id: install_id_for_cameron 18 | product: telepresence 19 | version: "2.14.0" 20 | pat: 21 | name: pat@pikachu.bigcorp.com 22 | namespace: default 23 | install_id: install_id_for_pat 24 | product: telepresence 25 | version: "2.14.0" 26 | license_key: license_key_for_pat 27 | -------------------------------------------------------------------------------- /pkg/client/cli/flags/deprecation.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/output" 7 | "github.com/telepresenceio/telepresence/v2/pkg/ioutil" 8 | ) 9 | 10 | // DeprecationIfChanged will print a deprecation warning on output.Info if the flag has changed. 11 | // 12 | // Use this method instead of the standard pflag deprecation to ensure that the deprecation message 13 | // doesn't clobber JSON output. 14 | func DeprecationIfChanged(cmd *cobra.Command, flagName, alternative string) { 15 | if flag := cmd.Flag(flagName); flag != nil && flag.Changed { 16 | ioutil.Printf(output.Info(cmd.Context()), "Flag --%s has been deprecated, %s\n", flagName, alternative) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/trafficManager-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "traffic-manager.name" $ }} 5 | namespace: {{ include "traffic-manager.namespace" $ }} 6 | labels: 7 | {{- include "telepresence.labels" $ | nindent 4 }} 8 | data: 9 | {{- if .Values.client }} 10 | client.yaml: | 11 | {{- toYaml .Values.client | nindent 4 }} 12 | {{- end }} 13 | {{- with .Values.intercept }} 14 | {{- if .environment }} 15 | agent-env.yaml: | 16 | {{- toYaml .environment | nindent 4 }} 17 | {{- end }} 18 | {{- end }} 19 | namespace-selector.yaml: | 20 | {{- toYaml (mustFromJson (include "traffic-manager.namespaceSelector" $)) | nindent 4 }} 21 | agent-state.yaml: | 22 | AgentStates: {} 23 | -------------------------------------------------------------------------------- /pkg/ioutil/safename.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import "strings" 4 | 5 | // SafeName returns a string that can safely be used as a file name or docker container. Only 6 | // characters [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed. Others are replaced by an underscore, or 7 | // if it's the very first character, by the character 'a'. 8 | func SafeName(name string) string { 9 | n := strings.Builder{} 10 | for i, c := range name { 11 | switch { 12 | case (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'): 13 | n.WriteByte(byte(c)) 14 | case i > 0 && (c == '_' || c == '.' || c == '-'): 15 | n.WriteByte(byte(c)) 16 | case i > 0: 17 | n.WriteByte('_') 18 | default: 19 | n.WriteByte('a') 20 | } 21 | } 22 | return n.String() 23 | } 24 | -------------------------------------------------------------------------------- /integration_test/itest/rm_as_root_windows.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "golang.org/x/sys/windows" 8 | 9 | "github.com/telepresenceio/telepresence/v2/pkg/shellquote" 10 | ) 11 | 12 | func rmAsRoot(ctx context.Context, file string) error { 13 | cwd, _ := os.Getwd() 14 | // UTF16PtrFromString can only fail if the argument contains a NUL byte. That will never happen here. 15 | verbPtr, _ := windows.UTF16PtrFromString("runas") 16 | exePtr, _ := windows.UTF16PtrFromString("del") 17 | cwdPtr, _ := windows.UTF16PtrFromString(cwd) 18 | argPtr, _ := windows.UTF16PtrFromString(shellquote.ShellArgsString(append([]string{"/f", "/q"}, file))) 19 | return windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, windows.SW_HIDE) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/agentmap/capsbase26.go: -------------------------------------------------------------------------------- 1 | package agentmap 2 | 3 | // CapsBase26 converts the given number into base 26, represented using the letters 'A' to 'Z'. 4 | func CapsBase26(v uint64) string { 5 | return addBase26('A', v) 6 | } 7 | 8 | // Base26 converts the given number into base 26, represented using the letters 'a' to 'z'. 9 | func Base26(v uint64) string { 10 | return addBase26('a', v) 11 | } 12 | 13 | // Base26 converts the given number into base 26 represented using the letters 'a' to 'z'. 14 | func addBase26(c byte, v uint64) string { 15 | i := 14 // covers v == math.MaxUint64 16 | b := make([]byte, i) 17 | for { 18 | l := v % 26 19 | i-- 20 | b[i] = c + byte(l) 21 | if v < 26 { 22 | break 23 | } 24 | v /= 26 25 | } 26 | return string(b[i:]) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/logging/dup_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package logging 4 | 5 | import ( 6 | "os" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | // dupStdOut ensures that anything written to stdout will end up in the given file. 12 | func dupStdOut(file *os.File) error { 13 | // https://github.com/golang/go/issues/325 14 | if err := unix.Dup2(int(file.Fd()), 1); err != nil { 15 | return err 16 | } 17 | os.Stdout = file 18 | return nil 19 | } 20 | 21 | // dupStdErr ensures that anything written to stderr will end up in the given file. 22 | func dupStdErr(file *os.File) error { 23 | // https://github.com/golang/go/issues/325 24 | if err := unix.Dup2(int(file.Fd()), 2); err != nil { 25 | return err 26 | } 27 | os.Stderr = file 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /cmd/cobraparser/README.md: -------------------------------------------------------------------------------- 1 | # Cobra Help Output Parser 2 | 3 | ## Parser 4 | The `cobraparser` is a tool to parse the output produced by the `spf13.CobraCommand` help function. The parser invokes an executable with `--help` and creates JSON structured data from the resulting output. Subcommands are traversed recursively. 5 | 6 | ## Generator Package 7 | The `generator` package contains the code to dynamically configure a `spf13.CobraCommand` using the JSON data produced by the parser. The command will be populated with the subcommands and flags defined in the JSON data. 8 | 9 | ## Sample usage: 10 | 11 | See `build-aux/main.mk` (the `pkg/client/cli/docker/compose/dc-cli.json` target) and the corresponding use of the generator package in `pkg/client/cli/docker/compose/config.go`. 12 | -------------------------------------------------------------------------------- /integration_test/testdata/echo-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS base 2 | 3 | # Build Delve 4 | RUN go install github.com/go-delve/delve/cmd/dlv@latest 5 | 6 | WORKDIR /echo-server 7 | COPY go.mod . 8 | COPY go.sum . 9 | # Get dependencies - will also be cached if we won't change mod/sum 10 | RUN go mod download 11 | COPY main.go . 12 | 13 | FROM base AS builder 14 | RUN go build -o echo-server . 15 | 16 | FROM base AS development 17 | RUN go build -gcflags="all=-N -l" -o echo-server . 18 | EXPOSE 40000 19 | CMD ["/go/bin/dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/echo-server/echo-server"] 20 | 21 | FROM alpine AS production 22 | COPY --from=builder /echo-server/echo-server / 23 | COPY certs certs 24 | CMD ["/echo-server"] 25 | -------------------------------------------------------------------------------- /pkg/tunnel/server_stream.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | func NewServerStream(ctx context.Context, tag Tag, grpcStream GRPCStream) (Stream, error) { 10 | s := &stream{tag: tag, grpcStream: grpcStream, syncRatio: 8, ackWindow: 1} 11 | m, err := s.Receive(ctx) 12 | if err != nil { 13 | return nil, fmt.Errorf("failed to read initial StreamInfo message: %w", err) 14 | } 15 | if m.Code() != streamInfo { 16 | return nil, errors.New("initial message was not StreamInfo") 17 | } 18 | if err = setConnectInfo(m, s); err != nil { 19 | return nil, fmt.Errorf("failed to parse StreamInfo message: %w", err) 20 | } 21 | if err = s.Send(ctx, StreamOKMessage()); err != nil { 22 | return nil, err 23 | } 24 | return s, nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/client/remotefs/fuseftp_docker.go: -------------------------------------------------------------------------------- 1 | //go:build docker 2 | 3 | package remotefs 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "sync" 9 | 10 | "github.com/telepresenceio/go-fuseftp/rpc" 11 | ) 12 | 13 | // NewFTPMounter returns nil. It's here to satisfy the linker. 14 | func NewFTPMounter(rpc.FuseFTPClient, *sync.WaitGroup) Mounter { 15 | return nil 16 | } 17 | 18 | type fuseFtpMgr struct{} 19 | 20 | func NewFuseFTPManager() FuseFTPManager { 21 | return &fuseFtpMgr{} 22 | } 23 | 24 | func (s *fuseFtpMgr) LinkedFTP() bool { 25 | return false 26 | } 27 | 28 | func (s *fuseFtpMgr) DeferInit(context.Context) error { 29 | return errors.New("fuseftp client is not available") 30 | } 31 | 32 | func (s *fuseFtpMgr) GetFuseFTPClient(context.Context) rpc.FuseFTPClient { 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/filelocation/osfile_linux.go: -------------------------------------------------------------------------------- 1 | package filelocation 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func userHomeDir() string { 10 | if v := os.Getenv("HOME"); v != "" { 11 | return v 12 | } 13 | panic("$HOME is not defined") 14 | } 15 | 16 | func userCacheDir(ctx context.Context) string { 17 | dir := os.Getenv("XDG_CACHE_HOME") 18 | if dir == "" { 19 | dir = filepath.Join(UserHomeDir(ctx), ".cache") 20 | } 21 | return dir 22 | } 23 | 24 | func userConfigDir(ctx context.Context) string { 25 | dir := os.Getenv("XDG_CONFIG_HOME") 26 | if dir == "" { 27 | dir = filepath.Join(UserHomeDir(ctx), ".config") 28 | } 29 | return dir 30 | } 31 | 32 | func appUserLogDir(ctx context.Context) string { 33 | return filepath.Join(AppUserCacheDir(ctx), "logs") 34 | } 35 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_version.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence version 3 | description: Show version 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Show version 8 | 9 | ### Usage: 10 | ``` 11 | telepresence version [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -h, --help help for version 17 | ``` 18 | 19 | ### Global Flags: 20 | ``` 21 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 22 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 23 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 24 | --use string Match expression that uniquely identifies the daemon container 25 | ``` 26 | -------------------------------------------------------------------------------- /integration_test/list_watch_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 8 | ) 9 | 10 | func (s *connectedSuite) Test_ListWatch() { 11 | svc := "echo-easy" 12 | 13 | s.Run("-C", func() { 14 | // Use a context to end tele list -w 15 | ctx := s.Context() 16 | cancelctx, cancel := context.WithCancel(ctx) 17 | ch := make(chan string) 18 | go func() { 19 | stdout, _, _ := itest.Telepresence(cancelctx, "list", "--output", "json-stream") 20 | ch <- stdout 21 | }() 22 | time.Sleep(time.Second) 23 | s.ApplyApp(ctx, svc, "deploy/"+svc) 24 | defer s.DeleteSvcAndWorkload(ctx, "deploy", svc) 25 | time.Sleep(time.Second) 26 | cancel() 27 | s.Contains(<-ch, svc) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/client/bwcompat/clusterinfo.go: -------------------------------------------------------------------------------- 1 | package bwcompat 2 | 3 | import ( 4 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 5 | "github.com/telepresenceio/telepresence/v2/pkg/iputil" 6 | ) 7 | 8 | func FixLegacyClusterInfo(mgrInfo *manager.ClusterInfo) { 9 | // Older clients use index 1 in the ClusterInfo to pass the kube-dns IP. It 10 | // will manifest itself as one entry of len 4 in the servicecidrs. 11 | if len(mgrInfo.ServiceCidrs) == 1 && len(mgrInfo.ServiceCidrs[0]) == 4 { 12 | // Older client with no support for multiple service subnets 13 | if mgrInfo.ServiceSubnet != nil { 14 | cidr := iputil.RPCToPrefix(mgrInfo.ServiceSubnet) 15 | sb, _ := cidr.MarshalBinary() 16 | mgrInfo.ServiceCidrs = [][]byte{sb} 17 | } else { 18 | mgrInfo.ServiceCidrs = nil 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pkg/client/config_util.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/log" 7 | ) 8 | 9 | // ReloadDaemonLogLevel calls SetLevel with the log level defined 10 | // for the rootDaemon or userDaemon 11 | // depending on the root flag. Assumes that the config has already been reloaded. 12 | func ReloadDaemonLogLevel(c context.Context) { 13 | newCfg := GetConfig(c) 14 | var level string 15 | levels := newCfg.LogLevels() 16 | switch ProcessName() { 17 | case RootDaemonName: 18 | level = levels.RootDaemon.String() 19 | case UserDaemonName: 20 | level = levels.UserDaemon.String() 21 | case KubeAuthDaemonName: 22 | level = levels.KubeAuthDaemon.String() 23 | default: 24 | level = levels.CLI.String() 25 | } 26 | log.SetLevel(c, level) 27 | } 28 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_curl.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence curl 3 | description: curl with daemon network 4 | hide_table_of_contents: true 5 | --- 6 | 7 | curl with daemon network 8 | 9 | ### Usage: 10 | ``` 11 | telepresence curl 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -h, --help help for curl 17 | ``` 18 | 19 | ### Global Flags: 20 | ``` 21 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 22 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 23 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 24 | --use string Match expression that uniquely identifies the daemon container 25 | ``` 26 | -------------------------------------------------------------------------------- /pkg/client/docker/volume_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/blang/semver/v4" 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | "github.com/telepresenceio/telepresence/v2/pkg/client" 11 | ) 12 | 13 | func Test_getLatestPluginVersion(t *testing.T) { 14 | c := dlog.NewTestContext(t, false) 15 | env, err := client.LoadEnv() 16 | require.NoError(t, err) 17 | c = client.WithEnv(c, env) 18 | 19 | cfg, err := client.LoadConfig(c) 20 | require.NoError(t, err) 21 | c = client.WithConfig(c, cfg) 22 | 23 | di := client.DockerImage(cfg.Docker().Telemount) 24 | ver, err := getLatestPluginVersion(c, pluginName(&di), &di) 25 | require.NoError(t, err) 26 | require.True(t, ver.EQ(zeroVersion) || semver.MustParse("0.1.3").LT(ver)) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/iputil/parse.go: -------------------------------------------------------------------------------- 1 | package iputil 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/netip" 7 | ) 8 | 9 | // ParseAddr is like netip.ParseAddr but removes any IPv4-mapped IPv6 address prefix. 10 | func ParseAddr(ipStr string) (ip netip.Addr, err error) { 11 | if ip, err = netip.ParseAddr(ipStr); err == nil { 12 | ip = ip.Unmap() 13 | } 14 | return ip, err 15 | } 16 | 17 | // SplitToIPPort splits the given address into an IP and a port number. It's 18 | // an error if the address is based on a hostname rather than an IP. 19 | func SplitToIPPort(netAddr net.Addr) (netip.AddrPort, error) { 20 | ipAddr, ok := netAddr.(interface{ AddrPort() netip.AddrPort }) 21 | if !ok { 22 | return netip.AddrPort{}, fmt.Errorf("address %q is not an IP:port address", netAddr) 23 | } 24 | return ipAddr.AddrPort(), nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/client/install_id.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "io/fs" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/google/uuid" 11 | 12 | "github.com/telepresenceio/telepresence/v2/pkg/dos" 13 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 14 | ) 15 | 16 | func InstallID(ctx context.Context) (string, error) { 17 | idFile := filepath.Join(filelocation.AppUserConfigDir(ctx), "id") 18 | data, err := dos.ReadFile(ctx, idFile) 19 | switch { 20 | case err == nil: 21 | return strings.TrimSpace(string(data)), nil 22 | case errors.Is(err, fs.ErrNotExist): 23 | id := uuid.New().String() 24 | if err = dos.WriteFile(ctx, idFile, []byte(id), 0o644); err != nil { 25 | return "", err 26 | } 27 | return id, nil 28 | default: 29 | return "", err 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packaging/bundle.wxs.in: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /pkg/filelocation/osfile_windows.go: -------------------------------------------------------------------------------- 1 | package filelocation 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func userHomeDir() string { 10 | if v := os.Getenv("USERPROFILE"); v != "" { 11 | return v 12 | } 13 | panic("%userprofile% is not defined") 14 | } 15 | 16 | func userCacheDir(ctx context.Context) string { 17 | dir := os.Getenv("LocalAppData") 18 | if dir == "" { 19 | dir = filepath.Join(UserHomeDir(ctx), "AppData", "Local") 20 | } 21 | return dir 22 | } 23 | 24 | func userConfigDir(ctx context.Context) string { 25 | dir := os.Getenv("AppData") 26 | if dir == "" { 27 | dir = filepath.Join(UserHomeDir(ctx), "AppData", "Roaming") 28 | } 29 | return dir 30 | } 31 | 32 | func appUserLogDir(ctx context.Context) string { 33 | return filepath.Join(AppUserCacheDir(ctx), "Logs") 34 | } 35 | -------------------------------------------------------------------------------- /examples/docker/iam-authenticator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS auth-builder 2 | RUN go install sigs.k8s.io/aws-iam-authenticator/cmd/aws-iam-authenticator@latest 3 | 4 | # Dockerfile with telepresence and its prerequisites 5 | FROM alpine 6 | 7 | # Install Telepresence prerequisites 8 | RUN apk add --no-cache curl iproute2 sshfs 9 | 10 | # Download and install the telepresence binary 11 | RUN curl -fL https://github.com/telepresenceio/telepresence/releases/download/v2.24.0-rc.0/telepresence-linux-amd64 -o telepresence && \ 12 | install -o root -g root -m 0755 telepresence /usr/local/bin/telepresence 13 | 14 | COPY --from=auth-builder /go/bin/aws-iam-authenticator ./aws-iam-authenticator 15 | RUN install -o root -g root -m 0755 aws-iam-authenticator /usr/local/bin/aws-iam-authenticator && \ 16 | rm aws-iam-authenticator 17 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: echo-no-svc 5 | labels: 6 | app: echo-no-svc 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: echo-no-svc 12 | template: 13 | metadata: 14 | labels: 15 | app: echo-no-svc 16 | spec: 17 | automountServiceAccountToken: false 18 | containers: 19 | - name: echo-server 20 | image: ghcr.io/telepresenceio/echo-server:latest 21 | imagePullPolicy: IfNotPresent 22 | ports: 23 | - name: http 24 | containerPort: 8080 25 | env: 26 | - name: PORT 27 | value: "8080" 28 | resources: 29 | limits: 30 | cpu: 50m 31 | memory: 8Mi 32 | -------------------------------------------------------------------------------- /pkg/client/remotefs/fuseftp_embedded.go: -------------------------------------------------------------------------------- 1 | //go:build !(docker || external_fuseftp || linked_fuseftp) 2 | 3 | package remotefs 4 | 5 | import ( 6 | "context" 7 | _ "embed" 8 | "errors" 9 | "io/fs" 10 | "os" 11 | "path/filepath" 12 | 13 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 14 | ) 15 | 16 | //go:embed fuseftp.bits 17 | var fuseftpBits []byte 18 | 19 | func getFuseFTPServer(ctx context.Context, exe string) (string, error) { 20 | qn := filepath.Join(filelocation.AppUserCacheDir(ctx), exe) 21 | var sz int 22 | st, err := os.Stat(qn) 23 | if err != nil { 24 | if !errors.Is(err, fs.ErrNotExist) { 25 | return "", err 26 | } 27 | sz = 0 28 | } else { 29 | sz = int(st.Size()) 30 | } 31 | if len(fuseftpBits) != sz { 32 | err = os.WriteFile(qn, fuseftpBits, 0o700) 33 | } 34 | return qn, err 35 | } 36 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_docker-run.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence docker-run 3 | description: Docker run with daemon network 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Docker run with daemon network 8 | 9 | ### Usage: 10 | ``` 11 | telepresence docker-run 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -h, --help help for docker-run 17 | ``` 18 | 19 | ### Global Flags: 20 | ``` 21 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 22 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 23 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 24 | --use string Match expression that uniquely identifies the daemon container 25 | ``` 26 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | {{- if not (and .Values.rbac .Values.rbac.only) }} 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: "{{ include "traffic-manager.name" $ }}-test-connection" 6 | namespace: {{ include "traffic-manager.namespace" $ }} 7 | labels: 8 | {{- include "telepresence.labels" $ | nindent 4 }} 9 | annotations: 10 | "helm.sh/hook": test-success 11 | spec: 12 | {{- with .Values.hooks.busybox.imagePullSecrets }} 13 | imagePullSecrets: 14 | {{- toYaml . | nindent 2 }} 15 | {{- end }} 16 | containers: 17 | - name: wget 18 | image: "{{ .Values.hooks.busybox.registry }}/{{ .Values.hooks.busybox.image }}:{{ .Values.hooks.busybox.tag }}" 19 | command: ['wget'] 20 | args: ['{{ include "traffic-manager.name" $ }}:8081'] 21 | restartPolicy: Never 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-containerport.yaml: -------------------------------------------------------------------------------- 1 | # The echo-no-containerport deployment doesn't expose any ports but will reply on port 8080 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: echo-no-containerport 6 | labels: 7 | app: echo-no-containerport 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: echo-no-containerport 13 | template: 14 | metadata: 15 | labels: 16 | app: echo-no-containerport 17 | spec: 18 | containers: 19 | - name: echo-server 20 | image: ghcr.io/telepresenceio/echo-server:latest 21 | imagePullPolicy: IfNotPresent 22 | env: 23 | - name: PORTS 24 | value: "8080,8081" 25 | resources: 26 | limits: 27 | cpu: 50m 28 | memory: 8Mi 29 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pv.goyaml: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/v2/integration_test/itest.PersistentVolume*/ -}} 2 | apiVersion: v1 3 | kind: PersistentVolume 4 | metadata: 5 | name: {{ .Name }} 6 | labels: 7 | purpose: tp-cli-testing 8 | {{- with .Annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | nodeAffinity: 14 | required: 15 | nodeSelectorTerms: 16 | - matchExpressions: 17 | - key: kubernetes.io/os 18 | operator: In 19 | values: 20 | - linux 21 | capacity: 22 | storage: 0.1Gi 23 | accessModes: 24 | - ReadWriteMany 25 | persistentVolumeReclaimPolicy: Retain 26 | hostPath: 27 | path: /tmp/{{ .Name }}-pv 28 | {{- with .StorageClassName }} 29 | storageClassName: {{ . }} 30 | {{- end }} 31 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/traffic-manager-5c69859f94-g4ntj: -------------------------------------------------------------------------------- 1 | 2021-09-16 17:06:51.8710 info Logging at this level "info" 2 | 2021-09-16 17:06:51.8711 info Traffic Manager v2.4.3-98-g0585bbd1-1631811285 [pid:1] 3 | 2021-09-16 17:06:51.9889 info Using DNS IP from kube-dns.kube-system 4 | 2021-09-16 17:06:51.9959 info Using cluster domain "cluster.local." 5 | 2021-09-16 17:06:52.0282 info Extracting service subnet 10.43.0.0/16 from create service error message 6 | 2021-09-16 17:06:52.0572 info Deriving subnets from podCIRs of nodes 7 | 2021-09-16 17:06:52.0852 info agent-injector : Mutating webhook service is listening on :8443 8 | 2021/09/16 17:06:52 Patching synced Node fe5953af-7777-4787-accf-719601327fb6 9 | 2021-09-16 17:07:03.4843 info agent-injector : Injecting traffic-agent into pod echo-auto-inject-6496f77cbd-.default 10 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | A few sentences describing the overall goals of the pull request's commits. 4 | 5 | ## Checklist 6 | 7 | 11 | 12 | - [ ] I made sure to update `./CHANGELOG.yml` (our release notes are generated from this file). 13 | - [ ] I made sure to add any documentation changes required for my change. 14 | - [ ] My change is adequately tested. 15 | - [ ] I updated `CONTRIBUTING.md` with any special dev tricks I had to use to work on this code efficiently. 16 | - [ ] Once my PR is ready to have integration tests ran, I posted the PR in #telepresence-oss channel on the 17 | [CNCF Slack](https://slack.cncf.io/) so that the "ok to test" label can be applied. -------------------------------------------------------------------------------- /pkg/client/cli/cmd/intercept.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 8 | ) 9 | 10 | func interceptCmd() *cobra.Command { 11 | ic := &intercept.Command{} 12 | cmd := &cobra.Command{ 13 | Use: "intercept [flags] [-- [[docker run flags] ] OR []] args...]", 14 | Args: cobra.MinimumNArgs(1), 15 | Short: "Intercept a service", 16 | Annotations: map[string]string{ 17 | ann.Session: ann.Required, 18 | ann.UpdateCheckFormat: ann.Tel2, 19 | }, 20 | SilenceUsage: true, 21 | SilenceErrors: true, 22 | RunE: ic.Run, 23 | ValidArgsFunction: intercept.ValidArgs, 24 | } 25 | ic.AddInterceptFlags(cmd) 26 | return cmd 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/replace.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 8 | ) 9 | 10 | func replaceCmd() *cobra.Command { 11 | ic := &intercept.Command{} 12 | cmd := &cobra.Command{ 13 | Use: "replace [flags] [-- [[docker run flags] ] OR []] args...]", 14 | Args: cobra.MinimumNArgs(1), 15 | Short: "Replace a container", 16 | Annotations: map[string]string{ 17 | ann.Session: ann.Required, 18 | ann.UpdateCheckFormat: ann.Tel2, 19 | }, 20 | SilenceUsage: true, 21 | SilenceErrors: true, 22 | RunE: ic.RunReplace, 23 | ValidArgsFunction: intercept.ValidArgs, 24 | } 25 | ic.AddReplaceFlags(cmd) 26 | return cmd 27 | } 28 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_helm_version.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence helm version 3 | description: Print the version of the Helm client 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Print the version of the Helm client 8 | 9 | ### Usage: 10 | ``` 11 | telepresence helm version [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -h, --help help for version 17 | ``` 18 | 19 | ### Global Flags: 20 | ``` 21 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 22 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 23 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 24 | --use string Match expression that uniquely identifies the daemon container 25 | ``` 26 | -------------------------------------------------------------------------------- /integration_test/itest/logdir.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "regexp" 9 | 10 | "github.com/datawire/dlib/dlog" 11 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 12 | ) 13 | 14 | func CleanLogDir(ctx context.Context, require *Requirements, nsRx, mgrNamespace, svcNameRx string) { 15 | logDir := filelocation.AppUserLogDir(ctx) 16 | files, err := os.ReadDir(logDir) 17 | require.NoError(err) 18 | match := regexp.MustCompile( 19 | fmt.Sprintf(`^(?:traffic-manager-[0-9a-z-]+\.%s|%s-[0-9a-z-]+\.%s)\.(?:log|yaml)$`, 20 | mgrNamespace, svcNameRx, nsRx)) 21 | 22 | for _, file := range files { 23 | if match.MatchString(file.Name()) { 24 | dlog.Infof(ctx, "Deleting log-file %s", file.Name()) 25 | require.NoError(os.Remove(filepath.Join(logDir, file.Name()))) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/wiretap.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 8 | ) 9 | 10 | func wiretapCmd() *cobra.Command { 11 | ic := &intercept.Command{ 12 | Wiretap: true, 13 | } 14 | cmd := &cobra.Command{ 15 | Use: "wiretap [flags] [-- ]", 16 | Args: cobra.MinimumNArgs(1), 17 | Short: "Wiretap a Service", 18 | Annotations: map[string]string{ 19 | ann.Session: ann.Required, 20 | ann.UpdateCheckFormat: ann.Tel2, 21 | }, 22 | SilenceUsage: true, 23 | SilenceErrors: true, 24 | RunE: ic.Run, 25 | ValidArgsFunction: intercept.ValidArgs, 26 | } 27 | ic.AddInterceptFlags(cmd) 28 | return cmd 29 | } 30 | -------------------------------------------------------------------------------- /pkg/k8sapi/kind.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import "slices" 4 | 5 | type Kind string 6 | 7 | const ( 8 | ServiceKind Kind = "Service" 9 | PodKind Kind = "Pod" 10 | DeploymentKind Kind = "Deployment" 11 | StatefulSetKind Kind = "StatefulSet" 12 | ReplicaSetKind Kind = "ReplicaSet" 13 | RolloutKind Kind = "Rollout" 14 | ) 15 | 16 | type Kinds []Kind 17 | 18 | func (k Kinds) Contains(kind Kind) bool { 19 | return slices.Contains(k, kind) 20 | } 21 | 22 | var ( 23 | KnownKinds = Kinds{ServiceKind, PodKind, DeploymentKind, StatefulSetKind, ReplicaSetKind, RolloutKind} //nolint:gochecknoglobals // constant 24 | KnownWorkloadKinds = Kinds{DeploymentKind, ReplicaSetKind, StatefulSetKind, RolloutKind} //nolint:gochecknoglobals // constant 25 | ) 26 | 27 | func (w Kind) IsValid() bool { 28 | return KnownKinds.Contains(w) 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/go-dependency-submission.yaml: -------------------------------------------------------------------------------- 1 | name: Go Dependency Submission 2 | on: 3 | push: 4 | branches: 5 | - release/v2 6 | 7 | # The API requires write permission on the repository to submit dependencies 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | go-action-detection: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: 'Checkout Repository' 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - uses: actions/setup-go@v5 20 | with: 21 | go-version: stable 22 | # we need fuseftp.bits 23 | - name: "Build dependencies" 24 | run: make build-deps 25 | 26 | - name: Run snapshot action 27 | uses: actions/go-dependency-submission@v1 28 | with: 29 | go-mod-path: go.mod 30 | go-build-target: cmd/telepresence/main.go 31 | -------------------------------------------------------------------------------- /charts/telepresence-oss/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: telepresence-oss 3 | description: A chart for deploying the server-side components of Telepresence 4 | type: application 5 | version: "1.1.1-bogus.overwritten.by.chart.go" 6 | keywords: 7 | - ambassador 8 | - telepresence 9 | - traffic-manager 10 | sources: 11 | - https://github.com/telepresenceio/telepresence 12 | icon: https://raw.githubusercontent.com/telepresenceio/telepresence.io/master/src/assets/images/telepresence-edgy.svg 13 | 14 | # Note: This is the version of the Traffic Manager that will be installed by 15 | # this chart. The telepresence CLI will always attempt to update the Traffic 16 | # Manager if it is not the same version as the CLI so ensure you are keeping 17 | # these in sync. 18 | appVersion: "1.1.1-bogus.overwritten.by.chart.go" 19 | 20 | annotations: 21 | artifacthub.io/license: Apache-2.0 22 | -------------------------------------------------------------------------------- /pkg/client/logging/stat.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/telepresenceio/telepresence/v2/pkg/dos" 8 | ) 9 | 10 | // FStat returns the file status/info of an open file. 11 | func FStat(file dos.File) (SysInfo, error) { 12 | return osFStat(file) 13 | } 14 | 15 | // SysInfo represents the elaborate info in a FileInfo.Sys(). The implementations are 16 | // os specific. 17 | // 18 | // Unix: 19 | // 20 | // info.Sys().(*syscall.Stat_t) 21 | // 22 | // Windows: 23 | // 24 | // info.Sys().(*syscall.Win32FileAttributeData) 25 | type SysInfo interface { 26 | fmt.Stringer 27 | 28 | Size() int64 29 | 30 | BirthTime() time.Time 31 | ModifyTime() time.Time // most recent content change 32 | ChangeTime() time.Time // most recent metadata change 33 | 34 | SetOwnerAndGroup(name string) error 35 | 36 | HaveSameOwnerAndGroup(SysInfo) bool 37 | } 38 | -------------------------------------------------------------------------------- /cmd/teleroute/DEVELOPING.md: -------------------------------------------------------------------------------- 1 | # Docker network plugin for Telepresence 2 | 3 | ## Debugging 4 | 5 | Start by configuring telepresence to not check for the latest version of the plugin, but instead use our debug version by 6 | adding the following yaml to the `config.yml` (on Linux, this will be in `~/.config/telepresence/config.yml`, and on mac 7 | you'll find it in `"$HOME/Library/Application Support/telepresence/config.yml"`: 8 | ```yaml 9 | intercept: 10 | teleroute: 11 | tag: debug 12 | ``` 13 | 14 | Build the plugin for debugging. The command both builds and enables the plugin: 15 | ```console 16 | $ make debug 17 | ``` 18 | 19 | Use runc to tail the plugin's log output 20 | 21 | ```console 22 | sudo runc --root /run/docker/runtime-runc/plugins.moby exec $(docker plugin list --no-trunc -f capability=networkdriver -f enabled=true -q) tail -n 400 -f /var/log/teleroute.log 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_leave.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence leave 3 | description: Remove existing intercept 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Remove existing intercept 8 | 9 | ### Usage: 10 | ``` 11 | telepresence leave [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -c, --container string Container name 17 | -h, --help help for leave 18 | ``` 19 | 20 | ### Global Flags: 21 | ``` 22 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 23 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 24 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 25 | --use string Match expression that uniquely identifies the daemon container 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_quit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence quit 3 | description: Tell telepresence daemons to quit 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Tell telepresence daemons to quit 8 | 9 | ### Usage: 10 | ``` 11 | telepresence quit [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -h, --help help for quit 17 | -s, --stop-daemons stop all local telepresence daemons 18 | ``` 19 | 20 | ### Global Flags: 21 | ``` 22 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 23 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 24 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 25 | --use string Match expression that uniquely identifies the daemon container 26 | ``` 27 | -------------------------------------------------------------------------------- /.github/workflows/release-teleroute.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | # These aren't regexps. They are "Workflow Filter patterns" 5 | - teleroute-[0-9]+.[0-9]+.[0-9] 6 | 7 | jobs: 8 | build-release: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | architecture: 13 | - amd64 14 | - arm64 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-go@v5 19 | with: 20 | go-version: stable 21 | - name: Push image 22 | working-directory: ./cmd/teleroute 23 | env: 24 | PLUGIN_ARCH: ${{ matrix.architecture }} 25 | VER: ${{ github.ref }} 26 | run: | 27 | echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u='${{ github.actor }}' --password-stdin 28 | PLUGIN_VERSION=${VER#refs/tags/teleroute-} make push 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [thallgren] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /pkg/workload/util.go: -------------------------------------------------------------------------------- 1 | package workload 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/runtime" 5 | 6 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 7 | "github.com/telepresenceio/telepresence/v2/pkg/k8sapi" 8 | ) 9 | 10 | func FromAny(obj any) (k8sapi.Workload, bool) { 11 | if ro, ok := obj.(runtime.Object); ok { 12 | if wl, err := k8sapi.WrapWorkload(ro); err == nil { 13 | return wl, true 14 | } 15 | } 16 | return nil, false 17 | } 18 | 19 | func RpcKind(s k8sapi.Kind) manager.WorkloadInfo_Kind { 20 | switch s { 21 | case k8sapi.DeploymentKind: 22 | return manager.WorkloadInfo_DEPLOYMENT 23 | case k8sapi.ReplicaSetKind: 24 | return manager.WorkloadInfo_REPLICASET 25 | case k8sapi.StatefulSetKind: 26 | return manager.WorkloadInfo_STATEFULSET 27 | case k8sapi.RolloutKind: 28 | return manager.WorkloadInfo_ROLLOUT 29 | default: 30 | return manager.WorkloadInfo_UNSPECIFIED 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /k8s/local-echo-easy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-easy" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-easy 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-easy" 19 | labels: 20 | app: echo-easy 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-easy 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-easy 30 | spec: 31 | containers: 32 | - name: echo-easy 33 | image: localhost:5000/echo-server:latest 34 | imagePullPolicy: Always 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /k8s/local-echo-next.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-next" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-next 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-next" 19 | labels: 20 | app: echo-next 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-next 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-next 30 | spec: 31 | containers: 32 | - name: echo-next 33 | image: localhost:5000/echo-server:latest 34 | imagePullPolicy: Always 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_serve.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence serve 3 | description: Start the browser on a remote service 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Start the browser on a remote service 8 | 9 | ### Usage: 10 | ``` 11 | telepresence serve [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -h, --help help for serve 17 | -p, --port uint16 service port (default 80) 18 | ``` 19 | 20 | ### Global Flags: 21 | ``` 22 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 23 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 24 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 25 | --use string Match expression that uniquely identifies the daemon container 26 | ``` 27 | -------------------------------------------------------------------------------- /pkg/vif/logging.go: -------------------------------------------------------------------------------- 1 | package vif 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "gvisor.dev/gvisor/pkg/log" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | ) 11 | 12 | type dlogEmitter struct { 13 | context.Context 14 | } 15 | 16 | func (l dlogEmitter) Emit(_ int, level log.Level, _ time.Time, format string, v ...interface{}) { //nolint:goprintffuncname // not our API 17 | switch level { 18 | case log.Debug: 19 | dlog.Debugf(l, format, v...) 20 | case log.Info: 21 | dlog.Infof(l, format, v...) 22 | case log.Warning: 23 | dlog.Warnf(l, format, v...) 24 | } 25 | } 26 | 27 | func InitLogger(ctx context.Context) { 28 | log.SetTarget(&dlogEmitter{Context: ctx}) 29 | var gl log.Level 30 | switch dlog.MaxLogLevel(ctx) { 31 | case dlog.LogLevelInfo: 32 | gl = log.Info 33 | case dlog.LogLevelDebug, dlog.LogLevelTrace: 34 | gl = log.Debug 35 | default: 36 | gl = log.Warning 37 | } 38 | log.SetLevel(gl) 39 | } 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for Telepresence 4 | 5 | --- 6 | 7 | **Please describe your use case / problem.** 8 | A clear and concise description of your use case / problem. The "why" is really valuable to us. For example, "I have a set of services whose clients have long-lived connections." 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Versions (please complete the following information)** 17 | - Output of `telepresence version` (just in case the feature exists in future versions) 18 | - Kubernetes Environment and Version 19 | 20 | **Additional context** 21 | Add any other context about the feature request here. 22 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_config_view.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence config view 3 | description: View current Telepresence configuration 4 | hide_table_of_contents: true 5 | --- 6 | 7 | View current Telepresence configuration 8 | 9 | ### Usage: 10 | ``` 11 | telepresence config view [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -c, --client-only Only view config from client file. 17 | -h, --help help for view 18 | ``` 19 | 20 | ### Global Flags: 21 | ``` 22 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 23 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 24 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 25 | --use string Match expression that uniquely identifies the daemon container 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_uninstall.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence uninstall 3 | description: Uninstall telepresence agents 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Uninstall telepresence agents 8 | 9 | ### Usage: 10 | ``` 11 | telepresence uninstall [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -a, --all-agents uninstall intercept agent on all workloads 17 | -h, --help help for uninstall 18 | ``` 19 | 20 | ### Global Flags: 21 | ``` 22 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 23 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 24 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 25 | --use string Match expression that uniquely identifies the daemon container 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_status.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence status 3 | description: Show connectivity status 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Show connectivity status 8 | 9 | ### Usage: 10 | ``` 11 | telepresence status [flags] 12 | ``` 13 | 14 | ### Flags: 15 | ``` 16 | -h, --help help for status 17 | --multi-daemon always use multi-daemon output format, even if there's only one daemon connected 18 | ``` 19 | 20 | ### Global Flags: 21 | ``` 22 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 23 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 24 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 25 | --use string Match expression that uniquely identifies the daemon container 26 | ``` 27 | -------------------------------------------------------------------------------- /integration_test/loglevel_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "path/filepath" 7 | "regexp" 8 | 9 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 10 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 11 | ) 12 | 13 | func (s *notConnectedSuite) Test_RootDaemonLogLevel() { 14 | require := s.Require() 15 | ctx := s.Context() 16 | s.TelepresenceConnect(ctx) 17 | itest.TelepresenceQuitOk(ctx) 18 | rootLogName := filepath.Join(filelocation.AppUserLogDir(ctx), "daemon.log") 19 | rootLog, err := os.Open(rootLogName) 20 | require.NoError(err) 21 | defer rootLog.Close() 22 | 23 | hasDebug := false 24 | scn := bufio.NewScanner(rootLog) 25 | match := regexp.MustCompile(` debug +rootd/server`) 26 | for scn.Scan() && !hasDebug { 27 | hasDebug = match.MatchString(scn.Text()) 28 | } 29 | s.True(hasDebug, "daemon.log does not contain expected debug statements") 30 | } 31 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pol-none.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: pol-none 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: pol-none 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: pol-none 19 | labels: 20 | app: pol-none 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: pol-none 26 | template: 27 | metadata: 28 | labels: 29 | app: pol-none 30 | spec: 31 | containers: 32 | - name: echo 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /pkg/client/docker/daemon_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/ioutil" 7 | ) 8 | 9 | func TestSafeContainerName(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | want string 13 | }{ 14 | { 15 | "@", 16 | "a", 17 | }, 18 | { 19 | "@x", 20 | "ax", 21 | }, 22 | { 23 | "x@", 24 | "x_", 25 | }, 26 | { 27 | "x@y", 28 | "x_y", 29 | }, 30 | { 31 | "x™y", // multibyte char 32 | "x_y", 33 | }, 34 | { 35 | "x™", // multibyte char 36 | "x_", 37 | }, 38 | { 39 | "_y", 40 | "ay", 41 | }, 42 | { 43 | "_y_", 44 | "ay_", 45 | }, 46 | // TODO: Add test cases. 47 | } 48 | for _, tt := range tests { 49 | t.Run(tt.name, func(t *testing.T) { 50 | if got := ioutil.SafeName(tt.name); got != tt.want { 51 | t.Errorf("SafeName() = %v, want %v", got, tt.want) 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | Congratulations! 3 | 4 | 5 | You have successfully installed the Traffic Manager component of Telepresence! 6 | Now your users will be able to `telepresence connect` to this Cluster and create 7 | intercepts for their services! 8 | 9 | -------------------------------------------------------------------------------- 10 | Next Steps 11 | -------------------------------------------------------------------------------- 12 | 13 | - Take a look at our RBAC documentation for setting up the minimal required RBAC 14 | roles for your users at https://www.telepresence.io/docs/reference/rbac 15 | 16 | - Ensure that you are keeping up to date with Telepresence releases 17 | https://github.com/telepresenceio/telepresence/releases so that your Traffic 18 | Manager is the same version as the telepresence client your users are running! 19 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-one.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-one" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-one 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-one" 19 | labels: 20 | app: echo-one 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-one 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-one 30 | spec: 31 | containers: 32 | - name: echo-one 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /pkg/dos/exe.go: -------------------------------------------------------------------------------- 1 | package dos 2 | 3 | import ( 4 | "context" 5 | "os" 6 | ) 7 | 8 | // Exe is an abstraction of the executable related functions with the same name in the os package. 9 | type Exe interface { 10 | Executable() (string, error) 11 | } 12 | 13 | type exeKey struct{} 14 | 15 | func WithExe(ctx context.Context, exe Exe) context.Context { 16 | return context.WithValue(ctx, exeKey{}, exe) 17 | } 18 | 19 | // ExeAPI returns the Exe that has been registered with the given context, or 20 | // the instance that delegates to the corresponding functions in the os package. 21 | func ExeAPI(ctx context.Context) Exe { 22 | if e, ok := ctx.Value(exeKey{}).(Exe); ok { 23 | return e 24 | } 25 | return osExe{} 26 | } 27 | 28 | func Executable(ctx context.Context) (string, error) { 29 | return ExeAPI(ctx).Executable() 30 | } 31 | 32 | type osExe struct{} 33 | 34 | func (osExe) Executable() (string, error) { 35 | return os.Executable() 36 | } 37 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/managerutil/ctx.go: -------------------------------------------------------------------------------- 1 | package managerutil 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/datawire/dlib/dlog" 7 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 8 | "github.com/telepresenceio/telepresence/v2/pkg/tunnel" 9 | ) 10 | 11 | func WithSessionInfo(ctx context.Context, si *manager.SessionInfo) context.Context { 12 | if id := si.GetSessionId(); id != "" { 13 | return WithSessionID(ctx, tunnel.SessionID(id)) 14 | } 15 | return ctx 16 | } 17 | 18 | func WithSessionID(ctx context.Context, sessionID tunnel.SessionID) context.Context { 19 | ctx = context.WithValue(ctx, sessionContextKey{}, sessionID) 20 | ctx = dlog.WithField(ctx, "session_id", sessionID) 21 | return ctx 22 | } 23 | 24 | func GetSessionID(ctx context.Context) tunnel.SessionID { 25 | if id, ok := ctx.Value(sessionContextKey{}).(tunnel.SessionID); ok { 26 | return id 27 | } 28 | return "" 29 | } 30 | 31 | type sessionContextKey struct{} 32 | -------------------------------------------------------------------------------- /k8s/dnsutils-headless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: dnsutils-headless 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | service: dnsutils-headless 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: StatefulSet 17 | metadata: 18 | name: dnsutils-headless 19 | labels: 20 | service: dnsutils-headless 21 | spec: 22 | replicas: 1 23 | serviceName: dnsutils-headless 24 | selector: 25 | matchLabels: 26 | service: dnsutils-headless 27 | template: 28 | metadata: 29 | labels: 30 | service: dnsutils-headless 31 | spec: 32 | containers: 33 | - name: dnsutils-headless 34 | image: gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 35 | command: 36 | - sleep 37 | - "3600" 38 | imagePullPolicy: IfNotPresent 39 | restartPolicy: Always 40 | -------------------------------------------------------------------------------- /pkg/client/userd/service.go: -------------------------------------------------------------------------------- 1 | package userd 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc" 7 | 8 | rpc "github.com/telepresenceio/telepresence/rpc/v2/connector" 9 | "github.com/telepresenceio/telepresence/v2/pkg/client/remotefs" 10 | ) 11 | 12 | // A Service is one that runs during the entire lifecycle of the daemon. 13 | // This should be used to augment the daemon with GRPC services. 14 | type Service interface { 15 | // ListenerAddress returns the address that this service is listening to. 16 | ListenerAddress(ctx context.Context) string 17 | 18 | Server() *grpc.Server 19 | 20 | ConnectorServer() rpc.ConnectorServer 21 | 22 | // FuseFTPMgr returns the manager responsible for creating a client that can connect to the FuseFTP service. 23 | FuseFTPMgr() remotefs.FuseFTPManager 24 | 25 | RootSessionInProcess() bool 26 | TeleroutePort() uint16 27 | 28 | LinkedFTP() bool 29 | 30 | InitFTPServer(context.Context) error 31 | } 32 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/client_experiment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: "echo-easy" 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: echo-easy 11 | ports: 12 | - name: proxied 13 | port: 80 14 | targetPort: http 15 | --- 16 | apiVersion: v1 17 | kind: ServiceAccount 18 | metadata: 19 | name: telepresence-test-developer 20 | 21 | --- 22 | kind: Role 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | metadata: 25 | name: traffic-manager-connect 26 | rules: 27 | - apiGroups: [""] 28 | resources: ["services"] 29 | verbs: ["get", "list"] 30 | 31 | --- 32 | apiVersion: rbac.authorization.k8s.io/v1 33 | kind: RoleBinding 34 | metadata: 35 | name: traffic-manager-connect 36 | subjects: 37 | - kind: ServiceAccount 38 | name: telepresence-test-developer 39 | roleRef: 40 | apiGroup: rbac.authorization.k8s.io 41 | name: traffic-manager-connect 42 | kind: Role 43 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-easy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-easy" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-easy 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-easy" 19 | labels: 20 | app: echo-easy 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-easy 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-easy 30 | spec: 31 | containers: 32 | - name: echo-easy 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/ingest.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ingest" 8 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 9 | ) 10 | 11 | func ingestCmd() *cobra.Command { 12 | ic := &ingest.Command{} 13 | cmd := &cobra.Command{ 14 | Use: "ingest [flags] [-- [[docker run flags] ] OR []] args...]", 15 | Args: cobra.MinimumNArgs(1), 16 | Short: "Ingest a container", 17 | Annotations: map[string]string{ 18 | ann.Session: ann.Required, 19 | ann.UpdateCheckFormat: ann.Tel2, 20 | }, 21 | SilenceUsage: true, 22 | SilenceErrors: true, 23 | RunE: ic.Run, 24 | ValidArgsFunction: intercept.ValidArgs, // a list that this command shares with intercept 25 | } 26 | ic.AddFlags(cmd) 27 | return cmd 28 | } 29 | -------------------------------------------------------------------------------- /k8s/echo-sc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-sc" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | service: echo-sc 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-sc" 19 | labels: 20 | service: echo-sc 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | service: echo-sc 26 | template: 27 | metadata: 28 | labels: 29 | service: echo-sc 30 | spec: 31 | securityContext: 32 | fsGroup: 1000 33 | runAsUser: 1000 34 | containers: 35 | - name: echo-sc 36 | image: ghcr.io/telepresenceio/echo-server:latest 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/health.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc/health/grpc_health_v1" 7 | ) 8 | 9 | // Perhaps replace this health check stuff with something more normal, i.e. 10 | // based on HTTP, since we'll likely be running the Injector as an HTTP service 11 | // from this same executable anyhow. 12 | 13 | type HealthChecker struct { 14 | grpc_health_v1.UnimplementedHealthServer 15 | } 16 | 17 | func (s *HealthChecker) Check(ctx context.Context, _ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) { 18 | return &grpc_health_v1.HealthCheckResponse{ 19 | Status: grpc_health_v1.HealthCheckResponse_SERVING, 20 | }, nil 21 | } 22 | 23 | func (s *HealthChecker) Watch(_ *grpc_health_v1.HealthCheckRequest, stream grpc_health_v1.Health_WatchServer) error { 24 | return stream.Send(&grpc_health_v1.HealthCheckResponse{ 25 | Status: grpc_health_v1.HealthCheckResponse_SERVING, 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/rs-echo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: rs-echo 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: rs-echo 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: ReplicaSet 17 | metadata: 18 | name: rs-echo 19 | labels: 20 | app: rs-echo 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: rs-echo 26 | template: 27 | metadata: 28 | labels: 29 | app: rs-echo 30 | budget: telepresence-test 31 | spec: 32 | containers: 33 | - name: rs-echo 34 | image: ghcr.io/telepresenceio/echo-server:latest 35 | imagePullPolicy: IfNotPresent 36 | ports: 37 | - name: http 38 | containerPort: 8080 39 | resources: 40 | limits: 41 | cpu: 50m 42 | memory: 128Mi 43 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/ss-echo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: ss-echo 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ss-echo 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: StatefulSet 17 | metadata: 18 | name: ss-echo 19 | labels: 20 | app: ss-echo 21 | spec: 22 | serviceName: "ss-echo" 23 | replicas: 2 24 | selector: 25 | matchLabels: 26 | app: ss-echo 27 | template: 28 | metadata: 29 | labels: 30 | app: ss-echo 31 | budget: telepresence-test 32 | spec: 33 | containers: 34 | - name: ss-echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | resources: 40 | limits: 41 | cpu: 50m 42 | memory: 128Mi 43 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-svc-ann.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: echo-no-svc-ann 5 | labels: 6 | app: echo-no-svc-ann 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: echo-no-svc-ann 12 | template: 13 | metadata: 14 | labels: 15 | app: echo-no-svc-ann 16 | budget: telepresence-test 17 | annotations: 18 | telepresence.io/inject-container-ports: http 19 | spec: 20 | automountServiceAccountToken: false 21 | containers: 22 | - name: echo-server 23 | image: ghcr.io/telepresenceio/echo-server:latest 24 | imagePullPolicy: IfNotPresent 25 | ports: 26 | - name: http 27 | containerPort: 8080 28 | env: 29 | - name: PORT 30 | value: "8080" 31 | resources: 32 | limits: 33 | cpu: 50m 34 | memory: 8Mi 35 | -------------------------------------------------------------------------------- /pkg/agentmap/capsbase26_test.go: -------------------------------------------------------------------------------- 1 | package agentmap 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCapsBase26(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | v uint64 14 | want string 15 | }{ 16 | { 17 | "zero", 18 | 0, 19 | "A", 20 | }, 21 | { 22 | "25", 23 | 25, 24 | "Z", 25 | }, 26 | { 27 | "26", 28 | 26, 29 | "BA", 30 | }, 31 | { 32 | "51", 33 | 26 + 25, 34 | "BZ", 35 | }, 36 | { 37 | "52", 38 | 2 * 26, 39 | "CA", 40 | }, 41 | { 42 | "1351", 43 | 2*26*26 - 1, 44 | "BZZ", 45 | }, 46 | { 47 | "1352", 48 | 2 * 26 * 26, 49 | "CAA", 50 | }, 51 | { 52 | "maxuint", 53 | math.MaxUint64, 54 | "HLHXCZMXSYUMQP", 55 | }, 56 | } 57 | for _, tt := range tests { 58 | t.Run(tt.name, func(t *testing.T) { 59 | assert.Equalf(t, tt.want, CapsBase26(tt.v), "CapsBase26(%v)", tt.v) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /k8s/manager.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: traffic-manager 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | app: traffic-manager 11 | telepresence: manager 12 | ports: 13 | - name: api 14 | port: 8081 15 | targetPort: api 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: traffic-manager 21 | labels: 22 | app: traffic-manager 23 | telepresence: manager 24 | spec: 25 | replicas: 1 26 | selector: 27 | matchLabels: 28 | app: traffic-manager 29 | telepresence: manager 30 | template: 31 | metadata: 32 | labels: 33 | app: traffic-manager 34 | telepresence: manager 35 | spec: 36 | containers: 37 | - name: traffic-manager 38 | image: ko://github.com/telepresenceio/telepresence/v2/cmd/traffic 39 | ports: 40 | - name: api 41 | containerPort: 8081 42 | restartPolicy: Always 43 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/curl.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "slices" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 9 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/global" 10 | ) 11 | 12 | func curlCmd() *cobra.Command { 13 | cmd := &cobra.Command{ 14 | Use: "curl", 15 | Short: "curl with daemon network", 16 | Args: cobra.ArbitraryArgs, 17 | Annotations: map[string]string{ 18 | ann.Session: ann.Optional, 19 | }, 20 | RunE: runCurl, 21 | ValidArgsFunction: cobra.NoFileCompletions, 22 | SilenceErrors: true, 23 | SilenceUsage: true, 24 | DisableFlagParsing: true, 25 | DisableFlagsInUseLine: true, 26 | DisableSuggestions: true, 27 | } 28 | return cmd 29 | } 30 | 31 | func runCurl(cmd *cobra.Command, args []string) error { 32 | global.SetProgressQuiet(cmd) 33 | return runDockerRun(cmd, slices.Insert(args, 0, "--rm", "curlimages/curl")) 34 | } 35 | -------------------------------------------------------------------------------- /integration_test/subdomain_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | 8 | "github.com/datawire/dlib/dlog" 9 | ) 10 | 11 | func (s *connectedSuite) Test_PodWithSubdomain() { 12 | c := s.Context() 13 | s.ApplyApp(c, "echo-w-subdomain", "deploy/echo-subsonic") 14 | defer func() { 15 | s.NoError(s.Kubectl(c, "delete", "svc", "subsonic")) 16 | s.NoError(s.Kubectl(c, "delete", "deploy", "echo-subsonic")) 17 | }() 18 | 19 | lookupHost := func(host string) { 20 | var err error 21 | s.Eventually(func() bool { 22 | c, cancel := context.WithTimeout(c, 1800*time.Millisecond) 23 | defer cancel() 24 | dlog.Info(c, "LookupHost("+host+")") 25 | _, err = net.DefaultResolver.LookupHost(c, host) 26 | return err == nil 27 | }, 10*time.Second, 2*time.Second, "%s did not resolve: %v", host, err) 28 | } 29 | lookupHost("echo.subsonic." + s.AppNamespace()) 30 | lookupHost("echo.subsonic." + s.AppNamespace() + ".svc.cluster.local") 31 | } 32 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/relnotes/relnotes.gomdx: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/tools/src/relnotesgen/relnotes.ChangeLog*/ -}} 2 | --- 3 | title: Release Notes 4 | --- 5 | 6 | import { Note, Title, Body } from '@site/src/components/ReleaseNotes' 7 | 8 | [comment]: # (Code generated by relnotesgen. DO NOT EDIT.) 9 | {{ $styles := .Styles -}} 10 | {{- $iconFormat := "images/%s.png" }} 11 | # Telepresence Release Notes 12 | {{- range .Items }} 13 | {{- if not (eq .DateString "(SUPERSEDED)")}} 14 | {{ if .Version}}## Version {{.Version}}{{- with .Date}} ({{- (.).UTC.Format "January _2"}}){{- end}}{{- end}} 15 | {{- end -}} 16 | {{- range .Notes }} 17 | 18 | {{.Title}} 19 | 20 | {{.Body}} 21 | 22 | {{- if .Image}} 23 | 24 | {{- end}} 25 | 26 | {{- end }} 27 | {{- end }} 28 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_mcp_start.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence mcp start 3 | description: Start the MCP server 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Start the MCP server 8 | 9 | ## Synopsis: 10 | 11 | Start stdio server to expose CLI commands to AI assistants 12 | 13 | ### Usage: 14 | ``` 15 | telepresence mcp start [flags] 16 | ``` 17 | 18 | ### Flags: 19 | ``` 20 | -h, --help help for start 21 | --log-level string Log level (debug, info, warn, error) 22 | ``` 23 | 24 | ### Global Flags: 25 | ``` 26 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 27 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 28 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 29 | --use string Match expression that uniquely identifies the daemon container 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_mcp_tools.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence mcp tools 3 | description: Export tools as JSON 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Export tools as JSON 8 | 9 | ## Synopsis: 10 | 11 | Export available MCP tools to mcp-tools.json for inspection 12 | 13 | ### Usage: 14 | ``` 15 | telepresence mcp tools [flags] 16 | ``` 17 | 18 | ### Flags: 19 | ``` 20 | -h, --help help for tools 21 | --log-level string Log level (debug, info, warn, error) 22 | ``` 23 | 24 | ### Global Flags: 25 | ``` 26 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 27 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 28 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 29 | --use string Match expression that uniquely identifies the daemon container 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_mcp_claude_list.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence mcp claude list 3 | description: Show Claude MCP servers 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Show Claude MCP servers 8 | 9 | ## Synopsis: 10 | 11 | Show all MCP servers configured in Claude Desktop 12 | 13 | ### Usage: 14 | ``` 15 | telepresence mcp claude list [flags] 16 | ``` 17 | 18 | ### Flags: 19 | ``` 20 | --config-path string Path to Claude config file 21 | -h, --help help for list 22 | ``` 23 | 24 | ### Global Flags: 25 | ``` 26 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 27 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 28 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 29 | --use string Match expression that uniquely identifies the daemon container 30 | ``` 31 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-same-target-port.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-stp 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-stp 10 | ports: 11 | - name: eighty 12 | port: 80 13 | targetPort: 8080 14 | - name: eighty-eighty 15 | port: 8080 16 | targetPort: 8080 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: echo-stp 22 | labels: 23 | app: echo-stp 24 | spec: 25 | replicas: 1 26 | selector: 27 | matchLabels: 28 | app: echo-stp 29 | template: 30 | metadata: 31 | labels: 32 | app: echo-stp 33 | spec: 34 | containers: 35 | - name: echo-stp 36 | image: ghcr.io/telepresenceio/echo-server:latest 37 | imagePullPolicy: IfNotPresent 38 | ports: 39 | - containerPort: 8080 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-w-subdomain.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: subsonic 6 | spec: 7 | selector: 8 | app: subsonic 9 | clusterIP: None 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo-subsonic 19 | labels: 20 | app: subsonic 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: subsonic 26 | template: 27 | metadata: 28 | labels: 29 | app: subsonic 30 | spec: 31 | hostname: echo 32 | subdomain: subsonic 33 | containers: 34 | - name: echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/namespaces.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: alpha 6 | labels: 7 | phase: a1 8 | scope: dev 9 | --- 10 | apiVersion: v1 11 | kind: Namespace 12 | metadata: 13 | name: beta 14 | labels: 15 | phase: a1 16 | scope: dev 17 | --- 18 | apiVersion: v1 19 | kind: Namespace 20 | metadata: 21 | name: alpha-test 22 | labels: 23 | phase: none 24 | scope: unscoped 25 | --- 26 | apiVersion: v1 27 | kind: Namespace 28 | metadata: 29 | name: alpha-rc 30 | labels: 31 | phase: a2 32 | scope: dev 33 | --- 34 | apiVersion: v1 35 | kind: Namespace 36 | metadata: 37 | name: beta-rc 38 | labels: 39 | phase: a2 40 | scope: dev 41 | --- 42 | apiVersion: v1 43 | kind: Namespace 44 | metadata: 45 | name: alpha-live 46 | labels: 47 | phase: ga 48 | scope: prod 49 | --- 50 | apiVersion: v1 51 | kind: Namespace 52 | metadata: 53 | name: beta-live 54 | labels: 55 | phase: ga 56 | scope: prod 57 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-headless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-headless 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | app: echo-headless 11 | ports: 12 | - name: http 13 | port: 8080 14 | targetPort: 8080 15 | --- 16 | apiVersion: apps/v1 17 | kind: StatefulSet 18 | metadata: 19 | name: echo-headless 20 | labels: 21 | app: echo-headless 22 | spec: 23 | replicas: 1 24 | serviceName: echo-headless 25 | selector: 26 | matchLabels: 27 | app: echo-headless 28 | template: 29 | metadata: 30 | labels: 31 | app: echo-headless 32 | spec: 33 | containers: 34 | - name: echo-headless 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | resources: 40 | limits: 41 | cpu: 50m 42 | memory: 128Mi 43 | 44 | -------------------------------------------------------------------------------- /integration_test/itest/assertions.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | type Requirements struct { 12 | *require.Assertions 13 | } 14 | 15 | func (r *Requirements) EventuallyContext(ctx context.Context, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { 16 | r.Eventually(func() bool { 17 | if ctx.Err() != nil { 18 | return true 19 | } 20 | return condition() 21 | }, waitFor, tick, msgAndArgs...) 22 | r.NoError(ctx.Err()) 23 | } 24 | 25 | type Assertions struct { 26 | *assert.Assertions 27 | } 28 | 29 | func (r *Assertions) EventuallyContext(ctx context.Context, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { 30 | return r.Eventually(func() bool { 31 | if ctx.Err() != nil { 32 | return true 33 | } 34 | return condition() 35 | }, waitFor, tick, msgAndArgs...) || r.NoError(ctx.Err()) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/client/cli/env/flags.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | type Flags struct { 8 | File string // --env-file 9 | Syntax Syntax // --env-syntax 10 | JSON string // --env-json 11 | } 12 | 13 | func (f *Flags) AddFlags(flagSet *pflag.FlagSet) { 14 | flagSet.StringVarP(&f.File, "env-file", "e", "", ``+ 15 | `Also emit the remote environment to an file. The syntax used in the file can be determined using flag --env-syntax`) 16 | 17 | flagSet.Var(&f.Syntax, "env-syntax", `Syntax used for env-file. One of `+SyntaxUsage()) 18 | 19 | flagSet.StringVarP(&f.JSON, "env-json", "j", "", `Also emit the remote environment to a file as a JSON blob.`) 20 | } 21 | 22 | func (f *Flags) MaybeWrite(env map[string]string) error { 23 | if f.File != "" { 24 | if err := f.Syntax.writeFile(f.File, env); err != nil { 25 | return err 26 | } 27 | } 28 | if f.JSON != "" { 29 | if err := SyntaxJSON.writeFile(f.JSON, env); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/authenticator/exec.go: -------------------------------------------------------------------------------- 1 | package authenticator 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | 8 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 9 | 10 | "github.com/telepresenceio/telepresence/v2/pkg/dos" 11 | "github.com/telepresenceio/telepresence/v2/pkg/proc" 12 | ) 13 | 14 | type execCredentialBinary struct{} 15 | 16 | func (e execCredentialBinary) Resolve( 17 | ctx context.Context, 18 | execConfig *clientcmdapi.ExecConfig, 19 | ) ([]byte, error) { 20 | var buf bytes.Buffer 21 | 22 | cmd := proc.CommandContext(ctx, execConfig.Command, execConfig.Args...) 23 | cmd.Stdout = &buf 24 | cmd.Stderr = dos.Stderr(ctx) 25 | cmd.Env = dos.Environ(ctx) 26 | if len(execConfig.Env) > 0 { 27 | em := dos.FromEnvPairs(cmd.Env) 28 | for _, ev := range execConfig.Env { 29 | em[ev.Name] = ev.Value 30 | } 31 | cmd.Env = em.Environ() 32 | } 33 | 34 | if err := cmd.Run(); err != nil { 35 | return nil, fmt.Errorf("failed to run host command: %w", err) 36 | } 37 | 38 | return buf.Bytes(), nil 39 | } 40 | -------------------------------------------------------------------------------- /k8s/apitest.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "apitest" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | service: apitest 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "apitest" 19 | labels: 20 | service: apitest 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | service: apitest 26 | template: 27 | metadata: 28 | labels: 29 | service: apitest 30 | spec: 31 | containers: 32 | - name: apitest 33 | image: localhost:5000/apiserveraccess:latest 34 | ports: 35 | - containerPort: 8080 36 | name: http 37 | env: 38 | - name: APP_PORT 39 | value: "8080" 40 | - name: LOG_LEVEL 41 | value: "DEBUG" 42 | resources: 43 | limits: 44 | cpu: 50m 45 | memory: 128Mi 46 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pol-enabled.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: pol-enabled 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: pol-enabled 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: pol-enabled 19 | labels: 20 | app: pol-enabled 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: pol-enabled 26 | template: 27 | metadata: 28 | annotations: 29 | telepresence.io/inject-traffic-agent: enabled 30 | labels: 31 | app: pol-enabled 32 | spec: 33 | containers: 34 | - name: echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /pkg/grpc/client/connect.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "google.golang.org/grpc" 8 | "google.golang.org/grpc/connectivity" 9 | ) 10 | 11 | func DialGRPC(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { 12 | conn, err := grpc.NewClient(addr, opts...) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | state := conn.GetState() 18 | conn.Connect() 19 | 20 | // Wait for the connection to reach READY state or fail 21 | for { 22 | switch state { 23 | case connectivity.Ready: 24 | // Connection is established 25 | return conn, nil 26 | case connectivity.Shutdown: 27 | conn.Close() 28 | return nil, fmt.Errorf("connection failed: state=%v", state) 29 | default: 30 | if !conn.WaitForStateChange(ctx, state) { 31 | // Normal. The context timed out before the connection reached READY state. 32 | conn.Close() 33 | return nil, fmt.Errorf("%v: %w", state, ctx.Err()) 34 | } 35 | state = conn.GetState() 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Telepresence Documentation 2 | 3 | This folder contains the Telepresence documentation in a format suitable for a versioned folder in the 4 | telepresenceio/telepresence.io repository. The folder will show up in that repository when a new minor revision 5 | tag is created here. 6 | 7 | Assuming that a 2.20.0 release is pending, and that a release/v2.20.0 branch has been created, then: 8 | ```console 9 | $ export TELEPRESENCE_VERSION=v2.20.0 10 | $ make prepare-release 11 | $ git push origin {,rpc/}v2.20.0 release/v2.20.0 12 | ``` 13 | 14 | will result in a `docs/v2.20` folder with this folder's contents in the telepresenceio/telepresence.io repository. 15 | 16 | Subsequent bugfix tags for the same minor tag, i.e.: 17 | ```console 18 | $ export TELEPRESENCE_VERSION=v2.20.1 19 | $ make prepare-release 20 | $ git push origin {,rpc/}v2.20.1 release/v2.20.1 21 | ``` 22 | will not result in a new folder when it is pushed, but it will update the content of the `docs/v2.20` folder to 23 | reflect this folder's content for that tag. 24 | -------------------------------------------------------------------------------- /pkg/client/logging/rotatingfile_windows.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "time" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | // IsTerminal returns whether the given file descriptor is a terminal. 10 | var IsTerminal = func(fd int) bool { //nolint:gochecknoglobals // os specific func replacement 11 | return false 12 | } 13 | 14 | // restoreCTimeAfterRename will restore the creation time on a file on Windows where 15 | // the file otherwise gets the creation time of the existing file that the operation 16 | // overwrites. 17 | func restoreCTimeAfterRename(path string, ctime time.Time) error { 18 | p16, e := windows.UTF16PtrFromString(path) 19 | if e != nil { 20 | return e 21 | } 22 | h, e := windows.CreateFile(p16, 23 | windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil, 24 | windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) 25 | if e != nil { 26 | return e 27 | } 28 | defer windows.Close(h) 29 | c := windows.NsecToFiletime(ctime.UnixNano()) 30 | return windows.SetFileTime(h, &c, nil, nil) 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PR' 2 | on: 3 | schedule: 4 | - cron: '12 10 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in 7 days.' 13 | stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment, or this will be closed in 14 days.' 14 | close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' 15 | close-pr-message: 'This PR was closed because it has been stalled for 14 days with no activity.' 16 | days-before-issue-stale: 60 17 | days-before-pr-stale: 30 18 | days-before-issue-close: 7 19 | days-before-pr-close: 14 20 | operations-per-run: 80 21 | exempt-issue-labels: feature,friction 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories 15 | vendor/ 16 | 17 | # Editor nonsense 18 | .vscode 19 | .idea 20 | *.iml 21 | *.swp 22 | .DS_Store 23 | 24 | build-output 25 | /tools/* 26 | !/tools/src/ 27 | 28 | # Don't accidentally add files from `go build` 29 | telepresence 30 | traffic 31 | tst-manager 32 | !/cmd/*/ 33 | mcp-tools.json 34 | 35 | # Include Chart directory 36 | !/charts/telepresence-oss 37 | # But don't include this one 38 | /charts/telepresence-oss/k8s-defs.json 39 | 40 | # Downloaded fuseftp bits 41 | fuseftp.bits 42 | 43 | # Needed to build on windows 44 | .wintools 45 | .gocache 46 | 47 | # Don't accidentally add binaries built byintegration tests 48 | /integration_test/testdata/echo-server/echo-server 49 | /build-aux/genversion/genversion 50 | 51 | # Vagrant 52 | .vagrant/ 53 | tests.log 54 | -------------------------------------------------------------------------------- /integration_test/itest/single_service.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | type SingleService interface { 10 | NamespacePair 11 | ServiceName() string 12 | } 13 | 14 | type singleService struct { 15 | NamespacePair 16 | serviceName string 17 | } 18 | 19 | func WithSingleService(h NamespacePair, serviceName string, f func(SingleService)) { 20 | h.HarnessT().Run(fmt.Sprintf("Test_Service_%s", serviceName), func(t *testing.T) { 21 | ctx := WithT(h.HarnessContext(), t) 22 | s := &singleService{NamespacePair: h, serviceName: serviceName} 23 | s.PushHarness(ctx, s.setup, s.tearDown) 24 | defer h.PopHarness() 25 | f(s) 26 | }) 27 | } 28 | 29 | func (h *singleService) setup(ctx context.Context) bool { 30 | h.ApplyEchoService(ctx, h.serviceName, 80) 31 | return true 32 | } 33 | 34 | func (h *singleService) tearDown(ctx context.Context) { 35 | h.DeleteSvcAndWorkload(ctx, "deploy", h.serviceName) 36 | } 37 | 38 | func (h *singleService) ServiceName() string { 39 | return h.serviceName 40 | } 41 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-double-one-unnamed.yaml: -------------------------------------------------------------------------------- 1 | # The echo-double-unnamed deployment exposes two unnamed ports, 8080 and 8081 from a single container 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: echo-double-one-unnamed 6 | labels: 7 | app: echo-double-one-unnamed 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: echo-double-one-unnamed 13 | template: 14 | metadata: 15 | annotations: 16 | telepresence.io/inject-container-ports: all 17 | labels: 18 | app: echo-double-one-unnamed 19 | spec: 20 | containers: 21 | - name: echo-server 22 | image: ghcr.io/telepresenceio/echo-server:latest 23 | imagePullPolicy: IfNotPresent 24 | ports: 25 | - containerPort: 8080 26 | - containerPort: 8081 27 | env: 28 | - name: PORTS 29 | value: "8080,8081" 30 | resources: 31 | limits: 32 | cpu: 50m 33 | memory: 8Mi 34 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pol-disabled.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: pol-disabled 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: pol-disabled 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: pol-disabled 19 | labels: 20 | app: pol-disabled 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: pol-disabled 26 | template: 27 | metadata: 28 | annotations: 29 | telepresence.io/inject-traffic-agent: disabled 30 | labels: 31 | app: pol-disabled 32 | spec: 33 | containers: 34 | - name: echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/quit.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/connect" 8 | ) 9 | 10 | func quit() *cobra.Command { 11 | quitDaemons := false 12 | cmd := &cobra.Command{ 13 | Use: "quit", 14 | Args: cobra.NoArgs, 15 | Short: "Tell telepresence daemons to quit", 16 | RunE: func(cmd *cobra.Command, _ []string) error { 17 | if quitDaemons { 18 | connect.InitProgressWriter(cmd) 19 | connect.Quit(cmd.Context()) 20 | } else { 21 | cmd.Annotations = map[string]string{ann.UserDaemon: ann.Optional} 22 | if err := connect.InitCommand(cmd); err != nil { 23 | return err 24 | } 25 | connect.Disconnect(cmd.Context()) 26 | } 27 | return nil 28 | }, 29 | ValidArgsFunction: cobra.NoFileCompletions, 30 | } 31 | flags := cmd.Flags() 32 | flags.BoolVarP(&quitDaemons, "stop-daemons", "s", false, "stop all local telepresence daemons") 33 | return cmd 34 | } 35 | -------------------------------------------------------------------------------- /pkg/routing/routing_test.go: -------------------------------------------------------------------------------- 1 | package routing 2 | 3 | import ( 4 | "net/netip" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | ) 11 | 12 | func TestGetRoutingTable_defaultRoute(t *testing.T) { 13 | ctx := dlog.NewTestContext(t, true) 14 | rt, err := GetRoutingTable(ctx) 15 | assert.NoError(t, err) 16 | var dflt *Route 17 | for _, r := range rt { 18 | if r.Default { 19 | dflt = r 20 | break 21 | } 22 | } 23 | assert.NotNil(t, dflt) 24 | assert.NotEqual(t, netip.IPv4Unspecified(), dflt.Gateway) 25 | assert.NotEqual(t, netip.IPv6Unspecified(), dflt.Gateway) 26 | } 27 | 28 | func TestGetRoutingTable(t *testing.T) { 29 | ctx := dlog.NewTestContext(t, true) 30 | rt, err := GetRoutingTable(ctx) 31 | assert.NoError(t, err) 32 | assert.NotEmpty(t, rt) 33 | for _, r := range rt { 34 | t.Logf("Route: %s", r) 35 | assert.False(t, r.LocalIP.IsUnspecified()) 36 | assert.NotZero(t, r.InterfaceIndex) 37 | assert.True(t, r.Default || !r.RoutedNet.Addr().IsUnspecified()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/informer/factory.go: -------------------------------------------------------------------------------- 1 | package informer 2 | 3 | import ( 4 | "k8s.io/client-go/informers" 5 | 6 | argorolloutsinformer "github.com/datawire/argo-rollouts-go-client/pkg/client/informers/externalversions" 7 | ) 8 | 9 | type GlobalFactory interface { 10 | GetK8sInformerFactory() informers.SharedInformerFactory 11 | GetArgoRolloutsInformerFactory() argorolloutsinformer.SharedInformerFactory 12 | } 13 | 14 | func NewDefaultGlobalFactory(k8s informers.SharedInformerFactory, argoRollouts argorolloutsinformer.SharedInformerFactory) GlobalFactory { 15 | return &defaultGlobalFactory{k8s: k8s, argoRollouts: argoRollouts} 16 | } 17 | 18 | type defaultGlobalFactory struct { 19 | k8s informers.SharedInformerFactory 20 | argoRollouts argorolloutsinformer.SharedInformerFactory 21 | } 22 | 23 | func (f *defaultGlobalFactory) GetK8sInformerFactory() informers.SharedInformerFactory { 24 | return f.k8s 25 | } 26 | 27 | func (f *defaultGlobalFactory) GetArgoRolloutsInformerFactory() argorolloutsinformer.SharedInformerFactory { 28 | return f.argoRollouts 29 | } 30 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Security updates will be provided for the latest 2.x release. 6 | 7 | 8 | ### How do we handle vulnerabilities 9 | 10 | #### User reports 11 | 12 | If you discover any security vulnerabilities, please follow these guidelines: 13 | 14 | - Email your findings to [secalert@tada.se](secalert@tada.se). 15 | - Provide sufficient details, including steps to reproduce the vulnerability. 16 | - Do not publicly disclose the issue until we have had a chance to address it. 17 | 18 | #### Dependabot 19 | 20 | We run dependabot against our repo. We also have it create PRs with the updates. 21 | 22 | One of the maintainers responsibilities is to review these PRs, make any necessary updates, 23 | and merge them in so that they go out in our next set of releases. 24 | 25 | #### Keeping Go updated 26 | 27 | We're set up to receive embargoed security announcements for Golang. When it happens, 28 | we create a new security incident, evaluate if we're impacted, and release a hotfix as soon as possible. 29 | 30 | -------------------------------------------------------------------------------- /pkg/log/discard.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "log" 7 | 8 | "github.com/datawire/dlib/dlog" 9 | ) 10 | 11 | type discard int 12 | 13 | func (d discard) Helper() { 14 | } 15 | 16 | func (d discard) Log(_ dlog.LogLevel, _ string) { 17 | } 18 | 19 | // We need to implement the UnformattedXXX functions to prevent that 20 | // dlog actually formats the messages prior to discarding them 21 | 22 | func (d discard) UnformattedLog(_ dlog.LogLevel, _ ...any) { 23 | } 24 | 25 | func (d discard) UnformattedLogf(_ dlog.LogLevel, _ string, _ ...any) { 26 | } 27 | 28 | func (d discard) UnformattedLogln(_ dlog.LogLevel, _ ...any) { 29 | } 30 | 31 | func (d discard) StdLogger(_ dlog.LogLevel) *log.Logger { 32 | return log.New(io.Discard, "", 0) 33 | } 34 | 35 | func (d discard) WithField(_ string, _ any) dlog.Logger { 36 | return d 37 | } 38 | 39 | // WithDiscardingLogger returns a context that discards all log output. 40 | func WithDiscardingLogger(ctx context.Context) context.Context { 41 | return dlog.WithLogger(ctx, discard(0)) 42 | } 43 | -------------------------------------------------------------------------------- /cmd/teleroute/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/cmd/teleroute 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | github.com/cenkalti/backoff/v4 v4.3.0 7 | github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 8 | github.com/puzpuzpuz/xsync/v4 v4.2.0 9 | github.com/sirupsen/logrus v1.9.3 10 | github.com/telepresenceio/telepresence/rpc/v2 v2.25.1 11 | google.golang.org/grpc v1.76.0 12 | google.golang.org/protobuf v1.36.10 13 | ) 14 | 15 | require ( 16 | github.com/Microsoft/go-winio v0.6.2 // indirect 17 | github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect 18 | github.com/docker/go-connections v0.6.0 // indirect 19 | github.com/google/go-cmp v0.7.0 // indirect 20 | github.com/stretchr/testify v1.8.2 // indirect 21 | golang.org/x/net v0.46.0 // indirect 22 | golang.org/x/sys v0.38.0 // indirect 23 | golang.org/x/text v0.30.0 // indirect 24 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251110190251-83f479183930 // indirect 25 | ) 26 | 27 | replace github.com/telepresenceio/telepresence/rpc/v2 => ./rpc 28 | -------------------------------------------------------------------------------- /integration_test/itest/suite.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stretchr/testify/suite" 7 | ) 8 | 9 | type TestingSuite interface { 10 | suite.TestingSuite 11 | Harness 12 | AmendSuiteContext(context.Context) context.Context 13 | Context() context.Context 14 | Assert() *Assertions 15 | Require() *Requirements 16 | SuiteName() string 17 | setContext(ctx context.Context) 18 | } 19 | 20 | type Suite struct { 21 | suite.Suite 22 | Harness 23 | ctx context.Context 24 | } 25 | 26 | func (s *Suite) AmendSuiteContext(ctx context.Context) context.Context { 27 | return ctx 28 | } 29 | 30 | //nolint:unused // Linter is confused about this one. 31 | func (s *Suite) setContext(ctx context.Context) { 32 | s.ctx = ctx 33 | } 34 | 35 | func (s *Suite) Context() context.Context { 36 | return WithT(s.ctx, s.T()) 37 | } 38 | 39 | func (s *Suite) Assert() *Assertions { 40 | return &Assertions{Assertions: s.Suite.Assert()} 41 | } 42 | 43 | func (s *Suite) Require() *Requirements { 44 | return &Requirements{Assertions: s.Suite.Require()} 45 | } 46 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-auto-inject.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-auto-inject 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-auto-inject 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo-auto-inject 19 | labels: 20 | app: echo-auto-inject 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-auto-inject 26 | template: 27 | metadata: 28 | annotations: 29 | telepresence.io/inject-traffic-agent: enabled 30 | labels: 31 | app: echo-auto-inject 32 | spec: 33 | containers: 34 | - name: echo-auto-inject 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /packaging/helmpackage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | 8 | "github.com/blang/semver/v4" 9 | 10 | telcharts "github.com/telepresenceio/telepresence/v2/charts" 11 | ) 12 | 13 | type sv semver.Version 14 | 15 | func (v *sv) String() string { 16 | return (*semver.Version)(v).String() 17 | } 18 | 19 | func (v *sv) Set(s string) error { 20 | ver, err := semver.Parse(s) 21 | if err == nil { 22 | *v = sv(ver) 23 | } 24 | return err 25 | } 26 | 27 | func main() { 28 | var output string 29 | var version sv 30 | flag.StringVar(&output, "o", "", "output file") 31 | flag.Var(&version, "v", "Helm chart version") 32 | flag.Parse() 33 | err := packageHelmChart(output, semver.Version(version)) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | } 38 | 39 | func packageHelmChart(filename string, version semver.Version) error { 40 | fh, err := os.Create(filename) 41 | if err != nil { 42 | return err 43 | } 44 | defer fh.Close() 45 | return telcharts.WriteChart(telcharts.DirTypeTelepresence, fh, telcharts.TelepresenceChartName, version) 46 | } 47 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 2 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 3 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 4 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 5 | google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= 6 | google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 7 | google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= 8 | google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 9 | google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= 10 | google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 11 | google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= 12 | google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 13 | -------------------------------------------------------------------------------- /k8s/echo-auto-headless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-auto-headless 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | service: echo-auto-headless 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: StatefulSet 17 | metadata: 18 | name: echo-auto-headless 19 | labels: 20 | service: echo-auto-headless 21 | spec: 22 | replicas: 1 23 | serviceName: echo-auto-headless 24 | selector: 25 | matchLabels: 26 | service: echo-auto-headless 27 | template: 28 | metadata: 29 | labels: 30 | service: echo-auto-headless 31 | annotations: 32 | telepresence.io/inject-traffic-agent: enabled 33 | telepresence.io/inject-service-ports: "8080" 34 | spec: 35 | containers: 36 | - name: echo-auto-headless 37 | image: jmalloc/echo-server 38 | ports: 39 | - containerPort: 8080 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | 45 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_mcp_cursor_list.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence mcp cursor list 3 | description: Show Cursor MCP servers 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Show Cursor MCP servers 8 | 9 | ## Synopsis: 10 | 11 | Show all MCP servers configured in Cursor 12 | 13 | ### Usage: 14 | ``` 15 | telepresence mcp cursor list [flags] 16 | ``` 17 | 18 | ### Flags: 19 | ``` 20 | --config-path string Path to Cursor config file 21 | -h, --help help for list 22 | --workspace List from workspace settings (.cursor/mcp.json) instead of user settings 23 | ``` 24 | 25 | ### Global Flags: 26 | ``` 27 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 28 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 29 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 30 | --use string Match expression that uniquely identifies the daemon container 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_mcp_vscode_list.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence mcp vscode list 3 | description: Show VSCode MCP servers 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Show VSCode MCP servers 8 | 9 | ## Synopsis: 10 | 11 | Show all MCP servers configured in VSCode 12 | 13 | ### Usage: 14 | ``` 15 | telepresence mcp vscode list [flags] 16 | ``` 17 | 18 | ### Flags: 19 | ``` 20 | --config-path string Path to VSCode config file 21 | -h, --help help for list 22 | --workspace List from workspace settings (.vscode/mcp.json) instead of user settings 23 | ``` 24 | 25 | ### Global Flags: 26 | ``` 27 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 28 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 29 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 30 | --use string Match expression that uniquely identifies the daemon container 31 | ``` 32 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-w-hostalias.goyaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo 19 | labels: 20 | app: echo 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo 26 | template: 27 | metadata: 28 | labels: 29 | app: echo 30 | spec: 31 | hostAliases: 32 | - ip: "{{ .AliasIP }}" 33 | hostnames: 34 | {{- range .Aliases }} 35 | - {{ . }} 36 | {{- end }} 37 | containers: 38 | - name: echo 39 | image: ghcr.io/telepresenceio/echo-server:latest 40 | imagePullPolicy: IfNotPresent 41 | ports: 42 | - containerPort: 8080 43 | name: http 44 | resources: 45 | limits: 46 | cpu: 50m 47 | memory: 128Mi 48 | -------------------------------------------------------------------------------- /pkg/client/cli/progress/quiet.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Docker Compose CLI authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package progress 18 | 19 | import "context" 20 | 21 | type quiet struct{} 22 | 23 | func (q quiet) Start(context.Context, string) { 24 | } 25 | 26 | func (q quiet) IsNoOp() bool { 27 | return false 28 | } 29 | 30 | func (q quiet) Stop() { 31 | } 32 | 33 | func (q quiet) Write(...*Event) { 34 | } 35 | 36 | func (q quiet) TailMsgf(_ string, _ ...any) { 37 | } 38 | 39 | func (q quiet) TriggerRefresh() { 40 | } 41 | -------------------------------------------------------------------------------- /pkg/matcher/header_stringer.go: -------------------------------------------------------------------------------- 1 | package matcher 2 | 3 | import ( 4 | "net/http" 5 | "sort" 6 | "strings" 7 | ) 8 | 9 | // HeaderStringer turns a http.Header into a fmt.Stringer. It is useful when it's desired to defer string formatting of 10 | // the header depending on loglevel, for instance: 11 | // 12 | // dlog.Debugf(c, "Header = %s", HeaderStringer(header)) 13 | // 14 | // would not perform the actual formatting unless the loglevel is DEBUG or higher. 15 | type HeaderStringer http.Header 16 | 17 | // String formats the Header to a comma-separated list of ordered key:value pairs. 18 | func (s HeaderStringer) String() string { 19 | h := http.Header(s) 20 | sb := strings.Builder{} 21 | ks := make([]string, len(h)) 22 | i := 0 23 | for k := range h { 24 | ks[i] = k 25 | i++ 26 | } 27 | sort.Strings(ks) 28 | for i, k := range ks { 29 | if i > 0 { 30 | sb.WriteByte(',') 31 | } 32 | sb.WriteString(k) 33 | sb.WriteByte(':') 34 | for p, v := range h[k] { 35 | if p > 0 { 36 | sb.WriteByte(';') 37 | } 38 | sb.WriteString(v) 39 | } 40 | } 41 | return sb.String() 42 | } 43 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-min.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: policy/v1 16 | kind: PodDisruptionBudget 17 | metadata: 18 | name: echo 19 | spec: 20 | minAvailable: 1 21 | selector: 22 | matchLabels: 23 | app: echo 24 | --- 25 | apiVersion: apps/v1 26 | kind: Deployment 27 | metadata: 28 | name: "echo" 29 | labels: 30 | app: echo 31 | spec: 32 | replicas: 1 33 | selector: 34 | matchLabels: 35 | app: echo 36 | template: 37 | metadata: 38 | labels: 39 | app: echo 40 | spec: 41 | containers: 42 | - name: echo 43 | image: ghcr.io/telepresenceio/echo-server:latest 44 | imagePullPolicy: IfNotPresent 45 | ports: 46 | - containerPort: 8080 47 | name: http 48 | resources: 49 | limits: 50 | cpu: 50m 51 | memory: 128Mi 52 | -------------------------------------------------------------------------------- /packaging/install-telepresence.ps1: -------------------------------------------------------------------------------- 1 | #Requires -RunAsAdministrator 2 | 3 | param 4 | ( 5 | $Path = "$env:ProgramFiles\telepresence" 6 | ) 7 | 8 | $current_directory = (Get-Location).path 9 | 10 | echo "Installing telepresence to $Path" 11 | 12 | Start-Process msiexec -Wait -verb runAs -Args "/i $current_directory\winfsp.msi /passive /qn /L*V winfsp-install.log" 13 | Start-Process msiexec -Wait -verb runAs -Args "/i $current_directory\sshfs-win.msi /passive /qn /L*V sshfs-win-install.log" 14 | 15 | if(!(test-path $Path)) 16 | { 17 | New-Item -ItemType Directory -Force -Path $Path 18 | } 19 | 20 | Copy-Item "telepresence.exe" -Destination "$Path" -Force 21 | Copy-Item "wintun.dll" -Destination "$Path" -Force 22 | 23 | # Update PATH if entries do not exist only 24 | $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") 25 | @("$Path", "C:\Program Files\SSHFS-Win\bin") | Where-Object { $currentPath -notlike "*$_*" } | ForEach-Object { $currentPath = "$_;$currentPath" } 26 | [Environment]::SetEnvironmentVariable("Path", $currentPath, "Machine") 27 | 28 | echo "Telepresence installed to $Path" -------------------------------------------------------------------------------- /pkg/authenticator/exec_test.go: -------------------------------------------------------------------------------- 1 | package authenticator 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 9 | ) 10 | 11 | func TestExecCredentialsNoLocalEnv(t *testing.T) { 12 | t.Setenv("GLOBAL_ENV", "global-val") 13 | 14 | config := &clientcmdapi.ExecConfig{ 15 | Command: "sh", 16 | Args: []string{"-c", "echo $GLOBAL_ENV/$LOCAL_ENV"}, 17 | } 18 | result, err := execCredentialBinary{}.Resolve(context.Background(), config) 19 | assert.NoError(t, err) 20 | assert.Equal(t, string(result), "global-val/\n") 21 | } 22 | 23 | func TestExecCredentialsYesLocalEnv(t *testing.T) { 24 | t.Setenv("GLOBAL_ENV", "global-val") 25 | 26 | config := &clientcmdapi.ExecConfig{ 27 | Command: "sh", 28 | Args: []string{"-c", "echo $GLOBAL_ENV/$LOCAL_ENV"}, 29 | Env: []clientcmdapi.ExecEnvVar{{Name: "LOCAL_ENV", Value: "local-val"}}, 30 | } 31 | result, err := execCredentialBinary{}.Resolve(context.Background(), config) 32 | assert.NoError(t, err) 33 | assert.Equal(t, string(result), "global-val/local-val\n") 34 | } 35 | -------------------------------------------------------------------------------- /rpc/agent/agent.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // The "agent" package describes the server implemented by the 4 | // in-cluster Agent 5 | package telepresence.agent; 6 | 7 | import "google/protobuf/empty.proto"; 8 | import "manager/manager.proto"; 9 | 10 | option go_package = "github.com/telepresenceio/telepresence/rpc/v2/agent"; 11 | 12 | service Agent { 13 | // Lookup performs a LookupIP in the cluster. 14 | rpc Lookup(manager.LookupRequest) returns (manager.LookupResponse); 15 | 16 | rpc Tunnel(stream manager.TunnelMessage) returns (stream manager.TunnelMessage); 17 | 18 | // Version returns the version information of the Manager. 19 | rpc Version(google.protobuf.Empty) returns (manager.VersionInfo2); 20 | 21 | // WatchDial makes it possible for the client side to receive DialRequests 22 | // from the traffic-agent. Requests are sent when an intercepted agent needs 23 | // a Tunnel to the Telepresence client on the workstation. The receiver of 24 | // the request dials a connection and responds with the needed Tunnel. 25 | rpc WatchDial(manager.SessionInfo) returns (stream manager.DialRequest); 26 | } -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Alex Gervais alex 2 | Alex Gervais 3 | Donny Yung 4 | Flynn 5 | John Esmet 6 | Luke Shumaker 7 | Maxime Legault-Venne 8 | Rafael Schloming 9 | Thomas Hallgren 10 | 11 | Abhay Saxena 12 | Alvaro Saurin <1841612+inercia@users.noreply.github.com> 13 | Philip Lombardi plombardi 14 | Philip Lombardi <893096+plombardi89@users.noreply.github.com> 15 | Philip Lombardi 16 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_config.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence config 3 | description: Telepresence configuration commands 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Telepresence configuration commands 8 | 9 | ### Usage: 10 | ``` 11 | telepresence config [command] [flags] 12 | ``` 13 | 14 | ### Available Commands: 15 | | Command | Description | 16 | |---------|-------------| 17 | | [view](telepresence_config_view) | View current Telepresence configuration | 18 | 19 | ### Flags: 20 | ``` 21 | -h, --help help for config 22 | ``` 23 | 24 | ### Global Flags: 25 | ``` 26 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 27 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 28 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 29 | --use string Match expression that uniquely identifies the daemon container 30 | ``` 31 | 32 | Use `telepresence config [command] --help` for more information about a command. 33 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/agent/fwd/udp_test.go: -------------------------------------------------------------------------------- 1 | package fwd 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 10 | ) 11 | 12 | func TestUDPDispatch_HTTPFilters_NotHandled(t *testing.T) { 13 | f := &udp{interceptor: &interceptor{ 14 | lCtx: context.Background(), 15 | intercepts: make(interceptControllerMap), 16 | wiretaps: make(interceptControllerMap), 17 | }} 18 | intercept := &manager.InterceptInfo{Spec: &manager.InterceptSpec{ 19 | HeaderFilters: map[string]string{"X-Test": "value"}, 20 | }} 21 | f.SetIntercepting([]*manager.InterceptInfo{intercept}) 22 | handled := f.IsHTTP() 23 | require.False(t, handled, "UDP dispatch should not handle HTTP filters") 24 | } 25 | 26 | func TestUDPDispatch_NoMechanism_NotHandled(t *testing.T) { 27 | f := &udp{interceptor: &interceptor{ 28 | lCtx: context.Background(), 29 | intercepts: make(interceptControllerMap), 30 | wiretaps: make(interceptControllerMap), 31 | }} 32 | f.SetIntercepting(nil) 33 | handled := f.IsHTTP() 34 | require.False(t, handled) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/client/cli/mount/prepare_windows.go: -------------------------------------------------------------------------------- 1 | package mount 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "io/fs" 8 | "os" 9 | 10 | "github.com/telepresenceio/telepresence/v2/pkg/errcat" 11 | ) 12 | 13 | func prepare(_ context.Context, _ string, mountPoint string) (string, error) { 14 | var err error 15 | if mountPoint == "" { 16 | // Find a free drive letter. Background at T, loop around and skip C and D, 17 | // A and B aren't often used nowadays. No floppy-disks. 18 | for _, c := range "TUVXYZABEFGHIJKLMNOPQR" { 19 | _, err = os.Stat(fmt.Sprintf(`%c:\`, c)) 20 | if errors.Is(err, fs.ErrNotExist) { 21 | return fmt.Sprintf(`%c:`, c), nil 22 | } 23 | } 24 | return "", errcat.User.New("found no available drive to use as mount point") 25 | } 26 | 27 | // Mount point must be a drive letter 28 | ok := len(mountPoint) == 2 && mountPoint[1] == ':' 29 | if ok { 30 | dl := mountPoint[0] 31 | ok = dl >= 'A' && dl <= 'Z' || dl >= 'a' && dl <= 'z' 32 | } 33 | if !ok { 34 | err = errcat.User.New("mount point must be a drive letter followed by a colon") 35 | } 36 | return mountPoint, err 37 | } 38 | -------------------------------------------------------------------------------- /pkg/client/stream_error.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "google.golang.org/grpc/codes" 8 | "google.golang.org/grpc/status" 9 | ) 10 | 11 | // RecvEOFError should be returned when a component has returned EOF from a stream. 12 | // Do not use this if, for example, the initial dial to a stream fails. 13 | type RecvEOFError struct { 14 | msg string 15 | err error 16 | } 17 | 18 | func (e *RecvEOFError) Error() string { 19 | return fmt.Sprintf("%s: %v", e.msg, e.err) 20 | } 21 | 22 | func (e *RecvEOFError) Unwrap() error { 23 | return e.err 24 | } 25 | 26 | // WrapRecvErr wraps an error from a Recv call. If the error is nil, nil is returned. 27 | // If the error indicates that the remote end has , a RecvEOFError wrapping the error will be returned. 28 | // Otherwise, the original error will be wrapped as fmt.Errorf("%s: %w", msg, err). 29 | func WrapRecvErr(err error, msg string) error { 30 | if err == nil { 31 | return nil 32 | } 33 | if status.Code(err) == codes.Unavailable || err == io.EOF { 34 | return &RecvEOFError{msg, err} 35 | } 36 | return fmt.Errorf("%s: %w", msg, err) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/grpc/server/context.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | func NewCombinedContext(a, b context.Context) context.Context { 9 | return &combinedContext{a, b, nil} 10 | } 11 | 12 | type combinedContext struct { 13 | a context.Context 14 | b context.Context 15 | err error 16 | } 17 | 18 | func (c *combinedContext) Deadline() (time.Time, bool) { 19 | if dla, ok := c.a.Deadline(); ok { 20 | if dlb, ok := c.b.Deadline(); ok { 21 | if dlb.Before(dla) { 22 | return dlb, ok 23 | } 24 | } 25 | return dla, ok 26 | } 27 | return c.b.Deadline() 28 | } 29 | 30 | func (c *combinedContext) Done() <-chan struct{} { 31 | done := make(chan struct{}) 32 | go func() { 33 | select { 34 | case <-c.a.Done(): 35 | c.err = c.a.Err() 36 | case <-c.b.Done(): 37 | c.err = c.b.Err() 38 | } 39 | close(done) 40 | }() 41 | return done 42 | } 43 | 44 | func (c *combinedContext) Err() error { 45 | return c.err 46 | } 47 | 48 | func (c *combinedContext) Value(key any) any { 49 | v := c.a.Value(key) 50 | if v == nil { 51 | v = c.b.Value(key) 52 | } 53 | return v 54 | } 55 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/relnotes/relnotes.gomd: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/tools/src/relnotesgen/relnotes.ChangeLog*/}} 2 | [comment]: # (Code generated by relnotesgen. DO NOT EDIT.) 3 | 4 | {{- $styles := .Styles -}} 5 | {{- $iconFormat := "images/%s.png" }} 6 | # Telepresence Release Notes 7 | {{- range .Items }} 8 | {{- if not (eq .DateString "(SUPERSEDED)")}} 9 | {{ if .Version}}## Version {{.Version}}{{- with .Date}} ({{- (.).UTC.Format "January _2"}}){{- end}}{{- end}} 10 | {{- end -}} 11 | {{- range .Notes }} 12 | ##
{{.Type}}
13 | {{- if .HRef}}[{{.Title}}]({{.HRef}}) 14 | {{- else if .Docs}}[{{.Title}}]({{.Docs}}) 15 | {{- else}}{{.Title}} 16 | {{- end -}}
17 |
18 | {{- /* An empty line is crucial here to ensure that the Body is treated as markdown */}} 19 | 20 | {{.Body}} 21 |
22 | {{ end }} 23 | {{- end }} 24 | -------------------------------------------------------------------------------- /.github/actions/upload-logs/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Upload logs" 2 | description: "Upload logs from the test run" 3 | runs: 4 | using: composite 5 | steps: 6 | - run: | 7 | LOGS="test-logs/${{ runner.os }}/${{ runner.arch }}" 8 | mkdir -p "$LOGS" 9 | if [[ $RUNNER_OS != Windows ]]; then 10 | rsync -ma --include='*/' --include='*.tap' --include='*.log' --include='Test*.webm' --exclude='*' . "$LOGS" 11 | fi 12 | for file in \ 13 | {"${XDG_CACHE_HOME:-$HOME/.cache}/telepresence/logs","$HOME/Library/Logs/telepresence","$LOCALAPPDATA/telepresence/logs","."}/*.log 14 | do 15 | if [ -s "$file" ]; then 16 | cp -v "$file" "$LOGS" || true 17 | fi 18 | done 19 | shell: bash 20 | name: Gather logs 21 | - name: Upload logs 22 | uses: actions/upload-artifact@v4 23 | with: 24 | # If an environment variable LOG_SUFFIX is set, it will be appended to the log filename. 25 | name: ${{github.job}}-logs-${{ env.LOG_SUFFIX }} 26 | path: | 27 | test-logs/${{ runner.os }}/${{ runner.arch }}/* 28 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/trafficManagerRbac/webhook-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (not (eq .Values.agentInjector.certificate.accessMethod "mount")) .Values.agentInjector.enabled }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | namespace: {{ include "traffic-manager.namespace" $ }} 6 | name: agent-injector-webhook-secret 7 | labels: {{- include "telepresence.labels" $ | nindent 4 }} 8 | rules: 9 | - apiGroups: 10 | - "" 11 | resources: 12 | - secrets 13 | resourceNames: [ {{ .Values.agentInjector.secret.name }} ] 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | 19 | --- 20 | apiVersion: rbac.authorization.k8s.io/v1 21 | kind: RoleBinding 22 | metadata: 23 | name: agent-injector-webhook-secret 24 | namespace: {{ include "traffic-manager.namespace" $ }} 25 | labels: {{- include "telepresence.labels" $ | nindent 4 }} 26 | roleRef: 27 | apiGroup: rbac.authorization.k8s.io 28 | kind: Role 29 | name: agent-injector-webhook-secret 30 | subjects: 31 | - kind: ServiceAccount 32 | name: traffic-manager 33 | namespace: {{ include "traffic-manager.namespace" $ }} 34 | {{- end }} -------------------------------------------------------------------------------- /integration_test/itest/tempdir.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "sync/atomic" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | type tempDirBase struct { 13 | tempDir string 14 | tempDirSeq uint64 15 | } 16 | 17 | type tempDirBaseKey struct{} 18 | 19 | func withTempDirBase(ctx context.Context, td *tempDirBase) context.Context { 20 | return context.WithValue(ctx, tempDirBaseKey{}, td) 21 | } 22 | 23 | // TempDir returns a temporary directory for the test to use. 24 | // The directory is automatically removed when the test and 25 | // all its subtests complete. 26 | // Each subsequent call to t.TempDir returns a unique directory; 27 | // if the directory creation fails, TempDir terminates the test by calling Fatal. 28 | func TempDir(ctx context.Context) string { 29 | t := getT(ctx) 30 | if td, ok := ctx.Value(tempDirBaseKey{}).(*tempDirBase); ok { 31 | seq := atomic.AddUint64(&td.tempDirSeq, 1) 32 | dir := fmt.Sprintf("%s%c%03d", td.tempDir, os.PathSeparator, seq) 33 | require.NoError(t, os.Mkdir(dir, 0o777)) 34 | return dir 35 | } 36 | return t.TempDir() 37 | } 38 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-vols.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-no-vols 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-no-vols 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo-no-vols 19 | labels: 20 | app: echo-no-vols 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-no-vols 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-no-vols 30 | budget: telepresence-test 31 | spec: 32 | automountServiceAccountToken: false 33 | containers: 34 | - name: echo-server 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - name: http 39 | containerPort: 8080 40 | env: 41 | - name: PORT 42 | value: "8080" 43 | resources: 44 | limits: 45 | cpu: 50m 46 | memory: 8Mi 47 | -------------------------------------------------------------------------------- /pkg/ioutil/free_ports.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import ( 4 | "net" 5 | "net/netip" 6 | ) 7 | 8 | // FreePortsTCP uses net.Listen repeatedly to choose free TCP ports for the localhost. It then immediately closes 9 | // the listeners and returns the addresses that were allocated. 10 | // 11 | // NOTE: Since the listeners are closed, there's a chance that someone else might allocate the returned addresses 12 | // before they are actually used. The chances are slim, though, since tests show that in most cases (at least on 13 | // macOS and Linux), the same address isn't allocated for a while even if the allocation is made from different 14 | // processes. 15 | func FreePortsTCP(count int) ([]netip.AddrPort, error) { 16 | ls := make([]net.Listener, 0, count) 17 | as := make([]netip.AddrPort, count) 18 | defer func() { 19 | for _, l := range ls { 20 | _ = l.Close() 21 | } 22 | }() 23 | for i := 0; i < count; i++ { 24 | if l, err := net.Listen("tcp", ":0"); err != nil { 25 | return nil, err 26 | } else { 27 | ls = append(ls, l) 28 | as[i] = l.Addr().(*net.TCPAddr).AddrPort() 29 | } 30 | } 31 | return as, nil 32 | } 33 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_mcp_stream.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence mcp stream 3 | description: Stream the MCP server over HTTP 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Stream the MCP server over HTTP 8 | 9 | ## Synopsis: 10 | 11 | Start HTTP server to expose CLI commands to AI assistants 12 | 13 | ### Usage: 14 | ``` 15 | telepresence mcp stream [flags] 16 | ``` 17 | 18 | ### Flags: 19 | ``` 20 | -h, --help help for stream 21 | --host string host to listen on 22 | --log-level string Log level (debug, info, warn, error) 23 | --port int port number to listen on (default 8080) 24 | ``` 25 | 26 | ### Global Flags: 27 | ``` 28 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 29 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 30 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 31 | --use string Match expression that uniquely identifies the daemon container 32 | ``` 33 | -------------------------------------------------------------------------------- /pkg/tunnel/provider.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc" 7 | 8 | "github.com/telepresenceio/telepresence/rpc/v2/agent" 9 | rpc "github.com/telepresenceio/telepresence/rpc/v2/manager" 10 | ) 11 | 12 | type Client interface { 13 | Send(*rpc.TunnelMessage) error 14 | Recv() (*rpc.TunnelMessage, error) 15 | grpc.ClientStream 16 | } 17 | 18 | type Provider interface { 19 | Tunnel(ctx context.Context, opts ...grpc.CallOption) (Client, error) 20 | } 21 | 22 | type mgrProvider struct { 23 | rpc.ManagerClient 24 | } 25 | 26 | func (m mgrProvider) Tunnel(ctx context.Context, opts ...grpc.CallOption) (Client, error) { 27 | return m.ManagerClient.Tunnel(ctx, opts...) 28 | } 29 | 30 | func ManagerProvider(m rpc.ManagerClient) Provider { 31 | return mgrProvider{m} 32 | } 33 | 34 | type agentProvider struct { 35 | agent.AgentClient 36 | } 37 | 38 | func (m agentProvider) Tunnel(ctx context.Context, opts ...grpc.CallOption) (Client, error) { 39 | return m.AgentClient.Tunnel(ctx, opts...) 40 | } 41 | 42 | func AgentProvider(a agent.AgentClient) Provider { 43 | return agentProvider{a} 44 | } 45 | -------------------------------------------------------------------------------- /docs/reference/cli/telepresence_mcp_claude_disable.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: telepresence mcp claude disable 3 | description: Remove server from Claude config 4 | hide_table_of_contents: true 5 | --- 6 | 7 | Remove server from Claude config 8 | 9 | ## Synopsis: 10 | 11 | Remove this application from Claude Desktop MCP servers 12 | 13 | ### Usage: 14 | ``` 15 | telepresence mcp claude disable [flags] 16 | ``` 17 | 18 | ### Flags: 19 | ``` 20 | --config-path string Path to Claude config file 21 | -h, --help help for disable 22 | --server-name string Name of the MCP server to remove (default: derived from executable name) 23 | ``` 24 | 25 | ### Global Flags: 26 | ``` 27 | --config string Path to the Telepresence configuration file (default "$HOME/.config/telepresence/config.yml") 28 | --output string Set the output format, supported values are 'json', 'yaml', and 'default' (default "default") 29 | --progress string Set type of progress output (auto, tty, plain, json, quiet) (default "auto") 30 | --use string Match expression that uniquely identifies the daemon container 31 | ``` 32 | -------------------------------------------------------------------------------- /pkg/slice/contains.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | // AppendUnique appends all elements that are not already present in dest to dest 4 | // and returns the result. 5 | func AppendUnique[E comparable](dest []E, src ...E) []E { 6 | for _, v := range src { 7 | if !Contains(dest, v) { 8 | dest = append(dest, v) 9 | } 10 | } 11 | return dest 12 | } 13 | 14 | // Contains returns true if the given slice contains the given element. 15 | func Contains[E comparable](vs []E, e E) bool { 16 | for _, v := range vs { 17 | if e == v { 18 | return true 19 | } 20 | } 21 | return false 22 | } 23 | 24 | // ContainsAll returns true if the first slice contains all elements in the second slice. 25 | func ContainsAll[E comparable](vs []E, es []E) bool { 26 | for _, e := range es { 27 | if !Contains(vs, e) { 28 | return false 29 | } 30 | } 31 | return true 32 | } 33 | 34 | // ContainsAny returns true if the first slice contains at least one of the elements in the second slice. 35 | func ContainsAny[E comparable](vs []E, es []E) bool { 36 | for _, e := range es { 37 | if Contains(vs, e) { 38 | return true 39 | } 40 | } 41 | return false 42 | } 43 | -------------------------------------------------------------------------------- /pkg/client/cli/progress/noop.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Docker Compose CLI authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package progress 18 | 19 | import ( 20 | "context" 21 | ) 22 | 23 | type noopWriter struct{} 24 | 25 | func (p noopWriter) Start(context.Context, string) { 26 | } 27 | 28 | func (p noopWriter) IsNoOp() bool { 29 | return true 30 | } 31 | 32 | func (p noopWriter) Write(...*Event) { 33 | } 34 | 35 | func (p noopWriter) TailMsgf(_ string, _ ...any) { 36 | } 37 | 38 | func (p noopWriter) Stop() { 39 | } 40 | 41 | func (p noopWriter) TriggerRefresh() { 42 | } 43 | -------------------------------------------------------------------------------- /cmd/cobraparser/main.go: -------------------------------------------------------------------------------- 1 | //go:build go1.24 2 | 3 | package main 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "log" 9 | "os" 10 | 11 | "github.com/spf13/pflag" 12 | 13 | "github.com/telepresenceio/telepresence/cmd/cobraparser/types" 14 | ) 15 | 16 | func main() { 17 | fs := pflag.NewFlagSet("cobraparse", pflag.ExitOnError) 18 | var helpArg string 19 | fs.BoolP("help", "h", false, "Help for this command") 20 | fs.StringVar(&helpArg, "help-arg", "--help", "Help for this argument") 21 | fs.Usage = func() { 22 | fmt.Fprint(fs.Output(), "Usage:\n cobraparse [...args]") 23 | fs.PrintDefaults() 24 | } 25 | _ = fs.Parse(os.Args[1:]) 26 | args := fs.Args() 27 | if len(args) == 0 { 28 | fs.Usage() 29 | os.Exit(2) 30 | } 31 | f := types.HelpFetcher{ 32 | Executable: args[0], 33 | HelpArg: helpArg, 34 | } 35 | root, err := f.BuildCommandTree(args[1:], 8) 36 | if err != nil { 37 | log.Fatalf("error: %v\n", err) 38 | } 39 | enc := json.NewEncoder(os.Stdout) 40 | enc.SetIndent("", " ") 41 | if err := enc.Encode(root); err != nil { 42 | log.Fatalf("error writing JSON: %v\n", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/list_namespaces.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/daemon" 9 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/output" 10 | ) 11 | 12 | type listNamespacesCommand struct { 13 | rq *daemon.CobraRequest 14 | } 15 | 16 | func listNamespaces() *cobra.Command { 17 | lnc := &listNamespacesCommand{} 18 | 19 | cmd := &cobra.Command{ 20 | Use: "list-namespaces", 21 | Args: cobra.NoArgs, 22 | Short: "Show all namespaces", 23 | RunE: lnc.run, 24 | ValidArgsFunction: cobra.NoFileCompletions, 25 | } 26 | lnc.rq = daemon.InitRequest(cmd) 27 | return cmd 28 | } 29 | 30 | func (lnc *listNamespacesCommand) run(cmd *cobra.Command, _ []string) error { 31 | nss, err := lnc.rq.GetAllNamespaces(cmd) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | ctx := cmd.Context() 37 | if output.WantsFormatted(cmd) { 38 | output.Object(ctx, nss, false) 39 | } else { 40 | for _, ns := range nss { 41 | fmt.Fprintln(output.Out(ctx), ns) 42 | } 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/client/ensured_state.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type ( 10 | Prolog func(context.Context) (acquired bool, err error) 11 | Action func(context.Context) error 12 | ) 13 | 14 | // WithEnsuredState calls prolog, and if that was successful, calls act. If epilog is not nil, it is guaranteed 15 | // to be called when prolog returns true. 16 | func WithEnsuredState(ctx context.Context, prolog Prolog, action, epilog Action) error { 17 | wasAcquired, err := prolog(ctx) 18 | if err != nil { 19 | return err 20 | } 21 | if wasAcquired && epilog != nil { 22 | defer func() { 23 | // The context might have been cancelled, so we use the original context 24 | // without cancellation, but with a deactivation timeout of 10 seconds. 25 | ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 10*time.Second) 26 | defer cancel() 27 | if cerr := epilog(ctx); cerr != nil { 28 | if err == nil { 29 | err = cerr 30 | } else { 31 | err = fmt.Errorf("%w\n%v", err, cerr) 32 | } 33 | } 34 | }() 35 | } 36 | if action != nil { 37 | err = action(ctx) 38 | } 39 | return err 40 | } 41 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/clientRbac/cluster-scope.yaml: -------------------------------------------------------------------------------- 1 | {{- /* 2 | These are the cluster-wide rbac roles + bindings that will be used by users 3 | who want to use telepresence once its components have been set 4 | up in the cluster. 5 | */}} 6 | {{- with .Values.clientRbac }} 7 | {{- if (and .create (not (or .namespaces (include "traffic-manager.namespaced" $)))) }} 8 | {{- $roleName := include "telepresence.clientRbacName" $ }} 9 | 10 | --- 11 | kind: ClusterRole 12 | apiVersion: rbac.authorization.k8s.io/v1 13 | metadata: 14 | name: {{ $roleName }} 15 | labels: 16 | {{- include "telepresence.labels" $ | nindent 4 }} 17 | rules: 18 | - apiGroups: [""] 19 | resources: ["namespaces"] 20 | verbs: ["get", "list", "watch"] 21 | {{- include "telepresence.clientRbacInterceptRules" $ }} 22 | 23 | --- 24 | apiVersion: rbac.authorization.k8s.io/v1 25 | kind: ClusterRoleBinding 26 | metadata: 27 | name: {{ $roleName }} 28 | labels: 29 | {{- include "telepresence.labels" $ | nindent 4 }} 30 | subjects: 31 | {{ toYaml .subjects }} 32 | roleRef: 33 | kind: ClusterRole 34 | name: {{ $roleName }} 35 | apiGroup: rbac.authorization.k8s.io 36 | 37 | {{- end }} 38 | {{- end }} 39 | -------------------------------------------------------------------------------- /pkg/dnsproxy/rpc.go: -------------------------------------------------------------------------------- 1 | package dnsproxy 2 | 3 | import ( 4 | "github.com/miekg/dns" 5 | "google.golang.org/grpc/codes" 6 | "google.golang.org/grpc/status" 7 | 8 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 9 | ) 10 | 11 | func ToRPC(rrs RRs, rCode int) (*manager.DNSResponse, error) { 12 | l := 0 13 | for _, rr := range rrs { 14 | l += dns.Len(rr) 15 | } 16 | rsp := &manager.DNSResponse{RCode: int32(rCode)} 17 | rrb := make([]byte, l) 18 | off := 0 19 | for _, rr := range rrs { 20 | var err error 21 | if off, err = dns.PackRR(rr, rrb, off, nil, false); err != nil { 22 | return nil, status.Errorf(codes.Internal, "unable to pack DNS reply: %v", err) 23 | } 24 | } 25 | rsp.Rrs = rrb 26 | return rsp, nil 27 | } 28 | 29 | func FromRPC(r *manager.DNSResponse) (RRs, int, error) { 30 | rrb := r.Rrs 31 | var rrs RRs 32 | off := 0 33 | for len(rrb) > off { 34 | var rr dns.RR 35 | var err error 36 | if rr, off, err = dns.UnpackRR(rrb, off); err != nil { 37 | return nil, dns.RcodeFormatError, status.Errorf(codes.InvalidArgument, "unable to unpack DNS response: %v", err) 38 | } 39 | rrs = append(rrs, rr) 40 | } 41 | return rrs, int(r.RCode), nil 42 | } 43 | --------------------------------------------------------------------------------