package openamt import ( "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "io" "net" "net/http" "strings" portainer "github.com/portainer/portainer/api" ) type CIRAConfig struct { ConfigName string `json:"configName"` MPSServerAddress string `json:"mpsServerAddress"` ServerAddressFormat int `json:"serverAddressFormat"` CommonName string `json:"commonName"` MPSPort int `json:"mpsPort"` Username string `json:"username"` MPSRootCertificate string `json:"mpsRootCertificate"` RegeneratePassword bool `json:"regeneratePassword"` AuthMethod int `json:"authMethod"` } func (service *Service) createOrUpdateCIRAConfig(configuration portainer.OpenAMTConfiguration, configName string) (*CIRAConfig, error) { ciraConfig, err := service.getCIRAConfig(configuration, configName) if err != nil { return nil, err } method := http.MethodPost if ciraConfig != nil { method = http.MethodPatch } ciraConfig, err = service.saveCIRAConfig(method, configuration, configName) if err != nil { return nil, err } return ciraConfig, nil } func (service *Service) getCIRAConfig(configuration portainer.OpenAMTConfiguration, configName string) (*CIRAConfig, error) { url := fmt.Sprintf("https://%s/rps/api/v1/admin/ciraconfigs/%s", configuration.MPSServer, configName) responseBody, err := service.executeGetRequest(url, configuration.Credentials.MPSToken) if err != nil { return nil, err } if responseBody == nil { return nil, nil } var result CIRAConfig err = json.Unmarshal(responseBody, &result) if err != nil { return nil, err } return &result, nil } func (service *Service) saveCIRAConfig(method string, configuration portainer.OpenAMTConfiguration, configName string) (*CIRAConfig, error) { url := fmt.Sprintf("https://%s/rps/api/v1/admin/ciraconfigs", configuration.MPSServer) certificate, err := service.getCIRACertificate(configuration) if err != nil { return nil, err } addressFormat, err := addressFormat(configuration.MPSServer) if err != nil { return nil, err } config := CIRAConfig{ ConfigName: configName, MPSServerAddress: configuration.MPSServer, CommonName: configuration.MPSServer, ServerAddressFormat: addressFormat, MPSPort: 4433, Username: "admin", MPSRootCertificate: certificate, RegeneratePassword: false, AuthMethod: 2, } payload, _ := json.Marshal(config) responseBody, err := service.executeSaveRequest(method, url, configuration.Credentials.MPSToken, payload) if err != nil { return nil, err } var result CIRAConfig err = json.Unmarshal(responseBody, &result) if err != nil { return nil, err } return &result, nil } func addressFormat(url string) (int, error) { ip := net.ParseIP(url) if ip == nil { return 201, nil // FQDN } if strings.Contains(url, ".") { return 3, nil // IPV4 } if strings.Contains(url, ":") { return 4, nil // IPV6 } return 0, fmt.Errorf("could not determine server address format for %s", url) } func (service *Service) getCIRACertificate(configuration portainer.OpenAMTConfiguration) (string, error) { loginURL := fmt.Sprintf("https://%s/mps/api/v1/ciracert", configuration.MPSServer) req, err := http.NewRequest(http.MethodGet, loginURL, nil) if err != nil { return "", err } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", configuration.Credentials.MPSToken)) response, err := service.httpsClient.Do(req) if err != nil { return "", err } if response.StatusCode != http.StatusOK { return "", errors.New(fmt.Sprintf("unexpected status code %s", response.Status)) } certificate, err := io.ReadAll(response.Body) if err != nil { return "", err } block, _ := pem.Decode(certificate) return base64.StdEncoding.EncodeToString(block.Bytes), nil }