Hidden interfaces, last-seen pings, activity by subnet
- interfaces.exclude in steward.yaml (names or shell patterns) leaves interfaces out of GET /interfaces, the web UI's picker and 'steward interface list'. Subnets on them are still served. - With the ping check on, active leases are pinged every minute; a client that answers gets a last-seen time (leases.last_seen, migration 4). Not answering never takes a client offline. - The activity log filters by subnet (--subnet, the Activity page, GET /events?subnet=). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6ad1bb9a0d
commit
5e6c61cc8e
31 files changed
+358
-39
No files matched your search
@@ -10,6 +10,7 @@ import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -35,6 +36,17 @@ type Config struct {
|
||||
Backup Backup `yaml:"backup"`
|
||||
// Jarl is the Jarl server Steward uses for DNS (docs/jarl.md).
|
||||
Jarl Jarl `yaml:"jarl"`
|
||||
// Interfaces says which of the host's interfaces are offered when
|
||||
// choosing a subnet's interface.
|
||||
Interfaces Interfaces `yaml:"interfaces"`
|
||||
}
|
||||
|
||||
// Interfaces filters the host's interfaces listed by the API, the web UI's
|
||||
// interface picker and 'steward interface list'.
|
||||
type Interfaces struct {
|
||||
// Exclude are interface names or shell patterns ("docker*", "veth*")
|
||||
// left out of the list. It doesn't stop a subnet from naming one.
|
||||
Exclude []string `yaml:"exclude"`
|
||||
}
|
||||
|
||||
// TLS is the web UI's certificate: from files, or obtained through ACME
|
||||
@@ -173,6 +185,7 @@ func Default() *Config {
|
||||
return &Config{
|
||||
Listen: []string{"127.0.0.1:8067"},
|
||||
TrustedProxies: []string{},
|
||||
Interfaces: Interfaces{Exclude: []string{}},
|
||||
Socket: "/run/steward/steward.sock",
|
||||
Database: "/var/lib/steward/steward.db",
|
||||
Log: Log{Level: "info", Format: "text"},
|
||||
@@ -225,6 +238,11 @@ func (c *Config) check() error {
|
||||
return fmt.Errorf("trusted_proxies: %v", err)
|
||||
}
|
||||
}
|
||||
for _, p := range c.Interfaces.Exclude {
|
||||
if _, err := path.Match(p, ""); err != nil || p == "" {
|
||||
return fmt.Errorf("interfaces.exclude: %q is not an interface name or pattern (e.g. lo, docker*, veth*)", p)
|
||||
}
|
||||
}
|
||||
if c.Database == "" || !filepath.IsAbs(c.Database) {
|
||||
return fmt.Errorf("database must be an absolute path")
|
||||
}
|
||||
@@ -354,6 +372,12 @@ tls:
|
||||
# ca_cert: "" # a CA to trust for Jarl's certificate
|
||||
# insecure: false
|
||||
|
||||
# Interfaces left out when choosing a subnet's interface (the web UI's
|
||||
# picker, 'steward interface list'): names or shell patterns. Subnets
|
||||
# already on one are still served.
|
||||
interfaces:
|
||||
exclude: [] # e.g. ["lo", "docker*", "veth*", "br-*"]
|
||||
|
||||
# Reverse proxies whose X-Forwarded-For and X-Forwarded-Proto are
|
||||
# believed.
|
||||
trusted_proxies: []
|
||||
|
||||
@@ -34,6 +34,7 @@ func TestErrors(t *testing.T) {
|
||||
"{tls: {cert_file: /a, key_file: /b, acme: {domain: d.example.com}}, jarl: {server: https://j, token: x}}": "can't both",
|
||||
"{tls: {acme: {domain: '*.example.com'}}, jarl: {server: https://j, token: x}}": "full name",
|
||||
"{tls: {acme: {domain: d.example.com, directory: http://x}}, jarl: {server: https://j, token: x}}": "https://",
|
||||
"interfaces: {exclude: ['eth[']}": "interfaces.exclude",
|
||||
}
|
||||
for yml, want := range cases {
|
||||
path := filepath.Join(t.TempDir(), "s.yaml")
|
||||
|
||||
Reference in new issue
Block a user