interfaces, cmd/tsshd: move interface lookup from tsshd to its own package

For reuse by derper, etc.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2020-02-21 08:13:21 -08:00
parent 37e115834e
commit 433b917977
3 changed files with 92 additions and 47 deletions

View File

@ -25,7 +25,6 @@ import (
"net"
"os"
"os/exec"
"strings"
"syscall"
"time"
"unsafe"
@ -33,6 +32,7 @@ import (
"github.com/gliderlabs/ssh"
"github.com/kr/pty"
gossh "golang.org/x/crypto/ssh"
"tailscale.com/interfaces"
)
var (
@ -57,7 +57,7 @@ func main() {
warned := false
for {
addr, iface, err := tailscaleInterface()
addr, iface, err := interfaces.Tailscale()
if err != nil {
log.Fatalf("listing interfaces: %v", err)
}
@ -87,50 +87,6 @@ func main() {
}
// tailscaleInterface returns an err on a fatal problem, and all zero values
// if no suitable inteface is found.
func tailscaleInterface() (net.IP, *net.Interface, error) {
ifs, err := net.Interfaces()
if err != nil {
return nil, nil, err
}
for _, iface := range ifs {
if !maybeTailscaleInterfaceName(iface.Name) {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok && isTailscaleIP(ipnet.IP) {
return ipnet.IP, &iface, nil
}
}
}
return nil, nil, nil
}
// maybeTailscaleInterfaceName reports whether s is an interface
// name that might be used by Tailscale.
func maybeTailscaleInterfaceName(s string) bool {
return strings.HasPrefix(s, "wg") ||
strings.HasPrefix(s, "ts") ||
strings.HasPrefix(s, "tailscale")
}
func isTailscaleIP(ip net.IP) bool {
return cgNAT.Contains(ip)
}
var cgNAT = func() *net.IPNet {
_, ipNet, err := net.ParseCIDR("100.64.0.0/10")
if err != nil {
panic(err)
}
return ipNet
}()
func handleSSH(s ssh.Session) {
user := s.User()
addr := s.RemoteAddr()
@ -140,7 +96,7 @@ func handleSSH(s ssh.Session) {
s.Exit(1)
return
}
if !isTailscaleIP(ta.IP) {
if !interfaces.IsTailscaleIP(ta.IP) {
log.Printf("tsshd: rejecting non-Tailscale addr %v", ta.IP)
s.Exit(1)
return

58
interfaces/interfaces.go Normal file
View File

@ -0,0 +1,58 @@
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package interfaces contains helpers for looking up system network interfaces.
package interfaces
import (
"net"
"strings"
)
// Tailscale returns the current machine's Tailscale interface, if any.
// If none is found, all zero values are returned.
// A non-nil error is only returned on a problem listing the system interfaces.
func Tailscale() (net.IP, *net.Interface, error) {
ifs, err := net.Interfaces()
if err != nil {
return nil, nil, err
}
for _, iface := range ifs {
if !maybeTailscaleInterfaceName(iface.Name) {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok && IsTailscaleIP(ipnet.IP) {
return ipnet.IP, &iface, nil
}
}
}
return nil, nil, nil
}
// maybeTailscaleInterfaceName reports whether s is an interface
// name that might be used by Tailscale.
func maybeTailscaleInterfaceName(s string) bool {
return strings.HasPrefix(s, "wg") ||
strings.HasPrefix(s, "ts") ||
strings.HasPrefix(s, "tailscale")
}
// IsTailscaleIP reports whether ip is an IP in a range used by
// Tailscale virtual network interfaces.
func IsTailscaleIP(ip net.IP) bool {
return cgNAT.Contains(ip)
}
var cgNAT = func() *net.IPNet {
_, ipNet, err := net.ParseCIDR("100.64.0.0/10")
if err != nil {
panic(err)
}
return ipNet
}()

View File

@ -0,0 +1,31 @@
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package interfaces
import (
"net"
"testing"
)
func TestIsTailscaleIP(t *testing.T) {
tests := []struct {
ip string
want bool
}{
{"100.81.251.94", true},
{"8.8.8.8", false},
}
for _, tt := range tests {
ip := net.ParseIP(tt.ip)
if ip == nil {
t.Fatalf("failed to parse IP %q", tt.ip)
}
got := IsTailscaleIP(ip)
if got != tt.want {
t.Errorf("F(%q) = %v; want %v", tt.ip, got, tt.want)
}
}
}