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:
Jesse Peterson
2017-05-29 19:43:43 -07:00
committed by GitHub
parent c4855b20f1
commit 8a67b37557
3 changed files with 94 additions and 6 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 {