mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-22 00:05:24 -06:00
338583c18a
These changes add support for digital signatures for policy bundles which can be used to verify their authenticity. Bundle signature verification involves the following steps: * Verify the JWT signature * Verify the files in the JWT payload exist in the bundle * Verify the file content of the files in bundle match with those in the payload This commit adds a new `sign` command to generate a digital signature for policy bundles. For more details, run "opa sign --help" The signatures generated by the 'sign' command can be verified by the 'build' command. The 'build' command can also sign the bundle it generates. The 'run' command can verify a signed bundle or skip verification altogether. OPA 'sign', 'build' and 'run' can be used to sign/verify bundles in bundle mode (--bundle) mode only. Verification can be also be performed when bundle downloading is enabled. Fixes: #1757 Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
190 lines
5.2 KiB
Go
190 lines
5.2 KiB
Go
// Copyright 2020 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package init is an internal package with helpers for data and policy loading during initialization.
|
|
package init
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/open-policy-agent/opa/ast"
|
|
"github.com/open-policy-agent/opa/bundle"
|
|
storedversion "github.com/open-policy-agent/opa/internal/version"
|
|
"github.com/open-policy-agent/opa/loader"
|
|
"github.com/open-policy-agent/opa/metrics"
|
|
"github.com/open-policy-agent/opa/storage"
|
|
)
|
|
|
|
// InsertAndCompileOptions contains the input for the operation.
|
|
type InsertAndCompileOptions struct {
|
|
Store storage.Store
|
|
Txn storage.Transaction
|
|
Files loader.Result
|
|
Bundles map[string]*bundle.Bundle
|
|
MaxErrors int
|
|
}
|
|
|
|
// InsertAndCompileResult contains the output of the operation.
|
|
type InsertAndCompileResult struct {
|
|
Compiler *ast.Compiler
|
|
Metrics metrics.Metrics
|
|
}
|
|
|
|
// InsertAndCompile writes data and policy into the store and returns a compiler for the
|
|
// store contents.
|
|
func InsertAndCompile(ctx context.Context, opts InsertAndCompileOptions) (*InsertAndCompileResult, error) {
|
|
|
|
if len(opts.Files.Documents) > 0 {
|
|
if err := opts.Store.Write(ctx, opts.Txn, storage.AddOp, storage.Path{}, opts.Files.Documents); err != nil {
|
|
return nil, errors.Wrap(err, "storage error")
|
|
}
|
|
}
|
|
|
|
policies := make(map[string]*ast.Module, len(opts.Files.Modules))
|
|
|
|
for id, parsed := range opts.Files.Modules {
|
|
policies[id] = parsed.Parsed
|
|
}
|
|
|
|
compiler := ast.NewCompiler().SetErrorLimit(opts.MaxErrors).WithPathConflictsCheck(storage.NonEmpty(ctx, opts.Store, opts.Txn))
|
|
m := metrics.New()
|
|
|
|
activation := &bundle.ActivateOpts{
|
|
Ctx: ctx,
|
|
Store: opts.Store,
|
|
Txn: opts.Txn,
|
|
Compiler: compiler,
|
|
Metrics: m,
|
|
Bundles: opts.Bundles,
|
|
ExtraModules: policies,
|
|
}
|
|
|
|
err := bundle.Activate(activation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Policies in bundles will have already been added to the store, but
|
|
// modules loaded outside of bundles will need to be added manually.
|
|
for id, parsed := range opts.Files.Modules {
|
|
if err := opts.Store.UpsertPolicy(ctx, opts.Txn, id, parsed.Raw); err != nil {
|
|
return nil, errors.Wrap(err, "storage error")
|
|
}
|
|
}
|
|
|
|
// Set the version in the store last to prevent data files from overwriting.
|
|
if err := storedversion.Write(ctx, opts.Store, opts.Txn); err != nil {
|
|
return nil, errors.Wrap(err, "storage error")
|
|
}
|
|
|
|
return &InsertAndCompileResult{Compiler: compiler, Metrics: m}, nil
|
|
}
|
|
|
|
// LoadPathsResult contains the output loading a set of paths.
|
|
type LoadPathsResult struct {
|
|
Bundles map[string]*bundle.Bundle
|
|
Files loader.Result
|
|
}
|
|
|
|
// WalkPathsResult contains the output loading a set of paths.
|
|
type WalkPathsResult struct {
|
|
BundlesLoader []BundleLoader
|
|
FileDescriptors []*Descriptor
|
|
}
|
|
|
|
// BundleLoader contains information about files in a bundle
|
|
type BundleLoader struct {
|
|
DirectoryLoader bundle.DirectoryLoader
|
|
IsDir bool
|
|
}
|
|
|
|
// Descriptor contains information about a file
|
|
type Descriptor struct {
|
|
Root string
|
|
Path string
|
|
}
|
|
|
|
// LoadPaths reads data and policy from the given paths and returns a set of bundles or
|
|
// raw loader file results.
|
|
func LoadPaths(paths []string, filter loader.Filter, asBundle bool, bvc *bundle.VerificationConfig, skipVerify bool) (*LoadPathsResult, error) {
|
|
|
|
var result LoadPathsResult
|
|
var err error
|
|
|
|
if asBundle {
|
|
result.Bundles = make(map[string]*bundle.Bundle, len(paths))
|
|
for _, path := range paths {
|
|
result.Bundles[path], err = loader.NewFileLoader().WithBundleVerificationConfig(bvc).
|
|
WithSkipBundleVerification(skipVerify).AsBundle(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &result, nil
|
|
}
|
|
|
|
files, err := loader.NewFileLoader().Filtered(paths, filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result.Files = *files
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
// WalkPaths reads data and policy from the given paths and returns a set of bundle directory loaders
|
|
// or descriptors that contain information about files.
|
|
func WalkPaths(paths []string, filter loader.Filter, asBundle bool) (*WalkPathsResult, error) {
|
|
|
|
var result WalkPathsResult
|
|
|
|
if asBundle {
|
|
result.BundlesLoader = make([]BundleLoader, len(paths))
|
|
for i, path := range paths {
|
|
bundleLoader, isDir, err := loader.GetBundleDirectoryLoader(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result.BundlesLoader[i] = BundleLoader{
|
|
DirectoryLoader: bundleLoader,
|
|
IsDir: isDir,
|
|
}
|
|
}
|
|
return &result, nil
|
|
}
|
|
|
|
result.FileDescriptors = []*Descriptor{}
|
|
for _, path := range paths {
|
|
filePaths, err := loader.FilteredPaths([]string{path}, filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, fp := range filePaths {
|
|
// Trim off the root directory and return path as if chrooted
|
|
cleanedPath := strings.TrimPrefix(fp, path)
|
|
if path == "." && filepath.Base(fp) == bundle.ManifestExt {
|
|
cleanedPath = fp
|
|
}
|
|
|
|
if !strings.HasPrefix(cleanedPath, "/") {
|
|
cleanedPath = "/" + cleanedPath
|
|
}
|
|
|
|
result.FileDescriptors = append(result.FileDescriptors, &Descriptor{
|
|
Root: path,
|
|
Path: cleanedPath,
|
|
})
|
|
}
|
|
}
|
|
|
|
return &result, nil
|
|
}
|