github.com/fafucoder/cilium@v1.6.11/cilium/cmd/policy.go (about)

     1  // Copyright 2017 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cmd
    16  
    17  import (
    18  	"bufio"
    19  	"encoding/json"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"path"
    24  	"path/filepath"
    25  	"regexp"
    26  	"strings"
    27  
    28  	"github.com/cilium/cilium/pkg/logging/logfields"
    29  	"github.com/cilium/cilium/pkg/policy/api"
    30  
    31  	"github.com/sirupsen/logrus"
    32  	"github.com/spf13/cobra"
    33  )
    34  
    35  // policyCmd represents the policy command
    36  var policyCmd = &cobra.Command{
    37  	Use:   "policy",
    38  	Short: "Manage security policies",
    39  }
    40  
    41  var (
    42  	ignoredMasksSource = []string{".git"}
    43  	ignoredMasks       []*regexp.Regexp
    44  )
    45  
    46  func init() {
    47  	ignoredMasks = make([]*regexp.Regexp, len(ignoredMasksSource))
    48  
    49  	for i := range ignoredMasksSource {
    50  		ignoredMasks[i] = regexp.MustCompile(ignoredMasksSource[i])
    51  	}
    52  
    53  	rootCmd.AddCommand(policyCmd)
    54  }
    55  
    56  func getContext(content []byte, offset int64) (int, string, int) {
    57  	if offset >= int64(len(content)) || offset < 0 {
    58  		return 0, fmt.Sprintf("[error: Offset %d is out of bounds 0..%d]", offset, len(content)), 0
    59  	}
    60  
    61  	lineN := strings.Count(string(content[:offset]), "\n") + 1
    62  
    63  	start := strings.LastIndexByte(string(content[:offset]), '\n')
    64  	if start == -1 {
    65  		start = 0
    66  	} else {
    67  		start++
    68  	}
    69  
    70  	end := strings.IndexByte(string(content[start:]), '\n')
    71  	var l string
    72  	if end == -1 {
    73  		l = string(content[start:])
    74  	} else {
    75  		end = end + start
    76  		l = string(content[start:end])
    77  	}
    78  
    79  	return lineN, l, (int(offset) - start)
    80  }
    81  
    82  func handleUnmarshalError(f string, content []byte, err error) error {
    83  	switch e := err.(type) {
    84  	case *json.SyntaxError:
    85  		line, ctx, off := getContext(content, e.Offset)
    86  
    87  		if off <= 1 {
    88  			return fmt.Errorf("malformed policy, not JSON?")
    89  		}
    90  
    91  		preoff := off - 1
    92  		pre := make([]byte, preoff)
    93  		copy(pre, ctx[:preoff])
    94  		for i := 0; i < preoff && i < len(pre); i++ {
    95  			if pre[i] != '\t' {
    96  				pre[i] = ' '
    97  			}
    98  		}
    99  
   100  		return fmt.Errorf("%s:%d: syntax error at offset %d:\n%s\n%s^",
   101  			path.Base(f), line, off, ctx, pre)
   102  	case *json.UnmarshalTypeError:
   103  		line, ctx, off := getContext(content, e.Offset)
   104  		return fmt.Errorf("%s:%d: unable to assign value '%s' to type '%v':\n%s\n%*c",
   105  			path.Base(f), line, e.Value, e.Type, ctx, off, '^')
   106  	default:
   107  		return fmt.Errorf("%s: unknown error:%s", path.Base(f), err)
   108  	}
   109  }
   110  
   111  func ignoredFile(name string) bool {
   112  	for i := range ignoredMasks {
   113  		if ignoredMasks[i].MatchString(name) {
   114  			logrus.WithField(logfields.Path, name).Debug("Ignoring file")
   115  			return true
   116  		}
   117  	}
   118  
   119  	return false
   120  }
   121  
   122  func loadPolicyFile(path string) (api.Rules, error) {
   123  	var content []byte
   124  	var err error
   125  	logrus.WithField(logfields.Path, path).Debug("Loading file")
   126  
   127  	if path == "-" {
   128  		content, err = ioutil.ReadAll(bufio.NewReader(os.Stdin))
   129  	} else {
   130  		content, err = ioutil.ReadFile(path)
   131  	}
   132  
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	var ruleList api.Rules
   138  	err = json.Unmarshal(content, &ruleList)
   139  	if err != nil {
   140  		return nil, handleUnmarshalError(path, content, err)
   141  	}
   142  
   143  	return ruleList, nil
   144  }
   145  
   146  func loadPolicy(name string) (api.Rules, error) {
   147  	logrus.WithField(logfields.Path, name).Debug("Entering directory")
   148  
   149  	if name == "-" {
   150  		return loadPolicyFile(name)
   151  	}
   152  
   153  	if fi, err := os.Stat(name); err != nil {
   154  		return nil, err
   155  	} else if fi.Mode().IsRegular() {
   156  		return loadPolicyFile(name)
   157  	} else if !fi.Mode().IsDir() {
   158  		return nil, fmt.Errorf("Error: %s is not a file or a directory", name)
   159  	}
   160  
   161  	files, err := ioutil.ReadDir(name)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	result := api.Rules{}
   167  	ruleList, err := processAllFilesFirst(name, files)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	result = append(result, ruleList...)
   172  
   173  	ruleList, err = recursiveSearch(name, files)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	result = append(result, ruleList...)
   178  
   179  	logrus.WithField(logfields.Path, name).Debug("Leaving directory")
   180  
   181  	return result, nil
   182  }
   183  
   184  func processAllFilesFirst(name string, files []os.FileInfo) (api.Rules, error) {
   185  	result := api.Rules{}
   186  
   187  	for _, f := range files {
   188  		if f.IsDir() || ignoredFile(path.Base(f.Name())) {
   189  			continue
   190  		}
   191  
   192  		ruleList, err := loadPolicyFile(filepath.Join(name, f.Name()))
   193  		if err != nil {
   194  			return nil, err
   195  		}
   196  
   197  		result = append(result, ruleList...)
   198  	}
   199  
   200  	return result, nil
   201  }
   202  
   203  func recursiveSearch(name string, files []os.FileInfo) (api.Rules, error) {
   204  	result := api.Rules{}
   205  	for _, f := range files {
   206  		if f.IsDir() {
   207  			if ignoredFile(path.Base(f.Name())) {
   208  				continue
   209  			}
   210  			subpath := filepath.Join(name, f.Name())
   211  			ruleList, err := loadPolicy(subpath)
   212  			if err != nil {
   213  				return nil, err
   214  			}
   215  			result = append(result, ruleList...)
   216  		}
   217  	}
   218  	return result, nil
   219  }