1+ # Licensed to the Apache Software Foundation (ASF) under one
2+ # or more contributor license agreements. See the NOTICE file
3+ # distributed with this work for additional information
4+ # regarding copyright ownership. The ASF licenses this file
5+ # to you under the Apache License, Version 2.0 (the
6+ # "License"); you may not use this file except in compliance
7+ # with the License. You may obtain a copy of the License at
8+ #
9+ # http://www.apache.org/licenses/LICENSE-2.0
10+ #
11+ # Unless required by applicable law or agreed to in writing,
12+ # software distributed under the License is distributed on an
13+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+ # KIND, either express or implied. See the License for the
15+ # specific language governing permissions and limitations
16+ # under the License.
17+ #
18+ # Automatically generated by addcopyright.py at 01/29/2013
19+ '''
20+ Created on Jan 2, 2013
21+
22+ @author: frank
23+ '''
24+ import cherrypy
25+ import sglib
26+ import xmlobject
27+ import types
28+ import uuid
29+ import os .path
30+ import sys
31+ import os
32+
33+ class SGRule (object ):
34+ def __init__ (self ):
35+ self .protocol = None
36+ self .start_port = None
37+ self .end_port = None
38+ self .allowed_ips = []
39+
40+ class IPSet (object ):
41+ IPSET_TYPE = 'hash:ip'
42+ def __init__ (self , setname , ips ):
43+ self .ips = ips
44+ self .name = setname
45+
46+ def create (self ):
47+ tmpname = str (uuid .uuid4 ()).replace ('-' , '' )[0 :30 ]
48+ sglib .ShellCmd ('ipset -N %s %s' % (tmpname , self .IPSET_TYPE ))()
49+ try :
50+ for ip in self .ips :
51+ sglib .ShellCmd ('ipset -A %s %s' % (tmpname , ip ))()
52+
53+ try :
54+ sglib .ShellCmd ('ipset -N %s %s' % (self .name , self .IPSET_TYPE ))()
55+ cherrypy .log ('created new ipset: %s' % self .name )
56+ except Exception :
57+ cherrypy .log ('%s already exists, no need to create new' % self .name )
58+ finally :
59+ sglib .ShellCmd ('ipset -W %s %s' % (tmpname , self .name ))()
60+ sglib .ShellCmd ('ipset -F %s' % tmpname )()
61+ sglib .ShellCmd ('ipset -X %s' % tmpname )()
62+
63+ @staticmethod
64+ def destroy_sets (sets_to_keep ):
65+ sets = sglib .ShellCmd ('ipset list' )()
66+ for s in sets .split ('\n ' ):
67+ if 'Name:' in s :
68+ set_name = s .split (':' , 1 )[1 ].strip ()
69+ if not set_name in sets_to_keep :
70+ sglib .ShellCmd ('ipset destroy %s' % set_name )()
71+ cherrypy .log ('destroyed unused ipset: %s' % set_name )
72+
73+ class SGAgent (object ):
74+ def __init__ (self ):
75+ pass
76+
77+ def _self_list (self , obj ):
78+ if isinstance (obj , types .ListType ):
79+ return obj
80+ else :
81+ return [obj ]
82+
83+ def set_rules (self , req ):
84+ body = req .body
85+ doc = xmlobject .loads (body )
86+ vm_name = doc .vmName .text_
87+ vm_id = doc .vmId .text_
88+ vm_ip = doc .vmIp .text_
89+ vm_mac = doc .vmMac .text_
90+ sig = doc .signature .text_
91+ seq = doc .sequenceNumber .text_
92+
93+ def parse_rules (rules , lst ):
94+ for i in self ._self_list (rules ):
95+ r = SGRule ()
96+ r .protocol = i .protocol .text_
97+ r .start_port = i .startPort .text_
98+ r .end_port = i .endPort .text_
99+ if hasattr (i , 'ip' ):
100+ for ip in self ._self_list (i .ip ):
101+ r .allowed_ips .append (ip .text_ )
102+ lst .append (r )
103+
104+ i_rules = []
105+ if hasattr (doc , 'ingressRules' ):
106+ parse_rules (doc .ingressRules , i_rules )
107+
108+ e_rules = []
109+ if hasattr (doc , 'egressRules' ):
110+ parse_rules (doc .egressRules , e_rules )
111+
112+ def create_chain (name ):
113+ try :
114+ sglib .ShellCmd ('iptables -F %s' % name )()
115+ except Exception :
116+ sglib .ShellCmd ('iptables -N %s' % name )()
117+
118+ def apply_rules (rules , chainname , direction , action , current_set_names ):
119+ create_chain (chainname )
120+ for r in i_rules :
121+ allow_any = False
122+ if '0.0.0.0/0' in r .allowed_ips :
123+ allow_any = True
124+ r .allowed_ips .remove ('0.0.0.0/0' )
125+
126+ if r .allowed_ips :
127+ setname = '_' .join ([chainname , r .protocol , r .start_port , r .end_port ])
128+ ipset = IPSet (setname , r .allowed_ips )
129+ ipset .create ()
130+ current_set_names .append (setname )
131+
132+ if r .protocol == 'all' :
133+ cmd = ['iptables -I' , chainname , '-m state --state NEW -m set --set' , setname , direction , '-j' , action ]
134+ sglib .ShellCmd (' ' .join (cmd ))()
135+ elif r .protocol != 'icmp' :
136+ port_range = ":" .join ([r .start_port , r .end_port ])
137+ cmd = ['iptables' , '-I' , chainname , '-p' , r .protocol , '-m' , r .protocol , '--dport' , port_range , '-m state --state NEW -m set --set' , setname , direction , '-j' , action ]
138+ sglib .ShellCmd (' ' .join (cmd ))()
139+ else :
140+ port_range = "/" .join ([r .start_port , r .end_port ])
141+ if r .start_port == "-1" :
142+ port_range = "any"
143+ cmd = ['iptables' , '-I' , i_chain_name , '-p' , 'icmp' , '--icmp-type' , port_range , '-m set --set' , setname , direction , '-j' , action ]
144+ sglib .ShellCmd (' ' .join (cmd ))()
145+
146+
147+ if allow_any and r .protocol != 'all' :
148+ if r .protocol != 'icmp' :
149+ port_range = ":" .join ([r .start_port , r .end_port ])
150+ cmd = ['iptables' , '-I' , chainname , '-p' , r .protocol , '-m' , r .protocol , '--dport' , port_range , '-m' , 'state' , '--state' , 'NEW' , '-j' , action ]
151+ sglib .ShellCmd (' ' .join (cmd ))()
152+ else :
153+ port_range = "/" .join ([r .start_port , r .end_port ])
154+ if r .start_port == "-1" :
155+ port_range = "any"
156+ cmd = ['iptables' , '-I' , i_chain_name , '-p' , 'icmp' , '--icmp-type' , port_range , '-j' , action ]
157+ sglib .ShellCmd (' ' .join (cmd ))()
158+
159+ current_sets = []
160+ i_chain_name = vm_name + '-in'
161+ apply_rules (i_rules , i_chain_name , 'src' , 'ACCEPT' , current_sets )
162+ e_chain_name = vm_name + '-eg'
163+ apply_rules (e_rules , e_chain_name , 'dst' , 'RETURN' , current_sets )
164+
165+ if e_rules :
166+ sglib .ShellCmd ('iptables -A %s -j RETURN' % e_chain_name )
167+ else :
168+ sglib .ShellCmd ('iptables -A %s -j DROP' % e_chain_name )
169+
170+ sglib .ShellCmd ('iptables -A %s -j DROP' % i_chain_name )
171+ IPSet .destroy_sets (current_sets )
172+
173+
174+ def echo (self , req ):
175+ cherrypy .log ("echo: I am alive" )
176+
177+ def index (self ):
178+ req = sglib .Request .from_cherrypy_request (cherrypy .request )
179+ cmd_name = req .headers ['command' ]
180+
181+ if not hasattr (self , cmd_name ):
182+ raise ValueError ("SecurityGroupAgent doesn't have a method called '%s'" % cmd_name )
183+ method = getattr (self , cmd_name )
184+
185+ return method (req )
186+ index .exposed = True
187+
188+ @staticmethod
189+ def start ():
190+ cherrypy .log .access_file = '/var/log/cs-securitygroup.log'
191+ cherrypy .log .error_file = '/var/log/cs-securitygroup.log'
192+ cherrypy .server .socket_host = '0.0.0.0'
193+ cherrypy .server .socket_port = 9988
194+ cherrypy .quickstart (SGAgent ())
195+
196+ @staticmethod
197+ def stop ():
198+ cherrypy .engine .exit ()
199+
200+ PID_FILE = '/var/run/cssgagent.pid'
201+ class SGAgentDaemon (sglib .Daemon ):
202+ def __init__ (self ):
203+ super (SGAgentDaemon , self ).__init__ (PID_FILE )
204+ self .is_stopped = False
205+ self .agent = SGAgent ()
206+ sglib .Daemon .register_atexit_hook (self ._do_stop )
207+
208+ def _do_stop (self ):
209+ if self .is_stopped :
210+ return
211+ self .is_stopped = True
212+ self .agent .stop ()
213+
214+ def run (self ):
215+ self .agent .start ()
216+
217+ def stop (self ):
218+ self .agent .stop ()
219+ super (SGAgentDaemon , self ).stop ()
220+
221+ def main ():
222+ usage = 'usage: python -c "from security_group_agent import cs_sg_agent; cs_sg_agent.main()" start|stop|restart'
223+ if len (sys .argv ) != 2 or not sys .argv [1 ] in ['start' , 'stop' , 'restart' ]:
224+ print usage
225+ sys .exit (1 )
226+
227+ cmd = sys .argv [1 ]
228+ agentdaemon = SGAgentDaemon ()
229+ if cmd == 'start' :
230+ agentdaemon .start ()
231+ elif cmd == 'stop' :
232+ agentdaemon .stop ()
233+ else :
234+ agentdaemon .restart ()
235+
236+ sys .exit (0 )
237+
0 commit comments