mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-12 21:35:40 +08:00
Add anchor certificate to DEP profile template (#181)
* Add anchor certificate to DEP profile template * Add -use-server-cert flag to generate certificate from server TLS certificate * Update CHANGELOG * Support adding certificate chains (rather than just individual certs) to DEP anchor certs * Resolves #107
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# v1.0.1 TBD
|
||||
|
||||
* Option to include SSL certificates in DEP profile template (-anchor and -use-server-cert) #107
|
||||
* /push and /v1/commands API endpoints require API authentication #157
|
||||
* Add `mdmctl` binary for interacting with the server over API. #127
|
||||
* Save DEP cursor for use after restart. #109
|
||||
|
||||
@@ -2,20 +2,43 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/micromdm/dep"
|
||||
"github.com/micromdm/micromdm/crypto"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func certificatesFromURL(serverURL string, insecure bool) ([]*x509.Certificate, error) {
|
||||
urlParsed, err := url.Parse(serverURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr := urlParsed.Host
|
||||
if urlParsed.Port() == "" {
|
||||
addr += ":443"
|
||||
}
|
||||
conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: insecure})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
return conn.ConnectionState().PeerCertificates, nil
|
||||
}
|
||||
|
||||
func (cmd *applyCommand) applyDEPProfile(args []string) error {
|
||||
flagset := flag.NewFlagSet("dep-profiles", flag.ExitOnError)
|
||||
var (
|
||||
flProfilePath = flagset.String("f", "", "filename of DEP profile to apply")
|
||||
flTemplate = flagset.Bool("template", false, "print a JSON example of a DEP profile")
|
||||
flAnchorFile = flagset.String("anchor", "", "filename of PEM certificate to add to trusted anchors")
|
||||
flUseServer = flagset.Bool("use-server-cert", false, "use the certificate presented by the server")
|
||||
)
|
||||
flagset.Usage = usageFor(flagset, "mdmctl apply dep-profiles [flags]")
|
||||
if err := flagset.Parse(args); err != nil {
|
||||
@@ -23,8 +46,22 @@ func (cmd *applyCommand) applyDEPProfile(args []string) error {
|
||||
}
|
||||
|
||||
if *flTemplate {
|
||||
printDEPProfileTemplate()
|
||||
return nil
|
||||
var anchorCerts []*x509.Certificate
|
||||
if *flAnchorFile != "" {
|
||||
certs, err := crypto.ReadPEMCertificatesFile(*flAnchorFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
anchorCerts = append(anchorCerts, certs...)
|
||||
}
|
||||
if *flUseServer {
|
||||
certs, err := certificatesFromURL(cmd.config.ServerURL, cmd.config.SkipVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
anchorCerts = append(anchorCerts, certs...)
|
||||
}
|
||||
return printDEPProfileTemplate(anchorCerts)
|
||||
}
|
||||
|
||||
if *flProfilePath == "" {
|
||||
@@ -54,9 +91,24 @@ func (cmd *applyCommand) applyDEPProfile(args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func printDEPProfileTemplate() {
|
||||
func printDEPProfileTemplate(anchorCerts []*x509.Certificate) error {
|
||||
var anchorCertStr string = "[]"
|
||||
|
||||
resp := `
|
||||
// convert certificates into base64 encoded strings
|
||||
// json.Marshal does this for us for byte[] arrays
|
||||
if len(anchorCerts) > 0 {
|
||||
var certs [][]byte
|
||||
for _, cert := range anchorCerts {
|
||||
certs = append(certs, cert.Raw)
|
||||
}
|
||||
jsonBytes, err := json.Marshal(certs)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
anchorCertStr = string(jsonBytes)
|
||||
}
|
||||
|
||||
resp := fmt.Sprintf(`
|
||||
{
|
||||
"profile_name": "(Required) Human readable name",
|
||||
"url": "https://mymdm.example.org/mdm/enroll",
|
||||
@@ -69,12 +121,13 @@ func printDEPProfileTemplate() {
|
||||
"support_phone_number": "(Optional) +1 408 555 1010",
|
||||
"support_email_address": "(Optional) support@example.com",
|
||||
"org_magic": "(Optional)",
|
||||
"anchor_certs": [],
|
||||
"anchor_certs": %s,
|
||||
"supervising_host_certs": [],
|
||||
"skip_setup_items": ["AppleID", "Android"],
|
||||
"department": "(Optional) support@example.com",
|
||||
"devices": ["SERIAL1","SERIAL2"]
|
||||
}
|
||||
`
|
||||
`, anchorCertStr)
|
||||
fmt.Println(resp)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
@@ -51,6 +53,38 @@ func SimpleSelfSignedRSAKeypair(cn string, days int) (key *rsa.PrivateKey, cert
|
||||
return key, cert, err
|
||||
}
|
||||
|
||||
func ReadPEMCertificateFile(path string) (*x509.Certificate, error) {
|
||||
certs, err := ReadPEMCertificatesFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(certs) != 1 {
|
||||
return nil, errors.New("incorrect number of certificates")
|
||||
}
|
||||
return certs[0], nil
|
||||
}
|
||||
|
||||
func ReadPEMCertificatesFile(path string) ([]*x509.Certificate, error) {
|
||||
pemData, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var asn1data []byte
|
||||
rest := pemData
|
||||
for {
|
||||
var block *pem.Block
|
||||
block, rest = pem.Decode(rest)
|
||||
if block == nil || block.Type != "CERTIFICATE" {
|
||||
return nil, errors.New("failed to decode PEM block containing certificate")
|
||||
}
|
||||
asn1data = append(asn1data, block.Bytes...)
|
||||
if len(rest) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return x509.ParseCertificates(asn1data)
|
||||
}
|
||||
|
||||
func WritePEMCertificateFile(cert *x509.Certificate, path string) error {
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user