@@ -47,18 +47,24 @@ func IsTLSDeviceVerified(ext *tlsca.DeviceExtensions) bool {
4747
4848// VerifyTLSUser verifies if the TLS identity has the required extensions to
4949// fulfill the device trust configuration.
50- func VerifyTLSUser (ctx context.Context , dt * types.DeviceTrust , identity tlsca.Identity ) error {
51- return verifyDeviceExtensions (ctx , dt , identity .Username , identity .IsBot (), IsTLSDeviceVerified (& identity .DeviceExtensions ))
50+ func VerifyTLSUser (ctx context.Context , dt * types.DeviceTrust , id tlsca.Identity ) error {
51+ return verifyDeviceExtensions (ctx ,
52+ dt ,
53+ id .Username ,
54+ VerifyTrustedDeviceModeParams {
55+ IsTrustedDevice : IsTLSDeviceVerified (& id .DeviceExtensions ),
56+ IsBot : id .IsBot (),
57+ })
5258}
5359
5460// IsSSHDeviceVerified returns true if cert contains all required device
5561// extensions.
56- func IsSSHDeviceVerified (ident * sshca.Identity ) bool {
62+ func IsSSHDeviceVerified (id * sshca.Identity ) bool {
5763 // Expect all device extensions to be present.
58- return ident != nil &&
59- ident .DeviceID != "" &&
60- ident .DeviceAssetTag != "" &&
61- ident .DeviceCredentialID != ""
64+ return id != nil &&
65+ id .DeviceID != "" &&
66+ id .DeviceAssetTag != "" &&
67+ id .DeviceCredentialID != ""
6268}
6369
6470// HasDeviceTrustExtensions returns true if the certificate's extension names
@@ -85,31 +91,92 @@ func HasDeviceTrustExtensions(extensions []string) bool {
8591
8692// VerifySSHUser verifies if the SSH certificate has the required extensions to
8793// fulfill the device trust configuration.
88- func VerifySSHUser (ctx context.Context , dt * types.DeviceTrust , ident * sshca.Identity ) error {
89- if ident == nil {
94+ func VerifySSHUser (ctx context.Context , dt * types.DeviceTrust , id * sshca.Identity ) error {
95+ if id == nil {
9096 return trace .BadParameter ("ssh identity required" )
9197 }
92- return verifyDeviceExtensions (ctx , dt , ident .Username , ident .IsBot (), IsSSHDeviceVerified (ident ))
98+ return verifyDeviceExtensions (ctx ,
99+ dt ,
100+ id .Username ,
101+ VerifyTrustedDeviceModeParams {
102+ IsTrustedDevice : IsSSHDeviceVerified (id ),
103+ IsBot : id .IsBot (),
104+ })
93105}
94106
95- func verifyDeviceExtensions (ctx context.Context , dt * types.DeviceTrust , username string , isBot bool , verified bool ) error {
96- mode := dtconfig .GetEnforcementMode (dt )
107+ func verifyDeviceExtensions (
108+ ctx context.Context ,
109+ dt * types.DeviceTrust ,
110+ username string ,
111+ params VerifyTrustedDeviceModeParams ,
112+ ) error {
113+ enforcementMode := dtconfig .GetEnforcementMode (dt )
97114
98- var pass bool
99- switch mode {
115+ if err := VerifyTrustedDeviceMode (enforcementMode , params ); err != nil {
116+ slog .DebugContext (ctx , "Device Trust: denied access for unidentified device" , "user" , username )
117+ return trace .Wrap (err )
118+ }
119+
120+ return nil
121+ }
122+
123+ // VerifyTrustedDeviceModeParams holds additional parameters for
124+ // [VerifyTrustedDeviceMode].
125+ type VerifyTrustedDeviceModeParams struct {
126+ // IsTrustedDevice informs if the device in use is trusted.
127+ IsTrustedDevice bool
128+ // IsBot informs if the user is a bot.
129+ IsBot bool
130+ // AllowEmptyMode allows an empty "enforcementMode", treating it similarly to
131+ // DeviceTrustModeOff.
132+ AllowEmptyMode bool
133+ }
134+
135+ // VerifyTrustedDeviceMode runs the fundamental device trust authorization
136+ // logic, checking an effective device trust mode against a set of access
137+ // params.
138+ //
139+ // Most callers should use a higher level function, such as [VerifyTLSUser] or
140+ // [VerifySSHUser].
141+ //
142+ // If enforcementMode comes from the global config it must be resolved via
143+ // [dtconfig.GetEnforcementMode] prior to calling the method.
144+ //
145+ // Returns an error, typically ErrTrustedDeviceRequired, if the checked device
146+ // is not allowed.
147+ func VerifyTrustedDeviceMode (
148+ enforcementMode constants.DeviceTrustMode ,
149+ params VerifyTrustedDeviceModeParams ,
150+ ) error {
151+ if enforcementMode == "" && params .AllowEmptyMode {
152+ return nil // Equivalent to mode=off.
153+ }
154+
155+ // Assume required so it denies by default.
156+ required := true
157+
158+ // Switch on mode before any exemptions so we catch unknown modes.
159+ switch enforcementMode {
100160 case constants .DeviceTrustModeOff , constants .DeviceTrustModeOptional :
101161 // OK, extensions not enforced.
102- pass = true
162+ required = false
163+
103164 case constants .DeviceTrustModeRequiredForHumans :
104165 // Humans must use trusted devices, bots can use untrusted devices.
105- pass = verified || isBot
166+ required = ! params .IsBot
167+
106168 case constants .DeviceTrustModeRequired :
107169 // Only trusted devices allowed for bot human and bot users.
108- pass = verified
170+
171+ default :
172+ slog .WarnContext (context .Background (),
173+ "Unknown device trust mode, treating device as untrusted" ,
174+ "mode" , enforcementMode ,
175+ )
176+ return trace .Wrap (ErrTrustedDeviceRequired )
109177 }
110178
111- if ! pass {
112- slog .DebugContext (ctx , "Device Trust: denied access for unidentified device" , "user" , username )
179+ if required && ! params .IsTrustedDevice {
113180 return trace .Wrap (ErrTrustedDeviceRequired )
114181 }
115182
0 commit comments