mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-03 12:21:35 +00:00
Upgrade yubkey for 5.0 release. Fixes #1587
This commit is contained in:
@@ -1314,6 +1314,10 @@ def doCheckServiceAccount(users):
|
|||||||
3,
|
3,
|
||||||
'Invalid private key in oauth2service.json. Please delete the file and then\nrecreate with "gam create project" or "gam use project"'
|
'Invalid private key in oauth2service.json. Please delete the file and then\nrecreate with "gam create project" or "gam use project"'
|
||||||
)
|
)
|
||||||
|
key_type = GM_Globals[GM_OAUTH2SERVICE_JSON_DATA].get('key_type', 'default')
|
||||||
|
if key_type == 'yubikey':
|
||||||
|
printPassFail('Skipping age check. YubiKey rotation not necessary.', test_pass)
|
||||||
|
else:
|
||||||
print(
|
print(
|
||||||
'Checking key age. Google recommends rotating keys on a routine basis...'
|
'Checking key age. Google recommends rotating keys on a routine basis...'
|
||||||
)
|
)
|
||||||
@@ -4473,15 +4477,7 @@ def getImap(users):
|
|||||||
soft_errors=True,
|
soft_errors=True,
|
||||||
userId='me')
|
userId='me')
|
||||||
if result:
|
if result:
|
||||||
enabled = result['enabled']
|
display.print_json(result)
|
||||||
if enabled:
|
|
||||||
print(
|
|
||||||
f'User: {user}, IMAP Enabled: {enabled}, autoExpunge: {result["autoExpunge"]}, expungeBehavior: {result["expungeBehavior"]}, maxFolderSize: {result["maxFolderSize"]}{currentCount(i, count)}'
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
print(
|
|
||||||
f'User: {user}, IMAP Enabled: {enabled}{currentCount(i, count)}'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def doPop(users):
|
def doPop(users):
|
||||||
@@ -7912,6 +7908,7 @@ def doCreateOrRotateServiceAccountKeys(iam=None,
|
|||||||
yk = yubikey.YubiKey(new_data)
|
yk = yubikey.YubiKey(new_data)
|
||||||
if 'yubikey_serial_number' not in new_data:
|
if 'yubikey_serial_number' not in new_data:
|
||||||
new_data['yubikey_serial_number'] = yk.get_serial_number()
|
new_data['yubikey_serial_number'] = yk.get_serial_number()
|
||||||
|
yk = yubikey.YubiKey(new_data)
|
||||||
if 'yubikey_slot' not in new_data:
|
if 'yubikey_slot' not in new_data:
|
||||||
new_data['yubikey_slot'] = 'AUTHENTICATION'
|
new_data['yubikey_slot'] = 'AUTHENTICATION'
|
||||||
publicKeyData = yk.get_certificate()
|
publicKeyData = yk.get_certificate()
|
||||||
@@ -12069,6 +12066,7 @@ def ProcessGAMCommand(args):
|
|||||||
action = sys.argv[2].lower().replace('_', '')
|
action = sys.argv[2].lower().replace('_', '')
|
||||||
if action == 'resetpiv':
|
if action == 'resetpiv':
|
||||||
yk = yubikey.YubiKey()
|
yk = yubikey.YubiKey()
|
||||||
|
yk.serial_number = yk.get_serial_number()
|
||||||
yk.reset_piv()
|
yk.reset_piv()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
users = getUsersToModify()
|
users = getUsersToModify()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from threading import Timer
|
|||||||
from cryptography.hazmat.primitives import hashes, serialization
|
from cryptography.hazmat.primitives import hashes, serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import padding
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
from smartcard.Exceptions import CardConnectionException
|
from smartcard.Exceptions import CardConnectionException
|
||||||
from ykman.device import connect_to_device
|
from ykman.device import list_all_devices
|
||||||
from ykman.piv import generate_self_signed_certificate, \
|
from ykman.piv import generate_self_signed_certificate, \
|
||||||
generate_chuid
|
generate_chuid
|
||||||
from yubikit.piv import DEFAULT_MANAGEMENT_KEY, \
|
from yubikit.piv import DEFAULT_MANAGEMENT_KEY, \
|
||||||
@@ -20,11 +20,14 @@ from yubikit.piv import DEFAULT_MANAGEMENT_KEY, \
|
|||||||
OBJECT_ID, \
|
OBJECT_ID, \
|
||||||
SLOT, \
|
SLOT, \
|
||||||
TOUCH_POLICY
|
TOUCH_POLICY
|
||||||
from yubikit.core.smartcard import ApduError
|
from yubikit.core.smartcard import ApduError, \
|
||||||
|
SmartCardConnection
|
||||||
from gam import controlflow
|
from gam import controlflow
|
||||||
|
|
||||||
|
|
||||||
class YubiKey():
|
class YubiKey():
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, service_account_info=None):
|
def __init__(self, service_account_info=None):
|
||||||
self.key_type = None
|
self.key_type = None
|
||||||
self.slot = None
|
self.slot = None
|
||||||
@@ -46,12 +49,16 @@ class YubiKey():
|
|||||||
self.pin = service_account_info.get('yubikey_pin')
|
self.pin = service_account_info.get('yubikey_pin')
|
||||||
self.key_id = service_account_info.get('private_key_id')
|
self.key_id = service_account_info.get('private_key_id')
|
||||||
|
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self):
|
||||||
try:
|
try:
|
||||||
conn, _, _ = connect_to_device(self.serial_number)
|
devices = list_all_devices()
|
||||||
|
for (device, info) in devices:
|
||||||
|
if info.serial == self.serial_number:
|
||||||
|
return device.open_connection(SmartCardConnection)
|
||||||
except CardConnectionException as err:
|
except CardConnectionException as err:
|
||||||
controlflow.system_error_exit(9, f'YubiKey - {err}')
|
controlflow.system_error_exit(9, f'YubiKey - {err}')
|
||||||
return conn
|
|
||||||
|
|
||||||
def get_certificate(self):
|
def get_certificate(self):
|
||||||
try:
|
try:
|
||||||
@@ -79,11 +86,22 @@ class YubiKey():
|
|||||||
|
|
||||||
def get_serial_number(self):
|
def get_serial_number(self):
|
||||||
try:
|
try:
|
||||||
_, _, info = connect_to_device(self.serial_number)
|
devices = list_all_devices()
|
||||||
|
if self.serial_number:
|
||||||
|
for (device, info) in devices:
|
||||||
|
if info.serial == self.serial_number:
|
||||||
return info.serial
|
return info.serial
|
||||||
|
msg = f'Could not find YubiKey with serial {self.serial_number}'
|
||||||
|
controlflow.system_error_exit(3, msg)
|
||||||
|
if len(devices) > 1:
|
||||||
|
serials = ', '.join([str(info.serial) for (_, info) in devices])
|
||||||
|
msg = f'Multiple YubiKeys connected. Specify yubikey_serial_number and one of {serials}'
|
||||||
|
controlflow.system_error_exit(4, msg)
|
||||||
|
return devices[0][1].serial
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
controlflow.system_error_exit(9, f'YubiKey - {err}')
|
controlflow.system_error_exit(9, f'YubiKey - {err}')
|
||||||
|
|
||||||
|
|
||||||
def reset_piv(self):
|
def reset_piv(self):
|
||||||
'''Resets YubiKey PIV app and generates new key for GAM to use.'''
|
'''Resets YubiKey PIV app and generates new key for GAM to use.'''
|
||||||
reply = str(input('This will wipe all PIV keys and configuration from your YubiKey. Are you sure? (y/N) ').lower().strip())
|
reply = str(input('This will wipe all PIV keys and configuration from your YubiKey. Are you sure? (y/N) ').lower().strip())
|
||||||
@@ -95,7 +113,9 @@ class YubiKey():
|
|||||||
piv = PivSession(conn)
|
piv = PivSession(conn)
|
||||||
piv.reset()
|
piv.reset()
|
||||||
rnd = SystemRandom()
|
rnd = SystemRandom()
|
||||||
pin_puk_chars = string.ascii_letters + string.digits + string.punctuation
|
pin_puk_chars = string.ascii_letters + \
|
||||||
|
string.digits + \
|
||||||
|
string.punctuation
|
||||||
new_puk = ''.join(rnd.choice(pin_puk_chars) for _ in range(8))
|
new_puk = ''.join(rnd.choice(pin_puk_chars) for _ in range(8))
|
||||||
new_pin = ''.join(rnd.choice(pin_puk_chars) for _ in range(8))
|
new_pin = ''.join(rnd.choice(pin_puk_chars) for _ in range(8))
|
||||||
piv.change_puk('12345678', new_puk)
|
piv.change_puk('12345678', new_puk)
|
||||||
@@ -155,3 +175,4 @@ class YubiKey():
|
|||||||
if 'mplock' in globals():
|
if 'mplock' in globals():
|
||||||
mplock.release()
|
mplock.release()
|
||||||
return signed
|
return signed
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ importlib.metadata; python_version < '3.8'
|
|||||||
passlib>=1.7.2
|
passlib>=1.7.2
|
||||||
pathvalidate
|
pathvalidate
|
||||||
python-dateutil
|
python-dateutil
|
||||||
yubikey-manager>=4.0.0
|
yubikey-manager>=5.0
|
||||||
|
|||||||
Reference in New Issue
Block a user