diff --git a/api/client/client.gen.go b/api/client/client.gen.go index 9e49fd317..ddfdc5526 100644 --- a/api/client/client.gen.go +++ b/api/client/client.gen.go @@ -159,6 +159,9 @@ type ClientInterface interface { PutFindingsFindingID(ctx context.Context, findingID FindingID, body PutFindingsFindingIDJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetOpenAPISpec request + GetOpenAPISpec(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetScanConfigs request GetScanConfigs(ctx context.Context, params *GetScanConfigsParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -520,6 +523,18 @@ func (c *Client) PutFindingsFindingID(ctx context.Context, findingID FindingID, return c.Client.Do(req) } +func (c *Client) GetOpenAPISpec(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetOpenAPISpecRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) GetScanConfigs(ctx context.Context, params *GetScanConfigsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetScanConfigsRequest(c.Server, params) if err != nil { @@ -1889,6 +1904,33 @@ func NewPutFindingsFindingIDRequestWithBody(server string, findingID FindingID, return req, nil } +// NewGetOpenAPISpecRequest generates requests for GetOpenAPISpec +func NewGetOpenAPISpecRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/openapi.json") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewGetScanConfigsRequest generates requests for GetScanConfigs func NewGetScanConfigsRequest(server string, params *GetScanConfigsParams) (*http.Request, error) { var err error @@ -2807,6 +2849,9 @@ type ClientWithResponsesInterface interface { PutFindingsFindingIDWithResponse(ctx context.Context, findingID FindingID, body PutFindingsFindingIDJSONRequestBody, reqEditors ...RequestEditorFn) (*PutFindingsFindingIDResponse, error) + // GetOpenAPISpec request + GetOpenAPISpecWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetOpenAPISpecResponse, error) + // GetScanConfigs request GetScanConfigsWithResponse(ctx context.Context, params *GetScanConfigsParams, reqEditors ...RequestEditorFn) (*GetScanConfigsResponse, error) @@ -3278,6 +3323,29 @@ func (r PutFindingsFindingIDResponse) StatusCode() int { return 0 } +type GetOpenAPISpecResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *json.RawMessage + JSONDefault *ApiResponse +} + +// Status returns HTTPResponse.Status +func (r GetOpenAPISpecResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetOpenAPISpecResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type GetScanConfigsResponse struct { Body []byte HTTPResponse *http.Response @@ -3803,6 +3871,15 @@ func (c *ClientWithResponses) PutFindingsFindingIDWithResponse(ctx context.Conte return ParsePutFindingsFindingIDResponse(rsp) } +// GetOpenAPISpecWithResponse request returning *GetOpenAPISpecResponse +func (c *ClientWithResponses) GetOpenAPISpecWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetOpenAPISpecResponse, error) { + rsp, err := c.GetOpenAPISpec(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetOpenAPISpecResponse(rsp) +} + // GetScanConfigsWithResponse request returning *GetScanConfigsResponse func (c *ClientWithResponses) GetScanConfigsWithResponse(ctx context.Context, params *GetScanConfigsParams, reqEditors ...RequestEditorFn) (*GetScanConfigsResponse, error) { rsp, err := c.GetScanConfigs(ctx, params, reqEditors...) @@ -4737,6 +4814,39 @@ func ParsePutFindingsFindingIDResponse(rsp *http.Response) (*PutFindingsFindingI return response, nil } +// ParseGetOpenAPISpecResponse parses an HTTP response from a GetOpenAPISpecWithResponse call +func ParseGetOpenAPISpecResponse(rsp *http.Response) (*GetOpenAPISpecResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetOpenAPISpecResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest json.RawMessage + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest ApiResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + // ParseGetScanConfigsResponse parses an HTTP response from a GetScanConfigsWithResponse call func ParseGetScanConfigsResponse(rsp *http.Response) (*GetScanConfigsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/api/openapi.yaml b/api/openapi.yaml index 33a359d1d..c829a65fc 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -5,6 +5,21 @@ info: version: 1.0.0 paths: + /openapi.json: + get: + summary: Get this OpenAPI spec + operationId: GetOpenAPISpec + responses: + 200: + description: Success + content: + application/json: + schema: + type: string + format: json + default: + $ref: '#/components/responses/UnknownError' + /assets: get: summary: Get assets diff --git a/api/server/server.gen.go b/api/server/server.gen.go index 176b4d504..0e66958fe 100644 --- a/api/server/server.gen.go +++ b/api/server/server.gen.go @@ -72,6 +72,9 @@ type ServerInterface interface { // Update a finding. // (PUT /findings/{findingID}) PutFindingsFindingID(ctx echo.Context, findingID FindingID) error + // Get this OpenAPI spec + // (GET /openapi.json) + GetOpenAPISpec(ctx echo.Context) error // Get all scan configs. // (GET /scanConfigs) GetScanConfigs(ctx echo.Context, params GetScanConfigsParams) error @@ -626,6 +629,15 @@ func (w *ServerInterfaceWrapper) PutFindingsFindingID(ctx echo.Context) error { return err } +// GetOpenAPISpec converts echo context to params. +func (w *ServerInterfaceWrapper) GetOpenAPISpec(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetOpenAPISpec(ctx) + return err +} + // GetScanConfigs converts echo context to params. func (w *ServerInterfaceWrapper) GetScanConfigs(ctx echo.Context) error { var err error @@ -1049,6 +1061,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/findings/:findingID", wrapper.GetFindingsFindingID) router.PATCH(baseURL+"/findings/:findingID", wrapper.PatchFindingsFindingID) router.PUT(baseURL+"/findings/:findingID", wrapper.PutFindingsFindingID) + router.GET(baseURL+"/openapi.json", wrapper.GetOpenAPISpec) router.GET(baseURL+"/scanConfigs", wrapper.GetScanConfigs) router.POST(baseURL+"/scanConfigs", wrapper.PostScanConfigs) router.DELETE(baseURL+"/scanConfigs/:scanConfigID", wrapper.DeleteScanConfigsScanConfigID) @@ -1067,105 +1080,106 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/cRJegdIvn///wAAAP//br+NM/LJAAA=", + "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", + "qXurAMsAAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/installation/aws/VmClarity.cfn b/installation/aws/VmClarity.cfn index ece5e2cbc..3e9d1c976 100644 --- a/installation/aws/VmClarity.cfn +++ b/installation/aws/VmClarity.cfn @@ -398,9 +398,19 @@ Resources: restart_policy: condition: on-failure + swagger-ui: + image: swaggerapi/swagger-ui:v5.3.1 + environment: + CONFIG_URL: /apidocs/swagger-config.json + configs: + - source: swagger_config + target: /usr/share/nginx/html/swagger-config.json + configs: gateway_config: file: ./gateway.conf + swagger_config: + file: ./swagger-config.json - APIServerContainerImage: !If [ APIServerContainerImageOverridden, !Ref APIServerContainerImageOverride, "ghcr.io/openclarity/vmclarity-apiserver:latest" ] OrchestratorContainerImage: !If [ OrchestratorContainerImageOverridden, !Ref OrchestratorContainerImageOverride, "ghcr.io/openclarity/vmclarity-orchestrator:latest" ] @@ -422,6 +432,17 @@ Resources: # Port number for the VMClarity backend server APISERVER_PORT=8888 + "/etc/vmclarity/swagger-config.json": + content: | + { + "urls": [ + { + "name": "VMClarity API", + "url": "/api/openapi.json" + } + ] + } + "/etc/vmclarity/orchestrator.env": content: Fn::Sub: @@ -522,6 +543,7 @@ Resources: server { listen 80; + absolute_redirect off; location / { proxy_pass http://ui/; @@ -532,8 +554,15 @@ Resources: } location /api/ { + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Prefix /api; + proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://apiserver/; } + + location /apidocs/ { + proxy_pass http://swagger-ui:8080/; + } } } mode: "000644" diff --git a/installation/azure/vmclarity-install.sh b/installation/azure/vmclarity-install.sh index b870361b2..6e0ddcef6 100644 --- a/installation/azure/vmclarity-install.sh +++ b/installation/azure/vmclarity-install.sh @@ -277,10 +277,32 @@ services: restart_policy: condition: on-failure + swagger-ui: + image: swaggerapi/swagger-ui:v5.3.1 + environment: + CONFIG_URL: /apidocs/swagger-config.json + configs: + - source: swagger_config + target: /usr/share/nginx/html/swagger-config.json + configs: gateway_config: file: ./gateway.conf + swagger_config: + file: ./swagger-config.json +EOF + +cat << 'EOF' > /etc/vmclarity/swagger-config.json +{ + "urls": [ + { + "name": "VMClarity API", + "url": "/api/openapi.json" + } + ] +} EOF +chmod 644 /etc/vmclarity/swagger-config.json cat << 'EOF' > /etc/vmclarity/uibackend.env ## @@ -336,6 +358,7 @@ http { server { listen 80; + absolute_redirect off; location / { proxy_pass http://ui/; @@ -346,8 +369,15 @@ http { } location /api/ { + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Prefix /api; + proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://apiserver/; } + + location /apidocs/ { + proxy_pass http://swagger-ui:8080/; + } } } EOF diff --git a/installation/azure/vmclarity.json b/installation/azure/vmclarity.json index 11f31c760..5f65e2f93 100644 --- a/installation/azure/vmclarity.json +++ b/installation/azure/vmclarity.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.19.5.34762", - "templateHash": "16573828559844980234" + "templateHash": "11101766410951708815" } }, "parameters": { @@ -556,7 +556,7 @@ "_generator": { "name": "bicep", "version": "0.19.5.34762", - "templateHash": "6010697131505150992" + "templateHash": "587257319525751437" } }, "parameters": { @@ -813,7 +813,7 @@ "AZURE_SCANNER_STORAGE_ACCOUNT_NAME": "[variables('storageAccountName')]", "AZURE_SCANNER_STORAGE_CONTAINER_NAME": "[variables('snapshotContainerName')]" }, - "scriptTemplate": "#!/bin/bash\n\nset -euo pipefail\n\nmkdir -p /etc/vmclarity\nmkdir -p /opt/vmclarity\n\ncat << 'EOF' > /etc/vmclarity/deploy.sh\n#!/bin/bash\nset -euo pipefail\n\n# Install the latest version of docker from the offical\n# docker repository instead of the older version built into\n# ubuntu, so that we can use docker compose v2.\n#\n# To install this we need to add the docker apt repo gpg key\n# to the apt keyring, and then add the apt sources based on\n# our version of ubuntu. Then we can finally apt install all\n# the required docker components.\napt-get update\napt-get install -y ca-certificates curl gnupg\nmkdir -p /etc/apt/keyrings\nchmod 755 /etc/apt/keyrings\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --yes --dearmor -o /etc/apt/keyrings/docker.gpg\nchmod a+r /etc/apt/keyrings/docker.gpg\necho \\\n \"deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \\\n \"$(. /etc/os-release && echo \"$VERSION_CODENAME\")\" stable\" | \\\n sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\napt-get update\napt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n\nif [ \"__DatabaseToUse__\" == \"Postgresql\" ]; then\n # Enable and start/restart postgres\n echo \"COMPOSE_PROFILES=postgres\" >> /etc/vmclarity/service.env\n\n # Configure the VMClarity backend to use the local postgres\n # service\n echo \"DATABASE_DRIVER=POSTGRES\" > /etc/vmclarity/apiserver.env\n echo \"DB_NAME=vmclarity\" >> /etc/vmclarity/apiserver.env\n echo \"DB_USER=vmclarity\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PASS=__PostgresDBPassword__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_HOST=postgres.service\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PORT_NUMBER=5432\" >> /etc/vmclarity/apiserver.env\nelif [ \"__DatabaseToUse__\" == \"External Postgresql\" ]; then\n # Configure the VMClarity backend to use the postgres\n # database configured by the user.\n echo \"DATABASE_DRIVER=POSTGRES\" > /etc/vmclarity/apiserver.env\n echo \"DB_NAME=__ExternalDBName__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_USER=__ExternalDBUsername__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PASS=__ExternalDBPassword__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_HOST=__ExternalDBHost__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PORT_NUMBER=__ExternalDBPort__\" >> /etc/vmclarity/apiserver.env\nelif [ \"__DatabaseToUse__\" == \"SQLite\" ]; then\n # Configure the VMClarity backend to use the SQLite DB\n # driver and configure the storage location so that it\n # persists.\n echo \"DATABASE_DRIVER=LOCAL\" > /etc/vmclarity/apiserver.env\n echo \"LOCAL_DB_PATH=/data/vmclarity.db\" >> /etc/vmclarity/apiserver.env\nfi\n\n# Replace anywhere in the config.env __CONTROLPLANE_HOST__\n# with the local ipv4 IP address of the VMClarity server.\nlocal_ip_address=\"$(curl -s -H Metadata:true --noproxy \"*\" \"http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/privateIpAddress?api-version=2021-02-01&format=text\")\"\nsed -i \"s/__CONTROLPLANE_HOST__/${local_ip_address}/\" /etc/vmclarity/orchestrator.env\n\n# Reload the systemd daemon to ensure that the VMClarity unit\n# has been detected.\nsystemctl daemon-reload\n\n# Create directory required for grype-server\n/usr/bin/mkdir -p /opt/grype-server\n/usr/bin/chown -R 1000:1000 /opt/grype-server\n\n# Create directory required for vmclarity apiserver\n/usr/bin/mkdir -p /opt/vmclarity\n\n# Create directory for exploit db server\n/usr/bin/mkdir -p /opt/exploits\n\n# Create directory for trivy server\n/usr/bin/mkdir -p /opt/trivy-server\n\n# Enable and start/restart VMClarity backend\nsystemctl enable vmclarity.service\nsystemctl restart vmclarity.service\nEOF\nchmod 744 /etc/vmclarity/deploy.sh\n\ncat << 'EOF' > /etc/vmclarity/orchestrator.env\nPROVIDER=Azure\nVMCLARITY_AZURE_SUBSCRIPTION_ID=__AZURE_SUBSCRIPTION_ID__\nVMCLARITY_AZURE_SCANNER_LOCATION=__AZURE_SCANNER_LOCATION__\nVMCLARITY_AZURE_SCANNER_RESOURCE_GROUP=__AZURE_SCANNER_RESOURCE_GROUP__\nVMCLARITY_AZURE_SCANNER_SUBNET_ID=__AZURE_SCANNER_SUBNET_ID__\nVMCLARITY_AZURE_SCANNER_PUBLIC_KEY=__AZURE_SCANNER_PUBLIC_KEY__\nVMCLARITY_AZURE_SCANNER_VM_SIZE=__AZURE_SCANNER_VM_SIZE__\nVMCLARITY_AZURE_SCANNER_IMAGE_PUBLISHER=__AZURE_SCANNER_IMAGE_PUBLISHER__\nVMCLARITY_AZURE_SCANNER_IMAGE_OFFER=__AZURE_SCANNER_IMAGE_OFFER__\nVMCLARITY_AZURE_SCANNER_IMAGE_SKU=__AZURE_SCANNER_IMAGE_SKU__\nVMCLARITY_AZURE_SCANNER_IMAGE_VERSION=__AZURE_SCANNER_IMAGE_VERSION__\nVMCLARITY_AZURE_SCANNER_SECURITY_GROUP=__AZURE_SCANNER_SECURITY_GROUP__\nVMCLARITY_AZURE_SCANNER_STORAGE_ACCOUNT_NAME=__AZURE_SCANNER_STORAGE_ACCOUNT_NAME__\nVMCLARITY_AZURE_SCANNER_STORAGE_CONTAINER_NAME=__AZURE_SCANNER_STORAGE_CONTAINER_NAME__\n\nAPISERVER_HOST=apiserver\nAPISERVER_PORT=8888\nSCANNER_CONTAINER_IMAGE=__ScannerContainerImage__\nSCANNER_VMCLARITY_APISERVER_ADDRESS=http://__CONTROLPLANE_HOST__:8888\nTRIVY_SERVER_ADDRESS=http://__CONTROLPLANE_HOST__:9992\nGRYPE_SERVER_ADDRESS=__CONTROLPLANE_HOST__:9991\nDELETE_JOB_POLICY=__AssetScanDeletePolicy__\nALTERNATIVE_FRESHCLAM_MIRROR_URL=http://__CONTROLPLANE_HOST__:1000/clamav\nEOF\nchmod 644 /etc/vmclarity/orchestrator.env\n\ncat << 'EOF' > /etc/vmclarity/vmclarity.yaml\nversion: '3'\n\nservices:\n apiserver:\n image: __APIServerContainerImage__\n command:\n - run\n - --log-level\n - info\n ports:\n - \"8888:8888\"\n env_file: ./apiserver.env\n volumes:\n - type: bind\n source: /opt/vmclarity\n target: /data\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n orchestrator:\n image: __OrchestratorContainerImage__\n command:\n - run\n - --log-level\n - info\n env_file: ./orchestrator.env\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n ui:\n image: __UIContainerImage__\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n uibackend:\n image: __UIBackendContainerImage__\n command:\n - run\n - --log-level\n - info\n env_file: ./uibackend.env\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n gateway:\n image: nginx\n ports:\n - \"80:80\"\n configs:\n - source: gateway_config\n target: /etc/nginx/nginx.conf\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n exploit-db-server:\n image: __ExploitDBServerContainerImage__\n ports:\n - \"1326:1326\"\n volumes:\n - type: bind\n source: /opt/exploits\n target: /vuls\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n trivy-server:\n image: __TrivyServerContainerImage__\n command:\n - server\n ports:\n - \"9992:9992\"\n env_file: ./trivy-server.env\n volumes:\n - type: bind\n source: /opt/trivy-server\n target: /home/scanner/.cache\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n grype-server:\n image: __GrypeServerContainerImage__\n command:\n - run\n - --log-level\n - warning\n ports:\n - \"9991:9991\"\n env_file: ./grype-server.env\n volumes:\n - type: bind\n source: /opt/grype-server\n target: /opt/grype-server\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n freshclam-mirror:\n image: __FreshclamMirrorContainerImage__\n ports:\n - \"1000:80\"\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n postgresql:\n image: __PostgresqlContainerImage__\n env_file: ./postgres.env\n ports:\n - \"5432:5432\"\n profiles:\n - postgres\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\nconfigs:\n gateway_config:\n file: ./gateway.conf\nEOF\n\ncat << 'EOF' > /etc/vmclarity/uibackend.env\n##\n## UIBackend configuration\n##\n# Host for the VMClarity backend server\nAPISERVER_HOST=apiserver\n# Port number for the VMClarity backend server\nAPISERVER_PORT=8888\nEOF\nchmod 644 /etc/vmclarity/uibackend.env\n\ncat << 'EOF' > /etc/vmclarity/service.env\n# COMPOSE_PROFILES=\nEOF\nchmod 644 /etc/vmclarity/service.env\n\ncat << 'EOF' > /etc/vmclarity/trivy-server.env\nTRIVY_LISTEN=0.0.0.0:9992\nTRIVY_CACHE_DIR=/home/scanner/.cache/trivy\nEOF\nchmod 644 /etc/vmclarity/trivy-server.env\n\ncat << 'EOF' > /etc/vmclarity/grype-server.env\nDB_ROOT_DIR=/opt/grype-server/db\nEOF\nchmod 644 /etc/vmclarity/grype-server.env\n\ncat << 'EOF' > /etc/vmclarity/postgres.env\nPOSTGRESQL_USERNAME=vmclarity\nPOSTGRESQL_PASSWORD=__PostgresDBPassword__\nPOSTGRESQL_DATABASE=vmclarity\nEOF\nchmod 644 /etc/vmclarity/postgres.env\n\ncat << 'EOF' > /etc/vmclarity/gateway.conf\nevents {\n worker_connections 1024;\n}\n\nhttp {\n upstream ui {\n server ui:80;\n }\n\n upstream uibackend {\n server uibackend:8890;\n }\n\n upstream apiserver {\n server apiserver:8888;\n }\n\n server {\n listen 80;\n\n location / {\n proxy_pass http://ui/;\n }\n\n location /ui/api/ {\n proxy_pass http://uibackend/;\n }\n\n location /api/ {\n proxy_pass http://apiserver/;\n }\n }\n}\nEOF\nchmod 644 /etc/vmclarity/gateway.conf\n\ncat << 'EOF' > /lib/systemd/system/vmclarity.service\n[Unit]\nDescription=VmClarity\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nTimeoutStartSec=0\nType=oneshot\nRemainAfterExit=true\nEnvironmentFile=/etc/vmclarity/service.env\nExecStart=/usr/bin/docker compose -p vmclarity -f /etc/vmclarity/vmclarity.yaml up -d --wait --remove-orphans\nExecStop=/usr/bin/docker compose -p vmclarity -f /etc/vmclarity/vmclarity.yaml down\n\n[Install]\nWantedBy=multi-user.target\nEOF\nchmod 644 /lib/systemd/system/vmclarity.service\n\n/etc/vmclarity/deploy.sh\n", + "scriptTemplate": "#!/bin/bash\n\nset -euo pipefail\n\nmkdir -p /etc/vmclarity\nmkdir -p /opt/vmclarity\n\ncat << 'EOF' > /etc/vmclarity/deploy.sh\n#!/bin/bash\nset -euo pipefail\n\n# Install the latest version of docker from the offical\n# docker repository instead of the older version built into\n# ubuntu, so that we can use docker compose v2.\n#\n# To install this we need to add the docker apt repo gpg key\n# to the apt keyring, and then add the apt sources based on\n# our version of ubuntu. Then we can finally apt install all\n# the required docker components.\napt-get update\napt-get install -y ca-certificates curl gnupg\nmkdir -p /etc/apt/keyrings\nchmod 755 /etc/apt/keyrings\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --yes --dearmor -o /etc/apt/keyrings/docker.gpg\nchmod a+r /etc/apt/keyrings/docker.gpg\necho \\\n \"deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \\\n \"$(. /etc/os-release && echo \"$VERSION_CODENAME\")\" stable\" | \\\n sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\napt-get update\napt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n\nif [ \"__DatabaseToUse__\" == \"Postgresql\" ]; then\n # Enable and start/restart postgres\n echo \"COMPOSE_PROFILES=postgres\" >> /etc/vmclarity/service.env\n\n # Configure the VMClarity backend to use the local postgres\n # service\n echo \"DATABASE_DRIVER=POSTGRES\" > /etc/vmclarity/apiserver.env\n echo \"DB_NAME=vmclarity\" >> /etc/vmclarity/apiserver.env\n echo \"DB_USER=vmclarity\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PASS=__PostgresDBPassword__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_HOST=postgres.service\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PORT_NUMBER=5432\" >> /etc/vmclarity/apiserver.env\nelif [ \"__DatabaseToUse__\" == \"External Postgresql\" ]; then\n # Configure the VMClarity backend to use the postgres\n # database configured by the user.\n echo \"DATABASE_DRIVER=POSTGRES\" > /etc/vmclarity/apiserver.env\n echo \"DB_NAME=__ExternalDBName__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_USER=__ExternalDBUsername__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PASS=__ExternalDBPassword__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_HOST=__ExternalDBHost__\" >> /etc/vmclarity/apiserver.env\n echo \"DB_PORT_NUMBER=__ExternalDBPort__\" >> /etc/vmclarity/apiserver.env\nelif [ \"__DatabaseToUse__\" == \"SQLite\" ]; then\n # Configure the VMClarity backend to use the SQLite DB\n # driver and configure the storage location so that it\n # persists.\n echo \"DATABASE_DRIVER=LOCAL\" > /etc/vmclarity/apiserver.env\n echo \"LOCAL_DB_PATH=/data/vmclarity.db\" >> /etc/vmclarity/apiserver.env\nfi\n\n# Replace anywhere in the config.env __CONTROLPLANE_HOST__\n# with the local ipv4 IP address of the VMClarity server.\nlocal_ip_address=\"$(curl -s -H Metadata:true --noproxy \"*\" \"http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/privateIpAddress?api-version=2021-02-01&format=text\")\"\nsed -i \"s/__CONTROLPLANE_HOST__/${local_ip_address}/\" /etc/vmclarity/orchestrator.env\n\n# Reload the systemd daemon to ensure that the VMClarity unit\n# has been detected.\nsystemctl daemon-reload\n\n# Create directory required for grype-server\n/usr/bin/mkdir -p /opt/grype-server\n/usr/bin/chown -R 1000:1000 /opt/grype-server\n\n# Create directory required for vmclarity apiserver\n/usr/bin/mkdir -p /opt/vmclarity\n\n# Create directory for exploit db server\n/usr/bin/mkdir -p /opt/exploits\n\n# Create directory for trivy server\n/usr/bin/mkdir -p /opt/trivy-server\n\n# Enable and start/restart VMClarity backend\nsystemctl enable vmclarity.service\nsystemctl restart vmclarity.service\nEOF\nchmod 744 /etc/vmclarity/deploy.sh\n\ncat << 'EOF' > /etc/vmclarity/orchestrator.env\nPROVIDER=Azure\nVMCLARITY_AZURE_SUBSCRIPTION_ID=__AZURE_SUBSCRIPTION_ID__\nVMCLARITY_AZURE_SCANNER_LOCATION=__AZURE_SCANNER_LOCATION__\nVMCLARITY_AZURE_SCANNER_RESOURCE_GROUP=__AZURE_SCANNER_RESOURCE_GROUP__\nVMCLARITY_AZURE_SCANNER_SUBNET_ID=__AZURE_SCANNER_SUBNET_ID__\nVMCLARITY_AZURE_SCANNER_PUBLIC_KEY=__AZURE_SCANNER_PUBLIC_KEY__\nVMCLARITY_AZURE_SCANNER_VM_SIZE=__AZURE_SCANNER_VM_SIZE__\nVMCLARITY_AZURE_SCANNER_IMAGE_PUBLISHER=__AZURE_SCANNER_IMAGE_PUBLISHER__\nVMCLARITY_AZURE_SCANNER_IMAGE_OFFER=__AZURE_SCANNER_IMAGE_OFFER__\nVMCLARITY_AZURE_SCANNER_IMAGE_SKU=__AZURE_SCANNER_IMAGE_SKU__\nVMCLARITY_AZURE_SCANNER_IMAGE_VERSION=__AZURE_SCANNER_IMAGE_VERSION__\nVMCLARITY_AZURE_SCANNER_SECURITY_GROUP=__AZURE_SCANNER_SECURITY_GROUP__\nVMCLARITY_AZURE_SCANNER_STORAGE_ACCOUNT_NAME=__AZURE_SCANNER_STORAGE_ACCOUNT_NAME__\nVMCLARITY_AZURE_SCANNER_STORAGE_CONTAINER_NAME=__AZURE_SCANNER_STORAGE_CONTAINER_NAME__\n\nAPISERVER_HOST=apiserver\nAPISERVER_PORT=8888\nSCANNER_CONTAINER_IMAGE=__ScannerContainerImage__\nSCANNER_VMCLARITY_APISERVER_ADDRESS=http://__CONTROLPLANE_HOST__:8888\nTRIVY_SERVER_ADDRESS=http://__CONTROLPLANE_HOST__:9992\nGRYPE_SERVER_ADDRESS=__CONTROLPLANE_HOST__:9991\nDELETE_JOB_POLICY=__AssetScanDeletePolicy__\nALTERNATIVE_FRESHCLAM_MIRROR_URL=http://__CONTROLPLANE_HOST__:1000/clamav\nEOF\nchmod 644 /etc/vmclarity/orchestrator.env\n\ncat << 'EOF' > /etc/vmclarity/vmclarity.yaml\nversion: '3'\n\nservices:\n apiserver:\n image: __APIServerContainerImage__\n command:\n - run\n - --log-level\n - info\n ports:\n - \"8888:8888\"\n env_file: ./apiserver.env\n volumes:\n - type: bind\n source: /opt/vmclarity\n target: /data\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n orchestrator:\n image: __OrchestratorContainerImage__\n command:\n - run\n - --log-level\n - info\n env_file: ./orchestrator.env\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n ui:\n image: __UIContainerImage__\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n uibackend:\n image: __UIBackendContainerImage__\n command:\n - run\n - --log-level\n - info\n env_file: ./uibackend.env\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n gateway:\n image: nginx\n ports:\n - \"80:80\"\n configs:\n - source: gateway_config\n target: /etc/nginx/nginx.conf\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n exploit-db-server:\n image: __ExploitDBServerContainerImage__\n ports:\n - \"1326:1326\"\n volumes:\n - type: bind\n source: /opt/exploits\n target: /vuls\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n trivy-server:\n image: __TrivyServerContainerImage__\n command:\n - server\n ports:\n - \"9992:9992\"\n env_file: ./trivy-server.env\n volumes:\n - type: bind\n source: /opt/trivy-server\n target: /home/scanner/.cache\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n grype-server:\n image: __GrypeServerContainerImage__\n command:\n - run\n - --log-level\n - warning\n ports:\n - \"9991:9991\"\n env_file: ./grype-server.env\n volumes:\n - type: bind\n source: /opt/grype-server\n target: /opt/grype-server\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n freshclam-mirror:\n image: __FreshclamMirrorContainerImage__\n ports:\n - \"1000:80\"\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n postgresql:\n image: __PostgresqlContainerImage__\n env_file: ./postgres.env\n ports:\n - \"5432:5432\"\n profiles:\n - postgres\n logging:\n driver: journald\n deploy:\n mode: replicated\n replicas: 1\n restart_policy:\n condition: on-failure\n\n swagger-ui:\n image: swaggerapi/swagger-ui:v5.3.1\n environment:\n CONFIG_URL: /apidocs/swagger-config.json\n configs:\n - source: swagger_config\n target: /usr/share/nginx/html/swagger-config.json\n\nconfigs:\n gateway_config:\n file: ./gateway.conf\n swagger_config:\n file: ./swagger-config.json\nEOF\n\ncat << 'EOF' > /etc/vmclarity/swagger-config.json\n{\n \"urls\": [\n {\n \"name\": \"VMClarity API\",\n \"url\": \"/api/openapi.json\"\n }\n ]\n}\nEOF\nchmod 644 /etc/vmclarity/swagger-config.json\n\ncat << 'EOF' > /etc/vmclarity/uibackend.env\n##\n## UIBackend configuration\n##\n# Host for the VMClarity backend server\nAPISERVER_HOST=apiserver\n# Port number for the VMClarity backend server\nAPISERVER_PORT=8888\nEOF\nchmod 644 /etc/vmclarity/uibackend.env\n\ncat << 'EOF' > /etc/vmclarity/service.env\n# COMPOSE_PROFILES=\nEOF\nchmod 644 /etc/vmclarity/service.env\n\ncat << 'EOF' > /etc/vmclarity/trivy-server.env\nTRIVY_LISTEN=0.0.0.0:9992\nTRIVY_CACHE_DIR=/home/scanner/.cache/trivy\nEOF\nchmod 644 /etc/vmclarity/trivy-server.env\n\ncat << 'EOF' > /etc/vmclarity/grype-server.env\nDB_ROOT_DIR=/opt/grype-server/db\nEOF\nchmod 644 /etc/vmclarity/grype-server.env\n\ncat << 'EOF' > /etc/vmclarity/postgres.env\nPOSTGRESQL_USERNAME=vmclarity\nPOSTGRESQL_PASSWORD=__PostgresDBPassword__\nPOSTGRESQL_DATABASE=vmclarity\nEOF\nchmod 644 /etc/vmclarity/postgres.env\n\ncat << 'EOF' > /etc/vmclarity/gateway.conf\nevents {\n worker_connections 1024;\n}\n\nhttp {\n upstream ui {\n server ui:80;\n }\n\n upstream uibackend {\n server uibackend:8890;\n }\n\n upstream apiserver {\n server apiserver:8888;\n }\n\n server {\n listen 80;\n absolute_redirect off;\n\n location / {\n proxy_pass http://ui/;\n }\n\n location /ui/api/ {\n proxy_pass http://uibackend/;\n }\n\n location /api/ {\n proxy_set_header X-Forwarded-Host $http_host;\n proxy_set_header X-Forwarded-Prefix /api;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_pass http://apiserver/;\n }\n\n location /apidocs/ {\n proxy_pass http://swagger-ui:8080/;\n }\n }\n}\nEOF\nchmod 644 /etc/vmclarity/gateway.conf\n\ncat << 'EOF' > /lib/systemd/system/vmclarity.service\n[Unit]\nDescription=VmClarity\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nTimeoutStartSec=0\nType=oneshot\nRemainAfterExit=true\nEnvironmentFile=/etc/vmclarity/service.env\nExecStart=/usr/bin/docker compose -p vmclarity -f /etc/vmclarity/vmclarity.yaml up -d --wait --remove-orphans\nExecStop=/usr/bin/docker compose -p vmclarity -f /etc/vmclarity/vmclarity.yaml down\n\n[Install]\nWantedBy=multi-user.target\nEOF\nchmod 644 /lib/systemd/system/vmclarity.service\n\n/etc/vmclarity/deploy.sh\n", "renderedScript": "[reduce(items(variables('params')), createObject('value', variables('scriptTemplate')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('__{0}__', lambdaVariables('next').key), lambdaVariables('next').value)))).value]", "osDiskType": "StandardSSD_LRS", "linuxConfiguration": { diff --git a/installation/docker/dockercompose.yml b/installation/docker/dockercompose.yml index 216c5c726..1258b76ab 100644 --- a/installation/docker/dockercompose.yml +++ b/installation/docker/dockercompose.yml @@ -147,9 +147,19 @@ services: restart_policy: condition: on-failure + swagger-ui: + image: swaggerapi/swagger-ui:v5.3.1 + environment: + CONFIG_URL: /apidocs/swagger-config.json + configs: + - source: swagger_config + target: /usr/share/nginx/html/swagger-config.json + configs: gateway_config: file: ./gateway.conf + swagger_config: + file: ./swagger-config.json volumes: apiserver-db-data: diff --git a/installation/docker/gateway.conf b/installation/docker/gateway.conf index 0b689442e..97b59c866 100644 --- a/installation/docker/gateway.conf +++ b/installation/docker/gateway.conf @@ -17,6 +17,7 @@ http { server { listen 80; + absolute_redirect off; location / { proxy_pass http://ui/; @@ -27,7 +28,14 @@ http { } location /api/ { + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Prefix /api; + proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://apiserver/; } + + location /apidocs/ { + proxy_pass http://swagger-ui:8080/; + } } } diff --git a/installation/docker/image_override.env b/installation/docker/image_override.env index 0c4ca25ed..1382db9b9 100644 --- a/installation/docker/image_override.env +++ b/installation/docker/image_override.env @@ -1,7 +1,7 @@ # This is an example of how to override the default set of VMClarity container # images used by docker compose. -APIServerContainerImage=docker.io/tehsmash/vmclarity-apiserver:microservices -OrchestratorContainerImage=docker.io/tehsmash/vmclarity-orchestrator:microservices -UIContainerImage=docker.io/tehsmash/vmclarity-ui:microservices -UIBackendContainerImage=docker.io/tehsmash/vmclarity-uibackend:microservices -ScannerContainerImage=docker.io/tehsmash/vmclarity-cli:microservices +APIServerContainerImage=docker.io/tehsmash/vmclarity-apiserver:swaggerui +OrchestratorContainerImage=docker.io/tehsmash/vmclarity-orchestrator:swaggerui +UIContainerImage=docker.io/tehsmash/vmclarity-ui:swaggerui +UIBackendContainerImage=docker.io/tehsmash/vmclarity-uibackend:swaggerui +ScannerContainerImage=docker.io/tehsmash/vmclarity-cli:swaggerui diff --git a/installation/docker/swagger-config.json b/installation/docker/swagger-config.json new file mode 100644 index 000000000..c6427676f --- /dev/null +++ b/installation/docker/swagger-config.json @@ -0,0 +1,8 @@ +{ + "urls": [ + { + "name": "VMClarity API", + "url": "/api/openapi.json" + } + ] +} diff --git a/installation/gcp/dm/components/vmclarity-install.sh b/installation/gcp/dm/components/vmclarity-install.sh index 0acbca342..26edb6af3 100644 --- a/installation/gcp/dm/components/vmclarity-install.sh +++ b/installation/gcp/dm/components/vmclarity-install.sh @@ -270,10 +270,32 @@ services: restart_policy: condition: on-failure + swagger-ui: + image: swaggerapi/swagger-ui:v5.3.1 + environment: + CONFIG_URL: /apidocs/swagger-config.json + configs: + - source: swagger_config + target: /usr/share/nginx/html/swagger-config.json + configs: gateway_config: file: ./gateway.conf + swagger_config: + file: ./swagger-config.json +EOF + +cat << 'EOF' > /etc/vmclarity/swagger-config.json +{{ + "urls": [ + {{ + "name": "VMClarity API", + "url": "/api/openapi.json" + }} + ] +}} EOF +chmod 644 /etc/vmclarity/swagger-config.json cat << 'EOF' > /etc/vmclarity/uibackend.env ## @@ -329,6 +351,7 @@ http {{ server {{ listen 80; + absolute_redirect off; location / {{ proxy_pass http://ui/; @@ -339,8 +362,15 @@ http {{ }} location /api/ {{ + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Prefix /api; + proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://apiserver/; }} + + location /apidocs/ {{ + proxy_pass http://swagger-ui:8080/; + }} }} }} EOF diff --git a/pkg/apiserver/rest/common.go b/pkg/apiserver/rest/common.go index 3963d3227..dcf21b3c6 100644 --- a/pkg/apiserver/rest/common.go +++ b/pkg/apiserver/rest/common.go @@ -16,10 +16,16 @@ package rest import ( + "fmt" + "net/http" + "net/url" + + "github.com/getkin/kin-openapi/openapi3" "github.com/labstack/echo/v4" log "github.com/sirupsen/logrus" "github.com/openclarity/vmclarity/api/models" + "github.com/openclarity/vmclarity/api/server" ) // nolint:wrapcheck @@ -33,3 +39,43 @@ func sendError(ctx echo.Context, code int, message string) error { func sendResponse(ctx echo.Context, code int, object interface{}) error { return ctx.JSON(code, object) } + +func (s *ServerImpl) GetOpenAPISpec(ctx echo.Context) error { + swagger, err := server.GetSwagger() + if err != nil { + return fmt.Errorf("failed to load swagger spec: %v", err) + } + + // Use the X-Forwarded-* headers to populate the OpenAPI spec with the + // location where the API is being served. Using this trick the + // swagger-ui service dynamically loads the OpenAPI spec from the + // APIServer and knows where to send the TryItNow requests. + // Wherever the API server is accessed from through a proxy or + // sub-domain this will correct the servers entry to match that clients + // access path. + headers := ctx.Request().Header + + log.Debugf("Got headers %#v", headers) + + serverurl := &url.URL{} + if forwardedHost, ok := headers["X-Forwarded-Host"]; ok { + proto := "http" + if forwardedProto, ok := headers["X-Forwarded-Proto"]; ok { + proto = forwardedProto[0] + } + serverurl.Scheme = proto + serverurl.Host = forwardedHost[0] + } + + if forwardedPrefix, ok := headers["X-Forwarded-Prefix"]; ok { + serverurl = serverurl.JoinPath(forwardedPrefix[0]) + } + + if *serverurl != (url.URL{}) { + swagger.AddServer(&openapi3.Server{ + URL: serverurl.String(), + }) + } + + return sendResponse(ctx, http.StatusOK, swagger) +} diff --git a/ui/src/layout/App/app.scss b/ui/src/layout/App/app.scss index 49557df6a..8a1b1a4bf 100644 --- a/ui/src/layout/App/app.scss +++ b/ui/src/layout/App/app.scss @@ -44,6 +44,21 @@ html body { margin-left: 10px; } } + .topbar-menu-items { + display: flex; + flex-grow: 1; + align-items: center; + justify-content: flex-end; + margin-left: 30px; + + .topbar-api-link { + display: flex; + align-items: center; + margin-right: 20px; + color: $color-grey; + text-decoration: none; + } + } } .sidebar-container { background-color: $color-main-dark; diff --git a/ui/src/layout/App/index.js b/ui/src/layout/App/index.js index c43f2ff10..6f3b03439 100644 --- a/ui/src/layout/App/index.js +++ b/ui/src/layout/App/index.js @@ -121,6 +121,9 @@ const Layout = () => { setRefreshTimestamp(Date.now())} /> } +
+ API Docs +
{ @@ -159,4 +162,4 @@ const App = () => (
) -export default App; \ No newline at end of file +export default App;