Compare commits
13 Commits
fix/EE-499
...
feat-fdo-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
540afcd179 | ||
|
|
64b9207497 | ||
|
|
b3dbfd1a5e | ||
|
|
0f08005982 | ||
|
|
587ea7e8ea | ||
|
|
fe82b23211 | ||
|
|
6099a916b9 | ||
|
|
3d9aae9760 | ||
|
|
1316168b6b | ||
|
|
88042f9b39 | ||
|
|
4515315f41 | ||
|
|
00bd7c3d02 | ||
|
|
0b8adb690e |
@@ -2,11 +2,13 @@ package fdo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -38,6 +40,10 @@ type ServiceInfo struct {
|
||||
func (c FDOOwnerClient) doDigestAuthReq(method, endpoint, contentType string, body io.Reader) (*http.Response, error) {
|
||||
transport := digest.NewTransport(c.Username, c.Password)
|
||||
|
||||
// TODO: REVIEW
|
||||
// Temporary work-around to support sending requests to HTTPS AIO local setups
|
||||
transport.Transport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
client, err := transport.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -70,7 +76,7 @@ func (c FDOOwnerClient) PostVoucher(ov []byte) (string, error) {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodPost,
|
||||
"api/v1/owner/vouchers",
|
||||
"application/cbor",
|
||||
"text/plain",
|
||||
bytes.NewReader(ov),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -90,25 +96,69 @@ func (c FDOOwnerClient) PostVoucher(ov []byte) (string, error) {
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) PutDeviceSVI(info ServiceInfo) error {
|
||||
values := url.Values{}
|
||||
values.Set("module", info.Module)
|
||||
values.Set("var", info.Var)
|
||||
values.Set("filename", info.Filename)
|
||||
values.Set("guid", info.GUID)
|
||||
values.Set("device", info.Device)
|
||||
values.Set("priority", strconv.Itoa(info.Priority))
|
||||
values.Set("os", info.OS)
|
||||
values.Set("version", info.Version)
|
||||
values.Set("arch", info.Arch)
|
||||
values.Set("crid", strconv.Itoa(info.CRID))
|
||||
values.Set("hash", info.Hash)
|
||||
// func (c FDOOwnerClient) PutDeviceSVI(info ServiceInfo) error {
|
||||
// values := url.Values{}
|
||||
// values.Set("module", info.Module)
|
||||
// values.Set("var", info.Var)
|
||||
// values.Set("filename", info.Filename)
|
||||
// values.Set("guid", info.GUID)
|
||||
// values.Set("device", info.Device)
|
||||
// values.Set("priority", strconv.Itoa(info.Priority))
|
||||
// values.Set("os", info.OS)
|
||||
// values.Set("version", info.Version)
|
||||
// values.Set("arch", info.Arch)
|
||||
// values.Set("crid", strconv.Itoa(info.CRID))
|
||||
// values.Set("hash", info.Hash)
|
||||
|
||||
// resp, err := c.doDigestAuthReq(
|
||||
// http.MethodPut,
|
||||
// "api/v1/device/svi?"+values.Encode(),
|
||||
// "application/octet-stream",
|
||||
// strings.NewReader(string(info.Bytes)),
|
||||
// )
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer resp.Body.Close()
|
||||
|
||||
// if resp.StatusCode != http.StatusOK {
|
||||
// return errors.New(http.StatusText(resp.StatusCode))
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Sending SVI instruction
|
||||
// curl -D - --digest -u ${api_user}: --location --request POST 'http://localhost:8080/api/v1/owner/svi' --header 'Content-Type: text/plain' --data-raw '[{"filedesc" : "setup.sh","resource" : "URL"}, {"write": "content_string"} {"exec" : ["bash","setup.sh"] }]'
|
||||
|
||||
// Uploading resources
|
||||
// curl -D - --digest -u ${api_user}: --location --request POST 'http://localhost:8080/api/v1/owner/resource?filename=fileName' --header 'Content-Type: text/plain' --data-binary '@< path to file >'
|
||||
|
||||
func SVIInstructionsToString(SVIInstructions []json.RawMessage) (string, error) {
|
||||
data, err := json.Marshal(SVIInstructions)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// curl -k -D - --digest -u apiUser:U8MdQyV7W9TUXtG --location --request POST 'https://localhost:8443/api/v1/owner/svi' \
|
||||
// --header 'Content-Type: text/plain' \
|
||||
// --data-raw '[{"filedesc" : "sample.txt","resource" : "file.txt"}]'
|
||||
|
||||
// curl -k -D - --digest -u apiUser:U8MdQyV7W9TUXtG --location --request POST 'https://localhost:8443/api/v1/owner/resource?filename=file.txt' --header 'Content-Type: text/plain' --data-binary '@/tmp/file.txt'
|
||||
|
||||
func (c FDOOwnerClient) PostResource(fileName string, resourceContent []byte) error {
|
||||
params := url.Values{
|
||||
"filename": []string{fileName},
|
||||
}
|
||||
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodPut,
|
||||
"api/v1/device/svi?"+values.Encode(),
|
||||
"application/octet-stream",
|
||||
strings.NewReader(string(info.Bytes)),
|
||||
http.MethodPost,
|
||||
"api/v1/owner/resource?"+params.Encode(),
|
||||
"text/plain",
|
||||
bytes.NewReader(resourceContent),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -122,12 +172,28 @@ func (c FDOOwnerClient) PutDeviceSVI(info ServiceInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) PutDeviceSVIRaw(info url.Values, body []byte) error {
|
||||
// TODO: REVIEW
|
||||
// Review comments
|
||||
|
||||
// POST a SVI to the Owner service
|
||||
// That SVI will instruct the device to retrieve a file called resourceName from the database and write it under the fileName path (in local process CWD? e.g. in agent CWD - not sure about that)
|
||||
|
||||
// To FileName
|
||||
// From ResourceName
|
||||
func (c FDOOwnerClient) PostSVI(fileName, resourceName string) error {
|
||||
op1 := json.RawMessage([]byte(fmt.Sprintf(`{"filedesc" : "%s", "resource": "%s"}`, fileName, resourceName)))
|
||||
|
||||
data := []json.RawMessage{op1}
|
||||
payload, err := SVIInstructionsToString(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodPut,
|
||||
"api/v1/device/svi?"+info.Encode(),
|
||||
"application/octet-stream",
|
||||
strings.NewReader(string(body)),
|
||||
http.MethodPost,
|
||||
"api/v1/owner/svi",
|
||||
"text/plain",
|
||||
bytes.NewReader([]byte(payload)),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -141,6 +207,59 @@ func (c FDOOwnerClient) PutDeviceSVIRaw(info url.Values, body []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// POST a SVI to the Owner service
|
||||
// That SVI will instruct the device to retrieve a file called resourceName from the database and write it under the fileName path (in local process CWD? e.g. in agent CWD - not sure about that)
|
||||
// It will then instruct the device to execute a command afterwards
|
||||
|
||||
// To FileName
|
||||
// From ResourceName
|
||||
// func (c FDOOwnerClient) PostSVIFileExec(fileName, resourceName string, execCommand []string) error {
|
||||
// op1 := json.RawMessage([]byte(fmt.Sprintf(`{"filedesc" : "%s", "resource": "%s"}`, fileName, resourceName)))
|
||||
// op2 := json.RawMessage([]byte(fmt.Sprintf(`{"exec" : ["%s"]}`, strings.Join(execCommand, "\",\""))))
|
||||
|
||||
// data := []json.RawMessage{op1, op2}
|
||||
// payload, err := SVIInstructionsToString(data)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// resp, err := c.doDigestAuthReq(
|
||||
// http.MethodPost,
|
||||
// "api/v1/owner/svi",
|
||||
// "text/plain",
|
||||
// strings.NewReader(payload),
|
||||
// )
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer resp.Body.Close()
|
||||
|
||||
// if resp.StatusCode != http.StatusOK {
|
||||
// return errors.New(http.StatusText(resp.StatusCode))
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (c FDOOwnerClient) PutDeviceSVIRaw(info url.Values, body []byte) error {
|
||||
// resp, err := c.doDigestAuthReq(
|
||||
// http.MethodPut,
|
||||
// "api/v1/device/svi?"+info.Encode(),
|
||||
// "application/octet-stream",
|
||||
// strings.NewReader(string(body)),
|
||||
// )
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer resp.Body.Close()
|
||||
|
||||
// if resp.StatusCode != http.StatusOK {
|
||||
// return errors.New(http.StatusText(resp.StatusCode))
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (c FDOOwnerClient) GetVouchers() ([]string, error) {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodGet,
|
||||
|
||||
@@ -8,11 +8,18 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
const (
|
||||
addrFormatFQDN = 201
|
||||
addrFormatIpv4 = 3
|
||||
addrFormatIpv6 = 4
|
||||
)
|
||||
|
||||
type CIRAConfig struct {
|
||||
ConfigName string `json:"configName"`
|
||||
MPSServerAddress string `json:"mpsServerAddress"`
|
||||
@@ -70,15 +77,15 @@ func (service *Service) saveCIRAConfig(method string, configuration portainer.Op
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addressFormat, err := addressFormat(configuration.MPSServer)
|
||||
addressFormat, serverAddress, err := addressFormat(configuration.MPSServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := CIRAConfig{
|
||||
ConfigName: configName,
|
||||
MPSServerAddress: configuration.MPSServer,
|
||||
CommonName: configuration.MPSServer,
|
||||
MPSServerAddress: serverAddress,
|
||||
CommonName: serverAddress,
|
||||
ServerAddressFormat: addressFormat,
|
||||
MPSPort: 4433,
|
||||
Username: "admin",
|
||||
@@ -101,18 +108,33 @@ func (service *Service) saveCIRAConfig(method string, configuration portainer.Op
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func addressFormat(url string) (int, error) {
|
||||
ip := net.ParseIP(url)
|
||||
if ip == nil {
|
||||
return 201, nil // FQDN
|
||||
// addressFormat returns the address format and the address for the given server address.
|
||||
// when using a IP:PORT format, only the IP is returned.
|
||||
// see https://github.com/open-amt-cloud-toolkit/rps/blob/b63e0112f8a6323764742165a2cd5b465d9a9a24/src/routes/admin/ciraconfig/ciraValidator.ts#L20-L25
|
||||
func addressFormat(u string) (int, string, error) {
|
||||
ip2 := net.ParseIP(u)
|
||||
if ip2 != nil {
|
||||
if ip2.To4() != nil {
|
||||
return addrFormatIpv4, u, nil
|
||||
}
|
||||
|
||||
return addrFormatIpv6, u, nil
|
||||
}
|
||||
if strings.Contains(url, ".") {
|
||||
return 3, nil // IPV4
|
||||
|
||||
_, err := url.Parse(u)
|
||||
if err == nil {
|
||||
return addrFormatFQDN, u, nil
|
||||
}
|
||||
if strings.Contains(url, ":") {
|
||||
return 4, nil // IPV6
|
||||
|
||||
host, _, err := net.SplitHostPort(u)
|
||||
if err == nil {
|
||||
if strings.Count(u, ":") >= 2 {
|
||||
return addrFormatIpv6, host, nil
|
||||
}
|
||||
return addrFormatIpv4, host, nil
|
||||
}
|
||||
return 0, fmt.Errorf("could not determine server address format for %s", url)
|
||||
|
||||
return 0, "", fmt.Errorf("could not determine server address format for %s", u)
|
||||
}
|
||||
|
||||
func (service *Service) getCIRACertificate(configuration portainer.OpenAMTConfiguration) (string, error) {
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package fdo
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/libhttp/request"
|
||||
@@ -17,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
deploymentScriptName = "fdo.sh"
|
||||
deploymentScriptName = "fdo-profile.sh"
|
||||
)
|
||||
|
||||
type deviceConfigurePayload struct {
|
||||
@@ -95,94 +91,173 @@ func (handler *Handler) fdoConfigureDevice(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
// enable fdo_sys
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"0"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"active"},
|
||||
"bytes": []string{"F5"}, // this is "true" in CBOR
|
||||
}, []byte("")); err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
// TODO: REVIEW
|
||||
// This might not be needed anymore
|
||||
|
||||
// if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
// "guid": []string{guid},
|
||||
// "priority": []string{"0"},
|
||||
// "module": []string{"fdo_sys"},
|
||||
// "var": []string{"active"},
|
||||
// "bytes": []string{"F5"}, // this is "true" in CBOR
|
||||
// }, []byte("")); err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
// }
|
||||
|
||||
// write down the edge id
|
||||
// if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
// "guid": []string{guid},
|
||||
// "priority": []string{"1"},
|
||||
// "module": []string{"fdo_sys"},
|
||||
// "var": []string{"filedesc"},
|
||||
// "filename": []string{"DEVICE_edgeid.txt"},
|
||||
// }, []byte(payload.EdgeID)); err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw(edgeid)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw(edgeid)", Err: err}
|
||||
// }
|
||||
|
||||
const deviceConfiguration = `
|
||||
GUID=%s
|
||||
DEVICE_NAME=%s
|
||||
EDGE_ID=%s
|
||||
EDGE_KEY=%s
|
||||
`
|
||||
|
||||
deviceConfData := fmt.Sprintf(deviceConfiguration, guid, payload.Name, payload.EdgeID, payload.EdgeKey)
|
||||
deviceConfResourceName := fmt.Sprintf("%s-agent.conf", guid)
|
||||
|
||||
err = fdoClient.PostResource(deviceConfResourceName, []byte(deviceConfData))
|
||||
if err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: UploadResource(DEVICE.conf)")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: UploadResource(DEVICE.conf)", Err: err}
|
||||
}
|
||||
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_edgeid.txt"},
|
||||
}, []byte(payload.EdgeID)); err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw(edgeid)")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw(edgeid)", Err: err}
|
||||
}
|
||||
// TODO: REVIEW
|
||||
// I believe this will need a filter on GUID
|
||||
// https://github.com/secure-device-onboard/pri-fidoiot/tree/1.1.0-rel/component-samples/demo/aio#service-info-filters
|
||||
|
||||
// err = fdoClient.PostSVIFile("DEVICE.conf", deviceConfResourceName)
|
||||
|
||||
// TODO: REVIEW whether the $guid shortcut works
|
||||
// If that's the case - then potentially we only need to do this once
|
||||
// and just do a PostResource here (the one above)
|
||||
// That would mean that we would need to relocate this action to somewhere else (a setup process after enabling the FDO integration for example)
|
||||
|
||||
// err = fdoClient.PostSVI("DEVICE.conf", "$(guid)-DEVICE.conf")
|
||||
// if err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PostSVIFile(DEVICE.conf)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PostSVIFile(DEVICE.conf)", Err: err}
|
||||
// }
|
||||
|
||||
// write down the edgekey
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_edgekey.txt"},
|
||||
}, []byte(payload.EdgeKey)); err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw(edgekey)")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw(edgekey)", Err: err}
|
||||
}
|
||||
|
||||
// if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
// "guid": []string{guid},
|
||||
// "priority": []string{"1"},
|
||||
// "module": []string{"fdo_sys"},
|
||||
// "var": []string{"filedesc"},
|
||||
// "filename": []string{"DEVICE_edgekey.txt"},
|
||||
// }, []byte(payload.EdgeKey)); err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw(edgekey)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw(edgekey)", Err: err}
|
||||
// }
|
||||
|
||||
// err = fdoClient.PostSVIFile("DEVICE_edgekey.txt", payload.EdgeKey)
|
||||
// if err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PostSVIFile(edgekey)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PostSVIFile(edgekey)", Err: err}
|
||||
// }
|
||||
|
||||
// write down the device name
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_name.txt"},
|
||||
}, []byte(payload.Name)); err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw(name)")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw(name)", Err: err}
|
||||
}
|
||||
|
||||
// if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
// "guid": []string{guid},
|
||||
// "priority": []string{"1"},
|
||||
// "module": []string{"fdo_sys"},
|
||||
// "var": []string{"filedesc"},
|
||||
// "filename": []string{"DEVICE_name.txt"},
|
||||
// }, []byte(payload.Name)); err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw(name)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw(name)", Err: err}
|
||||
// }
|
||||
|
||||
// err = fdoClient.PostSVIFile("DEVICE_name.txt", payload.Name)
|
||||
// if err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PostSVIFile(name)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PostSVIFile(name)", Err: err}
|
||||
// }
|
||||
|
||||
// write down the device GUID - used as the EDGE_DEVICE_GUID too
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_GUID.txt"},
|
||||
}, []byte(guid)); err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
}
|
||||
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{deploymentScriptName},
|
||||
}, fileContent); err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
}
|
||||
// if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
// "guid": []string{guid},
|
||||
// "priority": []string{"1"},
|
||||
// "module": []string{"fdo_sys"},
|
||||
// "var": []string{"filedesc"},
|
||||
// "filename": []string{"DEVICE_GUID.txt"},
|
||||
// }, []byte(guid)); err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
// }
|
||||
|
||||
b, err := cbor.Marshal([]string{"/bin/sh", deploymentScriptName})
|
||||
// err = fdoClient.PostSVIFile("DEVICE_GUID.txt", guid)
|
||||
// if err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PostSVIFile(guid)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PostSVIFile(guid)", Err: err}
|
||||
// }
|
||||
|
||||
// write down the profile script
|
||||
|
||||
// if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
// "guid": []string{guid},
|
||||
// "priority": []string{"1"},
|
||||
// "module": []string{"fdo_sys"},
|
||||
// "var": []string{"filedesc"},
|
||||
// "filename": []string{deploymentScriptName},
|
||||
// }, fileContent); err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
// }
|
||||
|
||||
// b, err := cbor.Marshal([]string{"/bin/sh", deploymentScriptName})
|
||||
// if err != nil {
|
||||
// logrus.WithError(err).Error("failed to marshal string to CBOR")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw() failed to encode", Err: err}
|
||||
// }
|
||||
|
||||
// cborBytes := strings.ToUpper(hex.EncodeToString(b))
|
||||
// logrus.WithField("cbor", cborBytes).WithField("string", deploymentScriptName).Info("converted to CBOR")
|
||||
|
||||
// if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
// "guid": []string{guid},
|
||||
// "priority": []string{"2"},
|
||||
// "module": []string{"fdo_sys"},
|
||||
// "var": []string{"exec"},
|
||||
// "bytes": []string{cborBytes},
|
||||
// }, []byte("")); err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
// }
|
||||
|
||||
// TODO: REVIEW
|
||||
// The PostResource and PostSVI steps probably can be done once just after creating the profile
|
||||
// PostResource should also be done after removing a profile
|
||||
// DelResource should also be done after deleting a profile
|
||||
err = fdoClient.PostResource(deploymentScriptName, fileContent)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to marshal string to CBOR")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw() failed to encode", Err: err}
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: UploadResource(DEVICE.conf)")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: UploadResource(DEVICE.conf)", Err: err}
|
||||
}
|
||||
|
||||
cborBytes := strings.ToUpper(hex.EncodeToString(b))
|
||||
logrus.WithField("cbor", cborBytes).WithField("string", deploymentScriptName).Info("converted to CBOR")
|
||||
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"2"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"exec"},
|
||||
"bytes": []string{cborBytes},
|
||||
}, []byte("")); err != nil {
|
||||
logrus.WithError(err).Info("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PutDeviceSVIRaw()", Err: err}
|
||||
}
|
||||
// TODO: REVIEW
|
||||
// Must be named OS_Install.sh to work with the BMO AIO setup
|
||||
// This might need to be configurable in the future
|
||||
// err = fdoClient.PostSVI("OS_Install.sh", deploymentScriptName)
|
||||
// if err != nil {
|
||||
// logrus.WithError(err).Info("fdoConfigureDevice: PostSVIFileExec(profile)")
|
||||
// return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "fdoConfigureDevice: PostSVIFileExec(profile)", Err: err}
|
||||
// }
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (handler *Handler) newFDOClient() (fdo.FDOOwnerClient, error) {
|
||||
OwnerURL: settings.FDOConfiguration.OwnerURL,
|
||||
Username: settings.FDOConfiguration.OwnerUsername,
|
||||
Password: settings.FDOConfiguration.OwnerPassword,
|
||||
Timeout: 5 * time.Second,
|
||||
Timeout: 10 * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,19 @@ func (handler *Handler) openAMTActivate(w http.ResponseWriter, r *http.Request)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to retrieve AMT information", Err: err}
|
||||
}
|
||||
if hostInfo.ControlModeRaw < 1 {
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Failed to activate device", Err: errors.New("failed to activate device")}
|
||||
}
|
||||
|
||||
// TODO: REVIEW
|
||||
// Should check here that the activation is OK (can check for control mode / RAS remote status)
|
||||
// If not, ask the user to check the logs
|
||||
|
||||
// We should also check for the following logs in the service container
|
||||
// INFO[0050] Status: Admin control mode., MEBx Password updated
|
||||
// INFO[0050] Network: Ethernet Configured.
|
||||
// INFO[0050] CIRA: Configured
|
||||
// without the 0050 as it might change at runtime
|
||||
// if these logs are not found, assume an error and don't remove the service container as it will be useful for troubleshooting
|
||||
// consider redirecting these logs to portainer
|
||||
|
||||
if hostInfo.UUID == "" {
|
||||
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to retrieve device UUID", Err: errors.New("unable to retrieve device UUID")}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/portainer/portainer/api/hostmanagement/openamt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/api/hostmanagement/openamt"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@@ -25,19 +26,18 @@ import (
|
||||
)
|
||||
|
||||
type HostInfo struct {
|
||||
EndpointID portainer.EndpointID `json:"EndpointID"`
|
||||
RawOutput string `json:"RawOutput"`
|
||||
AMT string `json:"AMT"`
|
||||
UUID string `json:"UUID"`
|
||||
DNSSuffix string `json:"DNS Suffix"`
|
||||
BuildNumber string `json:"Build Number"`
|
||||
ControlMode string `json:"Control Mode"`
|
||||
ControlModeRaw int `json:"Control Mode (Raw)"`
|
||||
EndpointID portainer.EndpointID `json:"EndpointID"`
|
||||
RawOutput string `json:"RawOutput"`
|
||||
AMT string `json:"AMT"`
|
||||
UUID string `json:"UUID"`
|
||||
DNSSuffix string `json:"DNS Suffix"`
|
||||
BuildNumber string `json:"Build Number"`
|
||||
ControlMode string `json:"Control Mode"`
|
||||
}
|
||||
|
||||
const (
|
||||
// TODO: this should get extracted to some configurable - don't assume Docker Hub is everyone's global namespace, or that they're allowed to pull images from the internet
|
||||
rpcGoImageName = "ptrrd/openamt:rpc-go-json"
|
||||
rpcGoImageName = "intel/oact-rpc-go:v2.1.0"
|
||||
rpcGoContainerName = "openamt-rpc-go"
|
||||
dockerClientTimeout = 5 * time.Minute
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user