Add a tool to generate manifests for Apple MDM (#10959)

Related to #9459 this will allow us to host a `fleetd` metadata
alongside the installer that can be used by Apple's MDM
`InstallEnterpriseApplication`
This commit is contained in:
Roberto Dip 2023-04-05 10:35:38 -03:00 committed by GitHub
parent ee135fe06b
commit ab583d66e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 170 additions and 0 deletions

View File

@ -0,0 +1,39 @@
// package appmanifest provides utilities for managing app manifest files
// used by MDM InstallApplication commands.
//
// It's heavily based on the micromdm/mdm/appmanifest package but it uses
// SHA256 as the hashing algorithm instead of MD5.
package appmanifest
import (
"crypto/sha256"
"fmt"
"io"
"github.com/micromdm/micromdm/mdm/appmanifest"
)
// Create builds an AppManifest using SHA256 checksums and the provided URL
func Create(file io.Reader, url string) (*appmanifest.Manifest, error) {
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return nil, err
}
sum := fmt.Sprintf("%x", hash.Sum(nil))
ast := appmanifest.Asset{
Kind: "software-package",
SHA256Size: int64(hash.Size()),
SHA256s: []string{sum},
URL: url,
}
return &appmanifest.Manifest{
ManifestItems: []appmanifest.Item{
{
Assets: []appmanifest.Asset{ast},
},
},
}, nil
}

View File

@ -0,0 +1,52 @@
package appmanifest
import (
"errors"
"io"
"strings"
"testing"
"github.com/micromdm/micromdm/mdm/appmanifest"
"github.com/stretchr/testify/require"
)
var errTest = errors.New("test error")
type alwaysFailReader struct{}
func (alwaysFailReader) Read(p []byte) (n int, err error) {
return 0, errTest
}
func TestCreate(t *testing.T) {
url := "https://test.example.com"
cases := []struct {
in io.Reader
out *appmanifest.Manifest
err error
}{
{
in: strings.NewReader("foo"),
out: &appmanifest.Manifest{
ManifestItems: []appmanifest.Item{{Assets: []appmanifest.Asset{{
Kind: "software-package",
SHA256Size: 32,
SHA256s: []string{"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"},
URL: "https://test.example.com",
}}}}},
err: nil,
},
{
in: alwaysFailReader{},
out: nil,
err: errTest,
},
}
for _, c := range cases {
m, err := Create(c.in, url)
require.Equal(t, c.out, m)
require.Equal(t, c.err, err)
}
}

View File

@ -0,0 +1,36 @@
## appmanifest
`appmanifest` is a tool that outputs to stdout a valid XML manifest that can be used by the MDM `InstallEnterpriseApplication` command to install a package.
```
$ go run tools/mdm/apple/appmanifest/main.go --help
Usage of appmanifest:
-pkg-file string
Path to a .pkg file
-pkg-url string
URL where the package will be served
```
### Example workflow
1. Create a fleetd installer
```
fleetctl package --type=pkg --fleet-desktop
```
2. Sign the installer so it can be installed via MDM
```
productsign --sign "Developer ID Installer: $DEVID_INFO" fleet-osquery.pkg fleetd-base.pkg
```
3. Run `appmanifest`
```
$ go run tools/mdm/apple/appmanifest/main.go \
-pkg-file fleetd-base.pkg \
-pkg-url $YOUR_URL > fleetd-base-manifest.plist
```
4. Upload `fleetd-base.pkg` to `$YOUR_URL` and `fleetd-base-manifest.plist` to a publicly accessible location.

View File

@ -0,0 +1,43 @@
// Command appmanifest takes a .pkg file and outputs an XML manifest for it
package main
import (
"bytes"
"flag"
"fmt"
"log"
"os"
"github.com/fleetdm/fleet/v4/server/mdm/apple/appmanifest"
"github.com/groob/plist"
)
func main() {
pkgFile := flag.String("pkg-file", "", "Path to a .pkg file")
pkgURL := flag.String("pkg-url", "", "URL where the package will be served")
flag.Parse()
if *pkgFile == "" || *pkgURL == "" {
log.Fatal("both --pkg-file and --pkg-url must be provided")
}
fp, err := os.Open(*pkgFile)
if err != nil {
log.Fatal(err)
}
defer fp.Close()
m, err := appmanifest.Create(fp, *pkgURL)
if err != nil {
log.Fatal(err)
}
var buf bytes.Buffer
enc := plist.NewEncoder(&buf)
enc.Indent(" ")
if err := enc.Encode(m); err != nil {
log.Fatal(err)
}
fmt.Println(buf.String())
}