Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/46644-bypass-end-user-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added a `--bypass-end-user-auth` flag to `fleetctl package` that configures the generated fleetd installer to skip the end-user authentication prompt during enrollment on Linux and Windows hosts (e.g. when the end user already authenticated via another MDM). Requires fleetd v1.60.0 or higher.
7 changes: 7 additions & 0 deletions client/orbit_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ var (
// - addr is the address of the Fleet server.
// - orbitHostInfo is the host system information used for enrolling to Fleet.
// - onGetConfigErrFns can be used to handle errors in the GetConfig request.
// - bypassEndUserAuth, when true, omits the end-user auth capability so the server enrolls the
// host without prompting for end-user authentication (only meaningful on Linux and Windows).
func NewOrbitClient(
rootDir string,
addr string,
Expand All @@ -212,8 +214,13 @@ func NewOrbitClient(
onGetConfigErrFns *OnGetConfigErrFuncs,
httpSignerWrapper func(*http.Client) *http.Client,
hostIdentityCertPath string,
bypassEndUserAuth bool,
) (*OrbitClient, error) {
orbitCapabilities := fleet.GetOrbitClientCapabilities()
if bypassEndUserAuth {
// Don't advertise the end-user auth capability so the Fleet server enrolls this host without prompting for EUA.
delete(orbitCapabilities, fleet.CapabilityEndUserAuth)
}
Comment thread
getvictor marked this conversation as resolved.
bc, err := NewBaseClient(addr, insecureSkipVerify, rootCA, "", fleetClientCert, orbitCapabilities, httpSignerWrapper)
if err != nil {
return nil, err
Expand Down
26 changes: 26 additions & 0 deletions client/orbit_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ func TestGetConfig(t *testing.T) {
)
}

func TestNewOrbitClientBypassEndUserAuth(t *testing.T) {
newClient := func(bypassEndUserAuth bool) *OrbitClient {
oc, err := NewOrbitClient(
t.TempDir(),
"https://fleet.example.com",
"",
true,
"secret",
nil,
fleet.OrbitHostInfo{HardwareUUID: "uuid", Hostname: "host"},
nil,
nil,
"",
bypassEndUserAuth,
)
require.NoError(t, err)
return oc
}

// With bypass enabled, orbit must not advertise the end-user auth capability.
require.NotContains(t, newClient(true).ClientCapabilities, fleet.CapabilityEndUserAuth)

// Without bypass, capabilities are unchanged from the default set.
require.Equal(t, fleet.GetOrbitClientCapabilities(), newClient(false).ClientCapabilities)
}

func clientWithConfig(cfg *fleet.OrbitConfig) *OrbitClient {
ctx, cancel := context.WithCancel(context.Background())
oc := &OrbitClient{
Expand Down
5 changes: 5 additions & 0 deletions cmd/fleetctl/fleetctl/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ func packageCommand() *cli.Command {
Usage: "Disable setup experience for Linux or Windows hosts",
Destination: &opt.DisableSetupExperience,
},
&cli.BoolFlag{
Name: "bypass-end-user-auth",
Usage: "Skip the end-user authentication prompt during fleetd enrollment (applies to Linux and Windows hosts; macOS only handles end-user auth during MDM enrollment)",
Destination: &opt.BypassEndUserAuth,
},
&cli.StringFlag{
Name: "update-url",
Usage: "URL for update server",
Expand Down
1 change: 1 addition & 0 deletions cmd/osquery-perf/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,7 @@ func (a *agent) runOrbitLoop() {
nil,
signerWrapper,
"",
false,
)
if err != nil {
log.Println("creating orbit client: ", err)
Expand Down
1 change: 1 addition & 0 deletions orbit/changes/46644-bypass-end-user-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added a `--bypass-end-user-auth` flag (env `ORBIT_BYPASS_END_USER_AUTH`) that skips the end-user authentication prompt during enrollment on Linux and Windows by not advertising the end-user auth capability to the Fleet server. When a Windows MDM EUA token is present, it takes precedence and end-user auth is still processed.
17 changes: 16 additions & 1 deletion orbit/cmd/orbit/orbit.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ func main() {
Usage: "Disables checking for setup experience on Linux or Windows hosts",
EnvVars: []string{"ORBIT_DISABLE_SETUP_EXPERIENCE"},
},
&cli.BoolFlag{
Name: "bypass-end-user-auth",
Usage: "Bypasses end-user authentication during fleetd enrollment on Linux and Windows",
EnvVars: []string{"ORBIT_BYPASS_END_USER_AUTH"},
},
}
app.Before = func(c *cli.Context) error {
// handle old installations, which had default root dir set to /var/lib/orbit
Expand Down Expand Up @@ -1169,6 +1174,14 @@ func orbitAction(c *cli.Context) error {
)
}

// Bypass end-user authentication only when there is no EUA token to process. When the Windows MDM installer supplies
// an EUA token, the user already authenticated during MDM enrollment and the server links the host's IdP account
// from that token. Processing the token requires that orbit keep advertising the end-user auth capability, so a
// present token takes precedence over the bypass flag.
euaToken := c.String("eua-token")
hasEUAToken := euaToken != "" && euaToken != constant.UnusedFlagKeyword
bypassEndUserAuth := c.Bool("bypass-end-user-auth") && !hasEUAToken

orbitClient, err = fleetclient.NewOrbitClient(
c.String("root-dir"),
fleetURL,
Expand All @@ -1187,6 +1200,7 @@ func orbitAction(c *cli.Context) error {
},
signerWrapper,
hostIdentityCertificatePath,
bypassEndUserAuth,
)
if err != nil {
return fmt.Errorf("error new orbit client: %w", err)
Expand All @@ -1204,7 +1218,7 @@ func orbitAction(c *cli.Context) error {

// Set the EUA token from the MSI installer (Windows MDM enrollment).
// Must be set before any authenticated request triggers enrollment.
if euaToken := c.String("eua-token"); euaToken != "" && euaToken != constant.UnusedFlagKeyword {
if hasEUAToken {
orbitClient.SetEUAToken(euaToken)
}

Expand Down Expand Up @@ -1448,6 +1462,7 @@ func orbitAction(c *cli.Context) error {
},
nil,
"",
bypassEndUserAuth,
)
if err != nil {
return fmt.Errorf("new client for capabilities checker: %w", err)
Expand Down
61 changes: 61 additions & 0 deletions orbit/pkg/packaging/bypass_end_user_auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package packaging

import (
"bytes"
"strings"
"testing"
"text/template"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestBypassEndUserAuthTemplates verifies the --bypass-end-user-auth switch is wired into the generated Linux env file
// and Windows MSI arguments when enabled, and absent when not. macOS is intentionally excluded.
func TestBypassEndUserAuthTemplates(t *testing.T) {
baseOpt := Options{
FleetURL: "https://fleet.example.com",
EnrollSecret: "secret",
OrbitChannel: "stable",
OsquerydChannel: "stable",
DesktopChannel: "stable",
NativePlatform: "windows",
Architecture: ArchAmd64,
}

// render executes tmpl with the bypass option toggled and returns the generated output.
render := func(t *testing.T, tmpl *template.Template, bypass bool) string {
t.Helper()
opt := baseOpt
opt.BypassEndUserAuth = bypass
var buf bytes.Buffer
require.NoError(t, tmpl.Execute(&buf, opt))
return buf.String()
}

t.Run("linux env file", func(t *testing.T) {
assert.Contains(t, render(t, envTemplate, true), "ORBIT_BYPASS_END_USER_AUTH=true")
assert.NotContains(t, render(t, envTemplate, false), "ORBIT_BYPASS_END_USER_AUTH")
})

t.Run("windows msi args", func(t *testing.T) {
// The flag is one of many appended to the service's ServiceInstall Arguments; isolate that line.
argsLine := func(output string) string {
t.Helper()
for line := range strings.SplitSeq(output, "\n") {
if strings.Contains(line, "Arguments=") && strings.Contains(line, "--fleet-url") {
return line
}
}
t.Fatal("ServiceInstall Arguments line not found in template output")
return ""
}
assert.Contains(t, argsLine(render(t, windowsWixTemplate, true)), "--bypass-end-user-auth")
assert.NotContains(t, argsLine(render(t, windowsWixTemplate, false)), "--bypass-end-user-auth")
})

// Guard the deliberate macOS exclusion: the flag must never leak into the launchd plist.
t.Run("macos launchd plist excluded", func(t *testing.T) {
assert.NotContains(t, render(t, macosLaunchdTemplate, true), "ORBIT_BYPASS_END_USER_AUTH")
})
Comment thread
getvictor marked this conversation as resolved.
}
1 change: 1 addition & 0 deletions orbit/pkg/packaging/linux_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ ORBIT_FLEET_DESKTOP_ALTERNATIVE_BROWSER_HOST={{ .FleetDesktopAlternativeBrowserH
{{ if .EndUserEmail }}ORBIT_END_USER_EMAIL={{.EndUserEmail}}{{ end }}
{{ if .FleetManagedHostIdentityCertificate }}ORBIT_FLEET_MANAGED_HOST_IDENTITY_CERTIFICATE=true{{ end }}
{{ if .DisableSetupExperience }}ORBIT_DISABLE_SETUP_EXPERIENCE=true{{ end }}
{{ if .BypassEndUserAuth }}ORBIT_BYPASS_END_USER_AUTH=true{{ end }}
`))

func writeEnvFile(opt Options, rootPath string) error {
Expand Down
3 changes: 3 additions & 0 deletions orbit/pkg/packaging/packaging.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ type Options struct {
DisableUpdates bool
// DisableSetupExperience disables setup experience for Linux hosts
Comment thread
getvictor marked this conversation as resolved.
DisableSetupExperience bool
// BypassEndUserAuth configures fleetd to skip end-user authentication during enrollment by not
// advertising the end-user auth capability to the Fleet server.
BypassEndUserAuth bool
// OrbitChannel is the update channel to use for Orbit.
OrbitChannel string
// OsquerydChannel is the update channel to use for Osquery (osqueryd).
Expand Down
2 changes: 1 addition & 1 deletion orbit/pkg/packaging/windows_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ var windowsWixTemplate = template.Must(template.New("").Option("missingkey=error
Start="auto"
Type="ownProcess"
Description="This service runs Fleet's osquery runtime and autoupdater (Orbit)."
Arguments='--root-dir "[ORBITROOT]." --log-file "[System64Folder]config\systemprofile\AppData\Local\FleetDM\Orbit\Logs\orbit-osquery.log" --fleet-url "[FLEET_URL]"{{ if .FleetCertificate }} --fleet-certificate "[ORBITROOT]fleet.pem"{{ end }}{{ if .EnrollSecret }} --enroll-secret-path "[ORBITROOT]secret.txt"{{ end }}{{if .Insecure }} --insecure{{ end }}{{ if .Debug }} --debug{{ end }}{{ if .UpdateURL }} --update-url "{{ .UpdateURL }}"{{ end }}{{ if .UpdateTLSServerCertificate }} --update-tls-certificate "[ORBITROOT]update.pem"{{ end }}{{ if .DisableUpdates }} --disable-updates{{ end }} --fleet-desktop="[FLEET_DESKTOP]" --desktop-channel {{ .DesktopChannel }}{{ if .FleetDesktopAlternativeBrowserHost }} --fleet-desktop-alternative-browser-host {{ .FleetDesktopAlternativeBrowserHost }}{{ end }} --orbit-channel "{{ .OrbitChannel }}" --osqueryd-channel "{{ .OsquerydChannel }}" --enable-scripts="[ENABLE_SCRIPTS]" {{ if and (ne .HostIdentifier "") (ne .HostIdentifier "uuid") }}--host-identifier={{ .HostIdentifier }}{{ end }}{{ $endUserEmailArg }}{{ $euaTokenArg }}{{ if .OsqueryDB }} --osquery-db="{{ .OsqueryDB }}"{{ end }}{{ if .DisableSetupExperience }} --disable-setup-experience{{ end }}'
Arguments='--root-dir "[ORBITROOT]." --log-file "[System64Folder]config\systemprofile\AppData\Local\FleetDM\Orbit\Logs\orbit-osquery.log" --fleet-url "[FLEET_URL]"{{ if .FleetCertificate }} --fleet-certificate "[ORBITROOT]fleet.pem"{{ end }}{{ if .EnrollSecret }} --enroll-secret-path "[ORBITROOT]secret.txt"{{ end }}{{if .Insecure }} --insecure{{ end }}{{ if .Debug }} --debug{{ end }}{{ if .UpdateURL }} --update-url "{{ .UpdateURL }}"{{ end }}{{ if .UpdateTLSServerCertificate }} --update-tls-certificate "[ORBITROOT]update.pem"{{ end }}{{ if .DisableUpdates }} --disable-updates{{ end }} --fleet-desktop="[FLEET_DESKTOP]" --desktop-channel {{ .DesktopChannel }}{{ if .FleetDesktopAlternativeBrowserHost }} --fleet-desktop-alternative-browser-host {{ .FleetDesktopAlternativeBrowserHost }}{{ end }} --orbit-channel "{{ .OrbitChannel }}" --osqueryd-channel "{{ .OsquerydChannel }}" --enable-scripts="[ENABLE_SCRIPTS]" {{ if and (ne .HostIdentifier "") (ne .HostIdentifier "uuid") }}--host-identifier={{ .HostIdentifier }}{{ end }}{{ $endUserEmailArg }}{{ $euaTokenArg }}{{ if .OsqueryDB }} --osquery-db="{{ .OsqueryDB }}"{{ end }}{{ if .DisableSetupExperience }} --disable-setup-experience{{ end }}{{ if .BypassEndUserAuth }} --bypass-end-user-auth{{ end }}'
>
<util:ServiceConfig
FirstFailureActionType="restart"
Expand Down
Loading