Skip to content
This repository was archived by the owner on Jan 12, 2023. It is now read-only.
Open
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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ FROM scratch
EXPOSE 8443/tcp
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /build/k-rail /k-rail
COPY --from=builder /build/k-rail-check /k-rail-check
USER 65534
ENTRYPOINT ["/k-rail", "-config", "/config/config.yml"]
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ ensure:

build:
protoc -I plugins/proto/ plugins/proto/plugin.proto --go_out=plugins=grpc:plugins/proto
CGO_ENABLED=0 go build -o k-rail cmd/main.go
CGO_ENABLED=0 go build -o k-rail cmd/k-rail/main.go
CGO_ENABLED=0 go build -o k-rail-check cmd/k-rail-check/main.go
CGO_ENABLED=0 go build -o plugin plugins/examples/example.go

test:
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ k-rail is a workload policy enforcement tool for Kubernetes. It can help you sec
- [Policies are enabled, but are not triggering when they should](#policies-are-enabled-but-are-not-triggering-when-they-should)
- [Policies are enabled, but a deployment is blocked and an exemption is needed](#policies-are-enabled-but-a-deployment-is-blocked-and-an-exemption-is-needed)
- [Checking the mTLS certificate expiration](#checking-the-mtls-certificate-expiration)
- [CLI tool](#cli-tool)
- [License](#license)

# Why k-rail?
Expand Down Expand Up @@ -704,6 +705,13 @@ $ kubectl get secret --namespace k-rail k-rail-cert -o json | jq -r '.data["cert
Subject: CN = k-rail.k-rail.svc
```

# CLI Tool
Policies can be checked from CI/CD without interacting with k8s clusters by using CLI tool `k-rail-check`.
It has the same flags as k-rail and can use the same configuration file.
The tool will try to parse any `.yaml` or `.yml` in directory supplied as an argument and output check results in text format.
You can also supply file to validate as an argument.
It will exit with non-zero exit code if it finds any policy violation.

# License

Copyright (c) 2019-present, Cruise LLC
Expand Down
9 changes: 9 additions & 0 deletions cmd/k-rail-check/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

import (
"github.com/cruise-automation/k-rail/v3/server"
)

func main() {
server.Check()
}
File renamed without changes.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/cruise-automation/k-rail/v3
go 1.16

require (
github.com/gertd/go-pluralize v0.2.0
github.com/gobwas/glob v0.2.3
github.com/golang/protobuf v1.5.1 // indirect
github.com/gorilla/mux v1.8.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gertd/go-pluralize v0.2.0 h1:VzWNnxkUo3wkW2Nmp+3ieHSTQQ0LBHeSVxlKsQPQ+UY=
github.com/gertd/go-pluralize v0.2.0/go.mod h1:4ouO1Ndf/r7sZMorwp4Sbfw80lUni+sd+o3qJR8L9To=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
Expand Down
16 changes: 8 additions & 8 deletions plugins/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
type Plugin struct {
name string
policyNames []string
client plugin.Client
client *plugin.Client
kRailPlugin KRailPlugin
}

Expand All @@ -44,10 +44,10 @@ func (p *Plugin) Validate(policyName string, ar *admissionv1.AdmissionRequest) (
// PluginPolicy implements the server.Policy interface
type PluginPolicy struct {
name string
plugin Plugin
plugin *Plugin
}

func NewPluginPolicy(name string, plugin Plugin) PluginPolicy {
func NewPluginPolicy(name string, plugin *Plugin) PluginPolicy {
return PluginPolicy{name: name, plugin: plugin}
}

Expand Down Expand Up @@ -95,20 +95,20 @@ func (p *KRailGRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBro
return &GRPCClient{client: proto.NewKRailPluginClient(c)}, nil
}

func PluginsFromDirectory(directory string) ([]Plugin, error) {
func PluginsFromDirectory(directory string) ([]*Plugin, error) {
binaries, err := filepath.Glob(directory)
if err != nil {
return []Plugin{}, err
return []*Plugin{}, err
}

pluginClients := []Plugin{}
pluginClients := []*Plugin{}
for _, binary := range binaries {
pluginClient, err := LaunchPluginProcess(binary)
if err != nil {
return pluginClients, err

}
pluginClients = append(pluginClients, *pluginClient)
pluginClients = append(pluginClients, pluginClient)
}
return pluginClients, nil
}
Expand Down Expand Up @@ -160,7 +160,7 @@ func LaunchPluginProcess(binaryPath string) (*Plugin, error) {
return &Plugin{
name: pluginName,
policyNames: policyNames,
client: *client,
client: client,
kRailPlugin: krailPlugin,
}, nil
}
118 changes: 118 additions & 0 deletions server/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package server

import (
"flag"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"

log "github.com/sirupsen/logrus"
admissionv1 "k8s.io/api/admission/v1"
)

var Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [OPTIONS] FILE|DIRECTORY ...\n", os.Args[0])
flag.PrintDefaults()
}

func Check() {
log.SetLevel(log.InfoLevel)

cfg := Config{}

flag.Usage = Usage
configPath, exemptionsPathGlob, pluginsPathGlob := parseFlags()
if flag.NArg() == 0 {
flag.Usage()
os.Exit(1)
}

err := cfg.load(configPath)
if err != nil {
log.Fatal(err)
}

exemptions, err := loadExemptions(exemptionsPathGlob)
if err != nil {
log.Fatal(err)
}

loadedPlugins, err := loadPlugins(pluginsPathGlob, cfg)
defer func() {
for _, plugin := range loadedPlugins {
plugin.Kill()
}
}()
if err != nil {
log.Fatal(err)
}

srv := Server{
Config: cfg,
Exemptions: exemptions,
Plugins: loadedPlugins,
}

srv.registerPolicies()

inputFile := flag.Arg(0)
stat, err := os.Stat(inputFile)
if err != nil {
log.Fatalf("cannot stat %s: %s", inputFile, err)
}

allowed := true

results := make(map[string][]admissionv1.AdmissionReview)

if stat.IsDir() {
err := filepath.Walk(inputFile, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
ext := filepath.Ext(path)
if ext == ".yml" || ext == ".yaml" {
reviews, err := srv.validateFile(path)
if err != nil {
log.Errorf("error validating %s: %s", path, err)
allowed = false
}
results[path] = reviews
}
}
return nil
})
if err != nil {
log.Errorf("error walking %s: %s", inputFile, err)
allowed = false
}
} else {
reviews, err := srv.validateFile(inputFile)
if err != nil {
log.Errorf("error validating %s: %s", inputFile, err)
allowed = false
}
results[inputFile] = reviews
}

for filename, reviews := range results {
for _, review := range reviews {
if !review.Response.Allowed {
allowed = false
// validateFile can be refactored to return some struct to avoid parsing response message
for _, violation := range strings.Split(review.Response.Result.Message, "\n") {
if violation != "" {
fmt.Printf("FAIL - %s - %s - %s\n", filename, review.Request.Name, violation)
}
}
}
}
}

if !allowed {
os.Exit(1)
}
}
27 changes: 27 additions & 0 deletions server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
package server

import (
"fmt"
"io/ioutil"

"github.com/cruise-automation/k-rail/v3/policies"
log "github.com/sirupsen/logrus"
"sigs.k8s.io/yaml"
)

type PolicySettings struct {
Expand All @@ -36,3 +41,25 @@ type Config struct {
PolicyConfig policies.Config `json:"policy_config"`
PluginConfig map[string]interface{} `json:"plugin_config"`
}

func (cfg *Config) load(configPath string) error {
yamlFile, err := ioutil.ReadFile(configPath)
if err != nil {
return fmt.Errorf("error loading yaml config: %s", err)
}
err = yaml.Unmarshal(yamlFile, &cfg)
if err != nil {
return fmt.Errorf("error unmarshalling yaml config: %s", err)
}

if len(cfg.LogLevel) == 0 {
log.SetLevel(log.InfoLevel)
} else {
level, err := log.ParseLevel(cfg.LogLevel)
if err != nil {
return fmt.Errorf("invalid log level set: %s", err)
}
log.SetLevel(level)
}
return nil
}
Loading