Debugged/fixed revoked key file generation.
This commit is contained in:
parent
0b207f6123
commit
4450ba773f
8
scripts/revoke-user-cert
Normal file → Executable file
8
scripts/revoke-user-cert
Normal file → Executable file
@ -21,7 +21,7 @@ from Crypto.PublicKey import RSA
|
|||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Revoke a Tatu-generated user SSH certificate.')
|
parser = argparse.ArgumentParser(description='Revoke a Tatu-generated user SSH certificate.')
|
||||||
parser.add_argument('--projid', '-P', required=True)
|
parser.add_argument('--projid', '-P', required=True)
|
||||||
parser.add_argument('--serial', '-K', required=True)
|
parser.add_argument('--serial', '-S', required=True)
|
||||||
parser.add_argument('--tatu-url', default= 'http://127.0.0.1:18322',
|
parser.add_argument('--tatu-url', default= 'http://127.0.0.1:18322',
|
||||||
help='URL of the Tatu API')
|
help='URL of the Tatu API')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -38,12 +38,6 @@ if not args.serial.isdigit():
|
|||||||
|
|
||||||
server = args.tatu_url
|
server = args.tatu_url
|
||||||
|
|
||||||
body = {
|
|
||||||
'serial': args.serial,
|
|
||||||
'auth_id': auth_id,
|
|
||||||
'key.pub': pubkeytext
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
server + '/revokeduserkeys/' + auth_id,
|
server + '/revokeduserkeys/' + auth_id,
|
||||||
data=json.dumps({'serial': args.serial})
|
data=json.dumps({'serial': args.serial})
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
import falcon
|
import falcon
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from tatu import models
|
from tatu.api import models
|
||||||
from tatu.db.persistence import SQLAlchemySessionManager
|
from tatu.db.persistence import SQLAlchemySessionManager
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -311,7 +311,7 @@ class RevokedUserKeys(object):
|
|||||||
self.session,
|
self.session,
|
||||||
auth_id,
|
auth_id,
|
||||||
serial=req.body.get('serial', None),
|
serial=req.body.get('serial', None),
|
||||||
key=req.body.get('key_id', None),
|
key_id=req.body.get('key_id', None),
|
||||||
cert=req.body.get('cert', None)
|
cert=req.body.get('cert', None)
|
||||||
)
|
)
|
||||||
resp.status = falcon.HTTP_OK
|
resp.status = falcon.HTTP_OK
|
||||||
|
@ -67,16 +67,20 @@ def createAuthority(session, auth_id):
|
|||||||
class UserCert(Base):
|
class UserCert(Base):
|
||||||
__tablename__ = 'user_certs'
|
__tablename__ = 'user_certs'
|
||||||
|
|
||||||
user_id = sa.Column(sa.String(36), primary_key=True)
|
serial = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||||
fingerprint = sa.Column(sa.String(60), primary_key=True)
|
user_id = sa.Column(sa.String(36))
|
||||||
|
fingerprint = sa.Column(sa.String(60))
|
||||||
auth_id = sa.Column(sa.String(36), sa.ForeignKey('authorities.auth_id'))
|
auth_id = sa.Column(sa.String(36), sa.ForeignKey('authorities.auth_id'))
|
||||||
cert = sa.Column(sa.Text)
|
cert = sa.Column(sa.Text)
|
||||||
serial = sa.Column(sa.Integer, index=True, autoincrement=True)
|
|
||||||
revoked = sa.Column(sa.Boolean, default=False)
|
revoked = sa.Column(sa.Boolean, default=False)
|
||||||
|
|
||||||
|
sa.Index('idx_user_finger', UserCert.user_id, UserCert.fingerprint, unique=True)
|
||||||
|
|
||||||
|
|
||||||
def getUserCert(session, user_id, fingerprint):
|
def getUserCert(session, user_id, fingerprint):
|
||||||
return session.query(UserCert).get([user_id, fingerprint])
|
return session.query(UserCert).filter(
|
||||||
|
UserCert.user_id == user_id).filter(
|
||||||
|
UserCert.fingerprint == fingerprint).one_or_none()
|
||||||
|
|
||||||
|
|
||||||
def getUserCerts(session):
|
def getUserCerts(session):
|
||||||
@ -90,21 +94,22 @@ def createUserCert(session, user_id, auth_id, pub):
|
|||||||
raise falcon.HTTPNotFound(
|
raise falcon.HTTPNotFound(
|
||||||
description='No Authority found with that ID')
|
description='No Authority found with that ID')
|
||||||
fingerprint = sshpubkeys.SSHKey(pub).hash_md5()
|
fingerprint = sshpubkeys.SSHKey(pub).hash_md5()
|
||||||
certRecord = session.query(UserCert).get([user_id, fingerprint])
|
certRecord = getUserCert(session, user_id, fingerprint)
|
||||||
if certRecord is not None:
|
if certRecord is not None:
|
||||||
return certRecord
|
return certRecord
|
||||||
cert = generateCert(getAuthUserKey(auth), pub,
|
|
||||||
principals='admin,root')
|
|
||||||
if cert is None:
|
|
||||||
raise falcon.HTTPInternalServerError(
|
|
||||||
"Failed to generate the certificate")
|
|
||||||
user = UserCert(
|
user = UserCert(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
fingerprint=fingerprint,
|
fingerprint=fingerprint,
|
||||||
auth_id=auth_id,
|
auth_id=auth_id,
|
||||||
cert=cert
|
|
||||||
)
|
)
|
||||||
session.add(user)
|
session.add(user)
|
||||||
|
session.flush()
|
||||||
|
user.cert = generateCert(getAuthUserKey(auth), pub, user=True,
|
||||||
|
principals='admin,root', serial=user.serial)
|
||||||
|
if user.cert is None:
|
||||||
|
raise falcon.HTTPInternalServerError(
|
||||||
|
"Failed to generate the certificate")
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@ -124,7 +129,9 @@ def getRevokedKeysBase64(session, auth_id):
|
|||||||
description='No Authority found with that ID')
|
description='No Authority found with that ID')
|
||||||
serials = [k.serial for k in session.query(RevokedKey).filter(
|
serials = [k.serial for k in session.query(RevokedKey).filter(
|
||||||
RevokedKey.auth_id == auth_id)]
|
RevokedKey.auth_id == auth_id)]
|
||||||
return revokedKeysBase64(getAuthUserKey(auth), serials)
|
user_key = RSA.importKey(getAuthUserKey(auth))
|
||||||
|
user_pub_key = user_key.publickey().exportKey('OpenSSH')
|
||||||
|
return revokedKeysBase64(user_pub_key, serials)
|
||||||
|
|
||||||
|
|
||||||
def revokeUserKey(session, auth_id, serial=None, key_id=None, cert=None):
|
def revokeUserKey(session, auth_id, serial=None, key_id=None, cert=None):
|
||||||
@ -140,7 +147,7 @@ def revokeUserKey(session, auth_id, serial=None, key_id=None, cert=None):
|
|||||||
raise falcon.HTTPBadRequest(
|
raise falcon.HTTPBadRequest(
|
||||||
"Incorrect CA ID for serial # {}".format(serial))
|
"Incorrect CA ID for serial # {}".format(serial))
|
||||||
ser = serial
|
ser = serial
|
||||||
elif key is not None:
|
elif key_id is not None:
|
||||||
# TODO(pino): look up the UserCert by key id and get the serial number
|
# TODO(pino): look up the UserCert by key id and get the serial number
|
||||||
pass
|
pass
|
||||||
elif cert is not None:
|
elif cert is not None:
|
||||||
@ -242,8 +249,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(getAuthHostKey(auth), pub,
|
cert = generateCert(getAuthHostKey(auth), pub, user=False)
|
||||||
hostname=token.hostname)
|
|
||||||
if cert == '':
|
if cert == '':
|
||||||
raise falcon.HTTPInternalServerError(
|
raise falcon.HTTPInternalServerError(
|
||||||
"Failed to generate the certificate")
|
"Failed to generate the certificate")
|
||||||
|
@ -22,7 +22,7 @@ def random_uuid():
|
|||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
def generateCert(auth_key, entity_key, hostname=None, principals='root'):
|
def generateCert(auth_key, entity_key, user=True, principals='root', serial=0):
|
||||||
# Temporarily write the authority private key, entity public key to files
|
# Temporarily write the authority private key, entity public key to files
|
||||||
temp_dir = mkdtemp()
|
temp_dir = mkdtemp()
|
||||||
ca_file = '/'.join([temp_dir, 'ca_key'])
|
ca_file = '/'.join([temp_dir, 'ca_key'])
|
||||||
@ -37,8 +37,8 @@ def generateCert(auth_key, entity_key, hostname=None, principals='root'):
|
|||||||
with open(pub_file, "w", 0o644) as text_file:
|
with open(pub_file, "w", 0o644) as text_file:
|
||||||
text_file.write(entity_key)
|
text_file.write(entity_key)
|
||||||
args = ['ssh-keygen', '-s', ca_file, '-I', 'testID', '-V',
|
args = ['ssh-keygen', '-s', ca_file, '-I', 'testID', '-V',
|
||||||
'-1d:+365d']
|
'-1d:+365d', '-z', str(serial)]
|
||||||
if hostname is None:
|
if user:
|
||||||
args.extend(['-n', principals, pub_file])
|
args.extend(['-n', principals, pub_file])
|
||||||
else:
|
else:
|
||||||
args.extend(['-h', pub_file])
|
args.extend(['-h', pub_file])
|
||||||
@ -51,22 +51,19 @@ def generateCert(auth_key, entity_key, hostname=None, principals='root'):
|
|||||||
return cert
|
return cert
|
||||||
|
|
||||||
|
|
||||||
def revokedKeysBase64(auth_key, serial_list):
|
def revokedKeysBase64(ca_public, serial_list):
|
||||||
# Temporarily write the authority private key and list of serials
|
# Temporarily write the authority private key and list of serials
|
||||||
temp_dir = mkdtemp()
|
temp_dir = mkdtemp()
|
||||||
ca_file = '/'.join([temp_dir, 'ca_key'])
|
ca_file = '/'.join([temp_dir, 'ca_public'])
|
||||||
serials_file = '/'.join([temp_dir, 'serials'])
|
serials_file = '/'.join([temp_dir, 'serials'])
|
||||||
revoked_file = '/'.join([temp_dir, 'revoked'])
|
revoked_file = '/'.join([temp_dir, 'revoked'])
|
||||||
try:
|
try:
|
||||||
fd = os.open(ca_file, os.O_WRONLY | os.O_CREAT, 0o600)
|
with open(ca_file, "w", 0o644) as text_file:
|
||||||
os.close(fd)
|
text_file.write(ca_public)
|
||||||
with open(ca_file, "w") as text_file:
|
|
||||||
text_file.write(auth_key)
|
|
||||||
with open(serials_file, "w", 0o644) as text_file:
|
with open(serials_file, "w", 0o644) as text_file:
|
||||||
for s in serial_list:
|
for s in serial_list:
|
||||||
text_file.write("serial: " + s + "\n")
|
text_file.write("serial: {}\n".format(s))
|
||||||
args = ['ssh-keygen', '-s', ca_file, '-k', '-f', revoked_file,
|
args = ['ssh-keygen', '-k', '-f', revoked_file, '-s', ca_file, serials_file]
|
||||||
serials_file]
|
|
||||||
subprocess.check_output(args, stderr=subprocess.STDOUT)
|
subprocess.check_output(args, stderr=subprocess.STDOUT)
|
||||||
# Return the base64 encoded contents of the revoked keys file
|
# Return the base64 encoded contents of the revoked keys file
|
||||||
with open(revoked_file, 'r') as text_file:
|
with open(revoked_file, 'r') as text_file:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user