Cert CLI stuff, module bump.

This commit is contained in:
Adam Ierymenko 2020-08-12 16:40:17 -07:00
parent 782f15f8c9
commit fe01352412
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
9 changed files with 162 additions and 19 deletions

View file

@ -28,8 +28,6 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
switch args[0] {
case "list":
case "newsid":
if len(args) > 2 {
Help()
@ -86,17 +84,18 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
Help()
return 1
}
var csr zerotier.Certificate
csrBytes, err := ioutil.ReadFile(args[1])
if err != nil {
fmt.Printf("ERROR: unable to read CSR from %s: %s\n", args[1], err.Error())
return 1
}
c, err := zerotier.NewCertificateFromBytes(csrBytes, false)
csr, err := zerotier.NewCertificateFromBytes(csrBytes, false)
if err != nil {
fmt.Printf("ERROR: CSR in %s is invalid: %s\n", args[1], err.Error())
return 1
}
id := readIdentity(args[2])
if id == nil {
fmt.Printf("ERROR: unable to read identity from %s\n", args[2])
@ -106,12 +105,13 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
fmt.Printf("ERROR: signing identity in %s lacks private key\n", args[2])
return 1
}
c, err = csr.Sign(id)
cert, err := csr.Sign(id)
if err != nil {
fmt.Printf("ERROR: error signing CSR or generating certificate: %s\n", err.Error())
return 1
}
cb, err := c.Marshal()
cb, err := cert.Marshal()
if err != nil {
fmt.Printf("ERROR: error marshaling signed certificate: %s\n", err.Error())
return 1
@ -124,7 +124,28 @@ func Cert(basePath string, authTokenGenerator func() string, args []string, json
return 1
}
case "verify":
case "verify", "dump":
if len(args) != 2 {
Help()
return 1
}
certBytes, err := ioutil.ReadFile(args[1])
if err != nil {
fmt.Printf("ERROR: unable to read certificate from %s: %s\n", args[1], err.Error())
return 1
}
cert, err := zerotier.NewCertificateFromBytes(certBytes, true)
if err != nil {
fmt.Printf("FAILED: certificate in %s invalid: %s\n", args[1], err.Error())
return 1
}
if args[0] == "dump" {
fmt.Println(cert.JSON())
} else {
fmt.Println("OK")
}
case "list":
case "show":
if len(args) != 1 {

View file

@ -37,7 +37,7 @@ Common Operations:
help Show this help
version Print version
status Show node status and configuration
· status Show node status and configuration
· set [option] [value] - Get or set node configuration
port <port> Primary P2P port
@ -95,11 +95,12 @@ Advanced Operations:
cert <command> [args] - Certificate management
· list List certificates in local node store
· show [serial] List or show details of a certificate
· show <serial> List or show details of a certificate
newsid <secret out> Create a new subject unique ID
newcsr <subject> <secret> <csr out> Create a subject CSR
sign <csr> <identity> <cert out> Sign a CSR to create a certificate
· verify <cert> Verify a certificate
verify <cert> Verify certificate (not entire chain)
dump <cert> Verify and print certificate
· import <cert> [trust,[trust]] Import certificate into this node
rootca Certificate is a root CA (trust flag)
ztrootset ZeroTier root node set (trust flag)

View file

@ -2411,7 +2411,7 @@ ZT_SDK_API int ZT_Node_tryPeer(
* @param cert Certificate object, or set to NULL if certData and certSize are to be used
* @param certData Certificate binary data if 'cert' is NULL, NULL otherwise
* @param certSize Size of certificate binary data, 0 if none
* @return
* @return Certificate error or ZT_CERTIFICATE_ERROR_NONE on success
*/
ZT_SDK_API enum ZT_CertificateError ZT_Node_addCertificate(
ZT_Node *node,
@ -2422,6 +2422,22 @@ ZT_SDK_API enum ZT_CertificateError ZT_Node_addCertificate(
const void *certData,
unsigned int certSize);
/**
* Delete a certificate from this node's certificate store
*
* Note that deleting CA certificates may also imply deletion of certificates
* that depend on them for full chain verification.
*
* @param node Node instance
* @param tptr Thread pointer to pass to functions/callbacks resulting from this call
* @param serialNo 48-byte / 384-bit serial number of certificate to delete
* @return OK (0) or error code
*/
ZT_SDK_API enum ZT_ResultCode ZT_Node_deleteCertificate(
ZT_Node *node,
void *tptr,
const void *serialNo);
/**
* List certificates installed in this node's trust store
*

2
go.mod
View file

@ -5,5 +5,5 @@ go 1.13
require (
github.com/Microsoft/go-winio v0.4.14
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d // indirect
)

4
go.sum
View file

@ -13,5 +13,5 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259 h1:so6Hr/LodwSZ5UQDu/7PmQiDeS112WwtLvU3lpSPZTU=
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d h1:QQrM/CCYEzTs91GZylDCQjGHudbPTxF/1fvXdVh5lMo=
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View file

@ -16,6 +16,7 @@ package zerotier
import (
"bytes"
secrand "crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
@ -217,10 +218,12 @@ func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool
if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) {
return true
}
ah = req.Header.Get("X-ZT1-Auth")
if len(ah) > 0 && strings.TrimSpace(ah) == token {
return true
}
_ = apiSendObj(out, req, http.StatusUnauthorized, &APIErr{"authorization token not found or incorrect (checked X-ZT1-Auth and Authorization headers)"})
return false
}
@ -252,6 +255,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
smux := http.NewServeMux()
// -----------------------------------------------------------------------------------------------------------------
smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) {
defer func() {
e := recover()
@ -298,6 +303,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
}
})
// -----------------------------------------------------------------------------------------------------------------
smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) {
defer func() {
e := recover()
@ -330,6 +337,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
}
})
// -----------------------------------------------------------------------------------------------------------------
smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) {
var err error
@ -397,6 +406,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
}
})
// -----------------------------------------------------------------------------------------------------------------
smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) {
defer func() {
e := recover()
@ -421,6 +432,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
}
if req.Method == http.MethodDelete {
if queriedID == 0 {
_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only specific networks can be deleted"})
return
@ -434,7 +446,9 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
}
}
_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"})
} else if req.Method == http.MethodPost || req.Method == http.MethodPut {
if queriedID == 0 {
_ = apiSendObj(out, req, http.StatusBadRequest, nil)
return
@ -460,7 +474,9 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
_ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n))
}
}
} else if req.Method == http.MethodGet || req.Method == http.MethodHead {
networks := node.Networks()
if queriedID == 0 { // no queried ID lists all networks
nws := make([]*APINetwork, 0, len(networks))
@ -477,12 +493,82 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
}
}
_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"})
} else {
out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
_ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method})
}
})
// -----------------------------------------------------------------------------------------------------------------
smux.HandleFunc("/cert/", func(out http.ResponseWriter, req *http.Request) {
defer func() {
e := recover()
if e != nil {
_ = apiSendObj(out, req, http.StatusInternalServerError, nil)
}
}()
if !apiCheckAuth(out, req, authToken) {
return
}
apiSetStandardHeaders(out)
var queriedSerialNo []byte
if len(req.URL.Path) > 6 {
b, err := base64.URLEncoding.DecodeString(req.URL.Path[6:])
if err != nil || len(b) != CertificateSerialNoSize {
_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid base64 serial number in certificate path"})
return
}
queriedSerialNo = b
}
if req.Method == http.MethodGet || req.Method == http.MethodHead {
certs, err := node.ListCertificates()
if err != nil {
_ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"unexpected error listing certificates"})
return
}
if len(queriedSerialNo) == CertificateSerialNoSize {
for _, c := range certs {
if bytes.Equal(c.Certificate.SerialNo, queriedSerialNo) {
_ = apiSendObj(out, req, http.StatusOK, c)
break
}
}
} else {
_ = apiSendObj(out, req, http.StatusOK, certs)
}
} else if req.Method == http.MethodPost || req.Method == http.MethodPut {
var lc LocalCertificate
if apiReadObj(out, req, &lc) == nil {
if lc.Certificate == nil {
_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"missing certificate"})
return
}
}
} else if req.Method == http.MethodDelete {
if len(queriedSerialNo) == CertificateSerialNoSize {
} else {
_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"certificate not found"})
}
} else {
out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
_ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method})
}
})
// -----------------------------------------------------------------------------------------------------------------
listener, err := createNamedSocketListener(basePath, APISocketName)
if err != nil {
return nil, nil, err

View file

@ -18,6 +18,7 @@ package zerotier
import "C"
import (
"encoding/base64"
"encoding/json"
"fmt"
"unsafe"
@ -96,6 +97,12 @@ type CertificateSubjectUniqueIDSecret struct {
UniqueIDSecret []byte `json:"uniqueIdSecret,omitempty"`
}
// LocalCertificate combines a certificate with its local trust flags.
type LocalCertificate struct {
Certificate *Certificate `json:"certificate,omitempty"`
LocalTrust uint `json:"localTrust"`
}
func certificateErrorToError(cerr int) error {
switch cerr {
case C.ZT_CERTIFICATE_ERROR_NONE:
@ -488,6 +495,11 @@ func (c *Certificate) JSON() string {
return string(j)
}
// URLSerialNo returns the serial number encoded for use in /cert/### local API URLs.
func (c *Certificate) URLSerialNo() string {
return base64.URLEncoding.EncodeToString(c.SerialNo)
}
// NewCertificateSubjectUniqueId creates a new certificate subject unique ID and corresponding private key.
// Right now only one type is supported: CertificateUniqueIdTypeNistP384
func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) {

View file

@ -519,7 +519,7 @@ func (n *Node) TryPeer(fpOrAddress interface{}, ep *Endpoint, retries int) bool
}
// ListCertificates lists certificates and their corresponding local trust flags.
func (n *Node) ListCertificates() (certs []*Certificate, localTrust []uint, err error) {
func (n *Node) ListCertificates() (certs []LocalCertificate, err error) {
cl := C.ZT_Node_listCertificates(n.zn)
if cl != nil {
defer C.ZT_freeQueryResult(unsafe.Pointer(cl))
@ -527,15 +527,22 @@ func (n *Node) ListCertificates() (certs []*Certificate, localTrust []uint, err
c := newCertificateFromCCertificate(unsafe.Pointer(uintptr(unsafe.Pointer(cl.certs)) + (i * pointerSize)))
if c != nil {
lt := *((*C.uint)(unsafe.Pointer(uintptr(unsafe.Pointer(cl.localTrust)) + (i * C.sizeof_int))))
certs = append(certs, c)
localTrust = append(localTrust, uint(lt))
certs = append(certs, LocalCertificate{Certificate: c, LocalTrust: uint(lt)})
}
}
}
return
}
// --------------------------------------------------------------------------------------------------------------------
// AddCertificate adds a certificate to this node's local certificate store (after verification).
func (n *Node) AddCertificate(cert *Certificate) error {
}
// DeleteCertificate deletes a certificate from this node's local certificate store.
func (n *Node) DeleteCertificate(serialNo []byte) error {
}
// -------------------------------------------------------------------------------------------------------------------
func (n *Node) runMaintenance() {
n.localConfigLock.RLock()

2
vendor/modules.txt vendored
View file

@ -4,6 +4,6 @@ github.com/Microsoft/go-winio/pkg/guid
# github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
github.com/hectane/go-acl
github.com/hectane/go-acl/api
# golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
# golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d
golang.org/x/sys/internal/unsafeheader
golang.org/x/sys/windows