feat(openamt): initial setup and create default CIRA config

This commit is contained in:
cheloRydel
2021-11-11 17:35:46 -03:00
parent 5a5b8911e1
commit 020c2efa42
10 changed files with 244 additions and 14 deletions

View File

@@ -14,6 +14,7 @@ import (
"github.com/portainer/portainer/api/cli"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/docker"
"github.com/portainer/portainer/api/intel/openamt"
"github.com/portainer/libhelm"
"github.com/portainer/portainer/api/exec"
@@ -468,6 +469,8 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
gitService := initGitService()
openAMTService := openamt.NewService()
cryptoService := initCryptoService()
digitalSignatureService := initDigitalSignatureService()
@@ -623,6 +626,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
LDAPService: ldapService,
OAuthService: oauthService,
GitService: gitService,
OpenAMTService: openAMTService,
ProxyManager: proxyManager,
KubernetesTokenCacheManager: kubernetesTokenCacheManager,
KubeConfigService: kubeConfigService,

View File

@@ -4,13 +4,16 @@ import (
"net/http"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/security"
)
// Handler is the HTTP handler used to handle OpenAMT operations.
type Handler struct {
*mux.Router
OpenAMTService portainer.OpenAMTService
}
// NewHandler returns a new Handler

View File

@@ -10,18 +10,19 @@ import (
)
type openAMTSubmitPayload struct {
EnableOpenAMT *bool
CertFileText *string
CertPassword *string
DomainName *string
UseWirelessConfig *bool
EnableOpenAMT *bool
CertFileText *string
CertPassword *string
DomainName *string
UseWirelessConfig *bool
WifiAuthenticationMethod *string
WifiEncryptionMethod *string
WifiSSID *string
WifiPskPass *string
WifiEncryptionMethod *string
WifiSSID *string
WifiPskPass *string
}
func (payload *openAMTSubmitPayload) Validate(r *http.Request) error {
return nil //TODO remove
if *payload.EnableOpenAMT {
if payload.DomainName == nil || *payload.DomainName == "" {
return errors.New("domain name must be provided")
@@ -73,6 +74,11 @@ func (handler *Handler) openAMTSubmit(w http.ResponseWriter, r *http.Request) *h
}
if *payload.EnableOpenAMT {
err := handler.OpenAMTService.ConfigureDefault()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "error configuring OpenAMT server", err}
}
return &httperror.HandlerError{http.StatusNotImplemented, "not implemented", errors.New("not implemented")}
}

View File

@@ -76,6 +76,7 @@ type Server struct {
FileService portainer.FileService
DataStore portainer.DataStore
GitService portainer.GitService
OpenAMTService portainer.OpenAMTService
JWTService portainer.JWTService
LDAPService portainer.LDAPService
OAuthService portainer.OAuthService
@@ -205,6 +206,7 @@ func (server *Server) Start() error {
sslHandler.SSLService = server.SSLService
var openAMTHandler = openamthandler.NewHandler(requestBouncer)
openAMTHandler.OpenAMTService = server.OpenAMTService
var stackHandler = stacks.NewHandler(requestBouncer)
stackHandler.DataStore = server.DataStore

View File

@@ -0,0 +1,57 @@
package openamt
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
)
type authenticationResponse struct {
Token string `json:"token"`
}
func (service *Service) executeOpenAMTAuthenticationRequest() (*authenticationResponse, error) {
loginURL := fmt.Sprintf("%v/mps/login/api/v1/authorize", MPS_SERVER_ADDRESS)
// TODO where to retrieve this from? autogenerated?
payload := map[string]string{
"username": "admin",
"password": "chelo.Port2021",
}
jsonValue, _ := json.Marshal(payload)
req, err := http.NewRequest(http.MethodPost, loginURL, bytes.NewBuffer(jsonValue))
req.Header.Set("Content-Type", "application/json")
response, err := service.httpsClient.Do(req)
if err != nil {
return nil, err
}
responseBody, readErr := ioutil.ReadAll(response.Body)
if readErr != nil {
return nil, readErr
}
var errorResponse errorResponse
err = json.Unmarshal(responseBody, &errorResponse)
if err != nil {
return nil, err
}
if len(errorResponse.Errors) > 0 {
return nil, errors.New(errorResponse.Errors[0].ErrorMsg)
}
if errorResponse.ErrorMsg != "" {
return nil, errors.New(errorResponse.ErrorMsg)
}
var token authenticationResponse
err = json.Unmarshal(responseBody, &token)
if err != nil {
return nil, err
}
return &token, nil
}

View File

@@ -0,0 +1,106 @@
package openamt
import (
"bytes"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
)
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) createCIRAConfig(token string) (*CIRAConfig, error) {
loginURL := fmt.Sprintf("%v/rps/api/v1/admin/ciraconfigs", MPS_SERVER_ADDRESS)
certificate, err := service.getCIRACertificate(token)
if err != nil {
return nil, err
}
payload := CIRAConfig{
ConfigName: "ciraConfigDefault",
MPSServerAddress: "192.168.0.100",
ServerAddressFormat: 3,
CommonName: "192.168.0.100",
MPSPort: 4433,
Username: "admin",
MPSRootCertificate: certificate,
RegeneratePassword: false,
AuthMethod: 2,
}
jsonValue, _ := json.Marshal(payload)
req, err := http.NewRequest(http.MethodPost, loginURL, bytes.NewBuffer(jsonValue))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
response, err := service.httpsClient.Do(req)
if err != nil {
return nil, err
}
responseBody, readErr := ioutil.ReadAll(response.Body)
if readErr != nil {
return nil, readErr
}
var errorResponse errorResponse
err = json.Unmarshal(responseBody, &errorResponse)
if err != nil {
return nil, err
}
if len(errorResponse.Errors) > 0 {
return nil, errors.New(errorResponse.Errors[0].ErrorMsg)
}
if errorResponse.ErrorMsg != "" {
return nil, errors.New(errorResponse.ErrorMsg)
}
if response.StatusCode != http.StatusCreated {
return nil, errors.New(fmt.Sprintf("unexpected status code %v", response.Status))
}
var result CIRAConfig
err = json.Unmarshal(responseBody, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func (service *Service) getCIRACertificate(token string) (string, error) {
loginURL := fmt.Sprintf("%v/mps/api/v1/ciracert", MPS_SERVER_ADDRESS)
req, err := http.NewRequest(http.MethodGet, loginURL, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
response, err := service.httpsClient.Do(req)
if err != nil {
return "", err
}
if response.StatusCode != http.StatusOK {
return "", errors.New(fmt.Sprintf("unexpected status code %v", response.Status))
}
certificate, err := io.ReadAll(response.Body)
if err != nil {
return "", err
}
block, _ := pem.Decode(certificate)
return base64.StdEncoding.EncodeToString(block.Bytes), nil
}

View File

@@ -0,0 +1,51 @@
package openamt
import (
"crypto/tls"
"net/http"
"time"
)
const (
MPS_SERVER_ADDRESS = "https://localhost"
)
// Service represents a service for managing an OpenAMT server.
type Service struct {
httpsClient *http.Client
}
// NewService initializes a new service.
func NewService() *Service {
return &Service{
httpsClient:
&http.Client{
Timeout: time.Second * time.Duration(5),
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
Proxy: http.ProxyFromEnvironment,
},
},
}
}
type errorResponse struct {
ErrorMsg string `json:"message"`
Errors []struct {
ErrorMsg string `json:"msg"`
} `json:"errors"`
}
func(service *Service) ConfigureDefault() error {
token, err := service.executeOpenAMTAuthenticationRequest()
if err != nil {
return err
}
_, err = service.createCIRAConfig(token.Token)
if err != nil {
return err
}
return nil
}

View File

@@ -1257,6 +1257,11 @@ type (
LatestCommitID(repositoryURL, referenceName, username, password string) (string, error)
}
// OpenAMTService represents a service for managing OpenAMT
OpenAMTService interface {
ConfigureDefault() error
}
// HelmUserRepositoryService represents a service to manage HelmUserRepositories
HelmUserRepositoryService interface {
HelmUserRepositoryByUserID(userID UserID) ([]HelmUserRepository, error)