Trying to bypass when not allowed returns an error (#40186)

**Related issue:** Resolves #36105
This commit is contained in:
Dante Catalfamo
2026-02-20 12:27:45 -05:00
committed by GitHub
parent 183face853
commit cb559db5e1
3 changed files with 109 additions and 2 deletions
@@ -7,11 +7,24 @@ import (
"time"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/jmoiron/sqlx"
)
func (ds *Datastore) ConditionalAccessBypassDevice(ctx context.Context, hostID uint) error {
const stmt = `
const checkStmt = `
SELECT
COUNT(*)
FROM
policy_membership pm
INNER JOIN
policies p ON pm.policy_id = p.id
WHERE
pm.host_id = ?
AND p.conditional_access_bypass_enabled = 0
AND pm.passes = 0
`
const insertStmt = `
INSERT INTO
host_conditional_access (host_id, bypassed_at)
VALUES
@@ -19,7 +32,17 @@ func (ds *Datastore) ConditionalAccessBypassDevice(ctx context.Context, hostID u
ON DUPLICATE KEY UPDATE
bypassed_at = NOW(6)`
if _, err := ds.writer(ctx).ExecContext(ctx, stmt, hostID); err != nil {
var blockCount uint
if err := sqlx.GetContext(ctx, ds.writer(ctx), &blockCount, checkStmt, hostID); err != nil {
return ctxerr.Wrap(ctx, err, "checking failing policy count")
}
if blockCount != 0 {
return &fleet.BadRequestError{Message: "host has failing non-bypassable policies"}
}
if _, err := ds.writer(ctx).ExecContext(ctx, insertStmt, hostID); err != nil {
return ctxerr.Wrap(ctx, err, "inserting host conditional bypass")
}
@@ -7,6 +7,8 @@ import (
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/fleetdm/fleet/v4/server/test"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/require"
)
@@ -18,6 +20,7 @@ func TestConditionalAccessBypass(t *testing.T) {
fn func(t *testing.T, ds *Datastore)
}{
{"ConditionalAccessBypassDevice", testConditionalAccessBypassDevice},
{"ConditionalAccessBypassDeviceWithBlockingPolicy", testConditionalAccessBypassDeviceWithBlockingPolicy},
{"ConditionalAccessConsumeBypass", testConditionalAccessConsumeBypass},
{"ConditionalAccessClearBypasses", testConditionalAccessClearBypasses},
{"ConditionalAccessBypassDeletedWithHost", testConditionalAccessBypassDeletedWithHost},
@@ -268,3 +271,51 @@ func testConditionalAccessBypassedAt(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.Nil(t, bypassedAtOther)
}
func testConditionalAccessBypassDeviceWithBlockingPolicy(t *testing.T, ds *Datastore) {
ctx := context.Background()
user := test.NewUser(t, ds, "Alice", "alice@example.com", true)
host, err := ds.NewHost(ctx, &fleet.Host{
DetailUpdatedAt: time.Now(),
LabelUpdatedAt: time.Now(),
PolicyUpdatedAt: time.Now(),
SeenTime: time.Now(),
NodeKey: ptr.String("blocking-policy-host"),
UUID: "blocking-policy-uuid",
Hostname: "blocking.local",
PrimaryIP: "192.168.1.10",
PrimaryMac: "30-65-EC-6F-C4-70",
})
require.NoError(t, err)
// Create a global policy with conditional_access_bypass_enabled defaulting to 1 (bypassable)
policy, err := ds.NewGlobalPolicy(ctx, &user.ID, fleet.PolicyPayload{
Name: "non-bypassable-policy",
Query: "select 1;",
})
require.NoError(t, err)
// Set conditional_access_bypass_enabled = 0 to make it non-bypassable
ExecAdhocSQL(t, ds, func(db sqlx.ExtContext) error {
_, err := db.ExecContext(ctx, `UPDATE policies SET conditional_access_bypass_enabled = 0 WHERE id = ?`, policy.ID)
return err
})
// Record a failing result for this policy on the host
err = ds.RecordPolicyQueryExecutions(ctx, host, map[uint]*bool{policy.ID: ptr.Bool(false)}, time.Now(), false)
require.NoError(t, err)
// Bypass should fail because the host has a failing non-bypassable policy
err = ds.ConditionalAccessBypassDevice(ctx, host.ID)
require.Error(t, err)
var badReqErr *fleet.BadRequestError
require.ErrorAs(t, err, &badReqErr)
// Verify no host_conditional_access row was created
var count int
innerErr := ds.writer(ctx).GetContext(ctx, &count, "SELECT COUNT(*) FROM host_conditional_access WHERE host_id = ?", host.ID)
require.NoError(t, innerErr)
require.Equal(t, 0, count)
}
@@ -24176,6 +24176,39 @@ FqU+KJOed6qlzj7qy+u5l6CQeajLGdjUxFlFyw==
require.NoError(t, getDeviceHostResp.Err)
require.False(t, getDeviceHostResp.Host.ConditionalAccessBypassed)
})
t.Run("bypass fails when host has failing non-bypassable policy", func(t *testing.T) {
token := fmt.Sprintf("bypass-nonbypassable-%s", uuid.New().String())
host := createHostAndDeviceToken(t, s.ds, token)
// Create a global policy
adminUser := s.users["admin1@example.com"]
policy, err := s.ds.NewGlobalPolicy(ctx, &adminUser.ID, fleet.PolicyPayload{
Name: fmt.Sprintf("non-bypassable-%s", uuid.New().String()),
Query: "select 1;",
})
require.NoError(t, err)
// Make the policy non-bypassable
mysql.ExecAdhocSQL(t, s.ds, func(db sqlx.ExtContext) error {
_, innerErr := db.ExecContext(ctx, `UPDATE policies SET conditional_access_bypass_enabled = 0 WHERE id = ?`, policy.ID)
return innerErr
})
// Record a failing result for this policy on the host
err = s.ds.RecordPolicyQueryExecutions(ctx, host, map[uint]*bool{policy.ID: ptr.Bool(false)}, time.Now(), false)
require.NoError(t, err)
// Bypass should fail with 400 Bad Request
var bypassResp bypassConditionalAccessResponse
s.DoJSON("POST", fmt.Sprintf("/api/v1/fleet/device/%s/bypass_conditional_access", token),
nil, http.StatusBadRequest, &bypassResp)
// Verify no bypass row was created
bypassedAt, err := s.ds.ConditionalAccessBypassedAt(ctx, host.ID)
require.NoError(t, err)
require.Nil(t, bypassedAt)
})
}
// generateTestCertForDeviceAuth generates a test certificate for device authentication.