github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/network/iptables/iptables.go (about) 1 package iptables 2 3 import ( 4 "bytes" 5 "fmt" 6 "net" 7 "os/exec" 8 "strings" 9 10 "sync" 11 12 "github.com/cloudfoundry-incubator/garden" 13 "github.com/cloudfoundry-incubator/garden-linux/logging" 14 "github.com/cloudfoundry/gunk/command_runner" 15 "github.com/pivotal-golang/lager" 16 ) 17 18 var protocols = map[garden.Protocol]string{ 19 garden.ProtocolAll: "all", 20 garden.ProtocolTCP: "tcp", 21 garden.ProtocolICMP: "icmp", 22 garden.ProtocolUDP: "udp", 23 } 24 25 // NewGlobalChain creates a chain without an associated log chain. 26 // The chain is not created by this package (currently it is created in net.sh). 27 // It is an error to attempt to call Setup on this chain. 28 func NewGlobalChain(name string, runner command_runner.CommandRunner, logger lager.Logger) Chain { 29 logger = logger.Session("global-chain", lager.Data{ 30 "name": name, 31 }) 32 return &chain{name: name, logChainName: "", runner: &logging.Runner{runner, logger}, logger: logger} 33 } 34 35 // NewLoggingChain creates a chain with an associated log chain. 36 // This allows NetOut calls with the 'log' parameter to succesfully log. 37 func NewLoggingChain(name string, useKernelLogging bool, runner command_runner.CommandRunner, logger lager.Logger) Chain { 38 logger = logger.Session("logging-chain", lager.Data{ 39 "name": name, 40 "useKernelLogging": useKernelLogging, 41 }) 42 return &chain{ 43 name: name, 44 logChainName: name + "-log", 45 useKernelLogging: useKernelLogging, 46 loglessRunner: runner, 47 runner: &logging.Runner{runner, logger}, 48 logger: logger, 49 } 50 } 51 52 //go:generate counterfeiter . Chain 53 type Chain interface { 54 // Create the actual iptable chains in the underlying system. 55 // logPrefix defines the log prefix used for logging this chain. 56 Setup(logPrefix string) error 57 58 // Destroy the actual iptable chains in the underlying system 59 TearDown() error 60 61 AppendRule(source string, destination string, jump Action) error 62 DeleteRule(source string, destination string, jump Action) error 63 64 AppendNatRule(source string, destination string, jump Action, to net.IP) error 65 DeleteNatRule(source string, destination string, jump Action, to net.IP) error 66 67 PrependFilterRule(rule garden.NetOutRule) error 68 } 69 70 type chain struct { 71 mu sync.Mutex 72 name string 73 logChainName string 74 useKernelLogging bool 75 runner command_runner.CommandRunner 76 loglessRunner command_runner.CommandRunner 77 logger lager.Logger 78 } 79 80 func (ch *chain) Setup(logPrefix string) error { 81 ch.mu.Lock() 82 defer ch.mu.Unlock() 83 84 logger := ch.logger.Session("setup", lager.Data{ 85 "logChainName": ch.logChainName, 86 }) 87 logger.Debug("started") 88 89 if ch.logChainName == "" { 90 // we still use net.sh to set up global non-logging chains 91 panic("cannot set up chains without associated log chains") 92 } 93 94 ch.TearDown() 95 96 if err := ch.runner.Run(exec.Command("/sbin/iptables", "-w", "-N", ch.logChainName)); err != nil { 97 return fmt.Errorf("iptables: log chain setup: %v", err) 98 } 99 logger.Debug("created") 100 101 logParams := ch.buildLogParams(logPrefix) 102 appendFlags := []string{"-w", "-A", ch.logChainName, "-m", "conntrack", "--ctstate", "NEW,UNTRACKED,INVALID", "--protocol", "tcp"} 103 if err := ch.runner.Run(exec.Command("/sbin/iptables", append(appendFlags, logParams...)...)); err != nil { 104 return fmt.Errorf("iptables: log chain setup: %v", err) 105 } 106 logger.Debug("conntrack-set-up") 107 108 if err := ch.runner.Run(exec.Command("/sbin/iptables", "-w", "-A", ch.logChainName, "--jump", "RETURN")); err != nil { 109 return fmt.Errorf("iptables: log chain setup: %v", err) 110 } 111 logger.Debug("ending") 112 113 return nil 114 } 115 116 func (ch *chain) buildLogParams(logPrefix string) []string { 117 if ch.useKernelLogging { 118 return []string{"--jump", "LOG", "--log-prefix", logPrefix} 119 } else { 120 return []string{"--jump", "NFLOG", "--nflog-prefix", logPrefix, "--nflog-group", "1"} 121 } 122 } 123 124 func (ch *chain) TearDown() error { 125 logger := ch.logger.Session("teardown", lager.Data{ 126 "logChainName": ch.logChainName, 127 }) 128 logger.Debug("started") 129 if ch.logChainName == "" { 130 // we still use net.sh to tear down global non-logging chains 131 panic("cannot tear down chains without associated log chains") 132 } 133 134 // it's ok to skip logs here, we expect this to fail if this is a 135 // pre-creation teardown 136 ch.loglessRunner.Run(exec.Command("/sbin/iptables", "-w", "-F", ch.logChainName)) 137 logger.Debug("flushed") 138 ch.loglessRunner.Run(exec.Command("/sbin/iptables", "-w", "-X", ch.logChainName)) 139 logger.Debug("ending") 140 return nil 141 } 142 143 func (ch *chain) AppendRule(source string, destination string, jump Action) error { 144 return ch.Create(&rule{ 145 source: source, 146 destination: destination, 147 jump: jump, 148 }) 149 } 150 151 func (ch *chain) DeleteRule(source string, destination string, jump Action) error { 152 return ch.Destroy(&rule{ 153 source: source, 154 destination: destination, 155 jump: jump, 156 }) 157 } 158 159 func (ch *chain) AppendNatRule(source string, destination string, jump Action, to net.IP) error { 160 return ch.Create(&rule{ 161 typ: Nat, 162 source: source, 163 destination: destination, 164 jump: jump, 165 to: to, 166 }) 167 } 168 169 func (ch *chain) DeleteNatRule(source string, destination string, jump Action, to net.IP) error { 170 return ch.Destroy(&rule{ 171 typ: Nat, 172 source: source, 173 destination: destination, 174 jump: jump, 175 to: to, 176 }) 177 } 178 179 type singleRule struct { 180 Protocol garden.Protocol 181 Networks *garden.IPRange 182 Ports *garden.PortRange 183 ICMPs *garden.ICMPControl 184 Log bool 185 } 186 187 func (ch *chain) PrependFilterRule(r garden.NetOutRule) error { 188 logger := ch.logger.Session("prepend-filter-rule", lager.Data{"rule": r}) 189 logger.Debug("started") 190 if len(r.Ports) > 0 && !allowsPort(r.Protocol) { 191 return fmt.Errorf("Ports cannot be specified for Protocol %s", strings.ToUpper(protocols[r.Protocol])) 192 } 193 194 single := singleRule{ 195 Protocol: r.Protocol, 196 ICMPs: r.ICMPs, 197 Log: r.Log, 198 } 199 200 // It should still loop once even if there are no networks or ports. 201 for j := 0; j < len(r.Networks) || j == 0; j++ { 202 for i := 0; i < len(r.Ports) || i == 0; i++ { 203 204 // Preserve nils unless there are ports specified 205 if len(r.Ports) > 0 { 206 single.Ports = &r.Ports[i] 207 } 208 209 // Preserve nils unless there are networks specified 210 if len(r.Networks) > 0 { 211 single.Networks = &r.Networks[j] 212 } 213 214 if err := ch.prependSingleRule(single); err != nil { 215 return err 216 } 217 } 218 } 219 220 logger.Debug("ending") 221 return nil 222 } 223 224 func allowsPort(p garden.Protocol) bool { 225 return p == garden.ProtocolTCP || p == garden.ProtocolUDP 226 } 227 228 func (ch *chain) prependSingleRule(r singleRule) error { 229 params := []string{"-w", "-I", ch.name, "1"} 230 231 protocolString, ok := protocols[r.Protocol] 232 233 if !ok { 234 return fmt.Errorf("invalid protocol: %d", r.Protocol) 235 } 236 237 params = append(params, "--protocol", protocolString) 238 239 network := r.Networks 240 if network != nil { 241 if network.Start != nil && network.End != nil { 242 params = append(params, "-m", "iprange", "--dst-range", network.Start.String()+"-"+network.End.String()) 243 } else if network.Start != nil { 244 params = append(params, "--destination", network.Start.String()) 245 } else if network.End != nil { 246 params = append(params, "--destination", network.End.String()) 247 } 248 } 249 250 ports := r.Ports 251 if ports != nil { 252 if ports.End != ports.Start { 253 params = append(params, "--destination-port", fmt.Sprintf("%d:%d", ports.Start, ports.End)) 254 } else { 255 params = append(params, "--destination-port", fmt.Sprintf("%d", ports.Start)) 256 } 257 } 258 259 if r.ICMPs != nil { 260 icmpType := fmt.Sprintf("%d", r.ICMPs.Type) 261 if r.ICMPs.Code != nil { 262 icmpType = fmt.Sprintf("%d/%d", r.ICMPs.Type, *r.ICMPs.Code) 263 } 264 265 params = append(params, "--icmp-type", icmpType) 266 } 267 268 if r.Log { 269 params = append(params, "--goto", ch.logChainName) 270 } else { 271 params = append(params, "--jump", "RETURN") 272 } 273 274 ch.logger.Debug("prepend-filter-rule", lager.Data{"parms": params}) 275 276 var stderr bytes.Buffer 277 cmd := exec.Command("/sbin/iptables", params...) 278 cmd.Stderr = &stderr 279 if err := ch.runner.Run(cmd); err != nil { 280 return fmt.Errorf("iptables: %v, %v", err, stderr.String()) 281 } 282 ch.logger.Debug("prependSingleRule-finished") 283 284 return nil 285 } 286 287 type rule struct { 288 typ Type 289 source string 290 destination string 291 to net.IP 292 jump Action 293 } 294 295 func (n *rule) create(chain string, runner command_runner.CommandRunner) error { 296 return runner.Run(exec.Command("/sbin/iptables", flags("-A", chain, n)...)) 297 } 298 299 func (n *rule) destroy(chain string, runner command_runner.CommandRunner) error { 300 return runner.Run(exec.Command("/sbin/iptables", flags("-D", chain, n)...)) 301 } 302 303 func flags(action, chain string, n *rule) []string { 304 rule := []string{"-w"} 305 306 if n.typ != "" { 307 rule = append(rule, "-t", string(n.typ)) 308 } 309 310 rule = append(rule, action, chain) 311 312 if n.source != "" { 313 rule = append(rule, "--source", n.source) 314 } 315 316 if n.destination != "" { 317 rule = append(rule, "--destination", n.destination) 318 } 319 320 rule = append(rule, "--jump", string(n.jump)) 321 322 if n.to != nil { 323 rule = append(rule, "--to", string(n.to.String())) 324 } 325 326 return rule 327 } 328 329 type Destroyable interface { 330 Destroy() error 331 } 332 333 type creater interface { 334 create(chain string, runner command_runner.CommandRunner) error 335 } 336 337 type destroyer interface { 338 destroy(chain string, runner command_runner.CommandRunner) error 339 } 340 341 func (c *chain) Create(rule creater) error { 342 return rule.create(c.name, c.runner) 343 } 344 345 func (c *chain) Destroy(rule destroyer) error { 346 return rule.destroy(c.name, c.runner) 347 } 348 349 type Action string 350 351 const ( 352 Return Action = "RETURN" 353 SourceNAT = "SNAT" 354 Reject = "REJECT" 355 Drop = "DROP" 356 ) 357 358 type Type string 359 360 const ( 361 Nat Type = "nat" 362 )