Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions tailscale.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ extern int TsnetGetIps(int sd, char *buf, size_t buflen);
extern int TsnetGetRemoteAddr(int listener, int conn, char *buf, size_t buflen);
extern int TsnetListen(int sd, char* net, char* addr, int* listenerOut);
extern int TsnetLoopback(int sd, char* addrOut, size_t addrLen, char* proxyOut, char* localOut);
extern int TsnetEnableFunnelToLocalhostPlaintextHttp1(int sd, int localhostPort);

tailscale tailscale_new() {
return TsnetNewServer();
Expand Down Expand Up @@ -106,3 +107,7 @@ int tailscale_loopback(tailscale sd, char* addr_out, size_t addrlen, char* proxy
int tailscale_errmsg(tailscale sd, char* buf, size_t buflen) {
return TsnetErrmsg(sd, buf, buflen);
}

int tailscale_enable_funnel_to_localhost_plaintext_http1(tailscale sd, int localhostPort) {
return TsnetEnableFunnelToLocalhostPlaintextHttp1(sd, localhostPort);
}
43 changes: 43 additions & 0 deletions tailscale.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import (
"net"
"os"
"regexp"
"strconv"
"strings"
"sync"
"syscall"
"unsafe"

"tailscale.com/hostinfo"
"tailscale.com/ipn"
"tailscale.com/tsnet"
"tailscale.com/types/logger"
)
Expand Down Expand Up @@ -531,3 +533,44 @@ func TsnetLoopback(sd C.int, addrOut *C.char, addrLen C.size_t, proxyOut *C.char

return 0
}

//export TsnetEnableFunnelToLocalhostPlaintextHttp1
func TsnetEnableFunnelToLocalhostPlaintextHttp1(sd C.int, localhostPort C.int) C.int {
s, err := getServer(sd)
if err != nil {
return s.recErr(err)
}

ctx := context.Background()
lc, err := s.s.LocalClient()
if err != nil {
return s.recErr(err)
}

st, err := lc.StatusWithoutPeers(ctx)
if err != nil {
return s.recErr(err)
}
domain := st.CertDomains[0]

hp := ipn.HostPort(net.JoinHostPort(domain, strconv.Itoa(443)))
tcpForward := fmt.Sprintf("127.0.0.1:%d", localhostPort)
sc := &ipn.ServeConfig{
TCP: map[uint16]*ipn.TCPPortHandler{
443: {
TCPForward: tcpForward,
TerminateTLS: domain,
},
},
AllowFunnel: map[ipn.HostPort]bool{
hp: true,
},
}

lc.SetServeConfig(ctx, sc)
if !sc.AllowFunnel[hp] {
return s.recErr(fmt.Errorf("libtailscale: failed to enable funnel"))
}

return 0
}
16 changes: 16 additions & 0 deletions tailscale.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,22 @@ extern int tailscale_accept(tailscale_listener listener, tailscale_conn* conn_ou
// Returns zero on success or -1 on error, call tailscale_errmsg for details.
extern int tailscale_loopback(tailscale sd, char* addr_out, size_t addrlen, char* proxy_cred_out, char* local_api_cred_out);

// tailscale_enable_funnel_to_localhost_plaintext_http1 configures sd to have
// Tailscale Funnel enabled, routing requests from the public web
// (without any authentication) down to this Tailscale node, requesting new
// LetsEncrypt TLS certs as needed, terminating TLS, and proxying all incoming
// HTTPS requests to http://127.0.0.1:localhostPort without TLS.
//
// There should be a plaintext HTTP/1 server listening on 127.0.0.1:localhostPort
// or tsnet will serve HTTP 502 errors.
//
// Expect junk traffic from the internet from bots watching the public CT logs.
//
// Returns:
// 0 - success
// -1 - other error, details printed to the tsnet logger
extern int tailscale_enable_funnel_to_localhost_plaintext_http1(tailscale sd, int localhostPort);

// tailscale_errmsg writes the details of the last error to buf.
//
// After returning, buf is always NUL-terminated.
Expand Down