Debugged cloud-init script; added script to get user cert.

This commit is contained in:
Pino de Candia 2017-11-21 01:31:36 -06:00
parent 6075058e1d
commit d6f4f557ac
6 changed files with 192 additions and 55 deletions

View File

@ -1,11 +1,87 @@
#cloud-config #cloud-config
mounts:
- [ /dev/disk/by-label/config-2, /mnt/config ]
packages:
- python
- python-requests
write_files: write_files:
- path: /etc/ssh/auth_principals/ubuntu - path: /root/setup-ssh.py
content: webRoot permissions: '0700'
- path: /etc/ssh/ca_users.pub owner: root:root
content: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDM+YVCEZ4xCqBIGOQOEsGzBzOFS3JNDtPxLAviBMtS4zCwuGmOMvAvatKtPY5E9JMnkhI72faJnwYc4w/pnXf4Sh6AnLfwcOoQ6U16iucfY8tPOeFQhKJokSRdwnfm08QMOHN0xzCA/tL6HHZgPXGHUgTL18kkjv5Zk5Nv1H/ciuOSz24edo94Fu9eIQkK1pUhdejC6hDKdbki/c/3coZU4ZNDdtIpRlGnrUNTaAIq+E0TYEZkgClglTlBQOTvUoRkxEng/U23dfBCCz5DfewfA+6higUil5lIvidbaFjUiTMox38w9fM0wzUUs3o5pC9X/H3BE4mBrfpS9VmYHgll root@Bamboo content: |
import json
import requests
import os
import subprocess
import uuid
def getVendordataFromConfigDrive():
path = '/mnt/config/openstack/latest/vendor_data2.json'
with open(path, 'r') as f:
json_string = f.read()
return json.loads(json_string)
def getInstanceAndProjectIdFromConfigDrive():
path = '/mnt/config/openstack/latest/meta_data.json'
with open(path, 'r') as f:
json_string = f.read()
metadata = json.loads(json_string)
assert 'uuid' in metadata
assert 'project_id' in metadata
return str(uuid.UUID(metadata['uuid'], version=4)), str(uuid.UUID(metadata['project_id'], version=4))
vendordata = getVendordataFromConfigDrive()
instance_id, project_id = getInstanceAndProjectIdFromConfigDrive()
assert 'tatu' in vendordata
tatu = vendordata['tatu']
assert 'token' in tatu
assert 'auth_pub_key_user' in tatu
assert 'principals' in tatu
principals = tatu['principals'].split(',')
with open('/etc/ssh/ssh_host_rsa_key.pub', 'r') as f:
host_key_pub = f.read()
server = 'http://172.24.4.1:18321'
hostcert_request = {
'token_id': tatu['token'],
'host_id': instance_id,
'key.pub': host_key_pub
}
print 'Request the host certificate.'
response = requests.post(
# Hard-coded SSHaaS API address will only work for devstack and requires
# routing and SNAT or DNAT.
# This eventually needs to be either:
# 1) 169.254.169.254 if there's a SSHaaS-proxy; OR
# 2) the real address of the API, possibly supplied in the vendordata and
# still requiring routing and SNAT or DNAT.
server + '/hostcerts',
data=json.dumps(hostcert_request)
)
assert response.status_code == 201
assert 'location' in response.headers
location = response.headers['location']
response = requests.get(server + location)
hostcert = json.loads(response.content)
assert 'host_id' in hostcert
assert hostcert['host_id'] == instance_id
assert 'fingerprint' in hostcert
assert 'auth_id' in hostcert
auth_id = str(uuid.UUID(hostcert['auth_id'], version=4))
assert auth_id == project_id
assert 'key-cert.pub' in hostcert
print 'Begin writing files.'
# Write the host's certificate
with open('/etc/ssh/ssh_host_rsa_key-cert.pub', 'w') as f:
f.write(hostcert['key-cert.pub'])
# Write the authorized principals file
os.mkdir('/etc/ssh/auth_principals')
with open('/etc/ssh/auth_principals/ubuntu', 'w') as f:
for p in principals:
f.write(p + os.linesep)
# Write the User CA public key file
with open('/etc/ssh/ca_user.pub', 'w') as f:
f.write(tatu['auth_pub_key_user'])
print 'All tasks completed.'
runcmd: runcmd:
- python /root/setup-ssh.py > /var/log/setup-ssh.log 2>&1
- sed -i -e '$aTrustedUserCAKeys /etc/ssh/ca_user.pub' /etc/ssh/sshd_config - sed -i -e '$aTrustedUserCAKeys /etc/ssh/ca_user.pub' /etc/ssh/sshd_config
- sed -i -e '$aAuthorizedPrincipalsFile /etc/ssh/auth_principals/%u' /etc/ssh/sshd_config - sed -i -e '$aAuthorizedPrincipalsFile /etc/ssh/auth_principals/%u' /etc/ssh/sshd_config
- sed -i -e '$aHostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub' /etc/ssh/sshd_config
- systemctl restart ssh - systemctl restart ssh

View File

@ -2,20 +2,15 @@ import json
import requests import requests
import os import os
import subprocess import subprocess
import uuid
def getVendordataFromMetadataAPI(): def getVendordataFromMetadataAPI():
response = requests.get( response = requests.get(
'http://169.254.169.254/openstack/2016-10-06/vendor_data2.json', 'http://169.254.169.254/openstack/latest/vendor_data2.json',
) )
assert response.status_code == 200 assert response.status_code == 200
return json.loads(response.content) return json.loads(response.content)
def getVendordataFromConfigDrive():
path = '/mnt/openstack/2016-10-06/vendor_data2.json'
with open(path, 'r') as f:
json_string = f.read()
return json.loads(json_string)
def getInstanceAndProjectIdFromMetadataAPI(): def getInstanceAndProjectIdFromMetadataAPI():
response = requests.get( response = requests.get(
'http://169.254.169.254/openstack/latest/meta_data.json', 'http://169.254.169.254/openstack/latest/meta_data.json',
@ -26,35 +21,44 @@ def getInstanceAndProjectIdFromMetadataAPI():
assert 'project_id' in metadata assert 'project_id' in metadata
return metadata['uuid'], metadata['project_id'] return metadata['uuid'], metadata['project_id']
def getVendordataFromConfigDrive():
path = '/mnt/config/openstack/latest/vendor_data2.json'
with open(path, 'r') as f:
json_string = f.read()
return json.loads(json_string)
def getInstanceAndProjectIdFromConfigDrive(): def getInstanceAndProjectIdFromConfigDrive():
path = '/mnt/openstack/latest/meta_data.json' path = '/mnt/config/openstack/latest/meta_data.json'
with open(path, 'r') as f: with open(path, 'r') as f:
json_string = f.read() json_string = f.read()
metadata = json.loads(json_string) metadata = json.loads(json_string)
assert 'uuid' in metadata assert 'uuid' in metadata
assert 'project_id' in metadata assert 'project_id' in metadata
return metadata['uuid'], metadata['project_id'] return str(uuid.UUID(metadata['uuid'], version=4)), str(uuid.UUID(metadata['project_id'], version=4))
#vendordata = getVendordataFromConfigDrive() vendordata = getVendordataFromConfigDrive()
vendordata = getVendordataFromMetadataAPI() #vendordata = getVendordataFromMetadataAPI()
#instance_id = getInstanceIdFromConfigDrive() instance_id, project_id = getInstanceAndProjectIdFromConfigDrive()
instance_id, project_id = getInstanceIdFromMetadataAPI() #instance_id, project_id = getInstanceIdFromMetadataAPI()
assert 'sshaas' in vendordata assert 'tatu' in vendordata
sshaas = vendordata['sshaas'] tatu = vendordata['tatu']
assert 'token' in sshaas assert 'token' in tatu
assert 'auth_pub_key_user' in sshaas assert 'auth_pub_key_user' in tatu
assert 'principals' in sshaas assert 'principals' in tatu
principals = sshaas['principals'].split(',') principals = tatu['principals'].split(',')
with open('~/.ssh/id_rsa.pub', 'r') as f: with open('/etc/ssh/ssh_host_rsa_key.pub', 'r') as f:
host_key_pub = f.read() host_key_pub = f.read()
server = 'http://172.24.4.1:18321'
hostcert_request = { hostcert_request = {
'token_id': sshaas['token'], 'token_id': tatu['token'],
'host_id': instance_id, 'host_id': instance_id,
'key.pub': host_key_pub 'key.pub': host_key_pub
} }
response = requests.post( response = requests.post(
# Hard-coded SSHaaS API address will only work for devstack and requires # Hard-coded SSHaaS API address will only work for devstack and requires
# routing and SNAT or DNAT. # routing and SNAT or DNAT.
@ -62,27 +66,27 @@ response = requests.post(
# 1) 169.254.169.254 if there's a SSHaaS-proxy; OR # 1) 169.254.169.254 if there's a SSHaaS-proxy; OR
# 2) the real address of the API, possibly supplied in the vendordata and # 2) the real address of the API, possibly supplied in the vendordata and
# still requiring routing and SNAT or DNAT. # still requiring routing and SNAT or DNAT.
'http://localhost:8000/hostcerts', server + '/hostcerts',
data=json.dumps(hostcert_request) data=json.dumps(hostcert_request)
) )
assert response.status_code == 201 assert response.status_code == 201
assert 'location' in response.headers assert 'location' in response.headers
location = response.headers['location'] location = response.headers['location']
print location
response = requests.get( response = requests.get(server + location)
'http://169.254.169.254' + location
)
hostcert = json.loads(response.content) hostcert = json.loads(response.content)
assert 'host_id' in metadata assert 'host_id' in hostcert
assert metadata['host_id'] == instance_id assert hostcert['host_id'] == instance_id
assert 'fingerprint' in metadata assert 'fingerprint' in hostcert
assert 'auth_id' in metadata assert 'auth_id' in hostcert
assert metadata['auth_id'] == project_id auth_id = str(uuid.UUID(hostcert['auth_id'], version=4))
assert 'key-cert.pub' in metadata assert auth_id == project_id
assert 'key-cert.pub' in hostcert
# Write the host's certificate # Write the host's certificate
with open('/etc/ssh/ssh_host_rsa_key-cert.pub', 'w') as f: with open('/etc/ssh/ssh_host_rsa_key-cert.pub', 'w') as f:
f.write(metadata['key-cert.pub']) f.write(hostcert['key-cert.pub'])
# Write the authorized principals file # Write the authorized principals file
os.mkdir('/etc/ssh/auth_principals') os.mkdir('/etc/ssh/auth_principals')
@ -90,11 +94,11 @@ with open('/etc/ssh/auth_principals/ubuntu', 'w') as f:
for p in principals: for p in principals:
f.write(p + os.linesep) f.write(p + os.linesep)
# Write the UserCA public key file # Write the User CA public key file
with open('/etc/ssh/user_ca.pub', 'w') as f: with open('/etc/ssh/ca_user.pub', 'w') as f:
f.write(sshaas['auth_pub_key_user']) f.write(tatu['auth_pub_key_user'])
subprocess.check_output("sed -i -e '$aTrustedUserCAKeys /etc/ssh/user_ca.pub' /etc/ssh/sshd_config") subprocess.check_output("sed -i -e '$aTrustedUserCAKeys /etc/ssh/ca_user.pub' /etc/ssh/sshd_config")
subprocess.check_output("sed -i -e '$aAuthorizedPrincipalsFile /etc/ssh/auth_principals/%u' /etc/ssh/sshd_config") subprocess.check_output("sed -i -e '$aAuthorizedPrincipalsFile /etc/ssh/auth_principals/%u' /etc/ssh/sshd_config")
subprocess.check_output("set -i -e '$aHostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub' /etc/ssh/sshd_config") subprocess.check_output("sed -i -e '$aHostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub' /etc/ssh/sshd_config")
subprocess.check_output("systemctl restart ssh") subprocess.check_output("systemctl restart ssh")

59
scripts/get_user_cert.py Normal file
View File

@ -0,0 +1,59 @@
import json
import requests
import os
import subprocess
import uuid
from Crypto.PublicKey import RSA
keyfile = '/opt/stack/.ssh/mykey'
user_id = str(uuid.uuid4())
auth_id = str(uuid.UUID('0852c6cd6209425c88de582acbcd1170', version=4))
key = RSA.generate(2048)
keytxt = key.exportKey('PEM')
pubkeytxt = key.publickey().exportKey('OpenSSH')
server = 'http://127.0.0.1:18321'
with open('/etc/ssh/ssh_host_rsa_key.pub', 'r') as f:
host_key_pub = f.read()
user = {
'user_id': user_id,
'auth_id': auth_id,
'key.pub': pubkeytxt
}
response = requests.post(
server + '/usercerts',
data=json.dumps(user)
)
assert response.status_code == 201
assert 'location' in response.headers
location = response.headers['location']
print location
response = requests.get(server + location)
usercert = json.loads(response.content)
assert 'user_id' in usercert
assert usercert['user_id'] == user_id
assert 'fingerprint' in usercert
assert 'auth_id' in usercert
au = str(uuid.UUID(usercert['auth_id'], version=4))
assert au == auth_id
assert 'key-cert.pub' in usercert
# Write the user's ID
with open(keyfile + '_user_id', 'w') as f:
f.write(user_id)
# Write the user private key
with open(keyfile, 'w') as f:
f.write(keytxt)
# Write the user public key
with open(keyfile + '.pub', 'w') as f:
f.write(pubkeytxt)
# Write the user certificate
with open(keyfile + '-cert.pub', 'w') as f:
f.write(usercert['key-cert.pub'])

View File

@ -6,14 +6,11 @@ import falcon
import sshpubkeys import sshpubkeys
import uuid import uuid
import os import os
from tatu.utils import generateCert from tatu.utils import generateCert,random_uuid
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
Base = declarative_base() Base = declarative_base()
def generate_uuid():
return str(uuid.uuid4())
class Authority(Base): class Authority(Base):
__tablename__ = 'authorities' __tablename__ = 'authorities'
@ -55,7 +52,7 @@ def createUserCert(session, user_id, auth_id, pub):
certRecord = session.query(UserCert).get([user_id, fingerprint]) certRecord = session.query(UserCert).get([user_id, fingerprint])
if certRecord is not None: if certRecord is not None:
raise falcon.HTTPConflict('This public key is already signed.') raise falcon.HTTPConflict('This public key is already signed.')
cert = generateCert(auth.user_key, pub) cert = generateCert(auth.user_key, pub, principals='admin,root')
if cert is None: if cert is None:
raise falcon.HTTPInternalServerError("Failed to generate the certificate") raise falcon.HTTPInternalServerError("Failed to generate the certificate")
user = UserCert( user = UserCert(
@ -72,7 +69,7 @@ class Token(Base):
__tablename__ = 'tokens' __tablename__ = 'tokens'
token_id = sa.Column(sa.String(36), primary_key=True, token_id = sa.Column(sa.String(36), primary_key=True,
default=generate_uuid) default=random_uuid)
auth_id = sa.Column(sa.String(36), sa.ForeignKey('authorities.auth_id')) auth_id = sa.Column(sa.String(36), sa.ForeignKey('authorities.auth_id'))
host_id = sa.Column(sa.String(36), index=True, unique=True) host_id = sa.Column(sa.String(36), index=True, unique=True)
hostname = sa.Column(sa.String(36)) hostname = sa.Column(sa.String(36))
@ -140,7 +137,7 @@ def createHostCert(session, token_id, host_id, pub):
certRecord = session.query(HostCert).get([host_id, fingerprint]) certRecord = session.query(HostCert).get([host_id, fingerprint])
if certRecord is not None: if certRecord is not None:
raise falcon.HTTPConflict('This public key is already signed.') raise falcon.HTTPConflict('This public key is already signed.')
cert = generateCert(auth.host_key, pub, token.hostname) cert = generateCert(auth.host_key, pub, hostname=token.hostname)
if cert == '': if cert == '':
raise falcon.HTTPInternalServerError("Failed to generate the certificate") raise falcon.HTTPInternalServerError("Failed to generate the certificate")
host = HostCert(host_id=host_id, host = HostCert(host_id=host_id,

View File

@ -7,6 +7,7 @@ import uuid
from tatu.api.app import create_app from tatu.api.app import create_app
from tatu.db.persistence import SQLAlchemySessionManager from tatu.db.persistence import SQLAlchemySessionManager
from tatu.db.models import Authority from tatu.db.models import Authority
from tatu.utils import random_uuid
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
import sshpubkeys import sshpubkeys
@ -19,9 +20,6 @@ def client(db):
api = create_app(db) api = create_app(db)
return testing.TestClient(api) return testing.TestClient(api)
def random_uuid():
return str(uuid.uuid4())
token_id = '' token_id = ''
host_id = random_uuid() host_id = random_uuid()

View File

@ -2,7 +2,10 @@ import os
import subprocess import subprocess
import uuid import uuid
def generateCert(auth_key, entity_key, hostname=None): def random_uuid():
return str(uuid.uuid4())
def generateCert(auth_key, entity_key, hostname=None, principals='root'):
# Temporarily write the authority private key and entity public key to files # Temporarily write the authority private key and entity public key to files
prefix = uuid.uuid4().hex prefix = uuid.uuid4().hex
# Todo: make the temporary directory configurable or secure it. # Todo: make the temporary directory configurable or secure it.
@ -20,7 +23,7 @@ def generateCert(auth_key, entity_key, hostname=None):
args = ['ssh-keygen', '-P "pinot"', '-s', ca_file, '-I testID', '-V', args = ['ssh-keygen', '-P "pinot"', '-s', ca_file, '-I testID', '-V',
'-1d:+365d', '-n'] '-1d:+365d', '-n']
if hostname is None: if hostname is None:
args.extend(['"myRoot,yourRoot"', pub_file]) args.extend(['"' + principals + '"', pub_file])
else: else:
args.extend([hostname, '-h', pub_file]) args.extend([hostname, '-h', pub_file])
print subprocess.check_output(args, stderr=subprocess.STDOUT) print subprocess.check_output(args, stderr=subprocess.STDOUT)