secrules.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // Copyright 2019 Yunion
  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. package secrules
  15. import (
  16. "errors"
  17. "fmt"
  18. "net"
  19. "strconv"
  20. "strings"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/util/regutils"
  23. "yunion.io/x/pkg/utils"
  24. )
  25. type TSecurityRuleDirection string
  26. const (
  27. SecurityRuleIngress = TSecurityRuleDirection("in")
  28. SecurityRuleEgress = TSecurityRuleDirection("out")
  29. )
  30. type TSecurityRuleAction string
  31. const (
  32. SecurityRuleAllow = TSecurityRuleAction("allow")
  33. SecurityRuleDeny = TSecurityRuleAction("deny")
  34. )
  35. type TSecurityRuleRelation string
  36. const (
  37. RELATION_INDEPENDENT = TSecurityRuleRelation("INDEPENDT")
  38. RELATION_IDENTICAL = TSecurityRuleRelation("IDENTICAL")
  39. RELATION_SUBSET = TSecurityRuleRelation("SUBSET")
  40. RELATION_SUPERSET = TSecurityRuleRelation("SUPERSET")
  41. RELATION_NEXT_AHEAD = TSecurityRuleRelation("NEXT_AHEAD")
  42. RELATION_NEXT_AFTER = TSecurityRuleRelation("NEXT_AFTER")
  43. RELATION_OVERLAP = TSecurityRuleRelation("OVERLAP")
  44. )
  45. type SecurityRule struct {
  46. Priority int // [1, 100]
  47. Action TSecurityRuleAction
  48. // distinguish between
  49. // * "" (empty) allow all ipv4 and ipv6
  50. // * 0.0.0.0/0 allow all ipv4
  51. // * ::/0 allow all IPv6
  52. IPNet *net.IPNet
  53. Protocol string
  54. Direction TSecurityRuleDirection
  55. PortStart int
  56. PortEnd int
  57. Ports []int
  58. Description string
  59. }
  60. const (
  61. DIR_IN = "in"
  62. DIR_OUT = "out"
  63. )
  64. const SEG_ACTION = 0
  65. const SEG_IP = 1
  66. const SEG_PROTO = 2
  67. const SEG_PORT = 3
  68. const SEG_END = 4
  69. // const ACTION_ALLOW = "allow"
  70. // const ACTION_DENY = "deny"
  71. const PROTO_ANY = "any"
  72. const PROTO_TCP = "tcp"
  73. const PROTO_UDP = "udp"
  74. const PROTO_ICMP = "icmp"
  75. // non-wild protocols
  76. var protocolsSupported = []string{
  77. PROTO_TCP,
  78. PROTO_UDP,
  79. PROTO_ICMP,
  80. }
  81. var (
  82. ErrInvalidProtocolAny = errors.New("invalid protocol any with port option")
  83. ErrInvalidProtocolICMP = errors.New("invalid protocol icmp with port option")
  84. ErrInvalidPriority = errors.New("invalid priority")
  85. ErrInvalidDirection = errors.New("invalid direction")
  86. ErrInvalidAction = errors.New("invalid action")
  87. ErrInvalidNet = errors.New("invalid net")
  88. ErrInvalidIPAddr = errors.New("invalid ip address")
  89. ErrInvalidProtocol = errors.New("invalid protocol")
  90. ErrInvalidPortRange = errors.New("invalid port range")
  91. ErrInvalidPort = errors.New("invalid port")
  92. )
  93. func parsePortString(ps string) (int, error) {
  94. p, err := strconv.ParseUint(ps, 10, 16)
  95. if err != nil || p == 0 {
  96. return 0, ErrInvalidPort
  97. }
  98. return int(p), nil
  99. }
  100. func MustParseSecurityRule(s string) *SecurityRule {
  101. r, err := ParseSecurityRule(s)
  102. if err != nil {
  103. msg := fmt.Sprintf("parse security rule %q: %v", s, err)
  104. panic(msg)
  105. }
  106. return r
  107. }
  108. func ParseSecurityRule(pattern string) (*SecurityRule, error) {
  109. rule := &SecurityRule{}
  110. for _, direction := range []TSecurityRuleDirection{SecurityRuleIngress, SecurityRuleEgress} {
  111. if len(pattern) > len(direction)+1 && pattern[:len(direction)+1] == string(direction)+":" {
  112. rule.Direction, pattern = direction, strings.Replace(pattern, string(direction)+":", "", -1)
  113. break
  114. }
  115. }
  116. if rule.Direction == "" {
  117. return nil, ErrInvalidDirection
  118. }
  119. status := SEG_ACTION
  120. data := strings.Split(strings.TrimSpace(pattern), " ")
  121. index, seg := 0, ""
  122. for status != SEG_END {
  123. seg = ""
  124. if len(data) >= index+1 {
  125. seg = data[index]
  126. index++
  127. }
  128. if status == SEG_ACTION {
  129. if seg == string(SecurityRuleAllow) || seg == string(SecurityRuleDeny) {
  130. if seg == string(SecurityRuleAllow) {
  131. rule.Action = SecurityRuleAllow
  132. } else {
  133. rule.Action = SecurityRuleDeny
  134. }
  135. status = SEG_IP
  136. } else {
  137. return nil, ErrInvalidAction
  138. }
  139. } else if status == SEG_IP {
  140. matched := rule.ParseCIDR(seg)
  141. if !matched {
  142. index--
  143. }
  144. status = SEG_PROTO
  145. } else if status == SEG_PROTO {
  146. if seg == PROTO_ANY || seg == PROTO_ICMP {
  147. status = SEG_END
  148. } else if seg == PROTO_TCP || seg == PROTO_UDP {
  149. status = SEG_PORT
  150. } else {
  151. return nil, ErrInvalidProtocol
  152. }
  153. rule.Protocol = seg
  154. } else if status == SEG_PORT {
  155. status = SEG_END
  156. if err := rule.ParsePorts(seg); err != nil {
  157. return nil, err
  158. }
  159. return rule, nil
  160. }
  161. }
  162. return rule, nil
  163. }
  164. func (rule *SecurityRule) ParseCIDR(cidr string) bool {
  165. if regutils.MatchCIDR(cidr) || regutils.MatchCIDR6(cidr) {
  166. _, rule.IPNet, _ = net.ParseCIDR(cidr)
  167. return true
  168. }
  169. if regutils.MatchIP4Addr(cidr) {
  170. rule.IPNet = &net.IPNet{
  171. IP: net.ParseIP(cidr),
  172. Mask: net.CIDRMask(32, 32),
  173. }
  174. return true
  175. } else if regutils.MatchIP6Addr(cidr) {
  176. rule.IPNet = &net.IPNet{
  177. IP: net.ParseIP(cidr),
  178. Mask: net.CIDRMask(128, 128),
  179. }
  180. return true
  181. }
  182. rule.IPNet = nil
  183. return false
  184. }
  185. func (rule *SecurityRule) IsWildMatch() bool {
  186. return rule.IPNet == nil &&
  187. rule.Protocol == PROTO_ANY &&
  188. len(rule.Ports) == 0 &&
  189. ((rule.PortStart <= 0 && rule.PortEnd <= 0) || (rule.PortStart == 1 && rule.PortEnd == 65535))
  190. }
  191. func (rule SecurityRule) protoRelation(_rule SecurityRule) TSecurityRuleRelation {
  192. if rule.Direction != _rule.Direction {
  193. return RELATION_INDEPENDENT
  194. }
  195. if rule.Protocol == _rule.Protocol {
  196. if utils.IsInStringArray(rule.Protocol, []string{PROTO_ANY, PROTO_ICMP}) {
  197. return RELATION_IDENTICAL
  198. }
  199. if rule.PortStart <= 0 && _rule.PortStart <= 0 {
  200. return RELATION_IDENTICAL
  201. }
  202. if rule.PortStart > 0 && _rule.PortStart <= 0 {
  203. return RELATION_SUBSET
  204. }
  205. if rule.PortStart <= 0 && _rule.PortStart > 0 {
  206. return RELATION_SUPERSET
  207. }
  208. if rule.PortEnd+1 == _rule.PortStart {
  209. return RELATION_NEXT_AHEAD
  210. }
  211. if rule.PortStart == _rule.PortEnd+1 {
  212. return RELATION_NEXT_AFTER
  213. }
  214. if rule.PortEnd < _rule.PortStart || rule.PortStart > _rule.PortEnd {
  215. return RELATION_INDEPENDENT
  216. }
  217. if rule.PortStart == _rule.PortStart && rule.PortEnd == _rule.PortEnd {
  218. return RELATION_IDENTICAL
  219. }
  220. if rule.PortStart <= _rule.PortStart && rule.PortEnd >= _rule.PortEnd {
  221. return RELATION_SUPERSET
  222. }
  223. if rule.PortStart >= _rule.PortStart && rule.PortEnd <= _rule.PortEnd {
  224. return RELATION_SUBSET
  225. }
  226. return RELATION_OVERLAP
  227. }
  228. if rule.Protocol == PROTO_ANY {
  229. return RELATION_SUPERSET
  230. }
  231. if _rule.Protocol == PROTO_ANY {
  232. return RELATION_SUBSET
  233. }
  234. return RELATION_INDEPENDENT
  235. }
  236. func (rule SecurityRule) merge(r SecurityRule) SecurityRule {
  237. if rule.getIPKey() != r.getIPKey() {
  238. panic(fmt.Sprintf("rule %v ip addr not equal rule %v", rule, r))
  239. }
  240. if rule.Action != r.Action {
  241. panic(fmt.Sprintf("rule %v action not equal rule %v", rule, r))
  242. }
  243. rel := rule.protoRelation(r)
  244. switch rel {
  245. case RELATION_NEXT_AHEAD:
  246. rule.PortEnd = r.PortEnd
  247. return rule
  248. case RELATION_NEXT_AFTER:
  249. rule.PortStart = r.PortStart
  250. return rule
  251. case RELATION_OVERLAP:
  252. if rule.PortStart > r.PortStart {
  253. rule.PortStart = r.PortStart
  254. }
  255. if rule.PortEnd < r.PortEnd {
  256. rule.PortEnd = r.PortEnd
  257. }
  258. return rule
  259. }
  260. panic(fmt.Errorf("rule %s can not merge rule %s relation: %s", rule.String(), r.String(), rel))
  261. }
  262. func (rule SecurityRule) getIPKey() string {
  263. if rule.IPNet == nil {
  264. return ""
  265. }
  266. return rule.IPNet.String()
  267. }
  268. func (rule *SecurityRule) ParsePorts(seg string) error {
  269. if len(seg) == 0 {
  270. rule.Ports = []int{}
  271. rule.PortStart = -1
  272. rule.PortEnd = -1
  273. return nil
  274. } else if idx := strings.Index(seg, "-"); idx > -1 {
  275. segs := strings.SplitN(seg, "-", 2)
  276. var ps, pe int
  277. var err error
  278. if ps, err = parsePortString(segs[0]); err != nil {
  279. return ErrInvalidPortRange
  280. }
  281. if pe, err = parsePortString(segs[1]); err != nil {
  282. return ErrInvalidPortRange
  283. }
  284. if ps > pe {
  285. ps, pe = pe, ps
  286. }
  287. rule.PortStart = ps
  288. rule.PortEnd = pe
  289. } else if idx := strings.Index(seg, ","); idx > -1 {
  290. ports := make([]int, 0)
  291. segs := strings.Split(seg, ",")
  292. for _, seg := range segs {
  293. p, err := parsePortString(seg)
  294. if err != nil {
  295. return err
  296. }
  297. ports = append(ports, p)
  298. }
  299. rule.Ports = ports
  300. } else {
  301. p, err := parsePortString(seg)
  302. if err != nil {
  303. return err
  304. }
  305. rule.PortStart, rule.PortEnd = p, p
  306. }
  307. return nil
  308. }
  309. func (rule *SecurityRule) ValidateRule() error {
  310. if !utils.IsInStringArray(string(rule.Direction), []string{string(DIR_IN), string(DIR_OUT)}) {
  311. return ErrInvalidDirection
  312. }
  313. if !utils.IsInStringArray(string(rule.Action), []string{string(SecurityRuleAllow), string(SecurityRuleDeny)}) {
  314. return ErrInvalidAction
  315. }
  316. if !utils.IsInStringArray(rule.Protocol, []string{PROTO_ANY, PROTO_ICMP, PROTO_TCP, PROTO_UDP}) {
  317. return ErrInvalidProtocol
  318. }
  319. if rule.Protocol == PROTO_ICMP {
  320. if len(rule.Ports) > 0 || rule.PortStart > 0 || rule.PortEnd > 0 {
  321. return ErrInvalidProtocolICMP
  322. }
  323. }
  324. if rule.Protocol == PROTO_ANY {
  325. if len(rule.Ports) > 0 || rule.PortStart > 0 || rule.PortEnd > 0 {
  326. return ErrInvalidProtocolAny
  327. }
  328. }
  329. if len(rule.Ports) > 0 {
  330. for i := 0; i < len(rule.Ports); i++ {
  331. if rule.Ports[i] < 1 || rule.Ports[i] > 65535 {
  332. return ErrInvalidPort
  333. }
  334. }
  335. }
  336. if rule.PortStart > 0 || rule.PortEnd > 0 {
  337. if rule.PortStart < 1 {
  338. return ErrInvalidPortRange
  339. }
  340. if rule.PortStart > rule.PortEnd {
  341. return ErrInvalidPortRange
  342. }
  343. if rule.PortStart > 65535 || rule.PortEnd > 65535 {
  344. return ErrInvalidPortRange
  345. }
  346. }
  347. if rule.Priority < 1 || rule.Priority > 100 {
  348. return ErrInvalidPriority
  349. }
  350. return nil
  351. }
  352. func (rule *SecurityRule) GetPortsString() string {
  353. if rule.PortStart > 0 && rule.PortEnd > 0 {
  354. if rule.PortStart < rule.PortEnd {
  355. return fmt.Sprintf("%d-%d", rule.PortStart, rule.PortEnd)
  356. }
  357. if rule.PortStart == rule.PortEnd {
  358. return fmt.Sprintf("%d", rule.PortStart)
  359. }
  360. // panic on this badness
  361. log.Errorf("invalid port range %d-%d", rule.PortStart, rule.PortEnd)
  362. return ""
  363. }
  364. if len(rule.Ports) > 0 {
  365. ps := []string{}
  366. for _, p := range rule.Ports {
  367. ps = append(ps, fmt.Sprintf("%d", p))
  368. }
  369. return strings.Join(ps, ",")
  370. }
  371. return ""
  372. }
  373. func (rule *SecurityRule) String() (result string) {
  374. s := []string{}
  375. s = append(s, string(rule.Direction)+":"+string(rule.Action))
  376. if rule.IPNet != nil {
  377. cidr := rule.IPNet.String()
  378. if regutils.MatchCIDR(cidr) {
  379. if ones, _ := rule.IPNet.Mask.Size(); ones < 32 {
  380. s = append(s, cidr)
  381. } else {
  382. s = append(s, rule.IPNet.IP.String())
  383. }
  384. } else if regutils.MatchCIDR6(cidr) {
  385. if ones, _ := rule.IPNet.Mask.Size(); ones < 128 {
  386. s = append(s, cidr)
  387. } else {
  388. s = append(s, rule.IPNet.IP.String())
  389. }
  390. }
  391. }
  392. s = append(s, rule.Protocol)
  393. if rule.Protocol == PROTO_TCP || rule.Protocol == PROTO_UDP {
  394. port := rule.GetPortsString()
  395. if len(port) > 0 {
  396. s = append(s, port)
  397. }
  398. }
  399. return strings.Join(s, " ")
  400. }
  401. func (rule *SecurityRule) equals(r *SecurityRule) bool {
  402. // essence of String, bom
  403. s0 := rule.String()
  404. s1 := r.String()
  405. return s0 == s1
  406. }
  407. func (rule *SecurityRule) netEquals(r *SecurityRule) bool {
  408. net0 := rule.IPNet.String()
  409. net1 := r.IPNet.String()
  410. return net0 == net1
  411. }
  412. func (rule *SecurityRule) cutOut(r SecurityRule) SecurityRuleSet {
  413. srcs := newSecurityRuleSetCuts(*rule) // securityRuleCuts{securityRuleCut{r: *rule}}
  414. //a := srcs
  415. srcs = srcs.cutOutProtocol(r.Protocol)
  416. log.Debugf("cutOutProtocol: rule %s cut %s output %s", rule.String(), r.Protocol, srcs.String())
  417. srcs = srcs.cutOutIPNet(r.Protocol, r.IPNet)
  418. log.Debugf("cutOutIPNet: rule %s cut %s output %s", rule.String(), r.IPNet, srcs.String())
  419. if len(r.Ports) > 0 {
  420. srcs = srcs.cutOutPorts(r.Protocol, []uint16(newPortsFromInts(r.Ports...)))
  421. } else if r.PortStart > 0 && r.PortEnd > 0 {
  422. srcs = srcs.cutOutPortRange(r.Protocol, uint16(r.PortStart), uint16(r.PortEnd))
  423. } else {
  424. srcs = srcs.cutOutPortsAll()
  425. }
  426. //fmt.Printf("a %s\n", a)
  427. //fmt.Printf("b %s\n", srcs)
  428. srs := srcs.securityRuleSet()
  429. log.Debugf("rule %s cut %s output %s", rule.String(), r.String(), srs.String())
  430. return srs
  431. }