mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-10 23:49:09 +02:00
Adds observability and fixes cross cases
- strict-kem vs strict-rp said "Connected + Quantum resistance: true" but it is actually blocked - perm-kem vs perm-rp "Connected + Quantum resistance: true" but it's a classic WG link, without PQ safety
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/anonymize"
|
"github.com/netbirdio/netbird/client/anonymize"
|
||||||
"github.com/netbirdio/netbird/client/configs"
|
"github.com/netbirdio/netbird/client/configs"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/pqkem"
|
||||||
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
||||||
"github.com/netbirdio/netbird/client/internal/updater/installer"
|
"github.com/netbirdio/netbird/client/internal/updater/installer"
|
||||||
nbstatus "github.com/netbirdio/netbird/client/status"
|
nbstatus "github.com/netbirdio/netbird/client/status"
|
||||||
@@ -708,6 +709,9 @@ func (g *BundleGenerator) addCommonConfigFields(configContent *strings.Builder)
|
|||||||
configContent.WriteString(fmt.Sprintf("DisableIPv6Discovery: %v\n", g.internalConfig.DisableIPv6Discovery))
|
configContent.WriteString(fmt.Sprintf("DisableIPv6Discovery: %v\n", g.internalConfig.DisableIPv6Discovery))
|
||||||
configContent.WriteString(fmt.Sprintf("RosenpassEnabled: %v\n", g.internalConfig.RosenpassEnabled))
|
configContent.WriteString(fmt.Sprintf("RosenpassEnabled: %v\n", g.internalConfig.RosenpassEnabled))
|
||||||
configContent.WriteString(fmt.Sprintf("RosenpassPermissive: %v\n", g.internalConfig.RosenpassPermissive))
|
configContent.WriteString(fmt.Sprintf("RosenpassPermissive: %v\n", g.internalConfig.RosenpassPermissive))
|
||||||
|
// ML-KEM (Rosenpass alternative) is env-driven, not part of the config, so read it here.
|
||||||
|
configContent.WriteString(fmt.Sprintf("MLKEMEnabled: %v\n", pqkem.Enabled()))
|
||||||
|
configContent.WriteString(fmt.Sprintf("MLKEMStrict: %v\n", pqkem.Strict()))
|
||||||
if g.internalConfig.ServerSSHAllowed != nil {
|
if g.internalConfig.ServerSSHAllowed != nil {
|
||||||
configContent.WriteString(fmt.Sprintf("ServerSSHAllowed: %v\n", *g.internalConfig.ServerSSHAllowed))
|
configContent.WriteString(fmt.Sprintf("ServerSSHAllowed: %v\n", *g.internalConfig.ServerSSHAllowed))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1187,7 +1187,13 @@ func isRosenpassEnabled(remoteRosenpassPubKey []byte) bool {
|
|||||||
// the status "Quantum resistance" field: either Rosenpass (the remote advertised a
|
// the status "Quantum resistance" field: either Rosenpass (the remote advertised a
|
||||||
// Rosenpass key) or the ML-KEM exchange (a PQ PSK has been derived for this peer).
|
// Rosenpass key) or the ML-KEM exchange (a PQ PSK has been derived for this peer).
|
||||||
func (conn *Conn) quantumResistant(remoteRosenpassPubKey []byte, pqEstablished bool) bool {
|
func (conn *Conn) quantumResistant(remoteRosenpassPubKey []byte, pqEstablished bool) bool {
|
||||||
return isRosenpassEnabled(remoteRosenpassPubKey) || pqEstablished
|
// Rosenpass protects the tunnel only when both sides run it: the local peer has a
|
||||||
|
// Rosenpass key AND the remote advertised one. Checking only the remote key would
|
||||||
|
// report a plain (or blocked) tunnel as quantum-resistant when the local side does
|
||||||
|
// not run Rosenpass — e.g. against a peer that merely advertises it in a mixed
|
||||||
|
// deployment. A homogeneous Rosenpass deployment (both sides on) is unaffected.
|
||||||
|
rosenpassActive := conn.config.RosenpassConfig.PubKey != nil && isRosenpassEnabled(remoteRosenpassPubKey)
|
||||||
|
return rosenpassActive || pqEstablished
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalConnStatus(in connStatusInputs) guard.ConnStatus {
|
func evalConnStatus(in connStatusInputs) guard.ConnStatus {
|
||||||
|
|||||||
@@ -142,6 +142,14 @@ type RosenpassState struct {
|
|||||||
Permissive bool
|
Permissive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MLKEMState contains the latest state of the ML-KEM post-quantum exchange, the
|
||||||
|
// Rosenpass alternative. Strict is the ML-KEM counterpart of Rosenpass non-permissive:
|
||||||
|
// it fails closed until the KEM PSK is established.
|
||||||
|
type MLKEMState struct {
|
||||||
|
Enabled bool
|
||||||
|
Strict bool
|
||||||
|
}
|
||||||
|
|
||||||
// NSGroupState represents the status of a DNS server group, including associated domains,
|
// NSGroupState represents the status of a DNS server group, including associated domains,
|
||||||
// whether it's enabled, and the last error message encountered during probing.
|
// whether it's enabled, and the last error message encountered during probing.
|
||||||
type NSGroupState struct {
|
type NSGroupState struct {
|
||||||
@@ -159,6 +167,7 @@ type FullStatus struct {
|
|||||||
SignalState SignalState
|
SignalState SignalState
|
||||||
LocalPeerState LocalPeerState
|
LocalPeerState LocalPeerState
|
||||||
RosenpassState RosenpassState
|
RosenpassState RosenpassState
|
||||||
|
MLKEMState MLKEMState
|
||||||
Relays []relay.ProbeResult
|
Relays []relay.ProbeResult
|
||||||
NSGroupStates []NSGroupState
|
NSGroupStates []NSGroupState
|
||||||
NumOfForwardingRules int
|
NumOfForwardingRules int
|
||||||
@@ -209,6 +218,8 @@ type Status struct {
|
|||||||
notifier *notifier
|
notifier *notifier
|
||||||
rosenpassEnabled bool
|
rosenpassEnabled bool
|
||||||
rosenpassPermissive bool
|
rosenpassPermissive bool
|
||||||
|
mlkemEnabled bool
|
||||||
|
mlkemStrict bool
|
||||||
// sessionExpiresAt is the absolute UTC instant at which the peer's SSO
|
// sessionExpiresAt is the absolute UTC instant at which the peer's SSO
|
||||||
// session expires. Zero when the peer is not SSO-tracked or login
|
// session expires. Zero when the peer is not SSO-tracked or login
|
||||||
// expiration is disabled. Populated from management LoginResponse /
|
// expiration is disabled. Populated from management LoginResponse /
|
||||||
@@ -952,6 +963,14 @@ func (d *Status) UpdateRosenpass(rosenpassEnabled, rosenpassPermissive bool) {
|
|||||||
d.rosenpassEnabled = rosenpassEnabled
|
d.rosenpassEnabled = rosenpassEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateMLKEM updates the ML-KEM post-quantum exchange configuration.
|
||||||
|
func (d *Status) UpdateMLKEM(mlkemEnabled, mlkemStrict bool) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
d.mlkemEnabled = mlkemEnabled
|
||||||
|
d.mlkemStrict = mlkemStrict
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Status) UpdateLazyConnection(enabled bool) {
|
func (d *Status) UpdateLazyConnection(enabled bool) {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
@@ -1044,6 +1063,15 @@ func (d *Status) GetRosenpassState() RosenpassState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Status) GetMLKEMState() MLKEMState {
|
||||||
|
d.mux.RLock()
|
||||||
|
defer d.mux.RUnlock()
|
||||||
|
return MLKEMState{
|
||||||
|
d.mlkemEnabled,
|
||||||
|
d.mlkemStrict,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Status) GetLazyConnection() bool {
|
func (d *Status) GetLazyConnection() bool {
|
||||||
d.mux.RLock()
|
d.mux.RLock()
|
||||||
defer d.mux.RUnlock()
|
defer d.mux.RUnlock()
|
||||||
@@ -1186,6 +1214,7 @@ func (d *Status) GetFullStatus() FullStatus {
|
|||||||
SignalState: d.GetSignalState(),
|
SignalState: d.GetSignalState(),
|
||||||
Relays: d.GetRelayStates(),
|
Relays: d.GetRelayStates(),
|
||||||
RosenpassState: d.GetRosenpassState(),
|
RosenpassState: d.GetRosenpassState(),
|
||||||
|
MLKEMState: d.GetMLKEMState(),
|
||||||
NSGroupStates: d.GetDNSStates(),
|
NSGroupStates: d.GetDNSStates(),
|
||||||
NumOfForwardingRules: len(d.ForwardingRules()),
|
NumOfForwardingRules: len(d.ForwardingRules()),
|
||||||
LazyConnectionEnabled: d.GetLazyConnection(),
|
LazyConnectionEnabled: d.GetLazyConnection(),
|
||||||
@@ -1559,6 +1588,8 @@ func (fs FullStatus) ToProto() *proto.FullStatus {
|
|||||||
pbFullStatus.LocalPeerState.WgPort = int32(fs.LocalPeerState.WgPort)
|
pbFullStatus.LocalPeerState.WgPort = int32(fs.LocalPeerState.WgPort)
|
||||||
pbFullStatus.LocalPeerState.RosenpassPermissive = fs.RosenpassState.Permissive
|
pbFullStatus.LocalPeerState.RosenpassPermissive = fs.RosenpassState.Permissive
|
||||||
pbFullStatus.LocalPeerState.RosenpassEnabled = fs.RosenpassState.Enabled
|
pbFullStatus.LocalPeerState.RosenpassEnabled = fs.RosenpassState.Enabled
|
||||||
|
pbFullStatus.LocalPeerState.MlkemEnabled = fs.MLKEMState.Enabled
|
||||||
|
pbFullStatus.LocalPeerState.MlkemStrict = fs.MLKEMState.Strict
|
||||||
pbFullStatus.NumberOfForwardingRules = int32(fs.NumOfForwardingRules)
|
pbFullStatus.NumberOfForwardingRules = int32(fs.NumOfForwardingRules)
|
||||||
pbFullStatus.LazyConnectionEnabled = fs.LazyConnectionEnabled
|
pbFullStatus.LazyConnectionEnabled = fs.LazyConnectionEnabled
|
||||||
|
|
||||||
|
|||||||
@@ -1686,6 +1686,8 @@ type LocalPeerState struct {
|
|||||||
Networks []string `protobuf:"bytes,7,rep,name=networks,proto3" json:"networks,omitempty"`
|
Networks []string `protobuf:"bytes,7,rep,name=networks,proto3" json:"networks,omitempty"`
|
||||||
Ipv6 string `protobuf:"bytes,8,opt,name=ipv6,proto3" json:"ipv6,omitempty"`
|
Ipv6 string `protobuf:"bytes,8,opt,name=ipv6,proto3" json:"ipv6,omitempty"`
|
||||||
WgPort int32 `protobuf:"varint,9,opt,name=wgPort,proto3" json:"wgPort,omitempty"`
|
WgPort int32 `protobuf:"varint,9,opt,name=wgPort,proto3" json:"wgPort,omitempty"`
|
||||||
|
MlkemEnabled bool `protobuf:"varint,10,opt,name=mlkemEnabled,proto3" json:"mlkemEnabled,omitempty"`
|
||||||
|
MlkemStrict bool `protobuf:"varint,11,opt,name=mlkemStrict,proto3" json:"mlkemStrict,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -1783,6 +1785,20 @@ func (x *LocalPeerState) GetWgPort() int32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LocalPeerState) GetMlkemEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.MlkemEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LocalPeerState) GetMlkemStrict() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.MlkemStrict
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// SignalState contains the latest state of a signal connection
|
// SignalState contains the latest state of a signal connection
|
||||||
type SignalState struct {
|
type SignalState struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@@ -7265,7 +7281,7 @@ const file_daemon_proto_rawDesc = "" +
|
|||||||
"\n" +
|
"\n" +
|
||||||
"sshHostKey\x18\x13 \x01(\fR\n" +
|
"sshHostKey\x18\x13 \x01(\fR\n" +
|
||||||
"sshHostKey\x12\x12\n" +
|
"sshHostKey\x12\x12\n" +
|
||||||
"\x04ipv6\x18\x14 \x01(\tR\x04ipv6\"\x9c\x02\n" +
|
"\x04ipv6\x18\x14 \x01(\tR\x04ipv6\"\xe2\x02\n" +
|
||||||
"\x0eLocalPeerState\x12\x0e\n" +
|
"\x0eLocalPeerState\x12\x0e\n" +
|
||||||
"\x02IP\x18\x01 \x01(\tR\x02IP\x12\x16\n" +
|
"\x02IP\x18\x01 \x01(\tR\x02IP\x12\x16\n" +
|
||||||
"\x06pubKey\x18\x02 \x01(\tR\x06pubKey\x12(\n" +
|
"\x06pubKey\x18\x02 \x01(\tR\x06pubKey\x12(\n" +
|
||||||
@@ -7275,7 +7291,10 @@ const file_daemon_proto_rawDesc = "" +
|
|||||||
"\x13rosenpassPermissive\x18\x06 \x01(\bR\x13rosenpassPermissive\x12\x1a\n" +
|
"\x13rosenpassPermissive\x18\x06 \x01(\bR\x13rosenpassPermissive\x12\x1a\n" +
|
||||||
"\bnetworks\x18\a \x03(\tR\bnetworks\x12\x12\n" +
|
"\bnetworks\x18\a \x03(\tR\bnetworks\x12\x12\n" +
|
||||||
"\x04ipv6\x18\b \x01(\tR\x04ipv6\x12\x16\n" +
|
"\x04ipv6\x18\b \x01(\tR\x04ipv6\x12\x16\n" +
|
||||||
"\x06wgPort\x18\t \x01(\x05R\x06wgPort\"S\n" +
|
"\x06wgPort\x18\t \x01(\x05R\x06wgPort\x12\"\n" +
|
||||||
|
"\fmlkemEnabled\x18\n" +
|
||||||
|
" \x01(\bR\fmlkemEnabled\x12 \n" +
|
||||||
|
"\vmlkemStrict\x18\v \x01(\bR\vmlkemStrict\"S\n" +
|
||||||
"\vSignalState\x12\x10\n" +
|
"\vSignalState\x12\x10\n" +
|
||||||
"\x03URL\x18\x01 \x01(\tR\x03URL\x12\x1c\n" +
|
"\x03URL\x18\x01 \x01(\tR\x03URL\x12\x1c\n" +
|
||||||
"\tconnected\x18\x02 \x01(\bR\tconnected\x12\x14\n" +
|
"\tconnected\x18\x02 \x01(\bR\tconnected\x12\x14\n" +
|
||||||
|
|||||||
@@ -412,6 +412,8 @@ message LocalPeerState {
|
|||||||
repeated string networks = 7;
|
repeated string networks = 7;
|
||||||
string ipv6 = 8;
|
string ipv6 = 8;
|
||||||
int32 wgPort = 9;
|
int32 wgPort = 9;
|
||||||
|
bool mlkemEnabled = 10;
|
||||||
|
bool mlkemStrict = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignalState contains the latest state of a signal connection
|
// SignalState contains the latest state of a signal connection
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
gstatus "google.golang.org/grpc/status"
|
gstatus "google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/pqkem"
|
||||||
"github.com/netbirdio/netbird/client/mdm"
|
"github.com/netbirdio/netbird/client/mdm"
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
)
|
)
|
||||||
@@ -133,6 +134,9 @@ func (s *Server) restartEngineForMDMLocked() error {
|
|||||||
s.config = config
|
s.config = config
|
||||||
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
||||||
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
||||||
|
// ML-KEM is env-driven today; mirror the Rosenpass update so the status stays in
|
||||||
|
// sync after an MDM config apply and the wiring is ready if it becomes a config field.
|
||||||
|
s.statusRecorder.UpdateMLKEM(pqkem.Enabled(), pqkem.Strict())
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(s.rootCtx)
|
ctx, cancel := context.WithCancel(s.rootCtx)
|
||||||
s.actCancel = cancel
|
s.actCancel = cancel
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/pqkem"
|
||||||
"github.com/netbirdio/netbird/client/internal/statemanager"
|
"github.com/netbirdio/netbird/client/internal/statemanager"
|
||||||
"github.com/netbirdio/netbird/client/internal/updater"
|
"github.com/netbirdio/netbird/client/internal/updater"
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
@@ -302,6 +303,7 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
||||||
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
||||||
|
s.statusRecorder.UpdateMLKEM(pqkem.Enabled(), pqkem.Strict())
|
||||||
s.localMetrics.Reconcile(config.LocalMetricsEnabled, config.LocalMetricsAddress)
|
s.localMetrics.Reconcile(config.LocalMetricsEnabled, config.LocalMetricsAddress)
|
||||||
|
|
||||||
if s.sessionWatcher == nil {
|
if s.sessionWatcher == nil {
|
||||||
@@ -1096,6 +1098,7 @@ func (s *Server) Up(callerCtx context.Context, msg *proto.UpRequest) (*proto.UpR
|
|||||||
|
|
||||||
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
||||||
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
||||||
|
s.statusRecorder.UpdateMLKEM(pqkem.Enabled(), pqkem.Strict())
|
||||||
s.localMetrics.Reconcile(s.config.LocalMetricsEnabled, s.config.LocalMetricsAddress)
|
s.localMetrics.Reconcile(s.config.LocalMetricsEnabled, s.config.LocalMetricsAddress)
|
||||||
|
|
||||||
s.clientRunning = true
|
s.clientRunning = true
|
||||||
@@ -1698,6 +1701,7 @@ func (s *Server) buildStatusResponse(ctx context.Context, msg *proto.StatusReque
|
|||||||
|
|
||||||
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
||||||
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
||||||
|
s.statusRecorder.UpdateMLKEM(pqkem.Enabled(), pqkem.Strict())
|
||||||
|
|
||||||
if msg.GetFullPeerStatus {
|
if msg.GetFullPeerStatus {
|
||||||
s.runProbes(ctx, msg.ShouldRunProbes)
|
s.runProbes(ctx, msg.ShouldRunProbes)
|
||||||
|
|||||||
+60
-25
@@ -155,6 +155,8 @@ type OutputOverview struct {
|
|||||||
FQDN string `json:"fqdn" yaml:"fqdn"`
|
FQDN string `json:"fqdn" yaml:"fqdn"`
|
||||||
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
||||||
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
|
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
|
||||||
|
MLKEMEnabled bool `json:"mlkemEnabled" yaml:"mlkemEnabled"`
|
||||||
|
MLKEMStrict bool `json:"mlkemStrict" yaml:"mlkemStrict"`
|
||||||
Networks []string `json:"networks" yaml:"networks"`
|
Networks []string `json:"networks" yaml:"networks"`
|
||||||
NumberOfForwardingRules int `json:"forwardingRules" yaml:"forwardingRules"`
|
NumberOfForwardingRules int `json:"forwardingRules" yaml:"forwardingRules"`
|
||||||
NSServerGroups []NsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
|
NSServerGroups []NsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
|
||||||
@@ -205,6 +207,8 @@ func ConvertToStatusOutputOverview(pbFullStatus *proto.FullStatus, opts ConvertO
|
|||||||
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
|
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
|
||||||
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
|
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
|
||||||
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
|
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
|
||||||
|
MLKEMEnabled: pbFullStatus.GetLocalPeerState().GetMlkemEnabled(),
|
||||||
|
MLKEMStrict: pbFullStatus.GetLocalPeerState().GetMlkemStrict(),
|
||||||
Networks: pbFullStatus.GetLocalPeerState().GetNetworks(),
|
Networks: pbFullStatus.GetLocalPeerState().GetNetworks(),
|
||||||
NumberOfForwardingRules: int(pbFullStatus.GetNumberOfForwardingRules()),
|
NumberOfForwardingRules: int(pbFullStatus.GetNumberOfForwardingRules()),
|
||||||
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
|
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
|
||||||
@@ -511,12 +515,14 @@ func (o *OutputOverview) GeneralSummary(showURL bool, showRelays bool, showNameS
|
|||||||
dnsServersString = fmt.Sprintf("%d/%d Available", countEnabled(o.NSServerGroups), len(o.NSServerGroups))
|
dnsServersString = fmt.Sprintf("%d/%d Available", countEnabled(o.NSServerGroups), len(o.NSServerGroups))
|
||||||
}
|
}
|
||||||
|
|
||||||
rosenpassEnabledStatus := "false"
|
// Quantum resistance is the outcome (post-quantum protected or not); the mechanism
|
||||||
if o.RosenpassEnabled {
|
// that provides it — ML-KEM or Rosenpass, strict or permissive — is a separate detail.
|
||||||
rosenpassEnabledStatus = "true"
|
mechanism := quantumResistanceMechanism(o.RosenpassEnabled, o.RosenpassPermissive, o.MLKEMEnabled, o.MLKEMStrict)
|
||||||
if o.RosenpassPermissive {
|
quantumResistanceStatus := "false"
|
||||||
rosenpassEnabledStatus = "true (permissive)" //nolint:gosec
|
mechanismLine := ""
|
||||||
}
|
if mechanism != "none" {
|
||||||
|
quantumResistanceStatus = "true"
|
||||||
|
mechanismLine = fmt.Sprintf("Quantum resistance mechanism: %s\n", mechanism)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazyConnectionEnabledStatus := "false"
|
lazyConnectionEnabledStatus := "false"
|
||||||
@@ -615,6 +621,7 @@ func (o *OutputOverview) GeneralSummary(showURL bool, showRelays bool, showNameS
|
|||||||
"Interface type: %s\n"+
|
"Interface type: %s\n"+
|
||||||
"Wireguard port: %s\n"+
|
"Wireguard port: %s\n"+
|
||||||
"Quantum resistance: %s\n"+
|
"Quantum resistance: %s\n"+
|
||||||
|
"%s"+
|
||||||
"Lazy connection: %s\n"+
|
"Lazy connection: %s\n"+
|
||||||
"SSH Server: %s\n"+
|
"SSH Server: %s\n"+
|
||||||
"Networks: %s\n"+
|
"Networks: %s\n"+
|
||||||
@@ -634,7 +641,8 @@ func (o *OutputOverview) GeneralSummary(showURL bool, showRelays bool, showNameS
|
|||||||
ipv6Line,
|
ipv6Line,
|
||||||
interfaceTypeString,
|
interfaceTypeString,
|
||||||
wgPortString,
|
wgPortString,
|
||||||
rosenpassEnabledStatus,
|
quantumResistanceStatus,
|
||||||
|
mechanismLine,
|
||||||
lazyConnectionEnabledStatus,
|
lazyConnectionEnabledStatus,
|
||||||
sshServerStatus,
|
sshServerStatus,
|
||||||
networks,
|
networks,
|
||||||
@@ -647,7 +655,7 @@ func (o *OutputOverview) GeneralSummary(showURL bool, showRelays bool, showNameS
|
|||||||
|
|
||||||
// FullDetailSummary returns a full detailed summary with peer details and events.
|
// FullDetailSummary returns a full detailed summary with peer details and events.
|
||||||
func (o *OutputOverview) FullDetailSummary() string {
|
func (o *OutputOverview) FullDetailSummary() string {
|
||||||
parsedPeersString := parsePeers(o.Peers, o.RosenpassEnabled, o.RosenpassPermissive)
|
parsedPeersString := parsePeers(o.Peers, o.RosenpassEnabled, o.RosenpassPermissive, o.MLKEMEnabled, o.MLKEMStrict)
|
||||||
parsedEventsString := parseEvents(o.Events)
|
parsedEventsString := parseEvents(o.Events)
|
||||||
summary := o.GeneralSummary(true, true, true, true)
|
summary := o.GeneralSummary(true, true, true, true)
|
||||||
|
|
||||||
@@ -690,6 +698,8 @@ func ToProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
|||||||
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
|
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
|
||||||
pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive
|
pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive
|
||||||
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
||||||
|
pbFullStatus.LocalPeerState.MlkemEnabled = fullStatus.MLKEMState.Enabled
|
||||||
|
pbFullStatus.LocalPeerState.MlkemStrict = fullStatus.MLKEMState.Strict
|
||||||
pbFullStatus.LocalPeerState.Networks = maps.Keys(fullStatus.LocalPeerState.Routes)
|
pbFullStatus.LocalPeerState.Networks = maps.Keys(fullStatus.LocalPeerState.Routes)
|
||||||
pbFullStatus.NumberOfForwardingRules = int32(fullStatus.NumOfForwardingRules)
|
pbFullStatus.NumberOfForwardingRules = int32(fullStatus.NumOfForwardingRules)
|
||||||
pbFullStatus.LazyConnectionEnabled = fullStatus.LazyConnectionEnabled
|
pbFullStatus.LazyConnectionEnabled = fullStatus.LazyConnectionEnabled
|
||||||
@@ -755,7 +765,47 @@ func ToProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
|||||||
return &pbFullStatus
|
return &pbFullStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePeers(peers PeersStateOutput, rosenpassEnabled, rosenpassPermissive bool) string {
|
// quantumResistanceMechanism reports which post-quantum mechanism the local client runs,
|
||||||
|
// as an enum: "none", "ML-KEM strict", "ML-KEM permissive", "RP strict", "RP permissive".
|
||||||
|
// ML-KEM and Rosenpass are mutually exclusive, so at most one is active; ML-KEM takes
|
||||||
|
// precedence if somehow both are set. Strict/permissive is the fail-closed vs fail-open mode.
|
||||||
|
func quantumResistanceMechanism(rosenpassEnabled, rosenpassPermissive, mlkemEnabled, mlkemStrict bool) string {
|
||||||
|
switch {
|
||||||
|
case mlkemEnabled && mlkemStrict:
|
||||||
|
return "ML-KEM strict"
|
||||||
|
case mlkemEnabled:
|
||||||
|
return "ML-KEM permissive"
|
||||||
|
case rosenpassEnabled && !rosenpassPermissive:
|
||||||
|
return "RP strict"
|
||||||
|
case rosenpassEnabled:
|
||||||
|
return "RP permissive"
|
||||||
|
default:
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// peerQuantumResistanceStatus renders the per-peer "Quantum resistance" field. established
|
||||||
|
// reports whether this peer's tunnel is post-quantum protected (Rosenpass or ML-KEM). When
|
||||||
|
// it is not, the reason is phrased for whichever mechanism is active locally.
|
||||||
|
func peerQuantumResistanceStatus(established, rosenpassEnabled, rosenpassPermissive, mlkemEnabled, mlkemStrict bool) string {
|
||||||
|
if established {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case mlkemEnabled && mlkemStrict:
|
||||||
|
return "false (ML-KEM strict: blocking peer traffic until the exchange converges)"
|
||||||
|
case mlkemEnabled:
|
||||||
|
return "false (ML-KEM: not converged yet, or peer does not run the exchange)"
|
||||||
|
case rosenpassEnabled && rosenpassPermissive:
|
||||||
|
return "false (remote didn't enable quantum resistance)"
|
||||||
|
case rosenpassEnabled:
|
||||||
|
return "false (connection won't work without a permissive mode)"
|
||||||
|
default:
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePeers(peers PeersStateOutput, rosenpassEnabled, rosenpassPermissive, mlkemEnabled, mlkemStrict bool) string {
|
||||||
var (
|
var (
|
||||||
peersString = ""
|
peersString = ""
|
||||||
)
|
)
|
||||||
@@ -782,22 +832,7 @@ func parsePeers(peers PeersStateOutput, rosenpassEnabled, rosenpassPermissive bo
|
|||||||
remoteICEEndpoint = peerState.IceCandidateEndpoint.Remote
|
remoteICEEndpoint = peerState.IceCandidateEndpoint.Remote
|
||||||
}
|
}
|
||||||
|
|
||||||
rosenpassEnabledStatus := "false"
|
rosenpassEnabledStatus := peerQuantumResistanceStatus(peerState.RosenpassEnabled, rosenpassEnabled, rosenpassPermissive, mlkemEnabled, mlkemStrict)
|
||||||
if rosenpassEnabled {
|
|
||||||
if peerState.RosenpassEnabled {
|
|
||||||
rosenpassEnabledStatus = "true"
|
|
||||||
} else {
|
|
||||||
if rosenpassPermissive {
|
|
||||||
rosenpassEnabledStatus = "false (remote didn't enable quantum resistance)"
|
|
||||||
} else {
|
|
||||||
rosenpassEnabledStatus = "false (connection won't work without a permissive mode)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if peerState.RosenpassEnabled {
|
|
||||||
rosenpassEnabledStatus = "false (connection might not work without a remote permissive mode)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
networks := "-"
|
networks := "-"
|
||||||
if len(peerState.Networks) > 0 {
|
if len(peerState.Networks) > 0 {
|
||||||
|
|||||||
@@ -375,6 +375,8 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
"fqdn": "some-localhost.awesome-domain.com",
|
"fqdn": "some-localhost.awesome-domain.com",
|
||||||
"quantumResistance": false,
|
"quantumResistance": false,
|
||||||
"quantumResistancePermissive": false,
|
"quantumResistancePermissive": false,
|
||||||
|
"mlkemEnabled": false,
|
||||||
|
"mlkemStrict": false,
|
||||||
"networks": [
|
"networks": [
|
||||||
"10.10.0.0/24"
|
"10.10.0.0/24"
|
||||||
],
|
],
|
||||||
@@ -494,6 +496,8 @@ wireguardPort: 51820
|
|||||||
fqdn: some-localhost.awesome-domain.com
|
fqdn: some-localhost.awesome-domain.com
|
||||||
quantumResistance: false
|
quantumResistance: false
|
||||||
quantumResistancePermissive: false
|
quantumResistancePermissive: false
|
||||||
|
mlkemEnabled: false
|
||||||
|
mlkemStrict: false
|
||||||
networks:
|
networks:
|
||||||
- 10.10.0.0/24
|
- 10.10.0.0/24
|
||||||
forwardingRules: 0
|
forwardingRules: 0
|
||||||
|
|||||||
Reference in New Issue
Block a user