Files
Sammy Kerata Oina d5badba547
CI / lint (push) Has been cancelled
CI / test (agent) (push) Has been cancelled
CI / test (cli) (push) Has been cancelled
CI / test (cmd) (push) Has been cancelled
CI / test (internal) (push) Has been cancelled
CI / test (manager, true) (push) Has been cancelled
CI / test (pkg) (push) Has been cancelled
CI / upload-coverage (push) Has been cancelled
COCOS-584 - Support multiple kbs (#587)
* feat: Implement per-resource KBS configuration, allowing algorithms and datasets to specify individual KBS URLs.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* refactor: Encapsulate CLI error handling and CVM certificate paths within the CLI struct, and add algorithm type to agent's algorithm structure.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* style: Remove blank lines and fix indentation in CLI commands.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* refactor: Update downloadAndDecryptGenericResource to accept KBS URL as a parameter and adjust related tests

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* refactor: group CLI configuration into structured types and simplify skopeo decryption key handling

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

---------

Signed-off-by: Sammy Oina <sammyoina@gmail.com>
2026-05-05 11:01:56 +02:00

135 lines
4.1 KiB
Go

// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package oci
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
)
const (
// OCICryptKeyproviderConfig is the environment variable for ocicrypt config.
OCICryptKeyproviderConfig = "OCICRYPT_KEYPROVIDER_CONFIG"
// DefaultOCICryptConfig is the default path to ocicrypt config.
DefaultOCICryptConfig = "/etc/ocicrypt_keyprovider.conf"
// DecryptionKeyProvider is the decryption key provider for CoCo.
DecryptionKeyProvider = "provider:attestation-agent:cc_kbc::null"
)
// SkopeoClient wraps skopeo command-line operations.
type SkopeoClient struct {
skopeoPath string
workDir string
}
// NewSkopeoClient creates a new Skopeo client.
func NewSkopeoClient(workDir string) (*SkopeoClient, error) {
// Find skopeo binary
skopeoPath, err := exec.LookPath("skopeo")
if err != nil {
return nil, fmt.Errorf("skopeo not found in PATH: %w", err)
}
// Ensure work directory exists
if err := os.MkdirAll(workDir, 0o755); err != nil {
return nil, fmt.Errorf("failed to create work directory: %w", err)
}
return &SkopeoClient{
skopeoPath: skopeoPath,
workDir: workDir,
}, nil
}
// PullAndDecrypt pulls an OCI image and decrypts it if encrypted.
func (s *SkopeoClient) PullAndDecrypt(ctx context.Context, source ResourceSource, destDir string) error {
// Ensure destination directory exists
if err := os.MkdirAll(destDir, 0o755); err != nil {
return fmt.Errorf("failed to create destination directory: %w", err)
}
args := []string{"copy"}
// Add decryption key if image is encrypted.
// The KBS URL is configured at the CoCo Keyprovider service level
// (via kernel cmdline agent.aa_kbc_params), not via ocicrypt's --decryption-key flag.
if source.Encrypted {
args = append(args, "--decryption-key", DecryptionKeyProvider)
}
// Add insecure policy for testing (TODO: use proper policy in production)
args = append(args, "--insecure-policy", "--src-tls-verify=false", "--dest-tls-verify=false")
// Source and destination
args = append(args, source.URI, "oci:"+destDir)
cmd := exec.CommandContext(ctx, s.skopeoPath, args...)
// Set OCICRYPT environment
cmd.Env = append(os.Environ(),
OCICryptKeyproviderConfig+"="+DefaultOCICryptConfig)
// Set working directory
cmd.Dir = s.workDir
// Capture output
// Debug: Print full command
fmt.Printf("executing skopeo command: %s %v\n", s.skopeoPath, args)
fmt.Printf("skopeo environment: %s\n", OCICryptKeyproviderConfig+"="+DefaultOCICryptConfig)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("skopeo copy failed: %w\nOutput: %s", err, string(output))
}
return nil
}
// Inspect inspects an OCI image and returns basic manifest information.
func (s *SkopeoClient) Inspect(ctx context.Context, imageRef string) (*ImageManifest, error) {
args := []string{"inspect", "--insecure-policy", "--tls-verify=false", imageRef}
cmd := exec.CommandContext(ctx, s.skopeoPath, args...)
cmd.Env = append(os.Environ(),
OCICryptKeyproviderConfig+"="+DefaultOCICryptConfig)
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("skopeo inspect failed: %w\nOutput: %s", err, string(output))
}
// For now, return basic info
// nolint:godox // TODO: Parse JSON output for detailed manifest info
return &ImageManifest{
Reference: imageRef,
}, nil
}
// ToDockerArchive converts an OCI directory to a Docker archive tarball.
func (s *SkopeoClient) ToDockerArchive(ctx context.Context, ociDir, destFile string) error {
args := []string{"copy", "--insecure-policy", "--src-tls-verify=false", "--dest-tls-verify=false", "oci:" + ociDir, "docker-archive:" + destFile}
cmd := exec.CommandContext(ctx, s.skopeoPath, args...)
cmd.Env = append(os.Environ(),
OCICryptKeyproviderConfig+"="+DefaultOCICryptConfig)
cmd.Dir = s.workDir
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("skopeo copy to docker-archive failed: %w\nOutput: %s", err, string(output))
}
return nil
}
// GetLocalImagePath returns the path to a local OCI image directory.
func (s *SkopeoClient) GetLocalImagePath(name string) string {
return filepath.Join(s.workDir, name)
}