cloudstack/systemvm/debian/opt/cloud/bin/passwd_server_ip.py
Rohit Yadav 85aee8d18d CLOUDSTACK-10013: SystemVM codebase refactorings and improvements
- Refactors and simplifies systemvm codebase file structures keeping
  the same resultant systemvm.iso packaging
- Password server systemd script and new postinit script that runs
  before sshd starts
- Fixes to keepalived and conntrackd config to make rVRs work again
- New /etc/issue featuring ascii based cloudmonkey logo/message and
  systemvmtemplate version
- SystemVM python codebase linted and tested. Added pylint/pep to
  Travis.
- iptables re-application fixes for non-VR systemvms.
- SystemVM template build fixes.
- Default secondary storage vm service offering boosted to have 2vCPUs
  and RAM equal to console proxy.
- Fixes to several marvin based smoke tests, especially rVR related
  tests. rVR tests to consider 3*advert_int+skew timeout before status
  is checked.

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2017-12-23 09:22:44 +05:30

195 lines
6.6 KiB
Python
Executable File

#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# 'License'); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Client usage examples;
# Getting password:
# wget -q -t 3 -T 20 -O - --header 'DomU_Request: send_my_password' <routerIP>:8080
# Send ack:
# wget -t 3 -T 20 -O - --header 'DomU_Request: saved_password' localhost:8080
# Save password only from within router:
# /opt/cloud/bin/savepassword.sh -v <IP> -p <password>
# curl --header 'DomU_Request: save_password' http://localhost:8080/ -F ip=<IP> -F password=<passwd>
import binascii
import cgi
import os
import sys
import syslog
import threading
import urlparse
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn #, ForkingMixIn
passMap = {}
secureToken = None
listeningAddress = '127.0.0.1'
lock = threading.RLock()
def getTokenFile():
return '/tmp/passwdsrvrtoken'
def getPasswordFile():
return '/var/cache/cloud/passwords-%s' % listeningAddress
def initToken():
global secureToken
if os.path.exists(getTokenFile()):
with open(getTokenFile(), 'r') as f:
secureToken = f.read()
if not secureToken:
secureToken = binascii.hexlify(os.urandom(16))
with open(getTokenFile(), 'w') as f:
f.write(secureToken)
def checkToken(token):
return token == secureToken
def loadPasswordFile():
try:
with file(getPasswordFile()) as f:
for line in f:
if '=' not in line: continue
key, value = line.strip().split('=', 1)
passMap[key] = value
except IOError:
pass
def savePasswordFile():
with lock:
try:
with file(getPasswordFile(), 'w') as f:
for ip in passMap:
f.write('%s=%s\n' % (ip, passMap[ip]))
f.close()
except IOError, e:
syslog.syslog('serve_password: Unable to save to password file %s' % e)
def getPassword(ip):
return passMap.get(ip, None)
def setPassword(ip, password):
if not ip or not password:
return
with lock:
passMap[ip] = password
def removePassword(ip):
with lock:
if ip in passMap:
del passMap[ip]
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
pass
class PasswordRequestHandler(BaseHTTPRequestHandler):
server_version = 'CloudStack Password Server'
sys_version = '4.x'
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.send_header('Server', 'CloudStack Password Server')
self.end_headers()
requestType = self.headers.get('DomU_Request')
clientAddress = self.client_address[0]
if requestType == 'send_my_password':
password = getPassword(clientAddress)
if not password:
self.wfile.write('saved_password')
syslog.syslog('serve_password: requested password not found for %s' % clientAddress)
else:
self.wfile.write(password)
syslog.syslog('serve_password: password sent to %s' % clientAddress)
elif requestType == 'saved_password':
removePassword(clientAddress)
savePasswordFile()
self.wfile.write('saved_password')
syslog.syslog('serve_password: saved_password ack received from %s' % clientAddress)
else:
self.send_response(400)
self.wfile.write('bad_request')
syslog.syslog('serve_password: bad_request from IP %s' % clientAddress)
return
def do_POST(self):
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD':'POST',
'CONTENT_TYPE':self.headers['Content-Type'],
})
self.send_response(200)
self.end_headers()
clientAddress = self.client_address[0]
if clientAddress not in ['localhost', '127.0.0.1', listeningAddress]:
syslog.syslog('serve_password: non-localhost IP trying to save password: %s' % clientAddress)
self.send_response(403)
return
if 'ip' not in form or 'password' not in form or 'token' not in form or self.headers.get('DomU_Request') != 'save_password':
syslog.syslog('serve_password: request trying to save password does not contain both ip and password')
self.send_response(403)
return
token = form['token'].value
if not checkToken(token):
syslog.syslog('serve_password: invalid save_password token received from %s' % clientAddress)
self.send_response(403)
return
ip = form['ip'].value
password = form['password'].value
if not ip or not password:
syslog.syslog('serve_password: empty ip/password[%s/%s] received from savepassword' % (ip, password))
return
syslog.syslog('serve_password: password saved for VM IP %s' % ip)
setPassword(ip, password)
savePasswordFile()
return
def log_message(self, format, *args):
return
def serve(HandlerClass = PasswordRequestHandler,
ServerClass = ThreadedHTTPServer):
global listeningAddress
if len(sys.argv) > 1:
listeningAddress = sys.argv[1]
server_address = (listeningAddress, 8080)
passwordServer = ServerClass(server_address, HandlerClass)
passwordServer.allow_reuse_address = True
sa = passwordServer.socket.getsockname()
initToken()
loadPasswordFile()
syslog.syslog('serve_password running on %s:%s' % (sa[0], sa[1]))
try:
passwordServer.serve_forever()
except KeyboardInterrupt:
syslog.syslog('serve_password shutting down')
passwordServer.socket.close()
except Exception, e:
syslog.syslog('serve_password hit exception %s -- died' % e)
passwordServer.socket.close()
if __name__ == '__main__':
serve()