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

new: introduce asset artifact type #309

Merged
merged 13 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ Currently, *falcoctl* supports only two types of artifacts: **plugin** and **rul
* `--annotation-source`: set annotation source for the artifact;
* `--depends-on`: set an artifact dependency (can be specified multiple times). Example: `--depends-on my-plugin:1.2.3`
* `--tag`: additional artifact tag. Can be repeated multiple time
* `--type`: type of artifact to be pushed. Allowed values: `rulesfile`, `plugin`
* `--type`: type of artifact to be pushed. Allowed values: `rulesfile`, `plugin`, `asset`

### Falcoctl registry pull
Pulling **artifacts** involves specifying the reference. The type of **artifact** is not required since the tool will implicitly extract it from the OCI **artifact**:
Expand Down
39 changes: 24 additions & 15 deletions cmd/artifact/follow/follow.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ Example - Install and follow "cloudtrail" plugins using a fully qualified refere
type artifactFollowOptions struct {
*options.Common
*options.Registry
rulesfilesDir string
pluginsDir string
*options.Directory
tmpDir string
every time.Duration
cron string
Expand All @@ -101,6 +100,7 @@ func NewArtifactFollowCmd(ctx context.Context, opt *options.Common) *cobra.Comma
o := artifactFollowOptions{
Common: opt,
Registry: &options.Registry{},
Directory: &options.Directory{},
closeChan: make(chan bool),
versions: config.FalcoVersions{},
}
Expand Down Expand Up @@ -147,26 +147,38 @@ func NewArtifactFollowCmd(ctx context.Context, opt *options.Common) *cobra.Comma
}

// Override "rulesfiles-dir" flag with viper config if not set by user.
f = cmd.Flags().Lookup(install.FlagRulesFilesDir)
f = cmd.Flags().Lookup(options.FlagRulesFilesDir)
if f == nil {
// should never happen
return fmt.Errorf("unable to retrieve flag %q", install.FlagRulesFilesDir)
return fmt.Errorf("unable to retrieve flag %q", options.FlagRulesFilesDir)
} else if !f.Changed && viper.IsSet(config.ArtifactFollowRulesfilesDirKey) {
val := viper.Get(config.ArtifactFollowRulesfilesDirKey)
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
return fmt.Errorf("unable to overwrite %q flag: %w", install.FlagRulesFilesDir, err)
return fmt.Errorf("unable to overwrite %q flag: %w", options.FlagRulesFilesDir, err)
}
}

// Override "plugins-dir" flag with viper config if not set by user.
f = cmd.Flags().Lookup(install.FlagPluginsFilesDir)
f = cmd.Flags().Lookup(options.FlagPluginsFilesDir)
if f == nil {
// should never happen
return fmt.Errorf("unable to retrieve flag %q", install.FlagPluginsFilesDir)
return fmt.Errorf("unable to retrieve flag %q", options.FlagPluginsFilesDir)
} else if !f.Changed && viper.IsSet(config.ArtifactFollowPluginsDirKey) {
val := viper.Get(config.ArtifactFollowPluginsDirKey)
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
return fmt.Errorf("unable to overwrite %q flag: %w", install.FlagPluginsFilesDir, err)
return fmt.Errorf("unable to overwrite %q flag: %w", options.FlagPluginsFilesDir, err)
}
}

// Override "assets-dir" flag with viper config if not set by user.
f = cmd.Flags().Lookup(options.FlagAssetsFilesDir)
if f == nil {
// should never happen
return fmt.Errorf("unable to retrieve flag %q", options.FlagAssetsFilesDir)
} else if !f.Changed && viper.IsSet(config.ArtifactFollowAssetsDirKey) {
val := viper.Get(config.ArtifactFollowAssetsDirKey)
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
return fmt.Errorf("unable to overwrite %q flag: %w", options.FlagAssetsFilesDir, err)
}
}

Expand Down Expand Up @@ -222,15 +234,11 @@ func NewArtifactFollowCmd(ctx context.Context, opt *options.Common) *cobra.Comma
}

o.Registry.AddFlags(cmd)
o.Directory.AddFlags(cmd)
cmd.Flags().DurationVarP(&o.every, "every", "e", config.FollowResync, "Time interval how often it checks for a new version of the "+
"artifact. Cannot be used together with 'cron' option.")
cmd.Flags().StringVar(&o.cron, "cron", "", "Cron-like string to specify interval how often it checks for a new version of the artifact."+
" Cannot be used together with 'every' option.")
// TODO (alacuku): move it in a dedicate data structure since they are in common with artifactInstall command.
cmd.Flags().StringVarP(&o.rulesfilesDir, install.FlagRulesFilesDir, "", config.RulesfilesDir,
"Directory where to install rules")
cmd.Flags().StringVarP(&o.pluginsDir, install.FlagPluginsFilesDir, "", config.PluginsDir,
"Directory where to install plugins")
cmd.Flags().StringVar(&o.tmpDir, "tmp-dir", "", "Directory where to save temporary files")
cmd.Flags().StringVar(&o.falcoVersions, "falco-versions", "http://localhost:8765/versions",
"Where to retrieve versions, it can be either an URL or a path to a file")
Expand Down Expand Up @@ -298,8 +306,9 @@ func (o *artifactFollowOptions) RunArtifactFollow(ctx context.Context, args []st
cfg := &follower.Config{
WaitGroup: &wg,
Resync: sched,
RulesfilesDir: o.rulesfilesDir,
PluginsDir: o.pluginsDir,
RulesfilesDir: o.RulesfilesDir,
PluginsDir: o.PluginsDir,
AssetsDir: o.AssetsDir,
ArtifactReference: ref,
PlainHTTP: o.PlainHTTP,
CloseChan: o.closeChan,
Expand Down
7 changes: 0 additions & 7 deletions cmd/artifact/install/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@
package install

const (

// FlagRulesFilesDir is the name of the flag to specify the directory path of the rules files.
FlagRulesFilesDir = "rulesfiles-dir"

// FlagPluginsFilesDir is the name of the flag to specify the directory path of the plugins.
FlagPluginsFilesDir = "plugins-dir"

// FlagAllowedTypes is the name of the flag to specify allowed artifact types.
FlagAllowedTypes = "allowed-types"

Expand Down
51 changes: 32 additions & 19 deletions cmd/artifact/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,18 @@ Example - Install "cloudtrail" plugins using a fully qualified reference:
type artifactInstallOptions struct {
*options.Common
*options.Registry
rulesfilesDir string
pluginsDir string
allowedTypes oci.ArtifactTypeSlice
resolveDeps bool
noVerify bool
*options.Directory
allowedTypes oci.ArtifactTypeSlice
resolveDeps bool
noVerify bool
}

// NewArtifactInstallCmd returns the artifact install command.
func NewArtifactInstallCmd(ctx context.Context, opt *options.Common) *cobra.Command {
o := artifactInstallOptions{
Common: opt,
Registry: &options.Registry{},
Common: opt,
Registry: &options.Registry{},
Directory: &options.Directory{},
}

cmd := &cobra.Command{
Expand All @@ -93,26 +93,38 @@ func NewArtifactInstallCmd(ctx context.Context, opt *options.Common) *cobra.Comm
Long: longInstall,
PreRunE: func(cmd *cobra.Command, args []string) error {
// Override "rulesfiles-dir" flag with viper config if not set by user.
f := cmd.Flags().Lookup(FlagRulesFilesDir)
f := cmd.Flags().Lookup(options.FlagRulesFilesDir)
if f == nil {
// should never happen
return fmt.Errorf("unable to retrieve flag %q", FlagRulesFilesDir)
return fmt.Errorf("unable to retrieve flag %q", options.FlagRulesFilesDir)
} else if !f.Changed && viper.IsSet(config.ArtifactInstallRulesfilesDirKey) {
val := viper.Get(config.ArtifactInstallRulesfilesDirKey)
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
return fmt.Errorf("unable to overwrite %q flag: %w", FlagRulesFilesDir, err)
return fmt.Errorf("unable to overwrite %q flag: %w", options.FlagRulesFilesDir, err)
}
}

// Override "plugins-dir" flag with viper config if not set by user.
f = cmd.Flags().Lookup(FlagPluginsFilesDir)
f = cmd.Flags().Lookup(options.FlagPluginsFilesDir)
if f == nil {
// should never happen
return fmt.Errorf("unable to retrieve flag %q", FlagPluginsFilesDir)
return fmt.Errorf("unable to retrieve flag %q", options.FlagPluginsFilesDir)
} else if !f.Changed && viper.IsSet(config.ArtifactInstallPluginsDirKey) {
val := viper.Get(config.ArtifactInstallPluginsDirKey)
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
return fmt.Errorf("unable to overwrite %q flag: %w", FlagPluginsFilesDir, err)
return fmt.Errorf("unable to overwrite %q flag: %w", options.FlagPluginsFilesDir, err)
}
}

// Override "assets-dir" flag with viper config if not set by user.
f = cmd.Flags().Lookup(options.FlagAssetsFilesDir)
if f == nil {
// should never happen
return fmt.Errorf("unable to retrieve flag %q", options.FlagAssetsFilesDir)
} else if !f.Changed && viper.IsSet(config.ArtifactFollowAssetsDirKey) {
val := viper.Get(config.ArtifactFollowAssetsDirKey)
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
return fmt.Errorf("unable to overwrite %q flag: %w", options.FlagAssetsFilesDir, err)
}
}

Expand Down Expand Up @@ -161,10 +173,7 @@ func NewArtifactInstallCmd(ctx context.Context, opt *options.Common) *cobra.Comm
}

o.Registry.AddFlags(cmd)
cmd.Flags().StringVarP(&o.rulesfilesDir, FlagRulesFilesDir, "", config.RulesfilesDir,
"directory where to install rules.")
cmd.Flags().StringVarP(&o.pluginsDir, FlagPluginsFilesDir, "", config.PluginsDir,
"directory where to install plugins.")
o.Directory.AddFlags(cmd)
cmd.Flags().Var(&o.allowedTypes, FlagAllowedTypes,
fmt.Sprintf(`list of artifact types that can be installed. If not specified or configured, all types are allowed.
It accepts comma separated values or it can be repeated multiple times.
Expand Down Expand Up @@ -300,9 +309,13 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
var destDir string
switch result.Type {
case oci.Plugin:
destDir = o.pluginsDir
destDir = o.PluginsDir
case oci.Rulesfile:
destDir = o.rulesfilesDir
destDir = o.RulesfilesDir
case oci.Asset:
destDir = o.AssetsDir
default:
return fmt.Errorf("unrecognized result type %q while pulling artifact", result.Type)
}

// Check if directory exists and is writable.
Expand Down
2 changes: 1 addition & 1 deletion cmd/artifact/list/artifact_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func NewArtifactListCmd(ctx context.Context, opt *options.Common) *cobra.Command
},
}

cmd.Flags().Var(&o.artifactType, "type", `Only list artifacts with a specific type. Allowed values: "rulesfile", "plugin""`)
cmd.Flags().Var(&o.artifactType, "type", `Only list artifacts with a specific type. Allowed values: "rulesfile", "plugin", "asset"`)
cmd.Flags().StringVar(&o.index, "index", "", "Only display artifacts from a configured index")

return cmd
Expand Down
2 changes: 1 addition & 1 deletion cmd/artifact/search/artifact_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func NewArtifactSearchCmd(ctx context.Context, opt *options.Common) *cobra.Comma
cmd.Flags().Float64VarP(&o.minScore, "min-score", "", defaultMinScore,
"the minimum score used to match artifact names with search keywords")

cmd.Flags().Var(&o.artifactType, "type", `Only search artifacts with a specific type. Allowed values: "rulesfile", "plugin""`)
cmd.Flags().Var(&o.artifactType, "type", `Only search artifacts with a specific type. Allowed values: "rulesfile", "plugin", "asset"`)

return cmd
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/registry/push/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
opts = append(opts, ocipusher.WithFilepathsAndPlatforms(paths, o.Platforms))
case oci.Rulesfile:
opts = append(opts, ocipusher.WithFilepaths(paths))
case oci.Asset:
opts = append(opts, ocipusher.WithFilepaths(paths))
}

res, err := pusher.Push(ctx, o.ArtifactType, ref, opts...)
Expand Down
13 changes: 7 additions & 6 deletions cmd/registry/push/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Flags:
--platform stringArray os and architecture of the artifact in OS/ARCH format (only for plugins artifacts)
-r, --requires stringArray set an artifact requirement (can be specified multiple times). Example: "--requires plugin_api_version:1.2.3"
-t, --tag stringArray additional artifact tag. Can be repeated multiple times
--type ArtifactType type of artifact to be pushed. Allowed values: "rulesfile", "plugin" (default )
--type ArtifactType type of artifact to be pushed. Allowed values: "rulesfile", "plugin", "asset" (default )
--version string set the version of the artifact

Global Flags:
Expand Down Expand Up @@ -99,7 +99,7 @@ Flags:
--platform stringArray os and architecture of the artifact in OS/ARCH format (only for plugins artifacts)
-r, --requires stringArray set an artifact requirement (can be specified multiple times). Example: "--requires plugin_api_version:1.2.3"
-t, --tag stringArray additional artifact tag. Can be repeated multiple times
--type ArtifactType type of artifact to be pushed. Allowed values: "rulesfile", "plugin"
--type ArtifactType type of artifact to be pushed. Allowed values: "rulesfile", "plugin", "asset"
loresuso marked this conversation as resolved.
Show resolved Hide resolved
--version string set the version of the artifact

Global Flags:
Expand Down Expand Up @@ -192,10 +192,10 @@ var registryPushTests = Describe("push", func() {

When("multiple rulesfiles", func() {
BeforeEach(func() {
args = []string{registryCmd, pushCmd, rulesRepo, "--config", configFile, rulesfiletgz, rulesfiletgz,
"--type", "rulesfile", "--version", "1.1.1", "--plain-http"}
args = []string{registryCmd, pushCmd, "--config", configFile,
"--type", "rulesfile", "--version", "1.1.1", "--plain-http", rulesRepo, rulesfiletgz, rulesfiletgz}
})
pushAssertFailedBehavior(registryPushUsage, "ERROR expecting 1 rulesfile object received 2: invalid number of rulesfiles")
pushAssertFailedBehavior(registryPushUsage, "ERROR expecting 1 rulesfile object, received 2: invalid number of rulesfiles")
})

When("unreachable registry", func() {
Expand Down Expand Up @@ -253,7 +253,8 @@ var registryPushTests = Describe("push", func() {
args = []string{registryCmd, pushCmd, pluginsRepo, pluginsRepo, "--config", configFile,
"--type", "wrongType", "--version", "1.1.1", "--plain-http"}
})
pushAssertFailedBehavior(registryPushUsage, "ERROR invalid argument \"wrongType\" for \"--type\" flag: must be one of \"rulesfile\", \"plugin\"")
pushAssertFailedBehavior(registryPushUsage, "ERROR invalid argument \"wrongType\" for \"--type\" "+
"flag: must be one of \"rulesfile\", \"plugin\", \"asset")
})
})

Expand Down
16 changes: 14 additions & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,15 @@ const (
PluginsDir = "/usr/share/falco/plugins"
// RulesfilesDir default path where rulesfiles are installed.
RulesfilesDir = "/etc/falco"
// AssetsDir default path where assets are installed.
AssetsDir = "/etc/falco/assets"
// FollowResync time interval how often it checks for newer version of the artifact.
// Default values is set every 24 hours.
FollowResync = time.Hour * 24

//
// Viper configuration keys.
//

// RegistryCredentialConfigKey is the Viper key for the credentials store path configuration.
//#nosec G101 -- false positive
Expand All @@ -84,8 +88,10 @@ const (
RegistryAuthBasicKey = "registry.auth.basic"
// RegistryAuthGcpKey is the Viper key for gcp authentication configuration.
RegistryAuthGcpKey = "registry.auth.gcp"

// IndexesKey is the Viper key for indexes configuration.
IndexesKey = "indexes"

// ArtifactFollowEveryKey is the Viper key for follower "every" configuration.
ArtifactFollowEveryKey = "artifact.follow.every"
// ArtifactFollowCronKey is the Viper key for follower "cron" configuration.
Expand All @@ -98,16 +104,22 @@ const (
ArtifactFollowRulesfilesDirKey = "artifact.follow.rulesfilesdir"
// ArtifactFollowPluginsDirKey is the Viper key for follower "pluginsDir" configuration.
ArtifactFollowPluginsDirKey = "artifact.follow.pluginsdir"
// ArtifactFollowAssetsDirKey is the Viper key for follower "pluginsDir" configuration.
ArtifactFollowAssetsDirKey = "artifact.follow.assetsdir"
// ArtifactFollowTmpDirKey is the Viper key for follower "pluginsDir" configuration.
ArtifactFollowTmpDirKey = "artifact.follow.tmpdir"

// ArtifactInstallArtifactsKey is the Viper key for installer "artifacts" configuration.
ArtifactInstallArtifactsKey = "artifact.install.refs"
// ArtifactInstallRulesfilesDirKey is the Viper key for follower "rulesFilesDir" configuration.
// ArtifactInstallRulesfilesDirKey is the Viper key for installer "rulesFilesDir" configuration.
ArtifactInstallRulesfilesDirKey = "artifact.install.rulesfilesdir"
// ArtifactInstallPluginsDirKey is the Viper key for follower "pluginsDir" configuration.
// ArtifactInstallPluginsDirKey is the Viper key for installer "pluginsDir" configuration.
ArtifactInstallPluginsDirKey = "artifact.install.pluginsdir"
// ArtifactInstallAssetsDirKey is the Viper key for installer "pluginsDir" configuration.
ArtifactInstallAssetsDirKey = "artifact.install.assetsdir"
// ArtifactInstallResolveDepsKey is the Viper key for installer "resolveDeps" configuration.
ArtifactInstallResolveDepsKey = "artifact.install.resolveDeps"

// ArtifactAllowedTypesKey is the Viper key for the whitelist of artifacts to be installed in the system.
ArtifactAllowedTypesKey = "artifact.allowedTypes"
// ArtifactNoVerifyKey is the Viper key for skipping signature verification.
Expand Down
8 changes: 6 additions & 2 deletions internal/follower/follower.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ type Config struct {
CloseChan <-chan bool
// Resync time after which periodically it checks for new a new version.
Resync cron.Schedule
// RulesfileDir directory where the rulesfile are stored.
// RulesfilesDir directory where the rulesfile are stored.
RulesfilesDir string
// PluginsDir directory where the plugins are stored.
// PluginsDir directory where plugins are stored.
PluginsDir string
// AssetsDir directory where assets are stored.
AssetsDir string
// ArtifactReference reference to the artifact in a remote repository.
ArtifactReference string
// PlainHTTP is set to true if all registry interaction must be in plain http.
Expand Down Expand Up @@ -311,6 +313,8 @@ func (f *Follower) destinationDir(res *oci.RegistryResult) string {
dir = f.PluginsDir
case oci.Rulesfile:
dir = f.RulesfilesDir
case oci.Asset:
dir = f.AssetsDir
}
return dir
}
Expand Down
Loading