Add prefs.ShieldsUp and --shields-up option.

This sets a default packet filter that blocks all incoming requests,
giving end users more control over who can get into their machine, even
if the admin hasn't set any central ACLs.

Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
pull/342/head
Avery Pennarun 5 years ago
parent 85e675940d
commit d7429b9a8d

@ -49,7 +49,7 @@ func main() {
upf.StringVar(&upArgs.server, "login-server", "https://login.tailscale.com", "base URL of control server") upf.StringVar(&upArgs.server, "login-server", "https://login.tailscale.com", "base URL of control server")
upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", false, "accept routes advertised by other Tailscale nodes") upf.BoolVar(&upArgs.acceptRoutes, "accept-routes", false, "accept routes advertised by other Tailscale nodes")
upf.BoolVar(&upArgs.noSingleRoutes, "no-single-routes", false, "don't install routes to single nodes") upf.BoolVar(&upArgs.noSingleRoutes, "no-single-routes", false, "don't install routes to single nodes")
upf.BoolVar(&upArgs.noPacketFilter, "no-packet-filter", false, "disable packet filter") upf.BoolVar(&upArgs.shieldsUp, "shields-up", false, "don't allow incoming connections")
upf.StringVar(&upArgs.advertiseRoutes, "advertise-routes", "", "routes to advertise to other nodes (comma-separated, e.g. 10.0.0.0/8,192.168.0.0/24)") upf.StringVar(&upArgs.advertiseRoutes, "advertise-routes", "", "routes to advertise to other nodes (comma-separated, e.g. 10.0.0.0/8,192.168.0.0/24)")
upf.StringVar(&upArgs.authKey, "authkey", "", "node authorization key") upf.StringVar(&upArgs.authKey, "authkey", "", "node authorization key")
upCmd := &ffcli.Command{ upCmd := &ffcli.Command{
@ -99,7 +99,7 @@ var upArgs struct {
server string server string
acceptRoutes bool acceptRoutes bool
noSingleRoutes bool noSingleRoutes bool
noPacketFilter bool shieldsUp bool
advertiseRoutes string advertiseRoutes string
authKey string authKey string
} }
@ -128,7 +128,7 @@ func runUp(ctx context.Context, args []string) error {
prefs.WantRunning = true prefs.WantRunning = true
prefs.RouteAll = upArgs.acceptRoutes prefs.RouteAll = upArgs.acceptRoutes
prefs.AllowSingleHosts = !upArgs.noSingleRoutes prefs.AllowSingleHosts = !upArgs.noSingleRoutes
prefs.UsePacketFilter = !upArgs.noPacketFilter prefs.ShieldsUp = upArgs.shieldsUp
prefs.AdvertiseRoutes = adv prefs.AdvertiseRoutes = adv
c, bc, ctx, cancel := connect(ctx) c, bc, ctx, cancel := connect(ctx)
@ -150,7 +150,7 @@ func runUp(ctx context.Context, args []string) error {
fmt.Fprintf(os.Stderr, "\nTo authorize your machine, visit (as admin):\n\n\t%s/admin/machines\n\n", upArgs.server) fmt.Fprintf(os.Stderr, "\nTo authorize your machine, visit (as admin):\n\n\t%s/admin/machines\n\n", upArgs.server)
case ipn.Starting, ipn.Running: case ipn.Starting, ipn.Running:
// Done full authentication process // Done full authentication process
fmt.Fprintf(os.Stderr, "\ntailscaled is authenticated, nothing more to do.\n\n") fmt.Fprintf(os.Stderr, "tailscaled is authenticated, nothing more to do.\n")
cancel() cancel()
} }
} }

@ -352,14 +352,13 @@ func (b *LocalBackend) Start(opts Options) error {
} }
func (b *LocalBackend) updateFilter(netMap *controlclient.NetworkMap) { func (b *LocalBackend) updateFilter(netMap *controlclient.NetworkMap) {
if !b.Prefs().UsePacketFilter { // TODO(apenwarr): don't replace filter at all if unchanged.
b.e.SetFilter(filter.NewAllowAll()) // TODO(apenwarr): print a diff instead of full filter.
} else if netMap == nil { if netMap == nil || b.Prefs().ShieldsUp {
// Not configured yet, block everything // Not configured yet or shields up, block everything
b.logf("netmap packet filter: (shields up)")
b.e.SetFilter(filter.NewAllowNone()) b.e.SetFilter(filter.NewAllowNone())
} else { } else {
// TODO(apenwarr): don't replace filter at all if unchanged.
// TODO(apenwarr): print a diff instead of full filter.
now := time.Now() now := time.Now()
if now.Sub(b.lastFilterPrint) > 1*time.Minute { if now.Sub(b.lastFilterPrint) > 1*time.Minute {
b.logf("netmap packet filter: %v", b.netMapCache.PacketFilter) b.logf("netmap packet filter: %v", b.netMapCache.PacketFilter)
@ -616,6 +615,8 @@ func (b *LocalBackend) SetPrefs(new *Prefs) {
cli.SetHostinfo(newHi) cli.SetHostinfo(newHi)
} }
b.updateFilter(b.netMapCache)
if old.WantRunning != new.WantRunning { if old.WantRunning != new.WantRunning {
b.stateMachine() b.stateMachine()
} else { } else {

@ -39,10 +39,11 @@ type Prefs struct {
// WantRunning indicates whether networking should be active on // WantRunning indicates whether networking should be active on
// this node. // this node.
WantRunning bool WantRunning bool
// UsePacketFilter indicates whether to enforce centralized ACLs // ShieldsUp indicates whether to block all incoming connections,
// on this node. If false, all traffic in and out of this node is // regardless of the control-provided packet filter. If false, we
// allowed. // use the packet filter as provided. If true, we block incoming
UsePacketFilter bool // connections.
ShieldsUp bool
// AdvertiseRoutes specifies CIDR prefixes to advertise into the // AdvertiseRoutes specifies CIDR prefixes to advertise into the
// Tailscale network as reachable through the current node. // Tailscale network as reachable through the current node.
AdvertiseRoutes []wgcfg.CIDR AdvertiseRoutes []wgcfg.CIDR
@ -51,6 +52,9 @@ type Prefs struct {
// notepad.exe on Windows, rather than loading them in a browser. // notepad.exe on Windows, rather than loading them in a browser.
// //
// TODO(danderson): remove? // TODO(danderson): remove?
// apenwarr 2020-04-29: Unfortunately this is still needed sometimes.
// Windows' default browser setting is sometimes screwy and this helps
// narrow it down a bit.
NotepadURLs bool NotepadURLs bool
// DisableDERP prevents DERP from being used. // DisableDERP prevents DERP from being used.
@ -74,9 +78,9 @@ func (p *Prefs) Pretty() string {
} else { } else {
pp = "Persist=nil" pp = "Persist=nil"
} }
return fmt.Sprintf("Prefs{ra=%v mesh=%v dns=%v want=%v notepad=%v derp=%v pf=%v routes=%v %v}", return fmt.Sprintf("Prefs{ra=%v mesh=%v dns=%v want=%v notepad=%v derp=%v shields=%v routes=%v %v}",
p.RouteAll, p.AllowSingleHosts, p.CorpDNS, p.WantRunning, p.RouteAll, p.AllowSingleHosts, p.CorpDNS, p.WantRunning,
p.NotepadURLs, !p.DisableDERP, p.UsePacketFilter, p.AdvertiseRoutes, pp) p.NotepadURLs, !p.DisableDERP, p.ShieldsUp, p.AdvertiseRoutes, pp)
} }
func (p *Prefs) ToBytes() []byte { func (p *Prefs) ToBytes() []byte {
@ -103,7 +107,7 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
p.WantRunning == p2.WantRunning && p.WantRunning == p2.WantRunning &&
p.NotepadURLs == p2.NotepadURLs && p.NotepadURLs == p2.NotepadURLs &&
p.DisableDERP == p2.DisableDERP && p.DisableDERP == p2.DisableDERP &&
p.UsePacketFilter == p2.UsePacketFilter && p.ShieldsUp == p2.ShieldsUp &&
compareIPNets(p.AdvertiseRoutes, p2.AdvertiseRoutes) && compareIPNets(p.AdvertiseRoutes, p2.AdvertiseRoutes) &&
p.Persist.Equals(p2.Persist) p.Persist.Equals(p2.Persist)
} }
@ -130,7 +134,6 @@ func NewPrefs() *Prefs {
AllowSingleHosts: true, AllowSingleHosts: true,
CorpDNS: true, CorpDNS: true,
WantRunning: true, WantRunning: true,
UsePacketFilter: true,
} }
} }
@ -156,7 +159,6 @@ func PrefsFromBytes(b []byte, enforceDefaults bool) (*Prefs, error) {
if enforceDefaults { if enforceDefaults {
p.RouteAll = true p.RouteAll = true
p.AllowSingleHosts = true p.AllowSingleHosts = true
p.UsePacketFilter = true
} }
return p, err return p, err
} }

@ -20,7 +20,7 @@ func fieldsOf(t reflect.Type) (fields []string) {
} }
func TestPrefsEqual(t *testing.T) { func TestPrefsEqual(t *testing.T) {
prefsHandles := []string{"ControlURL", "RouteAll", "AllowSingleHosts", "CorpDNS", "WantRunning", "UsePacketFilter", "AdvertiseRoutes", "NotepadURLs", "DisableDERP", "Persist"} prefsHandles := []string{"ControlURL", "RouteAll", "AllowSingleHosts", "CorpDNS", "WantRunning", "ShieldsUp", "AdvertiseRoutes", "NotepadURLs", "DisableDERP", "Persist"}
if have := fieldsOf(reflect.TypeOf(Prefs{})); !reflect.DeepEqual(have, prefsHandles) { if have := fieldsOf(reflect.TypeOf(Prefs{})); !reflect.DeepEqual(have, prefsHandles) {
t.Errorf("Prefs.Equal check might be out of sync\nfields: %q\nhandled: %q\n", t.Errorf("Prefs.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
have, prefsHandles) have, prefsHandles)
@ -123,13 +123,13 @@ func TestPrefsEqual(t *testing.T) {
}, },
{ {
&Prefs{UsePacketFilter: true}, &Prefs{ShieldsUp: true},
&Prefs{UsePacketFilter: false}, &Prefs{ShieldsUp: false},
false, false,
}, },
{ {
&Prefs{UsePacketFilter: true}, &Prefs{ShieldsUp: true},
&Prefs{UsePacketFilter: true}, &Prefs{ShieldsUp: true},
true, true,
}, },

Loading…
Cancel
Save