Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enabling dynamic api host for Enclave Manager and auth enforcement #1153

Merged
merged 16 commits into from
Aug 24, 2023
2 changes: 1 addition & 1 deletion cli/cli/helpers/portal_manager/binary_artifact_getter.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func DownloadRequiredKurtosisPortalBinary(ctx context.Context) (bool, error) {
return false, defaultToCurrentVersionOrError(currentVersionStrMaybe, err)
}
if currentVersionMatchesRequired {
logrus.Infof("Installed Kurtosis Portal version '%s' is the required one", requiredVersionStr)
logrus.Infof("Installed Kurtosis Portal version '%s'", requiredVersionStr)
return false, nil
}

Expand Down
53 changes: 39 additions & 14 deletions enclave-manager/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/kurtosis-tech/kurtosis/cloud/api/golang/kurtosis_backend_server_rpc_api_bindings"
"github.com/kurtosis-tech/kurtosis/cloud/api/golang/kurtosis_backend_server_rpc_api_bindings/kurtosis_backend_server_rpc_api_bindingsconnect"
connect_server "github.com/kurtosis-tech/kurtosis/connect-server"
"github.com/kurtosis-tech/kurtosis/contexts-config-store/store"
"github.com/kurtosis-tech/kurtosis/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings"
"github.com/kurtosis-tech/kurtosis/enclave-manager/api/golang/kurtosis_enclave_manager_api_bindings/kurtosis_enclave_manager_api_bindingsconnect"
"github.com/kurtosis-tech/stacktrace"
Expand All @@ -24,12 +25,12 @@ import (
)

const (
listenPort = 8081
grpcServerStopGracePeriod = 5 * time.Second
engineHostUrl = "http://localhost:9710"
kurtosisCloudApiHost = "https://cloud.kurtosis.com"
kurtosisCloudApiPort = 8080
enforceAuth = false // This setting is temporary and will be dynamically changed in following work
listenPort = 8081
grpcServerStopGracePeriod = 5 * time.Second
engineHostUrl = "http://localhost:9710"
kurtosisCloudApiHost = "https://cloud.kurtosis.com"
kurtosisCloudApiPort = 8080
KurtosisEnclaveManagerApiEnforceAuthKeyEnvVarArg = "KURTOSIS_ENCLAVE_MANAGER_API_ENFORCE_AUTH"
)

type Authentication struct {
Expand All @@ -39,16 +40,35 @@ type Authentication struct {

type WebServer struct {
engineServiceClient *kurtosis_engine_rpc_api_bindingsconnect.EngineServiceClient
enforceAuth bool
}

func NewWebserver() *WebServer {
func NewWebserver() (*WebServer, error) {
engineServiceClient := kurtosis_engine_rpc_api_bindingsconnect.NewEngineServiceClient(
http.DefaultClient,
engineHostUrl,
)
enforceAuth, err := LoadEnforceAuthSetting()
if err != nil {
return nil, err
}
return &WebServer{
engineServiceClient: &engineServiceClient,
enforceAuth: *enforceAuth,
}, nil
}

func LoadEnforceAuthSetting() (*bool, error) {
var enforceAuth = false
currentContext, err := store.GetContextsConfigStore().GetCurrentContext()
if err != nil {
return nil, stacktrace.Propagate(err, "an error occurred while getting the local context")
}
if store.IsRemote(currentContext) {
enforceAuth = true
}
logrus.Infof("Auth enforcement setting: %v", enforceAuth)
return &enforceAuth, nil
}

func (c *WebServer) Check(context.Context, *connect.Request[kurtosis_enclave_manager_api_bindings.HealthCheckRequest]) (*connect.Response[kurtosis_enclave_manager_api_bindings.HealthCheckResponse], error) {
Expand Down Expand Up @@ -87,7 +107,7 @@ func (c *WebServer) ValidateRequestAuthorization(
}

func (c *WebServer) GetEnclaves(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[kurtosis_engine_rpc_api_bindings.GetEnclavesResponse], error) {
auth, err := c.ValidateRequestAuthorization(ctx, enforceAuth, req.Header())
auth, err := c.ValidateRequestAuthorization(ctx, c.enforceAuth, req.Header())
if err != nil {
return nil, stacktrace.Propagate(err, "Authentication attempt failed")
}
Expand All @@ -106,7 +126,7 @@ func (c *WebServer) GetEnclaves(ctx context.Context, req *connect.Request[emptyp
return resp, nil
}
func (c *WebServer) GetServices(ctx context.Context, req *connect.Request[kurtosis_enclave_manager_api_bindings.GetServicesRequest]) (*connect.Response[kurtosis_core_rpc_api_bindings.GetServicesResponse], error) {
auth, err := c.ValidateRequestAuthorization(ctx, enforceAuth, req.Header())
auth, err := c.ValidateRequestAuthorization(ctx, c.enforceAuth, req.Header())
if err != nil {
return nil, stacktrace.Propagate(err, "Authentication attempt failed")
}
Expand Down Expand Up @@ -167,7 +187,7 @@ func (c *WebServer) GetServiceLogs(
}

func (c *WebServer) ListFilesArtifactNamesAndUuids(ctx context.Context, req *connect.Request[kurtosis_enclave_manager_api_bindings.GetListFilesArtifactNamesAndUuidsRequest]) (*connect.Response[kurtosis_core_rpc_api_bindings.ListFilesArtifactNamesAndUuidsResponse], error) {
auth, err := c.ValidateRequestAuthorization(ctx, enforceAuth, req.Header())
auth, err := c.ValidateRequestAuthorization(ctx, c.enforceAuth, req.Header())
if err != nil {
return nil, stacktrace.Propagate(err, "Authentication attempt failed")
}
Expand Down Expand Up @@ -229,7 +249,7 @@ func (c *WebServer) RunStarlarkPackage(ctx context.Context, req *connect.Request
}

func (c *WebServer) CreateEnclave(ctx context.Context, req *connect.Request[kurtosis_engine_rpc_api_bindings.CreateEnclaveArgs]) (*connect.Response[kurtosis_engine_rpc_api_bindings.CreateEnclaveResponse], error) {
auth, err := c.ValidateRequestAuthorization(ctx, enforceAuth, req.Header())
auth, err := c.ValidateRequestAuthorization(ctx, c.enforceAuth, req.Header())
if err != nil {
return nil, stacktrace.Propagate(err, "Authentication attempt failed")
}
Expand All @@ -250,7 +270,7 @@ func (c *WebServer) CreateEnclave(ctx context.Context, req *connect.Request[kurt
}

func (c *WebServer) InspectFilesArtifactContents(ctx context.Context, req *connect.Request[kurtosis_enclave_manager_api_bindings.InspectFilesArtifactContentsRequest]) (*connect.Response[kurtosis_core_rpc_api_bindings.InspectFilesArtifactContentsResponse], error) {
auth, err := c.ValidateRequestAuthorization(ctx, enforceAuth, req.Header())
auth, err := c.ValidateRequestAuthorization(ctx, c.enforceAuth, req.Header())
if err != nil {
return nil, stacktrace.Propagate(err, "Authentication attempt failed")
}
Expand Down Expand Up @@ -346,8 +366,12 @@ func (c *WebServer) ConvertJwtTokenToApiKey(
return nil, stacktrace.NewError("an empty API key was returned from Kurtosis Cloud Backend")
}

func RunEnclaveManagerApiServer() {
srv := NewWebserver()
func RunEnclaveManagerApiServer() error {
srv, err := NewWebserver()
if err != nil {
logrus.Fatal("an error occurred while processing the auth settings, exiting!", err)
return err
}
apiPath, handler := kurtosis_enclave_manager_api_bindingsconnect.NewKurtosisEnclaveManagerServerHandler(srv)

logrus.Infof("Web server running and listening on port %d", listenPort)
Expand All @@ -360,6 +384,7 @@ func RunEnclaveManagerApiServer() {
if err := apiServer.RunServerUntilInterruptedWithCors(cors.AllowAll()); err != nil {
logrus.Error("An error occurred running the server", err)
}
return nil
}

func getServiceLogsFromEngine(client *connect.ServerStreamForClient[kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse]) chan *kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse {
Expand Down
33 changes: 17 additions & 16 deletions engine/frontend/src/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import {createConnectTransport,} from "@bufbuild/connect-web";
import {
GetListFilesArtifactNamesAndUuidsRequest,
GetServicesRequest,
InspectFilesArtifactContentsRequest,
RunStarlarkPackageRequest
InspectFilesArtifactContentsRequest
} from "enclave-manager-sdk/build/kurtosis_enclave_manager_api_pb";
import {CreateEnclaveArgs} from "enclave-manager-sdk/build/engine_service_pb";
import {RunStarlarkPackageArgs} from "enclave-manager-sdk/build/api_container_service_pb";

const transport = createConnectTransport({
baseUrl: "http://localhost:8081"
})

const enclaveManagerClient = createPromiseClient(KurtosisEnclaveManagerServer, transport);
export const createClient = (apiHost) => {
const transport = createConnectTransport({baseUrl: apiHost})
return createPromiseClient(KurtosisEnclaveManagerServer, transport)
}

const createHeaderOptionsWithToken = (token) => {
const headers = new Headers();
Expand All @@ -24,11 +22,13 @@ const createHeaderOptionsWithToken = (token) => {
return {headers: headers}
}

export const getEnclavesFromEnclaveManager = async (token) => {
export const getEnclavesFromEnclaveManager = async (token, apiHost) => {
const enclaveManagerClient = createClient(apiHost);
return enclaveManagerClient.getEnclaves({}, createHeaderOptionsWithToken(token));
}

export const getServicesFromEnclaveManager = async (host, port, token) => {
export const getServicesFromEnclaveManager = async (host, port, token, apiHost) => {
const enclaveManagerClient = createClient(apiHost);
const request = new GetServicesRequest(
{
"apicIpAddress": host,
Expand All @@ -38,7 +38,8 @@ export const getServicesFromEnclaveManager = async (host, port, token) => {
return enclaveManagerClient.getServices(request, createHeaderOptionsWithToken(token));
}

export const listFilesArtifactNamesAndUuidsFromEnclaveManager = async (host, port, token) => {
export const listFilesArtifactNamesAndUuidsFromEnclaveManager = async (host, port, token, apiHost) => {
const enclaveManagerClient = createClient(apiHost);
const request = new GetListFilesArtifactNamesAndUuidsRequest(
{
"apicIpAddress": host,
Expand All @@ -48,7 +49,8 @@ export const listFilesArtifactNamesAndUuidsFromEnclaveManager = async (host, por
return enclaveManagerClient.listFilesArtifactNamesAndUuids(request, createHeaderOptionsWithToken(token));
}

export const inspectFilesArtifactContentsFromEnclaveManager = async (host, port, fileName, token) => {
export const inspectFilesArtifactContentsFromEnclaveManager = async (host, port, fileName, token, apiHost) => {
const enclaveManagerClient = createClient(apiHost);
const request = new InspectFilesArtifactContentsRequest(
{
"apicIpAddress": host,
Expand All @@ -62,7 +64,8 @@ export const inspectFilesArtifactContentsFromEnclaveManager = async (host, port,
}


export const createEnclaveFromEnclaveManager = async (enclaveName, logLevel, versionTag, token) => {
export const createEnclaveFromEnclaveManager = async (enclaveName, logLevel, versionTag, token, apiHost) => {
const enclaveManagerClient = createClient(apiHost);
const request = new CreateEnclaveArgs(
{
"enclaveName": enclaveName,
Expand All @@ -73,8 +76,8 @@ export const createEnclaveFromEnclaveManager = async (enclaveName, logLevel, ver
return enclaveManagerClient.createEnclave(request, createHeaderOptionsWithToken(token));
}

export const runStarlarkPackageFromEnclaveManager = async (host, port, packageId, args, token) => {

export const runStarlarkPackageFromEnclaveManager = async (host, port, packageId, args, token, apiHost) => {
const enclaveManagerClient = createClient(apiHost);
const request = new RunStarlarkPackageArgs(
{
"dryRun": false,
Expand All @@ -83,7 +86,5 @@ export const runStarlarkPackageFromEnclaveManager = async (host, port, packageI
"serializedParams": args,
}
)


return enclaveManagerClient.runStarlarkPackage(request, createHeaderOptionsWithToken(token));
}
24 changes: 7 additions & 17 deletions engine/frontend/src/api/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,19 @@ import {

const TransportProtocolEnum = ["tcp", "sctp", "udp"];

export const runStarlarkPackage = async (host, port, packageId, args, token) => {
return runStarlarkPackageFromEnclaveManager(host, port, packageId, args, token)

// const containerClient = new ApiContainerServicePromiseClient(url);
// const runStarlarkPackageArgs = new RunStarlarkPackageArgs();
//
// runStarlarkPackageArgs.setDryRun(false);
// runStarlarkPackageArgs.setRemote(true);
// runStarlarkPackageArgs.setPackageId(packageId);
// runStarlarkPackageArgs.setSerializedParams(args)
// const stream = containerClient.runStarlarkPackage(runStarlarkPackageArgs, null);
// return stream;x
export const runStarlarkPackage = async (host, port, packageId, args, token, apiHost) => {
return runStarlarkPackageFromEnclaveManager(host, port, packageId, args, token, apiHost)
}

const getDataFromApiContainer = async (request, process, token) => {
const data = await request(token)
return process(data)
}

export const getFileArtifactInfo = async (host, port, fileArtifactName, token) => {
export const getFileArtifactInfo = async (host, port, fileArtifactName, token, apiHost) => {
const makeGetFileArtifactInfo = async () => {
try {
return inspectFilesArtifactContentsFromEnclaveManager(host, port, fileArtifactName, token)
return inspectFilesArtifactContentsFromEnclaveManager(host, port, fileArtifactName, token, apiHost)
} catch (error) {
return {
files: []
Expand Down Expand Up @@ -76,7 +66,7 @@ export const getFileArtifactInfo = async (host, port, fileArtifactName, token) =
return response;
}

export const getEnclaveInformation = async (host, port, token) => {
export const getEnclaveInformation = async (host, port, token, apiHost) => {
if (host === "") {
return {
services: [],
Expand All @@ -86,14 +76,14 @@ export const getEnclaveInformation = async (host, port, token) => {

const makeGetServiceRequest = async () => {
try {
return await getServicesFromEnclaveManager(host, port, token);
return await getServicesFromEnclaveManager(host, port, token, apiHost);
} catch (error) {
return {serviceInfo: []}
}
}

const makeFileArtifactRequest = async () => {
return listFilesArtifactNamesAndUuidsFromEnclaveManager(host, port, token);
return listFilesArtifactNamesAndUuidsFromEnclaveManager(host, port, token, apiHost);
}

const processServiceRequest = (data) => {
Expand Down
44 changes: 9 additions & 35 deletions engine/frontend/src/api/enclave.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,8 @@
//import {EngineServicePromiseClient} from 'kurtosis-sdk/src/engine/kurtosis_engine_rpc_api_bindings/engine_service_grpc_web_pb'
import {runStarlarkPackage} from "./container"
import axios from "axios";
import {createClient, createEnclaveFromEnclaveManager, getEnclavesFromEnclaveManager} from "./api";

import {KurtosisEnclaveManagerServer} from "enclave-manager-sdk/build/kurtosis_enclave_manager_api_connect";

import {createPromiseClient} from "@bufbuild/connect";

import {createConnectTransport,} from "@bufbuild/connect-web";
import {createEnclaveFromEnclaveManager, getEnclavesFromEnclaveManager} from "./api";

const transport = createConnectTransport({
baseUrl: "http://localhost:8081"
})

const enclaveManagerClient = createPromiseClient(KurtosisEnclaveManagerServer, transport);
const ENGINE_URL = "http://localhost:8081"

const createApiPromiseClient = (apiClient) => {
if (apiClient) {
return `http://localhost:${apiClient.grpcPortOnHostMachine}`
}
return "";
}

export const makeRestApiRequest = async (url, data, config) => {
const response = await axios.post(`${ENGINE_URL}/${url}`, data, config)
return response;
}

export const getEnclavesFromKurtosis = async (token) => {
const data = await getEnclavesFromEnclaveManager(token);
export const getEnclavesFromKurtosis = async (token, apiHost) => {
const data = await getEnclavesFromEnclaveManager(token, apiHost);
if ("enclaveInfo" in data) {
return Object.keys(data.enclaveInfo).map(key => {
const enclave = data.enclaveInfo[key]
Expand All @@ -46,11 +19,11 @@ export const getEnclavesFromKurtosis = async (token) => {
return []
}

export const createEnclave = async (token) => {
export const createEnclave = async (token, apiHost) => {
const enclaveName = ""; // TODO We could make this input from the UI
const apiContainerVersionTag = "";
const apiContainerLogLevel = "info";
const response = await createEnclaveFromEnclaveManager(enclaveName, apiContainerLogLevel, apiContainerVersionTag, token)
const response = await createEnclaveFromEnclaveManager(enclaveName, apiContainerLogLevel, apiContainerVersionTag, token, apiHost)

const enclave = response.enclaveInfo;
return {
Expand All @@ -63,7 +36,8 @@ export const createEnclave = async (token) => {
}
}

export const getServiceLogs = async (ctrl, enclaveName, serviceUuid) => {
export const getServiceLogs = async (ctrl, enclaveName, serviceUuid, apiHost) => {
const enclaveManagerClient = createClient(apiHost);
const args = {
"enclaveIdentifier": enclaveName,
"serviceUuidSet": {
Expand All @@ -74,7 +48,7 @@ export const getServiceLogs = async (ctrl, enclaveName, serviceUuid) => {
return enclaveManagerClient.getServiceLogs(args, {signal: ctrl.signal});
}

export const runStarlark = async (host, port, packageId, args, token) => {
const stream = await runStarlarkPackage(host, port, packageId, args, token)
export const runStarlark = async (host, port, packageId, args, token, apiHost) => {
const stream = await runStarlarkPackage(host, port, packageId, args, token, apiHost)
return stream;
}
6 changes: 4 additions & 2 deletions engine/frontend/src/component/CreateEnclave.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import React, { useState} from 'react';
import {CreateEnclaveModal} from "./CreateEnclaveModal";
import {CreateEnclaveView} from "./CreateEnclaveView";
import { Route, Routes, useNavigate } from 'react-router-dom';
import {useAppContext} from "../context/AppState";

const CreateEnclave = ({addEnclave}) => {
const navigate = useNavigate()
const [name, setName] = useState('');
const [args, setArgs] = useState('{}')
const [enclave, setEnlave] = useState(null);
const {appData} = useAppContext()

const handleModalSubmit = (enclave) => {
setEnlave(enclave)
Expand All @@ -18,11 +20,11 @@ const CreateEnclave = ({addEnclave}) => {
return (
<div className='h-full w-full flex'>
<Routes>
<Route path="/create" element={<CreateEnclaveModal addEnclave={addEnclave} name={name} setName={setName} args={args} setArgs={setArgs} handleSubmit={handleModalSubmit}/>}/>
<Route path="/create" element={<CreateEnclaveModal addEnclave={addEnclave} name={name} setName={setName} args={args} setArgs={setArgs} handleSubmit={handleModalSubmit} apiHost={appData.apiHost}/>}/>
<Route path="/progress" element={<CreateEnclaveView args={args} packageId={name} enclave={enclave}/>} />
</Routes>
</div>
)
}

export default CreateEnclave;
export default CreateEnclave;