package asa import ( "errors" "fmt" "regexp" "sort" "strconv" "strings" "github.com/gofrs/uuid" "github.com/sirupsen/logrus" "github.rackspace.com/gorax/asaapi/pkg/asaModels" "github.rackspace.com/gorax/asaapi/pkg/asaModels/modules" "github.rackspace.com/gorax/clients/asahttp" "github.rackspace.com/gorax/clients/coreAPI" "github.rackspace.com/gorax/clients/mydp" "github.rackspace.com/gorax/clients/sshAPI" "github.rackspace.com/gorax/tools/util" ) type ( ASAConfig struct { AAAConfig *asaModels.AAAConfig `json:"aaa_config"` AAAServers *asaModels.AAAGroups `json:"aaa_servers"` AccessGroups []*asaModels.AccessGroup `json:"access_groups,omitempty"` AccessLists []*asaModels.AccessList `json:"access_lists,omitempty"` Banner []string `json:"banner"` BootList []*asaModels.BootFiles `json:"boot_system_list"` Clock asaModels.Clock `json:"clock"` Crypto asaModels.Crypto `json:"crypto,omitempty"` DHCPServer asaModels.DHCPServer `json:"dhcp"` DomainName string `json:"domain-name"` EnablePassword string `json:"enable"` Failover *asaModels.Failoverconfig `json:"failover_config"` GroupPolicies []*asaModels.GroupPolicy `json:"group_policies,omitempty"` Hostname string `json:"hostname"` HTTP asaModels.HTTPConfig `json:"http"` Interfaces []*asaModels.InterfaceConfig `json:"interface_config"` IPReversePathInts []string `json:"ip_verify_reverse_path_interfaces"` Logging asaModels.Logging `json:"logging"` Names asaModels.Names `json:"names,omitempty"` Nat *asaModels.Nat `json:"nat"` ObjectGroups asaModels.ObjectGroups `json:"object_groups,omitempty"` Objects asaModels.Objects `json:"objects,omitempty"` PasswordPolicy asaModels.PasswordPolicy `json:"password_policy,omitempty"` PolicyMaps asaModels.PolicyMaps `json:"policy_maps"` Prompt string `json:"prompt"` ProxyARP []*asaModels.ProxyARP `json:"proxy_arp"` ServicePolicies asaModels.ServicePolicies `json:"service_policies"` SNMP asaModels.SNMPConfig `json:"snmp_config"` SSHConfig asaModels.SSHConfig `json:"ssh_config"` SSLConfig *asaModels.SSLConfig `json:"ssl_config"` Statics []*asaModels.StaticNat `json:"static_nat"` Telnet asaModels.Telnet `json:"telnet"` TunnelGroups []*asaModels.TunnelGroup `json:"tunnel_groups,omitempty"` UserNames []*asaModels.UserName `json:"user_names"` WebVPN asaModels.WebVPN `json:"webvpn"` } ASAStatus struct { ACL []*asaModels.ShowAccessList `json:"access_lists"` ARP []*asaModels.ARPEntry `json:"arp_table"` Conns []*asaModels.ConnEntry `json:"connections"` Context asaModels.ContextInfo `json:"context"` CheckSum asaModels.CheckSum `json:"checksum"` Disk *asaModels.Disk `json:"disk"` Failover *asaModels.FailoverStatus `json:"failover"` Interfaces []*asaModels.InterfaceStatus `json:"interfaces"` Inventory asaModels.Inventory `json:"inventory"` Nat asaModels.NatStatus `json:"nat"` Routes []*asaModels.RouteEntry `json:"routes"` Startup asaModels.Startup `json:"startup"` Version *asaModels.Version `json:"version"` Xlate asaModels.Xlate `json:"xlate_table"` } ) type ASA struct { Mode string `json:"mode,omitempty"` Config ASAConfig `json:"configuration_items"` Device int `json:"device_number"` Failover *int `json:"failover_device"` Status ASAStatus `json:"status_items"` Modules map[string]modules.Module `json:"modules"` sshClient *sshAPI.SSHClient httpClient asahttp.ASAHttp Info *coreAPI.DeviceInfo dpInfo mydp.Device coreapi *coreAPI.Client changeLog []*sshAPI.Command log logrus.Ext1FieldLogger uuid *uuid.UUID useDRAC bool } func (a *ASA) AppendCommand(c *sshAPI.SSHCommand) { a.changeLog = append(a.changeLog, c.SSHCommand()) } func (a *ASA) AppendCommands(c *sshAPI.SSHCommands) { a.changeLog = append(a.changeLog, c.Commands...) } func (a *ASA) ChangeLog() []*sshAPI.Command { return a.changeLog } func (a *ASA) RunCommandList(cmds []string) error { cmdList := sshAPI.CommandList(cmds, 15, "#") res, err := a.sshClient.PostCommands(cmdList) if err != nil { return err } a.AppendCommands(res) return nil } func (a *ASA) DataCenter() string { return a.Info.DataCenter } func (a *ASA) IPAddress() *string { return &a.Info.PrimaryIP } func (a *ASA) DeviceData() *coreAPI.DeviceInfo { return a.Info } func (a *ASA) DeviceNum() int { if a.Info == nil { return 0 } if n, err := strconv.Atoi(a.Info.ID); err == nil { return n } return 0 } // ObjectGroupInUse looks to see if a given object-group is in use inside an ACL or another object-group func (a *ASA) ObjectGroupInUse(n string) error { a.Config.AccessLists = a.EmptyAccessLists() a.GetAccessLists() // Check ACL's for _, acl := range a.Config.AccessLists { for _, rule := range acl.Rules { se, err := regexp.Compile(n) if err != nil { return err } if se.MatchString(*rule) { return errors.New(fmt.Sprintf("Object-group %s is in use: access-list %s %s", n, acl.Name, *rule)) } } } // Check other ObjectGroups a.GetObjectGroups() for _, p := range a.Config.ObjectGroups.Icmp { for _, m := range p.IcmpObj { if *m.Name == n { return errors.New(fmt.Sprintf("Object-group %s is in use in object-group %s", n, *p.Name)) } } } for _, p := range a.Config.ObjectGroups.Network { for _, m := range p.NetworkObj { if m.Name != nil { if *m.Name == n { return errors.New(fmt.Sprintf("Object-group %s is in use in object-group %s", n, *p.Name)) } } } } for _, p := range a.Config.ObjectGroups.Service { for _, m := range p.ServiceObj { if m.Name == n { return errors.New(fmt.Sprintf("Object-group %s is in use in object-group %s", n, *p.Name)) } } } return nil } func OpenContext(device int, token, context string, log ...logrus.Ext1FieldLogger) (*ASA, error) { c, err := coreAPI.New(token) if err != nil { return nil, err } a, err := NewASAToken(c, device, log...) if err != nil { return nil, err } a.sshClient.Context = &context return a, nil } func NewASA(token string, device int, log ...logrus.Ext1FieldLogger) (*ASA, error) { c, err := coreAPI.New(token) if err != nil { return nil, err } return NewASAToken(c, device, log...) } func NewHTTPASA(token string, device int, user, pass string, log ...logrus.Ext1FieldLogger) (*ASA, error) { c, err := coreAPI.New(token) if err != nil { return nil, err } return NewASAHTTPToken(c, device, user, pass, log...) } func NewASAHTTPToken(c *coreAPI.Client, device int, user, pass string, log ...logrus.Ext1FieldLogger) (*ASA, error) { d, err := c.DeviceInfo("asa", device, false) if err != nil { return nil, err } // Only if we cannot find an IP address, attempt a core refresh, so we do not // overload coreapi with CTK calls. Referenced JIRA NI-928 if d.PrimaryIP == "" && d.DracIP == "" { d, err = c.DeviceInfo("asa", device, true) if err != nil { return nil, err } } return NewCoreASA(c, d, user, pass, log...) } func NewASAToken(c *coreAPI.Client, device int, log ...logrus.Ext1FieldLogger) (*ASA, error) { d, err := c.DeviceInfo("asa", device, false) if err != nil { return nil, err } // Only if we cannot find an IP address, attempt a core refresh, so we do not // overload coreapi with CTK calls. Referenced JIRA NI-928 if d.PrimaryIP == "" && d.DracIP == "" { d, err = c.DeviceInfo("asa", device, true) if err != nil { return nil, err } } return NewCoreDevice(c, d, log...) } // AsaCreate is for backwards compatibility to allow outside programs to use AsaCreate, even though it's been replaced by NewASAToken func AsaCreate(c *coreAPI.Client, device int, log ...logrus.Ext1FieldLogger) (*ASA, error) { return NewASAToken(c, device, log...) } func NewPIXToken(c *coreAPI.Client, device int, log ...logrus.Ext1FieldLogger) (*ASA, error) { d, err := c.DeviceInfo("pix", device, false) if err != nil { return nil, err } return NewCorePIX(c, d, log...) } func OpenCoreDeviceContext(device int, c *coreAPI.Client, log ...logrus.Ext1FieldLogger) (*ASA, error) { a, err := NewASAToken(c, device, log...) if err != nil { return nil, err } if strings.Contains(a.Info.PlatformModel, "Cisco ASA 5505") { return a, nil } if err := a.GetContext(); err != nil { return nil, err } if a.Status.Context.Mode == "multiple" { context := "system" a.sshClient.Context = &context } return a, nil } func NewCoreDevice(c *coreAPI.Client, d *coreAPI.DeviceInfo, log ...logrus.Ext1FieldLogger) (*ASA, error) { if d == nil { return nil, errors.New("*coreAPI.DeviceInfo required") } uid, err := uuid.NewV4() if err != nil { return nil, err } sa, err := sshAPI.NewSSHAPIClient(c, d, "asa", true, uid) if err != nil { return nil, err } return &ASA{ sshClient: sa, Failover: func() *int { if d.FailoverDevice == nil { return nil } if i, err := strconv.Atoi(d.FailoverDevice.FailoverID); err == nil { return &i } return nil }(), Device: util.Atoi(d.ID), Info: d, log: selectLog(log...), }, nil } func NewMyDpDevice(deviceID int, c *mydp.Client, core *coreAPI.Client, user, pass string, uid uuid.UUID, log ...logrus.Ext1FieldLogger) (*ASA, error) { dpDevice, err := c.DeviceByDeviceID(deviceID) if err != nil { return nil, err } device := &coreAPI.DeviceInfo{ PrimaryIP: dpDevice.PrimaryIP, Name: dpDevice.Name, DataCenter: dpDevice.DatacenterTitle, Status: dpDevice.Disabled, PlatformModel: dpDevice.SpecificDeviceType, AccountInfo: coreAPI.AccountInfo{ Name: dpDevice.CustomerName, Number: dpDevice.CustomerID, }, } sa, err := sshAPI.NewDPSSHAPIClient(*dpDevice, core, "asa", true, uid) if err != nil { return nil, err } ha, err := asahttp.NewASA(device, user, pass, true) if err != nil { return nil, err } return &ASA{ sshClient: sa, httpClient: *ha, Device: util.Atoi(dpDevice.DeviceID), dpInfo: *dpDevice, log: selectLog(log...), }, nil } func NewCoreASA(c *coreAPI.Client, d *coreAPI.DeviceInfo, user, pass string, log ...logrus.Ext1FieldLogger) (*ASA, error) { if d == nil { return nil, errors.New("Empty coreapi device data.") } sa, err := sshAPI.NewSSHClient(c, "asa", util.Atoi(d.ID), true) if err != nil { return nil, err } ha, err := asahttp.NewASA(d, user, pass, true) if err != nil { return nil, err } return &ASA{ sshClient: sa, httpClient: *ha, Failover: func() *int { if d.FailoverDevice == nil { return nil } if i, err := strconv.Atoi(d.FailoverDevice.FailoverID); err == nil { return &i } return nil }(), Device: util.Atoi(d.ID), Info: d, log: selectLog(log...), }, nil } func NewCorePIX(c *coreAPI.Client, d *coreAPI.DeviceInfo, log ...logrus.Ext1FieldLogger) (*ASA, error) { if d == nil { return nil, errors.New("Empty coreapi device data.") } sa, err := sshAPI.NewSSHClient(c, "pix", util.Atoi(d.ID), true) if err != nil { return nil, err } return &ASA{ sshClient: sa, Failover: func() *int { if d.FailoverDevice == nil { return nil } if i, err := strconv.Atoi(d.FailoverDevice.FailoverID); err == nil { return &i } return nil }(), Device: util.Atoi(d.ID), Info: d, log: selectLog(log...), }, nil } func NewActiveASA(devices []int, token string, log ...logrus.Ext1FieldLogger) (*ASA, error) { c, err := coreAPI.New(token) if err != nil { return nil, err } sort.Ints(devices) for _, d := range devices { fw, err := NewASAToken(c, d, log...) if err != nil { continue } if err = fw.IsActive(); err == nil { return fw, nil } } return nil, fmt.Errorf("No accessible devices found in %v", devices) } func OpenActive(c *coreAPI.Client, device int, context string, log ...logrus.Ext1FieldLogger) (*ASA, error) { fw, err := NewASAToken(c, device, log...) if err != nil { return nil, err } if context != "" { fw.sshClient.Context = &context } if err = fw.IsActive(); err == nil { return fw, nil } if fw.Info.FailoverDevice == nil { return nil, errors.New("No failover data found.") } if n, err := strconv.Atoi(fw.Info.FailoverDevice.FailoverID); err == nil { fw, err = NewASAToken(c, n, log...) if err != nil { return nil, err } if context != "" { fw.sshClient.Context = &context } if err = fw.IsActive(); err == nil { return fw, nil } } return nil, errors.New("Unable to determine active device.") } func OpenStandby(c *coreAPI.Client, device int, context string, log ...logrus.Ext1FieldLogger) (*ASA, error) { fw, err := NewASAToken(c, device, log...) if err != nil { return nil, err } if context != "" { fw.sshClient.Context = &context } if err = fw.IsStandby(); err == nil { return fw, nil } if fw.Info.FailoverDevice == nil { return nil, errors.New("No failover data found.") } if n, err := strconv.Atoi(fw.Info.FailoverDevice.FailoverID); err == nil { fw, err = NewASAToken(c, n, log...) if err != nil { return nil, err } if context != "" { fw.sshClient.Context = &context } if err = fw.IsStandby(); err == nil { return fw, nil } } return nil, errors.New("Unable to determine standby device.") }