Compare commits

..

11 Commits

Author SHA1 Message Date
RamiBerm
35dbd5fde2 TRA-3860 create main configmap for agent and tappers (#410)
* WIP

* Update options.go and serializable_regexp.go

* Update go.sum, go.sum, and 4 more files...

* Update go.sum, go.sum, and 4 more files...

* Update config.go and serializable_regexp.go

* Update config.go, config.json, and test.go

* Update tapRunner.go and provider.go

* Update provider.go

* Update tapRunner.go and provider.go

* Update config.json and test.go

* Update contract_validation.go, config.go, and 2 more files...

* Update main.go

* Update rulesHTTP.go

* Update config.go, size_enforcer.go, and 5 more files...

* Update config.go and config.go

Co-authored-by: Rami Berman <rami.berman@up9.com>
2021-10-31 15:29:05 +02:00
Igor Gov
2c29903910 Run snyk on all project with ignore current issues (#415) 2021-10-28 21:59:42 +03:00
Igor Gov
f49e29045c Adding Snyk to CI (#414) 2021-10-28 16:37:11 +03:00
Alex Haiut
67593345a7 upgraded base image to alpine:3.14 following snyk vuln report (#412) 2021-10-28 15:00:11 +03:00
RoyUP9
f069fdaae1 changed ask confirmation to be asked only once (#409) 2021-10-28 14:55:33 +03:00
gadotroee
d478a7ec43 TRA-3867 proxy host from config (#411) 2021-10-28 14:53:28 +03:00
gadotroee
af96e55f61 running proxy on 0.0.0.0 (#408) 2021-10-28 11:44:29 +03:00
RoyUP9
e3ead981ec fixed upload entries (#407) 2021-10-28 11:20:45 +03:00
RoyUP9
fbee4454e4 added k8s server version check (#404) 2021-10-27 09:35:30 +03:00
RamiBerm
e9e16551ad TRA-3850 mizu clean command + fix debug deploy (#403)
* debug mizu

* Update clean.go, cleanRunner.go, and tapRunner.go

* Update tapRunner.go

* Update cleanRunner.go

* Update tapRunner.go

Co-authored-by: Rami Berman <rami.berman@up9.com>
2021-10-27 09:28:54 +03:00
David Levanon
e635b97d11 remove duplicated tcp packet source (#405) 2021-10-26 18:16:33 +03:00
31 changed files with 545 additions and 254 deletions

View File

@@ -0,0 +1,25 @@
name: Security validation
on:
pull_request:
branches:
- 'develop'
- 'main'
jobs:
security:
name: Check for vulnerabilities
runs-on: ubuntu-latest
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
steps:
- uses: actions/checkout@v2
- uses: snyk/actions/setup@master
- name: Set up Go 1.16
uses: actions/setup-go@v2
with:
go-version: '1.16'
- name: Run snyl on all projects
run: snyk test --all-projects

View File

@@ -44,7 +44,7 @@ RUN go build -ldflags="-s -w \
COPY devops/build_extensions.sh ..
RUN cd .. && /bin/bash build_extensions.sh
FROM alpine:3.13.5
FROM alpine:3.14
RUN apk add bash libpcap-dev tcpdump
WORKDIR /app

View File

@@ -46,6 +46,10 @@ push-docker: ## Build and publish agent docker image.
@echo "publishing Docker image .. "
devops/build-push-featurebranch.sh
push-docker-debug:
@echo "publishing debug Docker image .. "
devops/build-push-featurebranch-debug.sh
build-docker-ci: ## Build agent docker image for CI.
@echo "building docker image for ci"
devops/build-agent-ci.sh

View File

@@ -171,6 +171,16 @@ against the contracts.
Please see [CONTRACT MONITORING](docs/CONTRACT_MONITORING.md) page for more details and syntax.
### Configure proxy host
By default, mizu will be accessible via local host: 'http://localhost:8899/mizu/', it is possible to change the host,
for instance, to '0.0.0.0' which can grant access via machine IP address.
This setting can be changed via command line flag `--set tap.proxy-host=<value>` or via config file:
tap
proxy-host: 0.0.0.0
and when changed it will support accessing by IP
## How to Run local UI
- run from mizu/agent `go run main.go --hars-read --hars-dir <folder>`

6
agent/.snyk Normal file
View File

@@ -0,0 +1,6 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.14.0
ignore:
SNYK-GOLANG-GITHUBCOMGINGONICGIN-1041736:
- '*':
reason: None Given

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"mizuserver/pkg/api"
"mizuserver/pkg/config"
"mizuserver/pkg/controllers"
"mizuserver/pkg/models"
"mizuserver/pkg/routes"
@@ -44,6 +45,9 @@ func main() {
logLevel := determineLogLevel()
logger.InitLoggerStderrOnly(logLevel)
flag.Parse()
if err := config.LoadConfig(); err != nil {
logger.Log.Fatalf("Error loading config file %v", err)
}
loadExtensions()
if !*tapperMode && !*apiServerMode && !*standaloneMode && !*harsReaderMode {
@@ -313,3 +317,4 @@ func determineLogLevel() (logLevel logging.Level) {
}
return
}

View File

@@ -24,7 +24,7 @@ const (
)
func loadOAS(ctx context.Context) (doc *openapi3.T, contractContent string, router routers.Router, err error) {
path := fmt.Sprintf("%s/%s", shared.RulePolicyPath, shared.ContractFileName)
path := fmt.Sprintf("%s%s", shared.ConfigDirPath, shared.ContractFileName)
bytes, err := ioutil.ReadFile(path)
if err != nil {
logger.Log.Error(err.Error())

View File

@@ -0,0 +1,57 @@
package config
import (
"encoding/json"
"fmt"
"github.com/up9inc/mizu/shared"
"io/ioutil"
"os"
)
// these values are used when the config.json file is not present
const (
defaultMaxDatabaseSizeBytes int64 = 200 * 1000 * 1000
defaultRegexTarget string = ".*"
)
var Config *shared.MizuAgentConfig
func LoadConfig() error {
if Config != nil {
return nil
}
filePath := fmt.Sprintf("%s%s", shared.ConfigDirPath, shared.ConfigFileName)
content, err := ioutil.ReadFile(filePath)
if err != nil {
if os.IsNotExist(err) {
return applyDefaultConfig()
}
return err
}
if err = json.Unmarshal(content, &Config); err != nil {
return err
}
return nil
}
func applyDefaultConfig() error {
defaultConfig, err := getDefaultConfig()
if err != nil {
return err
}
Config = defaultConfig
return nil
}
func getDefaultConfig() (*shared.MizuAgentConfig, error) {
regex, err := shared.CompileRegexToSerializableRegexp(defaultRegexTarget)
if err != nil {
return nil, err
}
return &shared.MizuAgentConfig{
TapTargetRegex: *regex,
MaxDBSizeBytes: defaultMaxDatabaseSizeBytes,
}, nil
}

View File

@@ -13,11 +13,12 @@ import (
)
const (
DBPath = "./entries.db"
OrderDesc = "desc"
OrderAsc = "asc"
LT = "lt"
GT = "gt"
DBPath = "./entries.db"
OrderDesc = "desc"
OrderAsc = "asc"
LT = "lt"
GT = "gt"
TimeFormat = "2006-01-02 15:04:05.000000000"
)
var (
@@ -57,7 +58,7 @@ func initDataBase(databasePath string) *gorm.DB {
return temp
}
func GetEntriesFromDb(timestampFrom int64, timestampTo int64, protocolName *string) []tapApi.MizuEntry {
func GetEntriesFromDb(timeFrom time.Time, timeTo time.Time, protocolName *string) []tapApi.MizuEntry {
order := OrderDesc
protocolNameCondition := "1 = 1"
if protocolName != nil {
@@ -67,7 +68,7 @@ func GetEntriesFromDb(timestampFrom int64, timestampTo int64, protocolName *stri
var entries []tapApi.MizuEntry
GetEntriesTable().
Where(protocolNameCondition).
Where(fmt.Sprintf("timestamp BETWEEN %v AND %v", timestampFrom, timestampTo)).
Where(fmt.Sprintf("created_at BETWEEN '%s' AND '%s'", timeFrom.Format(TimeFormat), timeTo.Format(TimeFormat))).
Order(fmt.Sprintf("timestamp %s", order)).
Find(&entries)

View File

@@ -1,12 +1,11 @@
package database
import (
"mizuserver/pkg/config"
"os"
"strconv"
"time"
"github.com/fsnotify/fsnotify"
"github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/shared/debounce"
"github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/shared/units"
@@ -14,7 +13,6 @@ import (
)
const percentageOfMaxSizeBytesToPrune = 15
const defaultMaxDatabaseSizeBytes int64 = 200 * 1000 * 1000
func StartEnforcingDatabaseSize() {
watcher, err := fsnotify.NewWatcher()
@@ -23,14 +21,8 @@ func StartEnforcingDatabaseSize() {
return
}
maxEntriesDBByteSize, err := getMaxEntriesDBByteSize()
if err != nil {
logger.Log.Fatalf("Error parsing max db size: %v\n", err)
return
}
checkFileSizeDebouncer := debounce.NewDebouncer(5*time.Second, func() {
checkFileSize(maxEntriesDBByteSize)
checkFileSize(config.Config.MaxDBSizeBytes)
})
go func() {
@@ -58,17 +50,6 @@ func StartEnforcingDatabaseSize() {
}
}
func getMaxEntriesDBByteSize() (int64, error) {
maxEntriesDBByteSize := defaultMaxDatabaseSizeBytes
var err error
maxEntriesDBSizeByteSEnvVarValue := os.Getenv(shared.MaxEntriesDBSizeBytesEnvVar)
if maxEntriesDBSizeByteSEnvVarValue != "" {
maxEntriesDBByteSize, err = strconv.ParseInt(maxEntriesDBSizeByteSEnvVarValue, 10, 64)
}
return maxEntriesDBByteSize, err
}
func checkFileSize(maxSizeBytes int64) {
fileStat, err := os.Stat(DBPath)
if err != nil {

View File

@@ -45,7 +45,7 @@ func ValidateService(serviceFromRule string, service string) bool {
}
func MatchRequestPolicy(harEntry har.Entry, service string) (resultPolicyToSend []RulesMatched, isEnabled bool) {
enforcePolicy, err := shared.DecodeEnforcePolicy(fmt.Sprintf("%s/%s", shared.RulePolicyPath, shared.RulePolicyFileName))
enforcePolicy, err := shared.DecodeEnforcePolicy(fmt.Sprintf("%s%s", shared.ConfigDirPath, shared.ValidationRulesFileName))
if err == nil && len(enforcePolicy.Rules) > 0 {
isEnabled = true
}

View File

@@ -206,13 +206,13 @@ func syncEntriesImpl(token string, model string, envPrefix string, uploadInterva
sleepTime := time.Second * time.Duration(uploadIntervalSec)
var timestampFrom int64 = 0
var timeFrom time.Time
protocolFilter := "http"
for {
timestampTo := time.Now().UnixNano() / int64(time.Millisecond)
logger.Log.Infof("Getting entries from %v, to %v\n", timestampFrom, timestampTo)
protocolFilter := "http"
entriesArray := database.GetEntriesFromDb(timestampFrom, timestampTo, &protocolFilter)
timeTo := time.Now()
logger.Log.Infof("Getting entries from %v, to %v\n", timeFrom.Format(time.RFC3339Nano), timeTo.Format(time.RFC3339Nano))
entriesArray := database.GetEntriesFromDb(timeFrom, timeTo, &protocolFilter)
if len(entriesArray) > 0 {
result := make([]har.Entry, 0)
@@ -276,13 +276,14 @@ func syncEntriesImpl(token string, model string, envPrefix string, uploadInterva
analyzeInformation.SentCount += len(entriesArray)
logger.Log.Infof("Finish uploading %v entries to %s\n", len(entriesArray), GetTrafficDumpUrl(envPrefix, model))
logger.Log.Infof("Uploaded %v entries until now", analyzeInformation.SentCount)
} else {
logger.Log.Infof("Nothing to upload")
}
logger.Log.Infof("Sleeping for %v...\n", sleepTime)
time.Sleep(sleepTime)
timestampFrom = timestampTo
timeFrom = timeTo
}
}

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"net"
"net/http"
"os"
"time"
"github.com/google/uuid"
@@ -33,19 +32,8 @@ func Login() error {
Token: token.AccessToken,
}
configFile, defaultConfigErr := config.GetConfigWithDefaults()
if defaultConfigErr != nil {
return fmt.Errorf("failed getting config with defaults, err: %v", defaultConfigErr)
}
if err := config.LoadConfigFile(config.Config.ConfigFilePath, configFile); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed getting config file, err: %v", err)
}
configFile.Auth = authConfig
if err := config.WriteConfig(configFile); err != nil {
return fmt.Errorf("failed writing config with auth, err: %v", err)
if err := config.UpdateConfig(func(configStruct *config.ConfigStruct) { configStruct.Auth = authConfig }); err != nil {
return fmt.Errorf("failed updating config with auth, err: %v", err)
}
config.Config.Auth = authConfig

20
cli/cmd/clean.go Normal file
View File

@@ -0,0 +1,20 @@
package cmd
import (
"github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/telemetry"
)
var cleanCmd = &cobra.Command{
Use: "clean",
Short: "Removes all mizu resources",
RunE: func(cmd *cobra.Command, args []string) error {
go telemetry.ReportRun("clean", nil)
performCleanCommand()
return nil
},
}
func init() {
rootCmd.AddCommand(cleanCmd)
}

17
cli/cmd/cleanRunner.go Normal file
View File

@@ -0,0 +1,17 @@
package cmd
import (
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/kubernetes"
"github.com/up9inc/mizu/shared/logger"
)
func performCleanCommand() {
kubernetesProvider, err := kubernetes.NewProvider(config.Config.KubeConfigPath())
if err != nil {
logger.Log.Error(err)
return
}
finishMizuExecution(kubernetesProvider)
}

View File

@@ -21,7 +21,7 @@ func GetApiServerUrl() string {
}
func startProxyReportErrorIfAny(kubernetesProvider *kubernetes.Provider, cancel context.CancelFunc) {
err := kubernetes.StartProxy(kubernetesProvider, config.Config.Tap.GuiPort, config.Config.MizuResourcesNamespace, mizu.ApiServerPodName)
err := kubernetes.StartProxy(kubernetesProvider, config.Config.Tap.ProxyHost, config.Config.Tap.GuiPort, config.Config.MizuResourcesNamespace, mizu.ApiServerPodName)
if err != nil {
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error occured while running k8s proxy %v\n"+
"Try setting different port by using --%s", errormessage.FormatError(err), configStructs.GuiPortTapName))

View File

@@ -80,10 +80,19 @@ Supported protocols are HTTP and gRPC.`,
func askConfirmation(flagName string) {
logger.Log.Infof(fmt.Sprintf(uploadTrafficMessageToConfirm, flagName))
if !config.Config.Tap.AskUploadConfirmation {
return
}
if !uiUtils.AskForConfirmation("Would you like to proceed [Y/n]: ") {
logger.Log.Infof("You can always run mizu without %s, aborting", flagName)
os.Exit(0)
}
if err := config.UpdateConfig(func(configStruct *config.ConfigStruct) { configStruct.Tap.AskUploadConfirmation = false }); err != nil {
logger.Log.Debugf("failed updating config with upload confirmation, err: %v", err)
}
}
func init() {

View File

@@ -2,8 +2,11 @@ package cmd
import (
"context"
"errors"
"fmt"
"io/ioutil"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"path"
"regexp"
"strings"
@@ -51,9 +54,9 @@ func RunMizuTap() {
return
}
var mizuValidationRules string
var serializedValidationRules string
if config.Config.Tap.EnforcePolicyFile != "" {
mizuValidationRules, err = readValidationRules(config.Config.Tap.EnforcePolicyFile)
serializedValidationRules, err = readValidationRules(config.Config.Tap.EnforcePolicyFile)
if err != nil {
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error reading policy file: %v", errormessage.FormatError(err)))
return
@@ -61,14 +64,14 @@ func RunMizuTap() {
}
// Read and validate the OAS file
var contract string
var serializedContract string
if config.Config.Tap.ContractFile != "" {
bytes, err := ioutil.ReadFile(config.Config.Tap.ContractFile)
if err != nil {
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error reading contract file: %v", errormessage.FormatError(err)))
return
}
contract = string(bytes)
serializedContract = string(bytes)
ctx := context.Background()
loader := &openapi3.Loader{Context: ctx}
@@ -84,6 +87,12 @@ func RunMizuTap() {
}
}
serializedMizuConfig, err := config.GetSerializedMizuConfig()
if err != nil {
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error composing mizu config: %v", errormessage.FormatError(err)))
return
}
kubernetesProvider, err := kubernetes.NewProvider(config.Config.KubeConfigPath())
if err != nil {
logger.Log.Error(err)
@@ -129,11 +138,18 @@ func RunMizuTap() {
return
}
defer finishMizuExecution(kubernetesProvider)
if err := createMizuResources(ctx, kubernetesProvider, mizuValidationRules, contract); err != nil {
if err := createMizuResources(ctx, kubernetesProvider, serializedValidationRules, serializedContract, serializedMizuConfig); err != nil {
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error creating resources: %v", errormessage.FormatError(err)))
var statusError *k8serrors.StatusError
if errors.As(err, &statusError) {
if statusError.ErrStatus.Reason == metav1.StatusReasonAlreadyExists {
logger.Log.Info("Mizu is already running in this namespace, change the `mizu-resources-namespace` configuration or run `mizu clean` to remove the currently running Mizu instance")
}
}
return
}
defer finishMizuExecution(kubernetesProvider)
go goUtils.HandleExcWrapper(watchApiServerPod, ctx, kubernetesProvider, cancel, mizuApiFilteringOptions)
go goUtils.HandleExcWrapper(watchTapperPod, ctx, kubernetesProvider, cancel)
@@ -152,7 +168,7 @@ func readValidationRules(file string) (string, error) {
return string(newContent), nil
}
func createMizuResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, mizuValidationRules string, contract string) error {
func createMizuResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedValidationRules string, serializedContract string, serializedMizuConfig string) error {
if !config.Config.IsNsRestrictedMode() {
if err := createMizuNamespace(ctx, kubernetesProvider); err != nil {
return err
@@ -163,15 +179,15 @@ func createMizuResources(ctx context.Context, kubernetesProvider *kubernetes.Pro
return err
}
if err := createMizuConfigmap(ctx, kubernetesProvider, mizuValidationRules, contract); err != nil {
if err := createMizuConfigmap(ctx, kubernetesProvider, serializedValidationRules, serializedContract, serializedMizuConfig); err != nil {
logger.Log.Warningf(uiUtils.Warning, fmt.Sprintf("Failed to create resources required for policy validation. Mizu will not validate policy rules. error: %v\n", errormessage.FormatError(err)))
}
return nil
}
func createMizuConfigmap(ctx context.Context, kubernetesProvider *kubernetes.Provider, data string, contract string) error {
err := kubernetesProvider.CreateConfigMap(ctx, config.Config.MizuResourcesNamespace, mizu.ConfigMapName, data, contract)
func createMizuConfigmap(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedValidationRules string, serializedContract string, serializedMizuConfig string) error {
err := kubernetesProvider.CreateConfigMap(ctx, config.Config.MizuResourcesNamespace, mizu.ConfigMapName, serializedValidationRules, serializedContract, serializedMizuConfig)
return err
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"k8s.io/apimachinery/pkg/util/json"
"os"
"reflect"
"strconv"
@@ -40,7 +41,7 @@ func InitConfig(cmd *cobra.Command) error {
configFilePathFlag := cmd.Flags().Lookup(ConfigFilePathCommandName)
configFilePath := configFilePathFlag.Value.String()
if err := LoadConfigFile(configFilePath, &Config); err != nil {
if err := loadConfigFile(configFilePath, &Config); err != nil {
if configFilePathFlag.Changed || !os.IsNotExist(err) {
return fmt.Errorf("invalid config, %w\n"+
"you can regenerate the file by removing it (%v) and using `mizu config -r`", err, configFilePath)
@@ -81,7 +82,27 @@ func WriteConfig(config *ConfigStruct) error {
return nil
}
func LoadConfigFile(configFilePath string, config *ConfigStruct) error {
type updateConfigStruct func(*ConfigStruct)
func UpdateConfig(updateConfigStruct updateConfigStruct) error {
configFile, err := GetConfigWithDefaults()
if err != nil {
return fmt.Errorf("failed getting config with defaults, err: %v", err)
}
if err := loadConfigFile(Config.ConfigFilePath, configFile); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed getting config file, err: %v", err)
}
updateConfigStruct(configFile)
if err := WriteConfig(configFile); err != nil {
return fmt.Errorf("failed writing config, err: %v", err)
}
return nil
}
func loadConfigFile(configFilePath string, config *ConfigStruct) error {
reader, openErr := os.Open(configFilePath)
if openErr != nil {
return openErr
@@ -343,3 +364,27 @@ func setZeroForReadonlyFields(currentElem reflect.Value) {
}
}
}
func GetSerializedMizuConfig() (string, error) {
mizuConfig, err := getMizuConfig()
if err != nil {
return "", err
}
serializedConfig, err := json.Marshal(mizuConfig)
if err != nil {
return "", err
}
return string(serializedConfig), nil
}
func getMizuConfig() (*shared.MizuAgentConfig, error) {
serializableRegex, err := shared.CompileRegexToSerializableRegexp(Config.Tap.PodRegexStr)
if err != nil {
return nil, err
}
config := shared.MizuAgentConfig{
TapTargetRegex: *serializableRegex,
MaxDBSizeBytes: Config.Tap.MaxEntriesDBSizeBytes(),
}
return &config, nil
}

View File

@@ -26,6 +26,7 @@ type TapConfig struct {
UploadIntervalSec int `yaml:"upload-interval" default:"10"`
PodRegexStr string `yaml:"regex" default:".*"`
GuiPort uint16 `yaml:"gui-port" default:"8899"`
ProxyHost string `yaml:"proxy-host" default:"127.0.0.1"`
Namespaces []string `yaml:"namespaces"`
Analysis bool `yaml:"analysis" default:"false"`
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
@@ -37,6 +38,7 @@ type TapConfig struct {
Workspace string `yaml:"workspace"`
EnforcePolicyFile string `yaml:"traffic-validation-file"`
ContractFile string `yaml:"contract"`
AskUploadConfirmation bool `yaml:"ask-upload-confirmation" default:"true"`
ApiServerResources Resources `yaml:"api-server-resources"`
TapperResources Resources `yaml:"tapper-resources"`
}

View File

@@ -7,14 +7,13 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/shared/semver"
"k8s.io/apimachinery/pkg/version"
"net/url"
"path/filepath"
"regexp"
"strconv"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/shared/logger"
"io"
@@ -83,6 +82,10 @@ func NewProvider(kubeConfigPath string) (*Provider, error) {
return nil, err
}
if err := validateKubernetesVersion(clientSet); err != nil {
return nil, err
}
return &Provider{
clientSet: clientSet,
kubernetesConfig: kubernetesConfig,
@@ -173,10 +176,8 @@ func (provider *Provider) CreateMizuApiServerPod(ctx context.Context, opts *ApiS
}
}
configMapVolumeName := &core.ConfigMapVolumeSource{}
configMapVolumeName.Name = mizu.ConfigMapName
configMapOptional := true
configMapVolumeName.Optional = &configMapOptional
configMapVolume := &core.ConfigMapVolumeSource{}
configMapVolume.Name = mizu.ConfigMapName
cpuLimit, err := resource.ParseQuantity(opts.Resources.CpuLimit)
if err != nil {
@@ -222,7 +223,7 @@ func (provider *Provider) CreateMizuApiServerPod(ctx context.Context, opts *ApiS
VolumeMounts: []core.VolumeMount{
{
Name: mizu.ConfigMapName,
MountPath: shared.RulePolicyPath,
MountPath: shared.ConfigDirPath,
},
},
Command: command,
@@ -231,10 +232,6 @@ func (provider *Provider) CreateMizuApiServerPod(ctx context.Context, opts *ApiS
Name: shared.SyncEntriesConfigEnvVar,
Value: string(marshaledSyncEntriesConfig),
},
{
Name: shared.MaxEntriesDBSizeBytesEnvVar,
Value: strconv.FormatInt(opts.MaxEntriesDBSizeBytes, 10),
},
{
Name: shared.DebugModeEnvVar,
Value: debugMode,
@@ -275,7 +272,7 @@ func (provider *Provider) CreateMizuApiServerPod(ctx context.Context, opts *ApiS
{
Name: mizu.ConfigMapName,
VolumeSource: core.VolumeSource{
ConfigMap: configMapVolumeName,
ConfigMap: configMapVolume,
},
},
},
@@ -491,14 +488,16 @@ func (provider *Provider) handleRemovalError(err error) error {
return err
}
func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string, configMapName string, data string, contract string) error {
if data == "" && contract == "" {
return nil
}
func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string, configMapName string, serializedValidationRules string, serializedContract string, serializedMizuConfig string) error {
configMapData := make(map[string]string, 0)
configMapData[shared.RulePolicyFileName] = data
configMapData[shared.ContractFileName] = contract
if serializedValidationRules != "" {
configMapData[shared.ValidationRulesFileName] = serializedValidationRules
}
if serializedContract != "" {
configMapData[shared.ContractFileName] = serializedContract
}
configMapData[shared.ConfigFileName] = serializedMizuConfig
configMap := &core.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
@@ -617,6 +616,24 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
noScheduleToleration.WithOperator(core.TolerationOpExists)
noScheduleToleration.WithEffect(core.TaintEffectNoSchedule)
volumeName := mizu.ConfigMapName
configMapVolume := applyconfcore.VolumeApplyConfiguration{
Name: &volumeName,
VolumeSourceApplyConfiguration: applyconfcore.VolumeSourceApplyConfiguration{
ConfigMap: &applyconfcore.ConfigMapVolumeSourceApplyConfiguration{
LocalObjectReferenceApplyConfiguration: applyconfcore.LocalObjectReferenceApplyConfiguration{
Name: &volumeName,
},
},
},
}
mountPath := shared.ConfigDirPath
configMapVolumeMount := applyconfcore.VolumeMountApplyConfiguration{
Name: &volumeName,
MountPath: &mountPath,
}
agentContainer.WithVolumeMounts(&configMapVolumeMount)
podSpec := applyconfcore.PodSpec()
podSpec.WithHostNetwork(true)
podSpec.WithDNSPolicy(core.DNSClusterFirstWithHostNet)
@@ -627,6 +644,7 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
podSpec.WithContainers(agentContainer)
podSpec.WithAffinity(affinity)
podSpec.WithTolerations(noExecuteToleration, noScheduleToleration)
podSpec.WithVolumes(&configMapVolume)
podTemplate := applyconfcore.PodTemplateSpec()
podTemplate.WithLabels(map[string]string{"app": tapperPodName})
@@ -761,3 +779,19 @@ func validateNotProxy(kubernetesConfig clientcmd.ClientConfig, restClientConfig
return nil
}
func validateKubernetesVersion(clientSet *kubernetes.Clientset) error {
serverVersion, err := clientSet.ServerVersion()
if err != nil {
logger.Log.Debugf("error while getting kubernetes server version, err: %v", err)
return nil
}
serverVersionSemVer := semver.SemVersion(serverVersion.GitVersion)
minKubernetesServerVersionSemVer := semver.SemVersion(mizu.MinKubernetesServerVersion)
if minKubernetesServerVersionSemVer.GreaterThan(serverVersionSemVer) {
return fmt.Errorf("kubernetes server version %v is not supported, supporting only kubernetes server version of %v or higher", serverVersion.GitVersion, mizu.MinKubernetesServerVersion)
}
return nil
}

View File

@@ -14,12 +14,12 @@ import (
const k8sProxyApiPrefix = "/"
const mizuServicePort = 80
func StartProxy(kubernetesProvider *Provider, mizuPort uint16, mizuNamespace string, mizuServiceName string) error {
func StartProxy(kubernetesProvider *Provider, proxyHost string, mizuPort uint16, mizuNamespace string, mizuServiceName string) error {
logger.Log.Debugf("Starting proxy. namespace: [%v], service name: [%s], port: [%v]", mizuNamespace, mizuServiceName, mizuPort)
filter := &proxy.FilterServer{
AcceptPaths: proxy.MakeRegexpArrayOrDie(proxy.DefaultPathAcceptRE),
RejectPaths: proxy.MakeRegexpArrayOrDie(proxy.DefaultPathRejectRE),
AcceptHosts: proxy.MakeRegexpArrayOrDie(proxy.DefaultHostAcceptRE),
AcceptHosts: proxy.MakeRegexpArrayOrDie("^.*"),
RejectMethods: proxy.MakeRegexpArrayOrDie(proxy.DefaultMethodRejectRE),
}
@@ -32,7 +32,7 @@ func StartProxy(kubernetesProvider *Provider, mizuPort uint16, mizuNamespace str
mux.Handle("/static/", getRerouteHttpHandlerMizuStatic(proxyHandler, mizuNamespace, mizuServiceName))
mux.Handle("/mizu/", getRerouteHttpHandlerMizuAPI(proxyHandler, mizuNamespace, mizuServiceName))
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", int(mizuPort)))
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", proxyHost, int(mizuPort)))
if err != nil {
return err
}

View File

@@ -14,17 +14,18 @@ var (
)
const (
MizuResourcesPrefix = "mizu-"
ApiServerPodName = MizuResourcesPrefix + "api-server"
ClusterRoleBindingName = MizuResourcesPrefix + "cluster-role-binding"
ClusterRoleName = MizuResourcesPrefix + "cluster-role"
K8sAllNamespaces = ""
RoleBindingName = MizuResourcesPrefix + "role-binding"
RoleName = MizuResourcesPrefix + "role"
ServiceAccountName = MizuResourcesPrefix + "service-account"
TapperDaemonSetName = MizuResourcesPrefix + "tapper-daemon-set"
TapperPodName = MizuResourcesPrefix + "tapper"
ConfigMapName = MizuResourcesPrefix + "policy"
MizuResourcesPrefix = "mizu-"
ApiServerPodName = MizuResourcesPrefix + "api-server"
ClusterRoleBindingName = MizuResourcesPrefix + "cluster-role-binding"
ClusterRoleName = MizuResourcesPrefix + "cluster-role"
K8sAllNamespaces = ""
RoleBindingName = MizuResourcesPrefix + "role-binding"
RoleName = MizuResourcesPrefix + "role"
ServiceAccountName = MizuResourcesPrefix + "service-account"
TapperDaemonSetName = MizuResourcesPrefix + "tapper-daemon-set"
TapperPodName = MizuResourcesPrefix + "tapper"
ConfigMapName = MizuResourcesPrefix + "config"
MinKubernetesServerVersion = "1.16.0"
)
func GetMizuFolderPath() string {

View File

@@ -12,7 +12,7 @@ FROM golang:1.16-alpine AS builder
# Set necessary environment variables needed for our image.
ENV CGO_ENABLED=1 GOOS=linux GOARCH=amd64
RUN apk add libpcap-dev gcc g++ make
RUN apk add libpcap-dev gcc g++ make bash
# Move to agent working directory (/agent-build).
WORKDIR /app/agent-build
@@ -20,17 +20,25 @@ WORKDIR /app/agent-build
COPY agent/go.mod agent/go.sum ./
COPY shared/go.mod shared/go.mod ../shared/
COPY tap/go.mod tap/go.mod ../tap/
COPY tap/api/go.* ../tap/api/
RUN go mod download
# cheap trick to make the build faster (As long as go.mod wasn't changes)
RUN go list -f '{{.Path}}@{{.Version}}' -m all | sed 1d | grep -e 'go-cache' -e 'sqlite' | xargs go get
ARG COMMIT_HASH
ARG GIT_BRANCH
ARG BUILD_TIMESTAMP
ARG SEM_VER
# Copy and build agent code
COPY shared ../shared
COPY tap ../tap
COPY agent .
RUN go build -gcflags="all=-N -l" -o mizuagent .
COPY devops/build_extensions_debug.sh ..
RUN cd .. && /bin/bash build_extensions_debug.sh
FROM golang:1.16-alpine
@@ -39,6 +47,7 @@ WORKDIR /app
# Copy binary and config files from /build to root folder of scratch container.
COPY --from=builder ["/app/agent-build/mizuagent", "."]
COPY --from=builder ["/app/agent/build/extensions", "extensions"]
COPY --from=site-build ["/app/ui-build/build", "site"]
# install remote debugging tool

View File

@@ -0,0 +1,28 @@
#!/bin/bash
set -e
GCP_PROJECT=up9-docker-hub
REPOSITORY=gcr.io/$GCP_PROJECT
SERVER_NAME=mizu
GIT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]')
DOCKER_REPO=$REPOSITORY/$SERVER_NAME/$GIT_BRANCH
SEM_VER=${SEM_VER=0.0.0}
DOCKER_TAGGED_BUILDS=("$DOCKER_REPO:latest" "$DOCKER_REPO:$SEM_VER")
if [ "$GIT_BRANCH" = 'develop' -o "$GIT_BRANCH" = 'master' -o "$GIT_BRANCH" = 'main' ]
then
echo "Pushing to $GIT_BRANCH is allowed only via CI"
exit 1
fi
echo "building ${DOCKER_TAGGED_BUILDS[@]}"
DOCKER_TAGS_ARGS=$(echo ${DOCKER_TAGGED_BUILDS[@]/#/-t }) # "-t FIRST_TAG -t SECOND_TAG ..."
docker build -f debug.Dockerfile $DOCKER_TAGS_ARGS --build-arg SEM_VER=${SEM_VER} --build-arg BUILD_TIMESTAMP=${BUILD_TIMESTAMP} --build-arg GIT_BRANCH=${GIT_BRANCH} --build-arg COMMIT_HASH=${COMMIT_HASH} .
for DOCKER_TAG in "${DOCKER_TAGGED_BUILDS[@]}"
do
echo pushing "$DOCKER_TAG"
docker push "$DOCKER_TAG"
done

View File

@@ -0,0 +1,12 @@
#!/bin/bash
for f in tap/extensions/*; do
if [ -d "$f" ]; then
extension=$(basename $f) && \
cd tap/extensions/${extension} && \
go build -gcflags="all=-N -l" -buildmode=plugin -o ../${extension}.so . && \
cd ../../.. && \
mkdir -p agent/build/extensions && \
cp tap/extensions/${extension}.so agent/build/extensions
fi
done

View File

@@ -6,10 +6,10 @@ const (
HostModeEnvVar = "HOST_MODE"
NodeNameEnvVar = "NODE_NAME"
TappedAddressesPerNodeDictEnvVar = "TAPPED_ADDRESSES_PER_HOST"
MaxEntriesDBSizeBytesEnvVar = "MAX_ENTRIES_DB_BYTES"
RulePolicyPath = "/app/enforce-policy/"
RulePolicyFileName = "enforce-policy.yaml"
ConfigDirPath = "/app/config/"
ValidationRulesFileName = "validation-rules.yaml"
ContractFileName = "contract-oas.yaml"
ConfigFileName = "mizu-config.json"
GoGCEnvVar = "GOGC"
DefaultApiServerPort = 8899
DebugModeEnvVar = "MIZU_DEBUG"

View File

@@ -18,6 +18,11 @@ const (
WebsocketMessageTypeOutboundLink WebSocketMessageType = "outboundLink"
)
type MizuAgentConfig struct {
TapTargetRegex SerializableRegexp `yaml:"tapTargetRegex"`
MaxDBSizeBytes int64 `yaml:"maxDBSizeBytes"`
}
type WebSocketMessageMetadata struct {
MessageType WebSocketMessageType `json:"messageType,omitempty"`
}

View File

@@ -0,0 +1,30 @@
package shared
import "regexp"
type SerializableRegexp struct {
regexp.Regexp
}
func CompileRegexToSerializableRegexp(expr string) (*SerializableRegexp, error) {
re, err := regexp.Compile(expr)
if err != nil {
return nil, err
}
return &SerializableRegexp{*re}, nil
}
// UnmarshalText is by json.Unmarshal.
func (r *SerializableRegexp) UnmarshalText(text []byte) error {
rr, err := CompileRegexToSerializableRegexp(string(text))
if err != nil {
return err
}
*r = *rr
return nil
}
// MarshalText is used by json.Marshal.
func (r *SerializableRegexp) MarshalText() ([]byte, error) {
return []byte(r.String()), nil
}

View File

@@ -1,150 +0,0 @@
package tap
import (
"fmt"
"io"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/ip4defrag"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/tap/diagnose"
)
type tcpPacketSource struct {
source *gopacket.PacketSource
handle *pcap.Handle
defragger *ip4defrag.IPv4Defragmenter
behaviour *tcpPacketSourceBehaviour
}
type tcpPacketSourceBehaviour struct {
snapLength int
promisc bool
tstype string
decoderName string
lazy bool
bpfFilter string
}
type tcpPacketInfo struct {
packet gopacket.Packet
source *tcpPacketSource
}
func NewTcpPacketSource(filename string, interfaceName string,
behaviour tcpPacketSourceBehaviour) (*tcpPacketSource, error) {
var err error
result := &tcpPacketSource{
defragger: ip4defrag.NewIPv4Defragmenter(),
behaviour: &behaviour,
}
if filename != "" {
if result.handle, err = pcap.OpenOffline(filename); err != nil {
return result, fmt.Errorf("PCAP OpenOffline error: %v", err)
}
} else {
// This is a little complicated because we want to allow all possible options
// for creating the packet capture handle... instead of all this you can
// just call pcap.OpenLive if you want a simple handle.
inactive, err := pcap.NewInactiveHandle(interfaceName)
if err != nil {
return result, fmt.Errorf("could not create: %v", err)
}
defer inactive.CleanUp()
if err = inactive.SetSnapLen(behaviour.snapLength); err != nil {
return result, fmt.Errorf("could not set snap length: %v", err)
} else if err = inactive.SetPromisc(behaviour.promisc); err != nil {
return result, fmt.Errorf("could not set promisc mode: %v", err)
} else if err = inactive.SetTimeout(time.Second); err != nil {
return result, fmt.Errorf("could not set timeout: %v", err)
}
if behaviour.tstype != "" {
if t, err := pcap.TimestampSourceFromString(behaviour.tstype); err != nil {
return result, fmt.Errorf("supported timestamp types: %v", inactive.SupportedTimestamps())
} else if err := inactive.SetTimestampSource(t); err != nil {
return result, fmt.Errorf("supported timestamp types: %v", inactive.SupportedTimestamps())
}
}
if result.handle, err = inactive.Activate(); err != nil {
return result, fmt.Errorf("PCAP Activate error: %v", err)
}
}
if behaviour.bpfFilter != "" {
logger.Log.Infof("Using BPF filter %q", behaviour.bpfFilter)
if err = result.handle.SetBPFFilter(behaviour.bpfFilter); err != nil {
return nil, fmt.Errorf("BPF filter error: %v", err)
}
}
var dec gopacket.Decoder
var ok bool
if behaviour.decoderName == "" {
behaviour.decoderName = result.handle.LinkType().String()
}
if dec, ok = gopacket.DecodersByLayerName[behaviour.decoderName]; !ok {
return nil, fmt.Errorf("no decoder named %v", behaviour.decoderName)
}
result.source = gopacket.NewPacketSource(result.handle, dec)
result.source.Lazy = behaviour.lazy
result.source.NoCopy = true
return result, nil
}
func (source *tcpPacketSource) close() {
if source.handle != nil {
source.handle.Close()
}
}
func (source *tcpPacketSource) readPackets(ipdefrag bool, packets chan<- tcpPacketInfo) error {
for {
packet, err := source.source.NextPacket()
if err == io.EOF {
return err
} else if err != nil {
if err.Error() != "Timeout Expired" {
logger.Log.Debugf("Error: %T", err)
}
continue
}
// defrag the IPv4 packet if required
if !ipdefrag {
ip4Layer := packet.Layer(layers.LayerTypeIPv4)
if ip4Layer == nil {
continue
}
ip4 := ip4Layer.(*layers.IPv4)
l := ip4.Length
newip4, err := source.defragger.DefragIPv4(ip4)
if err != nil {
logger.Log.Fatal("Error while de-fragmenting", err)
} else if newip4 == nil {
logger.Log.Debugf("Fragment...")
continue // packet fragment, we don't have whole packet yet.
}
if newip4.Length != l {
diagnose.InternalStats.Ipdefrag++
logger.Log.Debugf("Decoding re-assembled packet: %s", newip4.NextLayerType())
pb, ok := packet.(gopacket.PacketBuilder)
if !ok {
logger.Log.Panic("Not a PacketBuilder")
}
nextDecoder := newip4.NextLayerType()
_ = nextDecoder.Decode(newip4.Payload, pb)
}
}
packets <- tcpPacketInfo{
packet: packet,
source: source,
}
}
}

135
ui/.snyk Normal file
View File

@@ -0,0 +1,135 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.14.0
ignore:
SNYK-JS-AXIOS-1579269:
- '*':
reason: None Given
SNYK-JS-TRIMNEWLINES-1298042:
- '*':
reason: None Given
SNYK-JS-ANSIHTML-1296849:
- '*':
reason: None Given
SNYK-JS-ANSIREGEX-1583908:
- '*':
reason: None Given
SNYK-JS-BROWSERSLIST-1090194:
- '*':
reason: None Given
SNYK-JS-CSSWHAT-1298035:
- '*':
reason: None Given
SNYK-JS-DNSPACKET-1293563:
- '*':
reason: None Given
SNYK-JS-EJS-1049328:
- '*':
reason: None Given
SNYK-JS-GLOBPARENT-1016905:
- '*':
reason: None Given
SNYK-JS-IMMER-1540542:
- '*':
reason: None Given
SNYK-JS-LODASHTEMPLATE-1088054:
- '*':
reason: None Given
SNYK-JS-NODESASS-1059081:
- '*':
reason: None Given
SNYK-JS-NODESASS-535498:
- '*':
reason: None Given
SNYK-JS-NODESASS-535500:
- '*':
reason: None Given
SNYK-JS-NODESASS-535502:
- '*':
reason: None Given
SNYK-JS-NODESASS-540956:
- '*':
reason: Non given
SNYK-JS-NODESASS-540958:
- '*':
reason: Non given
SNYK-JS-NODESASS-540964:
- '*':
reason: Non given
SNYK-JS-NODESASS-540978:
- '*':
reason: Non given
SNYK-JS-NODESASS-540980:
- '*':
reason: Non given
SNYK-JS-NODESASS-540990:
- '*':
reason: Non given
SNYK-JS-NODESASS-540992:
- '*':
reason: Non given
SNYK-JS-NODESASS-540994:
- '*':
reason: Non given
SNYK-JS-NODESASS-540996:
- '*':
reason: Non given
SNYK-JS-NODESASS-540998:
- '*':
reason: Non given
SNYK-JS-NODESASS-541000:
- '*':
reason: Non given
SNYK-JS-NODESASS-541002:
- '*':
reason: Non given
SNYK-JS-NTHCHECK-1586032:
- '*':
reason: Non given
SNYK-JS-PATHPARSE-1077067:
- '*':
reason: Non given
SNYK-JS-POSTCSS-1090595:
- '*':
reason: Non given
SNYK-JS-POSTCSS-1255640:
- '*':
reason: Non given
SNYK-JS-PRISMJS-1314893:
- '*':
reason: Non given
SNYK-JS-PRISMJS-1585202:
- '*':
reason: Non given
SNYK-JS-PROMPTS-1729737:
- '*':
reason: Non given
SNYK-JS-SHELLQUOTE-1766506:
- '*':
reason: Non given
SNYK-JS-TAR-1536528:
- '*':
reason: Non given
SNYK-JS-TAR-1536531:
- '*':
reason: Non given
SNYK-JS-TAR-1536758:
- '*':
reason: Non given
SNYK-JS-TAR-1579147:
- '*':
reason: Non given
SNYK-JS-TAR-1579152:
- '*':
reason: Non given
SNYK-JS-TAR-1579155:
- '*':
reason: Non given
SNYK-JS-TMPL-1583443:
- '*':
reason: Non given
SNYK-JS-URLPARSE-1533425:
- '*':
reason: Non given
SNYK-JS-WS-1296835:
- '*':
reason: Non given