update iptables
This commit is contained in:
parent
f6f4258609
commit
f25fa59449
@ -47,7 +47,6 @@ resources:
|
|||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
yaml, cliErr := exec.Command("./jx", "import", "--resource", ts.URL).Output()
|
yaml, cliErr := exec.Command("./jx", "import", "--resource", ts.URL).Output()
|
||||||
slog.Info("TestCliHTTPSource", "err", cliErr)
|
|
||||||
assert.Nil(t, cliErr)
|
assert.Nil(t, cliErr)
|
||||||
assert.NotEqual(t, "", string(yaml))
|
assert.NotEqual(t, "", string(yaml))
|
||||||
assert.Greater(t, len(yaml), 0)
|
assert.Greater(t, len(yaml), 0)
|
||||||
|
@ -29,7 +29,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var GlobalOformat *string
|
var GlobalOformat *string
|
||||||
var GlobalOutput *string
|
var GlobalOutput string
|
||||||
var GlobalQuiet *bool
|
var GlobalQuiet *bool
|
||||||
|
|
||||||
var ImportMerge *bool
|
var ImportMerge *bool
|
||||||
@ -89,7 +89,6 @@ func LoadSourceURI(uri string) []*resource.Document {
|
|||||||
if extractErr != nil {
|
if extractErr != nil {
|
||||||
log.Fatal(extractErr)
|
log.Fatal(extractErr)
|
||||||
}
|
}
|
||||||
slog.Info("extract documents", "documents", extractDocuments)
|
|
||||||
return extractDocuments
|
return extractDocuments
|
||||||
}
|
}
|
||||||
return []*resource.Document{ resource.NewDocument() }
|
return []*resource.Document{ resource.NewDocument() }
|
||||||
@ -103,7 +102,6 @@ func ImportSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
var encoder resource.Encoder
|
|
||||||
merged := resource.NewDocument()
|
merged := resource.NewDocument()
|
||||||
documents := make([]*resource.Document, 0, 100)
|
documents := make([]*resource.Document, 0, 100)
|
||||||
for _,source := range cmd.Args() {
|
for _,source := range cmd.Args() {
|
||||||
@ -113,19 +111,19 @@ func ImportSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
switch *GlobalOformat {
|
switch *GlobalOformat {
|
||||||
case FormatYaml:
|
case FormatYaml:
|
||||||
encoder = resource.NewYAMLEncoder(output)
|
encoder = resource.NewYAMLEncoder(output)
|
||||||
case FormatJson:
|
case FormatJson:
|
||||||
encoder = resource.NewJSONEncoder(output)
|
encoder = resource.NewJSONEncoder(output)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if *GlobalOutput != "" {
|
slog.Info("main.ImportResource", "args", os.Args, "output", GlobalOutput)
|
||||||
_, err := target.TargetTypes.New(*GlobalOutput)
|
outputTarget, err := target.TargetTypes.New(GlobalOutput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
|
||||||
//outputTarget.EmitResources(
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(documents) == 0 {
|
if len(documents) == 0 {
|
||||||
@ -151,18 +149,18 @@ func ImportSubCommand(cmd *flag.FlagSet, output io.Writer) (err error) {
|
|||||||
} else {
|
} else {
|
||||||
if *ImportMerge {
|
if *ImportMerge {
|
||||||
merged.ResourceDecls = append(merged.ResourceDecls, d.ResourceDecls...)
|
merged.ResourceDecls = append(merged.ResourceDecls, d.ResourceDecls...)
|
||||||
slog.Info("merging", "doc", merged.ResourceDecls, "src", d.ResourceDecls)
|
|
||||||
} else {
|
} else {
|
||||||
if documentGenerateErr := encoder.Encode(d); documentGenerateErr != nil {
|
slog.Info("main.ImportResource", "outputTarget", outputTarget, "type", outputTarget.Type())
|
||||||
return documentGenerateErr
|
if outputErr := outputTarget.EmitResources([]*resource.Document{d}, nil); outputErr != nil {
|
||||||
|
return outputErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if *ImportMerge {
|
if *ImportMerge {
|
||||||
if documentGenerateErr := encoder.Encode(merged); documentGenerateErr != nil {
|
if outputErr := outputTarget.EmitResources([]*resource.Document{merged}, nil); outputErr != nil {
|
||||||
return documentGenerateErr
|
return outputErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -276,8 +274,8 @@ func main() {
|
|||||||
for _,subCmd := range jxSubCommands {
|
for _,subCmd := range jxSubCommands {
|
||||||
cmdFlagSet := flag.NewFlagSet(subCmd.Name, flag.ExitOnError)
|
cmdFlagSet := flag.NewFlagSet(subCmd.Name, flag.ExitOnError)
|
||||||
GlobalOformat = cmdFlagSet.String("oformat", "yaml", "Output serialization format")
|
GlobalOformat = cmdFlagSet.String("oformat", "yaml", "Output serialization format")
|
||||||
GlobalOutput = cmdFlagSet.String("output", "-", "Output target (default stdout)")
|
cmdFlagSet.StringVar(&GlobalOutput, "output", "-", "Output target (default stdout)")
|
||||||
GlobalOutput = cmdFlagSet.String("o", "-", "Output target (default stdout)")
|
cmdFlagSet.StringVar(&GlobalOutput, "o", "-", "Output target (default stdout)")
|
||||||
GlobalQuiet = cmdFlagSet.Bool("quiet", false, "Generate terse output.")
|
GlobalQuiet = cmdFlagSet.Bool("quiet", false, "Generate terse output.")
|
||||||
|
|
||||||
switch subCmd.Name {
|
switch subCmd.Name {
|
||||||
@ -295,22 +293,19 @@ func main() {
|
|||||||
}
|
}
|
||||||
case "import":
|
case "import":
|
||||||
cmdFlagSet.Usage = func() {
|
cmdFlagSet.Usage = func() {
|
||||||
fmt.Println("jx import source [source2]")
|
fmt.Println("jx import source...")
|
||||||
cmdFlagSet.PrintDefaults()
|
cmdFlagSet.PrintDefaults()
|
||||||
VersionUsage()
|
VersionUsage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slog.Info("command", "command", subCmd)
|
|
||||||
if os.Args[1] == subCmd.Name {
|
if os.Args[1] == subCmd.Name {
|
||||||
if e := subCmd.Run(cmdFlagSet, os.Stdout); e != nil {
|
if e := subCmd.Run(cmdFlagSet, os.Stdout); e != nil {
|
||||||
log.Fatal(e)
|
log.Fatal(e)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
flag.PrintDefaults()
|
|
||||||
VersionUsage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
flag.PrintDefaults()
|
||||||
|
VersionUsage()
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ func (d *Document) Filter(filter ResourceSelector) []*Declaration {
|
|||||||
resources := make([]*Declaration, 0, len(d.ResourceDecls))
|
resources := make([]*Declaration, 0, len(d.ResourceDecls))
|
||||||
for i := range d.ResourceDecls {
|
for i := range d.ResourceDecls {
|
||||||
filterResource := &d.ResourceDecls[i]
|
filterResource := &d.ResourceDecls[i]
|
||||||
if filter(filterResource) {
|
if filter == nil || filter(filterResource) {
|
||||||
resources = append(resources, &d.ResourceDecls[i])
|
resources = append(resources, &d.ResourceDecls[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ func init() {
|
|||||||
|
|
||||||
// Manage the state of file system objects
|
// Manage the state of file system objects
|
||||||
type File struct {
|
type File struct {
|
||||||
|
normalizePath bool `json:"-" yaml:"-"`
|
||||||
Path string `json:"path" yaml:"path"`
|
Path string `json:"path" yaml:"path"`
|
||||||
Owner string `json:"owner" yaml:"owner"`
|
Owner string `json:"owner" yaml:"owner"`
|
||||||
Group string `json:"group" yaml:"group"`
|
Group string `json:"group" yaml:"group"`
|
||||||
@ -69,13 +70,20 @@ type ResourceFileInfo struct {
|
|||||||
func NewFile() *File {
|
func NewFile() *File {
|
||||||
currentUser, _ := user.Current()
|
currentUser, _ := user.Current()
|
||||||
group, _ := user.LookupGroupId(currentUser.Gid)
|
group, _ := user.LookupGroupId(currentUser.Gid)
|
||||||
f := &File{Owner: currentUser.Username, Group: group.Name, Mode: "0644", FileType: RegularFile}
|
f := &File{ normalizePath: false, Owner: currentUser.Username, Group: group.Name, Mode: "0644", FileType: RegularFile}
|
||||||
slog.Info("NewFile()", "file", f)
|
slog.Info("NewFile()", "file", f)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewNormalizedFile() *File {
|
||||||
|
f := NewFile()
|
||||||
|
f.normalizePath = true
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
func (f *File) Clone() Resource {
|
func (f *File) Clone() Resource {
|
||||||
return &File {
|
return &File {
|
||||||
|
normalizePath: f.normalizePath,
|
||||||
Path: f.Path,
|
Path: f.Path,
|
||||||
Owner: f.Owner,
|
Owner: f.Owner,
|
||||||
Group: f.Group,
|
Group: f.Group,
|
||||||
@ -100,10 +108,9 @@ func (f *File) SetURI(uri string) error {
|
|||||||
resourceUri, e := url.Parse(uri)
|
resourceUri, e := url.Parse(uri)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
if resourceUri.Scheme == "file" {
|
if resourceUri.Scheme == "file" {
|
||||||
if absFilePath, err := filepath.Abs(filepath.Join(resourceUri.Hostname(), resourceUri.RequestURI())); err != nil {
|
f.Path = filepath.Join(resourceUri.Hostname(), resourceUri.RequestURI())
|
||||||
|
if err := f.NormalizePath(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
f.Path = absFilePath
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
e = fmt.Errorf("%w: %s is not a file", ErrInvalidResourceURI, uri)
|
e = fmt.Errorf("%w: %s is not a file", ErrInvalidResourceURI, uri)
|
||||||
@ -187,26 +194,31 @@ func (f *File) Apply() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) LoadDecl(yamlResourceDeclaration string) error {
|
func (f *File) LoadDecl(yamlResourceDeclaration string) (err error) {
|
||||||
d := NewYAMLStringDecoder(yamlResourceDeclaration)
|
d := NewYAMLStringDecoder(yamlResourceDeclaration)
|
||||||
return d.Decode(f)
|
err = d.Decode(f)
|
||||||
|
if err == nil {
|
||||||
|
f.UpdateContentAttributes()
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) ResolveId(ctx context.Context) string {
|
func (f *File) ResolveId(ctx context.Context) string {
|
||||||
filePath, fileAbsErr := filepath.Abs(f.Path)
|
if e := f.NormalizePath(); e != nil {
|
||||||
if fileAbsErr != nil {
|
panic(e)
|
||||||
panic(fileAbsErr)
|
|
||||||
}
|
}
|
||||||
f.Path = filePath
|
return f.Path
|
||||||
return filePath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) NormalizePath() error {
|
func (f *File) NormalizePath() error {
|
||||||
filePath, fileAbsErr := filepath.Abs(f.Path)
|
if f.normalizePath {
|
||||||
if fileAbsErr == nil {
|
filePath, fileAbsErr := filepath.Abs(f.Path)
|
||||||
f.Path = filePath
|
if fileAbsErr == nil {
|
||||||
|
f.Path = filePath
|
||||||
|
}
|
||||||
|
return fileAbsErr
|
||||||
}
|
}
|
||||||
return fileAbsErr
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) FileInfo() fs.FileInfo {
|
func (f *File) FileInfo() fs.FileInfo {
|
||||||
@ -214,7 +226,8 @@ func (f *File) FileInfo() fs.FileInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *ResourceFileInfo) Name() string {
|
func (f *ResourceFileInfo) Name() string {
|
||||||
return filepath.Base(f.resource.Path)
|
// return filepath.Base(f.resource.Path)
|
||||||
|
return f.resource.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ResourceFileInfo) Size() int64 {
|
func (f *ResourceFileInfo) Size() int64 {
|
||||||
@ -246,6 +259,11 @@ func (f *ResourceFileInfo) Sys() any {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *File) UpdateContentAttributes() {
|
||||||
|
f.Size = int64(len(f.Content))
|
||||||
|
f.Sha256 = fmt.Sprintf("%x", sha256.Sum256([]byte(f.Content)))
|
||||||
|
}
|
||||||
|
|
||||||
func (f *File) UpdateAttributesFromFileInfo(info os.FileInfo) error {
|
func (f *File) UpdateAttributesFromFileInfo(info os.FileInfo) error {
|
||||||
if info != nil {
|
if info != nil {
|
||||||
f.Mtime = info.ModTime()
|
f.Mtime = info.ModTime()
|
||||||
|
307
internal/resource/iptables.go
Normal file
307
internal/resource/iptables.go
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
_ "errors"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
_ "os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ResourceTypes.Register("iptable", func(u *url.URL) Resource {
|
||||||
|
i := NewIptable()
|
||||||
|
i.Table = IptableName(u.Hostname())
|
||||||
|
fields := strings.Split(u.Path, "/")
|
||||||
|
slog.Info("iptables factory", "iptable", i, "uri", u, "field", fields)
|
||||||
|
i.Chain = IptableChain(fields[1])
|
||||||
|
id, _ := strconv.ParseUint(fields[2], 10, 32)
|
||||||
|
i.Id = uint(id)
|
||||||
|
return i
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type IptableIPVersion string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IptableIPv4 IptableIPVersion = "ipv4"
|
||||||
|
IPtableIPv6 IptableIPVersion = "ipv6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IptableName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IptableNameFilter = "filter"
|
||||||
|
IptableNameNat = "nat"
|
||||||
|
IptableNameMangel = "mangle"
|
||||||
|
IptableNameRaw = "raw"
|
||||||
|
IptableNameSecurity = "security"
|
||||||
|
)
|
||||||
|
|
||||||
|
var IptableNumber = regexp.MustCompile(`^[0-9]+$`)
|
||||||
|
|
||||||
|
type IptableChain string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IptableChainInput = "INPUT"
|
||||||
|
IptableChainOutput = "OUTPUT"
|
||||||
|
IptableChainForward = "FORWARD"
|
||||||
|
IptableChainPreRouting = "PREROUTING"
|
||||||
|
IptableChainPostRouting = "POSTROUTING"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IptableProto string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IptableProtoTCP = "tcp"
|
||||||
|
IptableProtoUDP = "udp"
|
||||||
|
IptableProtoUDPLite = "udplite"
|
||||||
|
IptableProtoICMP = "icmp"
|
||||||
|
IptableProtoICMPv6 = "icmpv6"
|
||||||
|
IptableProtoESP = "ESP"
|
||||||
|
IptableProtoAH = "AH"
|
||||||
|
IptableProtoSCTP = "sctp"
|
||||||
|
IptableProtoMH = "mh"
|
||||||
|
IptableProtoAll = "all"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IptableCIDR string
|
||||||
|
|
||||||
|
type ExtensionFlag struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Value string `json:"value" yaml:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IptablePort uint16
|
||||||
|
|
||||||
|
// Manage the state of iptables rules
|
||||||
|
// iptable://filter/INPUT/0
|
||||||
|
type Iptable struct {
|
||||||
|
Id uint `json:"id" yaml:"id"`
|
||||||
|
Table IptableName `json:"table" yaml:"table"`
|
||||||
|
Chain IptableChain `json:"chain" yaml:"chain"`
|
||||||
|
Destination IptableCIDR `json:"destination,omitempty" yaml:"destination,omitempty"`
|
||||||
|
Source IptableCIDR `json:"source,omitempty" yaml:"source,omitempty"`
|
||||||
|
Dport IptablePort `json:"dport,omitempty" yaml:"dport,omitempty"`
|
||||||
|
Sport IptablePort `json:"sport,omitempty" yaml:"sport,omitempty"`
|
||||||
|
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||||
|
Out string `json:"out,omitempty" yaml:"out,omitempty"`
|
||||||
|
Match []string `json:"match,omitempty" yaml:"match,omitempty"`
|
||||||
|
Flags []ExtensionFlag `json:"extension_flags,omitempty" yaml:"extension_flags,omitempty"`
|
||||||
|
Proto IptableProto `json:"proto,omitempty" yaml:"proto,omitempty"`
|
||||||
|
Jump string `json:"jump" yaml:"jump"`
|
||||||
|
State string `json:"state" yaml:"state"`
|
||||||
|
|
||||||
|
CreateCommand *Command `yaml:"-" json:"-"`
|
||||||
|
ReadCommand *Command `yaml:"-" json:"-"`
|
||||||
|
UpdateCommand *Command `yaml:"-" json:"-"`
|
||||||
|
DeleteCommand *Command `yaml:"-" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIptable() *Iptable {
|
||||||
|
i := &Iptable{}
|
||||||
|
i.CreateCommand, i.ReadCommand, i.UpdateCommand, i.DeleteCommand = i.NewCRUD()
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) Clone() Resource {
|
||||||
|
return &Iptable {
|
||||||
|
Id: i.Id,
|
||||||
|
Table: i.Table,
|
||||||
|
Chain: i.Chain,
|
||||||
|
Destination: i.Destination,
|
||||||
|
Source: i.Source,
|
||||||
|
In: i.In,
|
||||||
|
Out: i.Out,
|
||||||
|
Match: i.Match,
|
||||||
|
Proto: i.Proto,
|
||||||
|
State: i.State,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) URI() string {
|
||||||
|
return fmt.Sprintf("iptable://%s/%s/%d", i.Table, i.Chain, i.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) SetURI(uri string) error {
|
||||||
|
resourceUri, e := url.Parse(uri)
|
||||||
|
if e == nil {
|
||||||
|
if resourceUri.Scheme == "iptable" {
|
||||||
|
i.Table = IptableName(resourceUri.Hostname())
|
||||||
|
fields := strings.Split(resourceUri.Path, "/")
|
||||||
|
i.Chain = IptableChain(fields[1])
|
||||||
|
id, _ := strconv.ParseUint(fields[2], 10, 32)
|
||||||
|
i.Id = uint(id)
|
||||||
|
} else {
|
||||||
|
e = fmt.Errorf("%w: %s is not an iptable rule", ErrInvalidResourceURI, uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) Validate() error {
|
||||||
|
s := NewSchema(i.Type())
|
||||||
|
jsonDoc, jsonErr := i.JSON()
|
||||||
|
if jsonErr == nil {
|
||||||
|
return s.Validate(string(jsonDoc))
|
||||||
|
}
|
||||||
|
return jsonErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) JSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) UnmarshalJSON(data []byte) error {
|
||||||
|
if unmarshalErr := json.Unmarshal(data, i); unmarshalErr != nil {
|
||||||
|
return unmarshalErr
|
||||||
|
}
|
||||||
|
i.CreateCommand, i.ReadCommand, i.UpdateCommand, i.DeleteCommand = i.NewCRUD()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
type decodeIptable Iptable
|
||||||
|
if unmarshalErr := value.Decode((*decodeIptable)(i)); unmarshalErr != nil {
|
||||||
|
return unmarshalErr
|
||||||
|
}
|
||||||
|
i.CreateCommand, i.ReadCommand, i.UpdateCommand, i.DeleteCommand = i.NewCRUD()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) NewCRUD() (create *Command, read *Command, update *Command, del *Command) {
|
||||||
|
return NewIptableCreateCommand(), NewIptableReadCommand(), NewIptableUpdateCommand(), NewIptableDeleteCommand()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) Apply() error {
|
||||||
|
|
||||||
|
switch i.State {
|
||||||
|
case "absent":
|
||||||
|
case "present":
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) Load(r io.Reader) error {
|
||||||
|
return NewYAMLDecoder(r).Decode(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) LoadDecl(yamlResourceDeclaration string) error {
|
||||||
|
return NewYAMLStringDecoder(yamlResourceDeclaration).Decode(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (i *Iptable) ResolveId(ctx context.Context) string {
|
||||||
|
// uri := fmt.Sprintf("%s?gateway=%s&interface=%s&rtid=%s&metric=%d&type=%s&scope=%s",
|
||||||
|
// n.To, n.Gateway, n.Interface, n.Rtid, n.Metric, n.RouteType, n.Scope)
|
||||||
|
// n.Id = hex.EncodeToString([]byte(uri))
|
||||||
|
return fmt.Sprintf("%d", i.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) Read(ctx context.Context) ([]byte, error) {
|
||||||
|
out, err := i.ReadCommand.Execute(i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exErr := i.ReadCommand.Extractor(out, i)
|
||||||
|
if exErr != nil {
|
||||||
|
return nil, exErr
|
||||||
|
}
|
||||||
|
return yaml.Marshal(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iptable) Type() string { return "iptable" }
|
||||||
|
|
||||||
|
func NewIptableCreateCommand() *Command {
|
||||||
|
c := NewCommand()
|
||||||
|
c.Path = "iptables"
|
||||||
|
c.Args = []CommandArg{
|
||||||
|
CommandArg("-t"),
|
||||||
|
CommandArg("{{ .Table }}"),
|
||||||
|
CommandArg("-R"),
|
||||||
|
CommandArg("{{ .Chain }}"),
|
||||||
|
CommandArg("{{ .Id }}"),
|
||||||
|
CommandArg("{{ if .In }}-i {{ .In }}{{ else if .Out }}-o {{ .Out }}{{ end }}"),
|
||||||
|
CommandArg("{{ range .Match }}-m {{ . }} {{ end }}"),
|
||||||
|
CommandArg("{{ if .Source }}-s {{ .Source }}{{ end }}"),
|
||||||
|
CommandArg("{{ if .Destination }}-d {{ .Destination }}{{ end }}"),
|
||||||
|
CommandArg("{{ if .Jump }}-j {{ .Jump }}{{ end }}"),
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIptableReadCommand() *Command {
|
||||||
|
c := NewCommand()
|
||||||
|
c.Path = "iptables"
|
||||||
|
c.Args = []CommandArg{
|
||||||
|
CommandArg("-t"),
|
||||||
|
CommandArg("{{ .Table }}"),
|
||||||
|
CommandArg("-S"),
|
||||||
|
CommandArg("{{ .Chain }}"),
|
||||||
|
CommandArg("{{ .Id }}"),
|
||||||
|
}
|
||||||
|
c.Extractor = func(out []byte, target any) error {
|
||||||
|
i := target.(*Iptable)
|
||||||
|
ruleFields := strings.Split(strings.TrimSpace(string(out)), " ")
|
||||||
|
switch ruleFields[0] {
|
||||||
|
case "-A":
|
||||||
|
//chain := ruleFields[1]
|
||||||
|
flags := ruleFields[2:]
|
||||||
|
for optind,opt := range flags {
|
||||||
|
if optind > len(flags) - 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
optValue := flags[optind + 1]
|
||||||
|
switch opt {
|
||||||
|
case "-i":
|
||||||
|
i.In = optValue
|
||||||
|
case "-o":
|
||||||
|
i.Out = optValue
|
||||||
|
case "-m":
|
||||||
|
i.Match = append(i.Match, optValue)
|
||||||
|
case "-s":
|
||||||
|
i.Source = IptableCIDR(optValue)
|
||||||
|
case "-d":
|
||||||
|
i.Destination = IptableCIDR(optValue)
|
||||||
|
case "-p":
|
||||||
|
i.Proto = IptableProto(optValue)
|
||||||
|
case "-j":
|
||||||
|
i.Jump = optValue
|
||||||
|
case "--dport":
|
||||||
|
port,_ := strconv.ParseUint(optValue, 10, 16)
|
||||||
|
i.Dport = IptablePort(port)
|
||||||
|
case "--sport":
|
||||||
|
port,_ := strconv.ParseUint(optValue, 10, 16)
|
||||||
|
i.Sport = IptablePort(port)
|
||||||
|
default:
|
||||||
|
if opt[0] == '-' {
|
||||||
|
i.Flags = append(i.Flags, ExtensionFlag{ Name: strings.Trim(opt, "-"), Value: strings.TrimSpace(optValue)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.State = "present"
|
||||||
|
default:
|
||||||
|
i.State = "absent"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIptableUpdateCommand() *Command {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIptableDeleteCommand() *Command {
|
||||||
|
return nil
|
||||||
|
}
|
76
internal/resource/iptables_test.go
Normal file
76
internal/resource/iptables_test.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||||
|
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "encoding/json"
|
||||||
|
_ "fmt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
_ "gopkg.in/yaml.v3"
|
||||||
|
_ "io"
|
||||||
|
_ "log"
|
||||||
|
_ "net/http"
|
||||||
|
_ "net/http/httptest"
|
||||||
|
_ "net/url"
|
||||||
|
_ "os"
|
||||||
|
_ "path/filepath"
|
||||||
|
_ "strings"
|
||||||
|
_ "syscall"
|
||||||
|
"testing"
|
||||||
|
_ "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewIptableResource(t *testing.T) {
|
||||||
|
i := NewIptable()
|
||||||
|
assert.NotNil(t, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIptableApplyResourceTransformation(t *testing.T) {
|
||||||
|
i := NewIptable()
|
||||||
|
assert.NotNil(t, i)
|
||||||
|
|
||||||
|
//e := f.Apply()
|
||||||
|
//assert.Equal(t, nil, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadIptable(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
testRule := NewIptable()
|
||||||
|
assert.NotNil(t, testRule)
|
||||||
|
|
||||||
|
declarationAttributes := `
|
||||||
|
id: 0
|
||||||
|
table: "filter"
|
||||||
|
chain: "INPUT"
|
||||||
|
source: "192.168.0.0/24"
|
||||||
|
destination: "192.168.0.1"
|
||||||
|
jump: "ACCEPT"
|
||||||
|
state: present
|
||||||
|
`
|
||||||
|
m := &MockCommand{
|
||||||
|
Executor: func(value any) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
Extractor: func(output []byte, target any) error {
|
||||||
|
testRule.Table = "filter"
|
||||||
|
testRule.Chain = "INPUT"
|
||||||
|
testRule.Id = 0
|
||||||
|
testRule.In = "eth0"
|
||||||
|
testRule.Source = "192.168.0.0/24"
|
||||||
|
testRule.State = "present"
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
e := testRule.LoadDecl(declarationAttributes)
|
||||||
|
assert.Nil(t, e)
|
||||||
|
testRule.ReadCommand = (*Command)(m)
|
||||||
|
// testRuleErr := testRule.Apply()
|
||||||
|
// assert.Nil(t, testRuleErr)
|
||||||
|
r, e := testRule.Read(ctx)
|
||||||
|
|
||||||
|
assert.Nil(t, e)
|
||||||
|
assert.NotNil(t, r)
|
||||||
|
assert.Equal(t, "eth0", testRule.In)
|
||||||
|
}
|
@ -38,6 +38,7 @@ func TestResolveId(t *testing.T) {
|
|||||||
testFile := NewResource("file://../../README.md")
|
testFile := NewResource("file://../../README.md")
|
||||||
assert.NotNil(t, testFile)
|
assert.NotNil(t, testFile)
|
||||||
|
|
||||||
|
testFile.(*File).normalizePath = true
|
||||||
absolutePath, e := filepath.Abs("../../README.md")
|
absolutePath, e := filepath.Abs("../../README.md")
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
{ "$ref": "http-declaration.jsonschema" },
|
{ "$ref": "http-declaration.jsonschema" },
|
||||||
{ "$ref": "user-declaration.jsonschema" },
|
{ "$ref": "user-declaration.jsonschema" },
|
||||||
{ "$ref": "exec-declaration.jsonschema" },
|
{ "$ref": "exec-declaration.jsonschema" },
|
||||||
{ "$ref": "network_route-declaration.jsonschema" }
|
{ "$ref": "network_route-declaration.jsonschema" },
|
||||||
|
{ "$ref": "iptable-declaration.jsonschema" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
internal/resource/schemas/iptable-declaration.jsonschema
Normal file
17
internal/resource/schemas/iptable-declaration.jsonschema
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"$id": "iptable-declaration.jsonschema",
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "iptable-declaration",
|
||||||
|
"type": "object",
|
||||||
|
"required": [ "type", "attributes" ],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Resource type name.",
|
||||||
|
"enum": [ "iptable" ]
|
||||||
|
},
|
||||||
|
"attributes": {
|
||||||
|
"$ref": "iptable.jsonschema"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
internal/resource/schemas/iptable.jsonschema
Normal file
68
internal/resource/schemas/iptable.jsonschema
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"$id": "iptable.jsonschema",
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "iptable",
|
||||||
|
"type": "object",
|
||||||
|
"required": [ "chain" ],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Rule table name"
|
||||||
|
},
|
||||||
|
"chain": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Rule chain name"
|
||||||
|
},
|
||||||
|
"destination": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Destination CIDR",
|
||||||
|
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Source CIDR",
|
||||||
|
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$"
|
||||||
|
},
|
||||||
|
"in": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Input ethernet device"
|
||||||
|
},
|
||||||
|
"out": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Output ethernet device"
|
||||||
|
},
|
||||||
|
"match": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Rule match extensions",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extension_flags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"proto": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Rule protocol",
|
||||||
|
"pattern": "^(tcp|udp|udplite|icmp|icmpv6|ESP|AH|sctp|mh|all|[0-9]+)$"
|
||||||
|
},
|
||||||
|
"jump": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user