add logic to configure FileVault + escrow (#10160)

Related to #9495, this adds the underlying methods to send a
configuration profile that enables FileVault and FileVault Escrow, so we
can fetch and decrypt the encryption key later on.

These methods still need to be called somewhere, and they might need to
be moved outside of `Service`, but at least this gives us a start.
This commit is contained in:
Roberto Dip 2023-03-01 10:43:15 -03:00 committed by GitHub
parent f3ed6f3037
commit 164bb4bf5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 332 additions and 2 deletions

View File

@ -1,7 +1,9 @@
package service package service
import ( import (
"bytes"
"context" "context"
"encoding/base64"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/fleet"
@ -111,3 +113,34 @@ func (svc *Service) MDMAppleEraseDevice(ctx context.Context, hostID uint) error
} }
return nil return nil
} }
func (svc *Service) MDMAppleEnableFileVaultAndEscrow(ctx context.Context, teamID uint) error {
cert, _, _, err := svc.config.MDM.AppleSCEP()
if err != nil {
return ctxerr.Wrap(ctx, err, "enabling FileVault")
}
var contents bytes.Buffer
params := fileVaultProfileOptions{
PayloadIdentifier: apple_mdm.FleetFileVaultPayloadIdentifier,
Base64DerCertificate: base64.StdEncoding.EncodeToString(cert.Leaf.Raw),
}
if err := fileVaultProfileTemplate.Execute(&contents, params); err != nil {
return ctxerr.Wrap(ctx, err, "enabling FileVault")
}
mc := fleet.Mobileconfig(contents.Bytes())
cp, err := mc.ParseConfigProfile()
if err != nil {
return ctxerr.Wrap(ctx, err, "enabling FileVault")
}
cp.TeamID = &teamID
_, err = svc.ds.NewMDMAppleConfigProfile(ctx, *cp)
return ctxerr.Wrap(ctx, err, "enabling FileVault")
}
func (svc *Service) MDMAppleDisableFileVaultAndEscrow(ctx context.Context, teamID uint) error {
err := svc.ds.DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx, teamID, apple_mdm.FleetFileVaultPayloadIdentifier)
return ctxerr.Wrap(ctx, err, "disabling FileVault")
}

View File

@ -0,0 +1,78 @@
package service
import "text/template"
type fileVaultProfileOptions struct {
PayloadIdentifier string
Base64DerCertificate string
}
var fileVaultProfileTemplate = template.Must(template.New("").Option("missingkey=error").Parse(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>Defer</key>
<true/>
<key>Enable</key>
<string>On</string>
<key>PayloadDisplayName</key>
<string>FileVault 2</string>
<key>PayloadIdentifier</key>
<string>com.apple.MCX.FileVault2.3548D750-6357-4910-8DEA-D80ADCE2C787</string>
<key>PayloadType</key>
<string>com.apple.MCX.FileVault2</string>
<key>PayloadUUID</key>
<string>3548D750-6357-4910-8DEA-D80ADCE2C787</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>ShowRecoveryKey</key>
<false/>
</dict>
<dict>
<key>EncryptCertPayloadUUID</key>
<string>A326B71F-EB80-41A5-A8CD-A6F932544281</string>
<key>Location</key>
<string>Fleet</string>
<key>PayloadDisplayName</key>
<string>FileVault Recovery Key Escrow</string>
<key>PayloadIdentifier</key>
<string>com.apple.security.FDERecoveryKeyEscrow.3690D771-DCB8-4D5D-97D6-209A138DF03E</string>
<key>PayloadType</key>
<string>com.apple.security.FDERecoveryKeyEscrow</string>
<key>PayloadUUID</key>
<string>3C329F2B-3D47-4141-A2B5-5C52A2FD74F8</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
<dict>
<key>PayloadCertificateFileName</key>
<string>Fleet certificate</string>
<key>PayloadContent</key>
<data>{{ .Base64DerCertificate }}</data>
<key>PayloadDisplayName</key>
<string>Certificate Root</string>
<key>PayloadIdentifier</key>
<string>com.apple.security.root.A326B71F-EB80-41A5-A8CD-A6F932544281</string>
<key>PayloadType</key>
<string>com.apple.security.pkcs1</string>
<key>PayloadUUID</key>
<string>A326B71F-EB80-41A5-A8CD-A6F932544281</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>Disk encryption</string>
<key>PayloadIdentifier</key>
<string>{{ .PayloadIdentifier }}</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>74FEAC88-B614-468E-A4B4-B4B0C93B5D52</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>`))

View File

@ -0,0 +1,139 @@
package service
import (
"context"
"errors"
"strings"
"testing"
"github.com/fleetdm/fleet/v4/server/config"
"github.com/fleetdm/fleet/v4/server/fleet"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/stretchr/testify/require"
)
func setup() (*mock.Store, *Service) {
ds := new(mock.Store)
svc := &Service{
ds: ds,
config: config.FleetConfig{
MDM: config.MDMConfig{
AppleSCEPCertBytes: testCert,
AppleSCEPKeyBytes: testKey,
},
},
}
return ds, svc
}
func TestMDMAppleEnableFileVaultAndEscrow(t *testing.T) {
ctx := context.Background()
t.Run("fails if SCEP is not configured", func(t *testing.T) {
ds := new(mock.Store)
svc := &Service{ds: ds, config: config.FleetConfig{}}
err := svc.MDMAppleEnableFileVaultAndEscrow(ctx, 0)
require.Error(t, err)
})
t.Run("fails if the profile can't be saved in the db", func(t *testing.T) {
ds, svc := setup()
testErr := errors.New("test")
ds.NewMDMAppleConfigProfileFunc = func(ctx context.Context, p fleet.MDMAppleConfigProfile) (*fleet.MDMAppleConfigProfile, error) {
return nil, testErr
}
err := svc.MDMAppleEnableFileVaultAndEscrow(ctx, 0)
require.ErrorIs(t, err, testErr)
require.True(t, ds.NewMDMAppleConfigProfileFuncInvoked)
})
t.Run("happy path", func(t *testing.T) {
var teamID uint = 4
ds, svc := setup()
ds.NewMDMAppleConfigProfileFunc = func(ctx context.Context, p fleet.MDMAppleConfigProfile) (*fleet.MDMAppleConfigProfile, error) {
require.Equal(t, &teamID, p.TeamID)
require.Equal(t, p.Identifier, apple_mdm.FleetFileVaultPayloadIdentifier)
require.Equal(t, p.Name, "Disk encryption")
require.Contains(t, string(p.Mobileconfig), `MIID6DCCAdACFGX99Sw4aF2qKGLucoIWQRAXHrs1MA0GCSqGSIb3DQEBCwUAMDUxEzARBgNVBAoMClJlZGlzIFRlc3QxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMTEwMTkxNzM0MzlaFw0yMjEwMTkxNzM0MzlaMCwxEzARBgNVBAoMClJlZGlzIFRlc3QxFTATBgNVBAMMDEdlbmVyaWMtY2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKSHcH8EjSvp3Nm4IHAFxG9DZm8+0h1BwU0OX0VHcJ+Cf+f6h0XYMcMo9LFEpnUJRRMjKrM4mkI75NIIufNBN+GrtqqTPTid8wfOGu/Ufa5EEU1hb2j7AiMlpM6i0+ZysXSNo+Vc/cNZT0PXfyOtJnYm6p9WZM84ID1t2ea0bLwC12cTKv5oybVGtJHh76TRxAR3FeQ9+SY30vUAxYm6oWyYho8rRdKtUSe11pXj6OhxxfTZnsSWn4lo0uBpXai63XtieTVpz74htSNC1bunIGv7//m5F60sH5MrF5JSkPxfCfgqski84ICDSRNlvpT+eMPiygAAJ8zY8wYUXRYFYTUCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAAAw+6Uz2bAcXgQ7fQfdOm+T6FLRBcr8PD4ajOvSu/T+HhVVjE26Qt2IBwFEYve2FvDxrBCF8aQYZcyQqnP8bdKebnWAaqL8BbTwLWW+fDuZLO2b4QHjAEdEKKdZC5/FRpQrkerf5CCPTHE+5M17OZg41wdVYnCEwJOkP5pUAVsmwtrSwVeIquy20TZO0qbscDQETf7NIJgW0IXg82wBe53Rv4/wL3Ybq13XVRGYiJrwpaNTfUNgsDWqgwlQ5L2GOLDgg8S2NoF9mWVgCGSp3a2eHW+EmBRQ1OP6EYQtIhKdGLrSndAOMJ2ER1pgHWUFKkWQaZ9i37Dx2j7P5c4/XNeVozcRQcLwKwN+n8k+bwIYcTX0HMOVFYm+WiFi/gjI860Tx853Sc0nkpOXmBCeHSXigGUscgjBYbmJz4iExXuwgawLXKLDKs0yyhLDnKEjmx/Vhz03JpsVFJ84kSWkTZkYsXiG306TxuJCX9zAt1z+6ClieTTGiFY+D8DfkC4H82rlPEtImpZ6rInsMUlAykImpd58e4PMSa+w/wSHXDvwFP7py1Gvz3XvcbGLmpBXblxTUpToqC7zSQJhHOMBBt6XnhcRwd6G9Vj/mQM3FvJIrxtKk8O7FwMJloGivS85OEzCIur5A+bObXbM2pcI8y4ueHE4NtElRBwn859AdB2k=`)
return nil, nil
}
err := svc.MDMAppleEnableFileVaultAndEscrow(ctx, teamID)
require.NoError(t, err)
require.True(t, ds.NewMDMAppleConfigProfileFuncInvoked)
})
}
func TestMDMAppleDisableFileVaultAndEscrow(t *testing.T) {
var wantTeamID uint
ds, svc := setup()
ds.DeleteMDMAppleConfigProfileByTeamAndIdentifierFunc = func(ctx context.Context, teamID uint, profileIdentifier string) error {
require.Equal(t, wantTeamID, teamID)
require.Equal(t, apple_mdm.FleetFileVaultPayloadIdentifier, profileIdentifier)
return nil
}
err := svc.MDMAppleDisableFileVaultAndEscrow(context.Background(), wantTeamID)
require.NoError(t, err)
require.True(t, ds.DeleteMDMAppleConfigProfileByTeamAndIdentifierFuncInvoked)
}
var (
testCert = `-----BEGIN CERTIFICATE-----
MIID6DCCAdACFGX99Sw4aF2qKGLucoIWQRAXHrs1MA0GCSqGSIb3DQEBCwUAMDUx
EzARBgNVBAoMClJlZGlzIFRlc3QxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhv
cml0eTAeFw0yMTEwMTkxNzM0MzlaFw0yMjEwMTkxNzM0MzlaMCwxEzARBgNVBAoM
ClJlZGlzIFRlc3QxFTATBgNVBAMMDEdlbmVyaWMtY2VydDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKSHcH8EjSvp3Nm4IHAFxG9DZm8+0h1BwU0OX0VH
cJ+Cf+f6h0XYMcMo9LFEpnUJRRMjKrM4mkI75NIIufNBN+GrtqqTPTid8wfOGu/U
fa5EEU1hb2j7AiMlpM6i0+ZysXSNo+Vc/cNZT0PXfyOtJnYm6p9WZM84ID1t2ea0
bLwC12cTKv5oybVGtJHh76TRxAR3FeQ9+SY30vUAxYm6oWyYho8rRdKtUSe11pXj
6OhxxfTZnsSWn4lo0uBpXai63XtieTVpz74htSNC1bunIGv7//m5F60sH5MrF5JS
kPxfCfgqski84ICDSRNlvpT+eMPiygAAJ8zY8wYUXRYFYTUCAwEAATANBgkqhkiG
9w0BAQsFAAOCAgEAAAw+6Uz2bAcXgQ7fQfdOm+T6FLRBcr8PD4ajOvSu/T+HhVVj
E26Qt2IBwFEYve2FvDxrBCF8aQYZcyQqnP8bdKebnWAaqL8BbTwLWW+fDuZLO2b4
QHjAEdEKKdZC5/FRpQrkerf5CCPTHE+5M17OZg41wdVYnCEwJOkP5pUAVsmwtrSw
VeIquy20TZO0qbscDQETf7NIJgW0IXg82wBe53Rv4/wL3Ybq13XVRGYiJrwpaNTf
UNgsDWqgwlQ5L2GOLDgg8S2NoF9mWVgCGSp3a2eHW+EmBRQ1OP6EYQtIhKdGLrSn
dAOMJ2ER1pgHWUFKkWQaZ9i37Dx2j7P5c4/XNeVozcRQcLwKwN+n8k+bwIYcTX0H
MOVFYm+WiFi/gjI860Tx853Sc0nkpOXmBCeHSXigGUscgjBYbmJz4iExXuwgawLX
KLDKs0yyhLDnKEjmx/Vhz03JpsVFJ84kSWkTZkYsXiG306TxuJCX9zAt1z+6Clie
TTGiFY+D8DfkC4H82rlPEtImpZ6rInsMUlAykImpd58e4PMSa+w/wSHXDvwFP7py
1Gvz3XvcbGLmpBXblxTUpToqC7zSQJhHOMBBt6XnhcRwd6G9Vj/mQM3FvJIrxtKk
8O7FwMJloGivS85OEzCIur5A+bObXbM2pcI8y4ueHE4NtElRBwn859AdB2k=
-----END CERTIFICATE-----`
testKey = testingKey(`-----BEGIN RSA TESTING KEY-----
MIIEogIBAAKCAQEApIdwfwSNK+nc2bggcAXEb0Nmbz7SHUHBTQ5fRUdwn4J/5/qH
Rdgxwyj0sUSmdQlFEyMqsziaQjvk0gi580E34au2qpM9OJ3zB84a79R9rkQRTWFv
aPsCIyWkzqLT5nKxdI2j5Vz9w1lPQ9d/I60mdibqn1ZkzzggPW3Z5rRsvALXZxMq
/mjJtUa0keHvpNHEBHcV5D35JjfS9QDFibqhbJiGjytF0q1RJ7XWlePo6HHF9Nme
xJafiWjS4GldqLrde2J5NWnPviG1I0LVu6cga/v/+bkXrSwfkysXklKQ/F8J+Cqy
SLzggINJE2W+lP54w+LKAAAnzNjzBhRdFgVhNQIDAQABAoIBAAtUbFHC3XnVq+iu
PkWYkBNdX9NvTwbGvWnyAGuD5OSHFwnBfck4fwzCaD9Ay/mpPsF3nXwj/LNs7m/s
O+ndZty6d2S9qOyaK98wuTgkuNbkRxC+Ee73wgjrkbLNEax/32p4Sn4D7lGid8vj
LhUl2k0ult+MEnsWkVnJk8TITeiQaT2AHhMr3HKdaI86hJJfam3wEBiLBglnnKqA
TInMqHoudnFOn/C8iVCFuHCE0oo1dMalbc4rlZuRBqezVhbSMWPLypMVXQb7eixM
ScJ3m8+DooGDSIe+EW/afhN2VnFbrhQC9/DlxGfwTwsUseWv7pgp53ufyyAzzydn
2plW/4ECgYEA1Va5RzSUDxr75JX003YZiBcYrG268vosiNYWRhE7frvn5EorZBRW
t4R70Y2gcXA10aPHzpbq40t6voWtpkfynU3fyRzbBmwfiWLEgckrYMwtcNz8nhG2
ETAg4LXO9CufbwuDa66h76TpkBzQVNc5TSbBUr/apLDWjKPMz6qW7VUCgYEAxW4K
Yqp3NgJkC5DhuD098jir9AH96hGhUryOi2CasCvmbjWCgWdolD7SRZJfxOXFOtHv
7Dkp9glA1Cg/nSmEHKslaTJfBIWK+5rqVD6k6kZE/+4QQWQtUxXXVgGINnGrnPvo
6MlRJxqGUtYJ0GRTFJP4Py0gwuzf5BMIwe+fpGECgYAOhLRfMCjTTlbOG5ZpvaPH
Kys2sNEEMBpPxaIGaq3N1iPV2WZSjT/JhW6XuDevAJ/pAGhcmtCpXz2fMaG7qzHL
mr0cBqaxLTKIOvx8iKA3Gi4NfDyE1Ve6m7fhEv5eh4l2GSZ8cYn7sRFkCVH0NCFm
KrkFVKEgjBhNwefySf2zcQKBgHDVPgw7nlv4q9LMX6RbI98eMnAG/2XZ45gUeWcA
tAeBX3WXEVoBjoxDBwuJ5z/xjXHbb8JSvT+G9E0MH6cjhgSYb44aoqFD7TV0yP2S
u8/Ej0SxewrURO8aKXJW99Edz9WtRuRbwgyWJTSMbRlzbOPy2UrJ8NJWbHK9yiCE
YXmhAoGAA3QUiCCl11c1C4VsF68Fa2i7qwnty3fvFidZpW3ds0tzZdIvkpRLp5+u
XAJ5+zStdEGdnu0iXALQlY7ektawXguT/zYKg3nfS9RMGW6CxZotn4bqfQwDuttf
b1xn1jGQd/o0xFf9ojpDNy6vNojidQGHh6E3h0GYvxbnQmVNq5U=
-----END RSA TESTING KEY-----`)
)
// prevent static analysis tools from raising issues due to detection of
// private key in code.
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }

View File

@ -140,6 +140,20 @@ func (ds *Datastore) DeleteMDMAppleConfigProfile(ctx context.Context, profileID
return nil return nil
} }
func (ds *Datastore) DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx context.Context, teamID uint, profileIdentifier string) error {
res, err := ds.writer.ExecContext(ctx, `DELETE FROM mdm_apple_configuration_profiles WHERE team_id = ? AND identifier = ?`, teamID, profileIdentifier)
if err != nil {
return ctxerr.Wrap(ctx, err)
}
if deleted, _ := res.RowsAffected(); deleted == 0 {
message := fmt.Sprintf("identifier: %s, team_id: %d", profileIdentifier, teamID)
return ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithMessage(message))
}
return nil
}
func (ds *Datastore) GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) { func (ds *Datastore) GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) {
stmt := fmt.Sprintf(` stmt := fmt.Sprintf(`
SELECT SELECT

View File

@ -26,6 +26,7 @@ func TestMDMAppleConfigProfile(t *testing.T) {
{"TestNewMDMAppleConfigProfileDuplicateName", testNewMDMAppleConfigProfileDuplicateName}, {"TestNewMDMAppleConfigProfileDuplicateName", testNewMDMAppleConfigProfileDuplicateName},
{"TestNewMDMAppleConfigProfileDuplicateIdentifier", testNewMDMAppleConfigProfileDuplicateIdentifier}, {"TestNewMDMAppleConfigProfileDuplicateIdentifier", testNewMDMAppleConfigProfileDuplicateIdentifier},
{"TestDeleteMDMAppleConfigProfile", testDeleteMDMAppleConfigProfile}, {"TestDeleteMDMAppleConfigProfile", testDeleteMDMAppleConfigProfile},
{"TestDeleteMDMAppleConfigProfileByTeamAndIdentifier", testDeleteMDMAppleConfigProfileByTeamAndIdentifier},
{"TestListMDMAppleConfigProfiles", testListMDMAppleConfigProfiles}, {"TestListMDMAppleConfigProfiles", testListMDMAppleConfigProfiles},
{"TestHostDetailsMDMProfiles", testHostDetailsMDMProfiles}, {"TestHostDetailsMDMProfiles", testHostDetailsMDMProfiles},
{"TestBatchSetMDMAppleProfiles", testBatchSetMDMAppleProfiles}, {"TestBatchSetMDMAppleProfiles", testBatchSetMDMAppleProfiles},
@ -166,6 +167,20 @@ func testDeleteMDMAppleConfigProfile(t *testing.T, ds *Datastore) {
require.ErrorIs(t, err, sql.ErrNoRows) require.ErrorIs(t, err, sql.ErrNoRows)
} }
func testDeleteMDMAppleConfigProfileByTeamAndIdentifier(t *testing.T, ds *Datastore) {
ctx := context.Background()
initialCP := storeDummyConfigProfileForTest(t, ds)
err := ds.DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx, *initialCP.TeamID, initialCP.Identifier)
require.NoError(t, err)
_, err = ds.GetMDMAppleConfigProfile(ctx, initialCP.ProfileID)
require.ErrorIs(t, err, sql.ErrNoRows)
err = ds.DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx, *initialCP.TeamID, initialCP.Identifier)
require.ErrorIs(t, err, sql.ErrNoRows)
}
func storeDummyConfigProfileForTest(t *testing.T, ds *Datastore) *fleet.MDMAppleConfigProfile { func storeDummyConfigProfileForTest(t *testing.T, ds *Datastore) *fleet.MDMAppleConfigProfile {
dummyMC := fleet.Mobileconfig([]byte("DummyTestMobileconfigBytes")) dummyMC := fleet.Mobileconfig([]byte("DummyTestMobileconfigBytes"))
dummyCP := fleet.MDMAppleConfigProfile{ dummyCP := fleet.MDMAppleConfigProfile{

View File

@ -717,10 +717,14 @@ type Datastore interface {
// For global config profiles, specify zero as the team id. // For global config profiles, specify zero as the team id.
ListMDMAppleConfigProfiles(ctx context.Context, teamID *uint) ([]*MDMAppleConfigProfile, error) ListMDMAppleConfigProfiles(ctx context.Context, teamID *uint) ([]*MDMAppleConfigProfile, error)
// DeleteMDMAppleConfigProfile deleted the mdm config profile corresponding to the specified // DeleteMDMAppleConfigProfile deletes the mdm config profile corresponding
// profile id. // to the specified profile id.
DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error
// DeleteMDMAppleConfigProfileByTeamAndIdentifier deletes a configuration
// profile using the unique key defined by `team_id` and `identifier`
DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx context.Context, teamID uint, profileIdentifier string) error
// GetHostMDMProfiles returns the MDM profile information for the specified host UUID. // GetHostMDMProfiles returns the MDM profile information for the specified host UUID.
GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]HostMDMAppleProfile, error) GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]HostMDMAppleProfile, error)

View File

@ -624,6 +624,15 @@ type Service interface {
// MMDAppleEraseDevice erases a host // MMDAppleEraseDevice erases a host
MDMAppleEraseDevice(ctx context.Context, hostID uint) error MDMAppleEraseDevice(ctx context.Context, hostID uint) error
// MDMAppleEnableFileVaultAndEscrow adds a configuration profile for the
// given team that enables FileVault with a config that allows Fleet to
// escrow the recovery key.
MDMAppleEnableFileVaultAndEscrow(ctx context.Context, teamID uint) error
// MDMAppleDisableFileVaultAndEscrow removes the FileVault configuration
// profile for the given team.
MDMAppleDisableFileVaultAndEscrow(ctx context.Context, teamID uint) error
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// CronSchedulesService // CronSchedulesService

View File

@ -39,6 +39,10 @@ const (
// used by Fleet MDM on the enrollment profile. // used by Fleet MDM on the enrollment profile.
FleetPayloadIdentifier = "com.fleetdm.fleet.mdm.apple" FleetPayloadIdentifier = "com.fleetdm.fleet.mdm.apple"
// FleetFileVaultPayloadIdentifier is the value for the PayloadIdentifier
// used by Fleet to configure FileVault and FileVault Escrow.
FleetFileVaultPayloadIdentifier = "com.fleetdm.fleet.mdm.filevault"
// FleetdConfigPayloadIdentifier is the value for the PayloadIdentifier used // FleetdConfigPayloadIdentifier is the value for the PayloadIdentifier used
// by fleetd to read configuration values from the system. // by fleetd to read configuration values from the system.
FleetdConfigPayloadIdentifier = "com.fleetdm.fleetd.config" FleetdConfigPayloadIdentifier = "com.fleetdm.fleetd.config"

View File

@ -515,6 +515,8 @@ type ListMDMAppleConfigProfilesFunc func(ctx context.Context, teamID *uint) ([]*
type DeleteMDMAppleConfigProfileFunc func(ctx context.Context, profileID uint) error type DeleteMDMAppleConfigProfileFunc func(ctx context.Context, profileID uint) error
type DeleteMDMAppleConfigProfileByTeamAndIdentifierFunc func(ctx context.Context, teamID uint, profileIdentifier string) error
type GetHostMDMProfilesFunc func(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) type GetHostMDMProfilesFunc func(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error)
type NewMDMAppleEnrollmentProfileFunc func(ctx context.Context, enrollmentPayload fleet.MDMAppleEnrollmentProfilePayload) (*fleet.MDMAppleEnrollmentProfile, error) type NewMDMAppleEnrollmentProfileFunc func(ctx context.Context, enrollmentPayload fleet.MDMAppleEnrollmentProfilePayload) (*fleet.MDMAppleEnrollmentProfile, error)
@ -1314,6 +1316,9 @@ type DataStore struct {
DeleteMDMAppleConfigProfileFunc DeleteMDMAppleConfigProfileFunc DeleteMDMAppleConfigProfileFunc DeleteMDMAppleConfigProfileFunc
DeleteMDMAppleConfigProfileFuncInvoked bool DeleteMDMAppleConfigProfileFuncInvoked bool
DeleteMDMAppleConfigProfileByTeamAndIdentifierFunc DeleteMDMAppleConfigProfileByTeamAndIdentifierFunc
DeleteMDMAppleConfigProfileByTeamAndIdentifierFuncInvoked bool
GetHostMDMProfilesFunc GetHostMDMProfilesFunc GetHostMDMProfilesFunc GetHostMDMProfilesFunc
GetHostMDMProfilesFuncInvoked bool GetHostMDMProfilesFuncInvoked bool
@ -3139,6 +3144,13 @@ func (s *DataStore) DeleteMDMAppleConfigProfile(ctx context.Context, profileID u
return s.DeleteMDMAppleConfigProfileFunc(ctx, profileID) return s.DeleteMDMAppleConfigProfileFunc(ctx, profileID)
} }
func (s *DataStore) DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx context.Context, teamID uint, profileIdentifier string) error {
s.mu.Lock()
s.DeleteMDMAppleConfigProfileByTeamAndIdentifierFuncInvoked = true
s.mu.Unlock()
return s.DeleteMDMAppleConfigProfileByTeamAndIdentifierFunc(ctx, teamID, profileIdentifier)
}
func (s *DataStore) GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) { func (s *DataStore) GetHostMDMProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) {
s.mu.Lock() s.mu.Lock()
s.GetHostMDMProfilesFuncInvoked = true s.GetHostMDMProfilesFuncInvoked = true

View File

@ -1518,6 +1518,18 @@ func (svc *Service) BatchSetMDMAppleProfiles(ctx context.Context, tmID *uint, tm
return nil return nil
} }
////////////////////////////////////////////////////////////////////////////////
// FileVault-related free version implementation
////////////////////////////////////////////////////////////////////////////////
func (svc *Service) MDMAppleEnableFileVaultAndEscrow(ctx context.Context, teamID uint) error {
return fleet.ErrMissingLicense
}
func (svc *Service) MDMAppleDisableFileVaultAndEscrow(ctx context.Context, teamID uint) error {
return fleet.ErrMissingLicense
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Implementation of nanomdm's CheckinAndCommandService interface // Implementation of nanomdm's CheckinAndCommandService interface
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -1421,6 +1421,16 @@ func TestMDMAppleReconcileProfiles(t *testing.T) {
require.True(t, ds.BulkUpsertMDMAppleHostProfilesFuncInvoked) require.True(t, ds.BulkUpsertMDMAppleHostProfilesFuncInvoked)
} }
func TestAppleMDMFileVaultEscrowFunctions(t *testing.T) {
svc := Service{}
err := svc.MDMAppleEnableFileVaultAndEscrow(context.Background(), uint(1))
require.ErrorIs(t, fleet.ErrMissingLicense, err)
err = svc.MDMAppleDisableFileVaultAndEscrow(context.Background(), uint(1))
require.ErrorIs(t, fleet.ErrMissingLicense, err)
}
func mobileconfigForTest(name, identifier string) []byte { func mobileconfigForTest(name, identifier string) []byte {
return []byte(fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?> return []byte(fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">