Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

base kmsauth token on bastion_user instead of remote_usernames #58

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion bless/aws_lambda/bless_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
USERNAME_VALIDATION_OPTION, \
KMSAUTH_SECTION, \
KMSAUTH_USEKMSAUTH_OPTION, \
KMSAUTH_REMOTE_USERNAMES_ALLOWED_OPTION, \
KMSAUTH_SERVICE_ID_OPTION, \
TEST_USER_OPTION, \
CERTIFICATE_EXTENSIONS_OPTION, \
Expand Down Expand Up @@ -134,6 +135,17 @@ def lambda_handler(event, context=None, ca_private_key_password=None,
# Authenticate the user with KMS, if key is setup
if config.get(KMSAUTH_SECTION, KMSAUTH_USEKMSAUTH_OPTION):
if request.kmsauth_token:
# Allow bless to sign the cert for a different remote user than the name of the user who signed it
allowed_remotes = config.get(KMSAUTH_SECTION, KMSAUTH_REMOTE_USERNAMES_ALLOWED_OPTION)
if allowed_remotes:
allowed_users = allowed_remotes.split(',')
requested_remotes = request.remote_usernames.split(',')
if allowed_users != ['*'] and not all([u in allowed_users for u in requested_remotes]):
return error_response('KMSAuthValidationError',
'unallowed remote_usernames [{}]'.format(request.remote_usernames))
elif request.remote_usernames != request.bastion_user:
return error_response('KMSAuthValidationError',
'remote_usernames must be the same as bastion_user')
try:
validator = KMSTokenValidator(
None,
Expand All @@ -143,7 +155,7 @@ def lambda_handler(event, context=None, ca_private_key_password=None,
)
# decrypt_token will raise a TokenValidationError if token doesn't match
validator.decrypt_token(
"2/user/{}".format(request.remote_usernames),
"2/user/{}".format(request.bastion_user),
request.kmsauth_token
)
except TokenValidationError as e:
Expand Down
4 changes: 4 additions & 0 deletions bless/config/bless_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
KMSAUTH_KEY_ID_OPTION = 'kmsauth_key_id'
KMSAUTH_KEY_ID_DEFAULT = ''

KMSAUTH_REMOTE_USERNAMES_ALLOWED_OPTION = 'kmsauth_remote_usernames_allowed'
KMSAUTH_REMOTE_USERNAMES_ALLOWED_OPTION_DEFAULT = None

KMSAUTH_SERVICE_ID_OPTION = 'kmsauth_serviceid'
KMSAUTH_SERVICE_ID_DEFAULT = None

Expand Down Expand Up @@ -77,6 +80,7 @@ def __init__(self, aws_region, config_file):
TEST_USER_OPTION: TEST_USER_DEFAULT,
KMSAUTH_SERVICE_ID_OPTION: KMSAUTH_SERVICE_ID_DEFAULT,
KMSAUTH_KEY_ID_OPTION: KMSAUTH_KEY_ID_DEFAULT,
KMSAUTH_REMOTE_USERNAMES_ALLOWED_OPTION: KMSAUTH_REMOTE_USERNAMES_ALLOWED_OPTION_DEFAULT,
KMSAUTH_USEKMSAUTH_OPTION: KMSAUTH_USEKMSAUTH_DEFAULT,
CERTIFICATE_EXTENSIONS_OPTION: CERTIFICATE_EXTENSIONS_DEFAULT,
USERNAME_VALIDATION_OPTION: USERNAME_VALIDATION_DEFAULT,
Expand Down
5 changes: 5 additions & 0 deletions bless/config/bless_deploy_example.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ ca_private_key_file = <INSERT_YOUR_ENCRYPTED_PEM_FILE_NAME>
# If using kmsauth, you need to set the kmsauth service name. Users need to set the 'to'
# context to this same service name when they create a kmsauth token.
# kmsauth_serviceid = bless-production

# By default, kmsauth requires that requested bastion_user must be the same as the requested remote_usernames. If you
# want Bless to sign a certificate for a different remote_usernames (like root, or a shared admin account), you must
# specify those allowed names here. * will allow signing for all remote_usernames
# kmsauth_remote_usernames_allowed = ubuntu,root,ec2-user,stufflikethat
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pycparser==2.17
pyflakes==1.5.0
pyparsing==2.1.10
pytest==3.0.6
pytest-mock==1.6.0
python-dateutil==2.6.0
s3transfer==0.1.10
six==1.10.0
33 changes: 33 additions & 0 deletions tests/aws_lambda/bless-test-kmsauth-different-remote.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[Bless CA]
ca_private_key_file = tests/aws_lambda/only-use-for-unit-tests.pem
us-east-1_password = bogus-password-for-unit-test
us-west-2_password = bogus-password-for-unit-test

[KMS Auth]
use_kmsauth = True
kmsauth_key_id = alias/authnz-iad, alias/authnz-sfo
kmsauth_serviceid = kmsauth-prod
kmsauth_remote_usernames_allowed = ubuntu,alloweduser

# todo get from config, with some sane defaults
#[loggers]
#keys=root
#
#[handlers]
#keys=stream_handler
#
#[formatters]
#keys=formatter
#
#[logger_root]
#level=INFO
#handlers=stream_handler
#
#[handler_stream_handler]
#class=StreamHandler
#level=DEBUG
#formatter=formatter
#args=(sys.stderr,)
#
#[formatter_formatter]
#format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
62 changes: 62 additions & 0 deletions tests/aws_lambda/test_bless_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,36 @@ class Context(object):
"bastion_user_ip": "127.0.0.1"
}

INVALID_TEST_KMSAUTH_REQUEST_USERNAME_DOESNT_MATCH_REMOTE = {
"remote_usernames": "userb",
"public_key_to_sign": EXAMPLE_RSA_PUBLIC_KEY,
"command": "ssh user@server",
"bastion_ips": "127.0.0.1",
"bastion_user": "usera",
"bastion_user_ip": "127.0.0.1",
"kmsauth_token": "validkmsauthtoken"
}

INVALID_TEST_KMSAUTH_REQUEST_DIFFERENT_REMOTE_USER = {
"remote_usernames": "root",
"public_key_to_sign": EXAMPLE_RSA_PUBLIC_KEY,
"command": "ssh user@server",
"bastion_ips": "127.0.0.1",
"bastion_user": "usera",
"bastion_user_ip": "127.0.0.1",
"kmsauth_token": "validkmsauthtoken"
}

VALID_TEST_KMSAUTH_REQUEST_DIFFERENT_REMOTE_USER = {
"remote_usernames": "alloweduser",
"public_key_to_sign": EXAMPLE_RSA_PUBLIC_KEY,
"command": "ssh user@server",
"bastion_ips": "127.0.0.1",
"bastion_user": "usera",
"bastion_user_ip": "127.0.0.1",
"kmsauth_token": "validkmsauthtoken"
}

os.environ['AWS_REGION'] = 'us-west-2'


Expand Down Expand Up @@ -284,3 +314,35 @@ def test_invalid_request_with_multiple_principals():
config_file=os.path.join(os.path.dirname(__file__),
'bless-test.cfg'))
assert output['errorType'] == 'InputValidationError'


def test_invalid_request_with_mismatched_bastion_and_remote():
'''
Test default kmsauth behavior, that a bastion_user and remote_usernames must match
:return:
'''
output = lambda_handler(INVALID_TEST_KMSAUTH_REQUEST_USERNAME_DOESNT_MATCH_REMOTE, context=Context,
ca_private_key_password=RSA_CA_PRIVATE_KEY_PASSWORD,
entropy_check=False,
config_file=os.path.join(os.path.dirname(__file__),
'bless-test-kmsauth.cfg'))
assert output['errorType'] == 'KMSAuthValidationError'


def test_invalid_request_with_unallowed_remote():
output = lambda_handler(INVALID_TEST_KMSAUTH_REQUEST_DIFFERENT_REMOTE_USER, context=Context,
ca_private_key_password=RSA_CA_PRIVATE_KEY_PASSWORD,
entropy_check=False,
config_file=os.path.join(os.path.dirname(__file__),
'bless-test-kmsauth-different-remote.cfg'))
assert output['errorType'] == 'KMSAuthValidationError'


def test_valid_request_with_allowed_remote(mocker):
mocker.patch("kmsauth.KMSTokenValidator.decrypt_token")
output = lambda_handler(VALID_TEST_KMSAUTH_REQUEST_DIFFERENT_REMOTE_USER, context=Context,
ca_private_key_password=RSA_CA_PRIVATE_KEY_PASSWORD,
entropy_check=False,
config_file=os.path.join(os.path.dirname(__file__),
'bless-test-kmsauth-different-remote.cfg'))
assert output['certificate'].startswith('ssh-rsa-cert-v01@openssh.com ')