mirror of https://github.com/portainer/portainer
				
				
				
			
		
			
				
	
	
		
			165 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
package fdo
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"net/url"
 | 
						|
	"strconv"
 | 
						|
	"time"
 | 
						|
 | 
						|
	httperror "github.com/portainer/libhttp/error"
 | 
						|
	"github.com/portainer/libhttp/request"
 | 
						|
	"github.com/portainer/libhttp/response"
 | 
						|
	portainer "github.com/portainer/portainer/api"
 | 
						|
	"github.com/portainer/portainer/api/hostmanagement/fdo"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
type fdoConfigurePayload portainer.FDOConfiguration
 | 
						|
 | 
						|
func validateURL(u string) error {
 | 
						|
	p, err := url.Parse(u)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if p.Scheme != "http" && p.Scheme != "https" {
 | 
						|
		return errors.New("invalid scheme provided, must be 'http' or 'https'")
 | 
						|
	}
 | 
						|
 | 
						|
	if p.Host == "" {
 | 
						|
		return errors.New("invalid host provided")
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (payload *fdoConfigurePayload) Validate(r *http.Request) error {
 | 
						|
	if payload.Enabled {
 | 
						|
		if err := validateURL(payload.OwnerURL); err != nil {
 | 
						|
			return fmt.Errorf("owner server URL: %w", err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (handler *Handler) saveSettings(config portainer.FDOConfiguration) error {
 | 
						|
	settings, err := handler.DataStore.Settings().Settings()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	settings.FDOConfiguration = config
 | 
						|
 | 
						|
	return handler.DataStore.Settings().UpdateSettings(settings)
 | 
						|
}
 | 
						|
 | 
						|
func (handler *Handler) newFDOClient() (fdo.FDOOwnerClient, error) {
 | 
						|
	settings, err := handler.DataStore.Settings().Settings()
 | 
						|
	if err != nil {
 | 
						|
		return fdo.FDOOwnerClient{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	return fdo.FDOOwnerClient{
 | 
						|
		OwnerURL: settings.FDOConfiguration.OwnerURL,
 | 
						|
		Username: settings.FDOConfiguration.OwnerUsername,
 | 
						|
		Password: settings.FDOConfiguration.OwnerPassword,
 | 
						|
		Timeout:  5 * time.Second,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// @id fdoConfigure
 | 
						|
// @summary Enable Portainer's FDO capabilities
 | 
						|
// @description Enable Portainer's FDO capabilities
 | 
						|
// @description **Access policy**: administrator
 | 
						|
// @tags intel
 | 
						|
// @security jwt
 | 
						|
// @accept json
 | 
						|
// @produce json
 | 
						|
// @param body body fdoConfigurePayload true "FDO Settings"
 | 
						|
// @success 204 "Success"
 | 
						|
// @failure 400 "Invalid request"
 | 
						|
// @failure 403 "Permission denied to access settings"
 | 
						|
// @failure 500 "Server error"
 | 
						|
// @router /fdo [post]
 | 
						|
func (handler *Handler) fdoConfigure(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
 | 
						|
	var payload fdoConfigurePayload
 | 
						|
 | 
						|
	err := request.DecodeAndValidateJSONPayload(r, &payload)
 | 
						|
	if err != nil {
 | 
						|
		logrus.WithError(err).Error("Invalid request payload")
 | 
						|
		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid request payload", Err: err}
 | 
						|
	}
 | 
						|
 | 
						|
	settings := portainer.FDOConfiguration(payload)
 | 
						|
	if err = handler.saveSettings(settings); err != nil {
 | 
						|
		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Error saving FDO settings", Err: err}
 | 
						|
	}
 | 
						|
 | 
						|
	profiles, err := handler.DataStore.FDOProfile().FDOProfiles()
 | 
						|
	if err != nil {
 | 
						|
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Error saving FDO settings", Err: err}
 | 
						|
	}
 | 
						|
	if len(profiles) == 0 {
 | 
						|
		err = handler.addDefaultProfile()
 | 
						|
		if err != nil {
 | 
						|
			return &httperror.HandlerError{http.StatusInternalServerError, err.Error(), err}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return response.Empty(w)
 | 
						|
}
 | 
						|
 | 
						|
func (handler *Handler) addDefaultProfile() error {
 | 
						|
	profileID := handler.DataStore.FDOProfile().GetNextIdentifier()
 | 
						|
	profile := &portainer.FDOProfile{
 | 
						|
		ID:   portainer.FDOProfileID(profileID),
 | 
						|
		Name: "Docker Standalone + Edge",
 | 
						|
	}
 | 
						|
 | 
						|
	filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(profile.ID)), []byte(defaultProfileFileContent))
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	profile.FilePath = filePath
 | 
						|
	profile.DateCreated = time.Now().Unix()
 | 
						|
 | 
						|
	err = handler.DataStore.FDOProfile().Create(profile)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
const defaultProfileFileContent = `
 | 
						|
#!/bin/bash -ex
 | 
						|
 | 
						|
env > env.log
 | 
						|
 | 
						|
export AGENT_IMAGE=portainer/agent:2.11.0
 | 
						|
export GUID=$(cat DEVICE_GUID.txt)
 | 
						|
export DEVICE_NAME=$(cat DEVICE_name.txt)
 | 
						|
export EDGE_ID=$(cat DEVICE_edgeid.txt)
 | 
						|
export EDGE_KEY=$(cat DEVICE_edgekey.txt)
 | 
						|
export AGENTVOLUME=$(pwd)/data/portainer_agent_data/
 | 
						|
 | 
						|
mkdir -p ${AGENTVOLUME}
 | 
						|
 | 
						|
docker pull ${AGENT_IMAGE}
 | 
						|
 | 
						|
docker run -d \
 | 
						|
    -v /var/run/docker.sock:/var/run/docker.sock \
 | 
						|
    -v /var/lib/docker/volumes:/var/lib/docker/volumes \
 | 
						|
    -v /:/host \
 | 
						|
    -v ${AGENTVOLUME}:/data \
 | 
						|
    --restart always \
 | 
						|
    -e EDGE=1 \
 | 
						|
    -e EDGE_ID=${EDGE_ID} \
 | 
						|
    -e EDGE_KEY=${EDGE_KEY} \
 | 
						|
    -e CAP_HOST_MANAGEMENT=1 \
 | 
						|
    -e EDGE_INSECURE_POLL=1 \
 | 
						|
    --name portainer_edge_agent \
 | 
						|
    ${AGENT_IMAGE}
 | 
						|
`
 |