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

Add sts_endpoint_url option #140

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,15 @@ idp: .idp.docker
docker compose up -d
@touch $@

# TODO: Is this right?
idp-down:
docker compose down --rmi all
rm -f .idp.docker

idp-clean:
docker-compose down --rmi=all
rm -f .idp.docker

.install-build: $(RELEASE)
install-build: .install-build
pip install dist/$(WHEEL)
Expand Down Expand Up @@ -233,5 +238,5 @@ clean: idp-down
rm -rf build dist src/*.egg-info .eggs
make -C docs clean

clean-all: clean
clean-all: clean idp-clean
rm -rf cache
75 changes: 44 additions & 31 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
version: "3.3"

services:
ldap:
# https://github.com/bitnami/containers/tree/main/bitnami/openldap#readme
image: bitnami/openldap:latest
platform: linux
environment:
- LDAP_USERS=user01,user02,nobody
- LDAP_PASSWORDS=password,password,password
logging:
options:
tag: "ldap"
networks:
- back

idp:
image: test-idp:latest
build: src/idp
logging:
options:
tag: "idp"
networks:
- front
- back
ports:
- "8443:443"

networks:
front:
back:
version: "3.3"

services:
ldap:
# https://github.com/bitnami/containers/tree/main/bitnami/openldap#readme
image: bitnami/openldap:latest
platform: linux
environment:
- LDAP_USERS=user01,user02,nobody
- LDAP_PASSWORDS=password,password,password
logging:
options:
tag: "ldap"
networks:
- back

idp:
image: test-idp:latest
build: src/idp
logging:
options:
tag: "idp"
networks:
- front
- back
ports:
- "8443:443"

aws:
image: motoserver/moto:4.1.4
environment:
# Enable IAM. If not set everything is permitted.
- INITIAL_NO_AUTH_ACTION_COUNT=0
logging:
options:
tag: "aws"
networks:
- front
ports:
- "5000:5000"

networks:
front:
back:
7 changes: 7 additions & 0 deletions docs/readme/body.rst
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,13 @@ http_header_passcode

http_header_passcode = X-Shibboleth-Duo-Passcode

sts_endpoint_url
The endpoint URL to use to communicate with the AWS Security
Token Service (STS). Generally, only needed for testing and
debugging::

sts_endpoint_url = http://localhost:5000

verify_ssl_certificate
Whether to verify the SSL certificate from the IdP. Defaults to true.

Expand Down
7 changes: 7 additions & 0 deletions src/awscli_login/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ class Login(ExternalCommand):
'default': None,
'help_text': 'HTTP Header to store the user\'s Duo factor'
},
{
'name': 'sts-endpoint-url',
'no_paramfile': True,
'default': None,
'help_text': 'AWS STS endpoint URL to retrieve credentials from'
},
{
'name': 'http_header_passcode',
'default': None,
Expand Down Expand Up @@ -223,6 +229,7 @@ class Configure(BasicCommand):
* **duration** - Time in seconds credentials are valid
* **http_header_factor** - HTTP Header to store Duo factor
* **http_header_passcode** - HTTP Header to store passcode
* **sts_endpoint_url** - Set to override default AWS STS endpoint
* **verify_ssl_certificate** - Set to False to skip check of IdP SSL cert
''')
SYNOPSIS = ('aws login configure')
Expand Down
5 changes: 4 additions & 1 deletion src/awscli_login/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ def save_sts_token(profile: Profile, client: Client, saml: str,

def login(profile: Profile, session: Session, interactive: bool = True):
session.set_credentials(None, None) # Disable credential lookup
client = session.create_client('sts')
client = session.create_client(
'sts',
endpoint_url=profile.sts_endpoint_url
)

# Exit if already logged in
if interactive:
Expand Down
2 changes: 2 additions & 0 deletions src/awscli_login/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Profile:
http_header_factor: str
http_header_passcode: str
verify_ssl_certificate: bool = True
sts_endpoint_url: Optional[str]

# path to profile configuration file
config_file: str
Expand All @@ -108,6 +109,7 @@ class Profile:
'http_header_factor': None,
'http_header_passcode': None,
'verify_ssl_certificate': True,
'sts_endpoint_url': None,
}

_cli_only: Dict[str, Any] = {
Expand Down
5 changes: 5 additions & 0 deletions src/integration_tests/tests/common-docker-idp.bash
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
load 'base'
eval "_base_$(declare -f setup)" # Rename setup to _base_setup

# TODO: Can we use add code so we can use --endpoint-url instead
# of --sts-endpoint-url?
export LOGIN="login --sts-endpoint-url=http://localhost:5000 --verify-ssl-certificate=false --password password"
export AWS="aws --endpoint-url=http://localhost:5000"

setup() {
_base_setup

Expand Down
26 changes: 18 additions & 8 deletions src/integration_tests/tests/docker-idp-login.bats
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,27 @@ load 'common-docker-idp'

EOF

run aws login --verify-ssl-certificate=false --password password
run aws $LOGIN
assert_failure
assert_output "404 Client Error: for url: https://localhost:8443/bad/endpoint"
run aws login --verify-ssl-certificate=false --password password \
--ecp-endpoint-url 'https://localhost:8443/idp/profile/SAML2/SOAP/ECP'
assert_failure
assert_output -e "An error occurred \(InvalidIdentityToken\) when calling the AssumeRoleWithSAML operation: Specified provider doesn't exist \(Service: AWSOpenIdDiscoveryService; Status Code: 400; Error Code: AuthSamlManifestNotFoundException; Request ID: [0-9a-f-]+; Proxy: null\)"
run aws $LOGIN --ecp-endpoint-url 'https://localhost:8443/idp/profile/SAML2/SOAP/ECP'
assert_success
assert_output ""
assert_output ""
}

@test "Test no --sts-endpoint-url" {
# TODO: Revert to old "Login with Docker Id"
run aws $LOGIN
assert_success
assert_output ""
}

@test "Login with Docker IdP" {
run aws login --verify-ssl-certificate=false --password password
assert_failure
assert_output -e "An error occurred \(InvalidIdentityToken\) when calling the AssumeRoleWithSAML operation: Specified provider doesn't exist \(Service: AWSOpenIdDiscoveryService; Status Code: 400; Error Code: AuthSamlManifestNotFoundException; Request ID: [0-9a-f-]+; Proxy: null\)"
run $AWS $LOGIN
assert_success
assert_output ""

run $AWS sts get-caller-identity
assert_output ""
}
7 changes: 4 additions & 3 deletions src/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class MockProfile():
force_refresh = False
name = 'default'
verify_ssl_certificate = True
sts_endpoint_url = None

def raise_if_logged_in(self):
return
Expand Down Expand Up @@ -94,7 +95,7 @@ def test_interactive_login(
""" Interactive login wo/refreshable creds should prompt user. """
login(self.profile, self.session, interactive=True)
self.session.set_credentials.assert_called_with(None, None)
self.session.create_client.assert_called_with("sts")
self.session.create_client.assert_called_with("sts", endpoint_url=None)

self.profile.get_username.assert_called()
refresh.assert_called_with(
Expand Down Expand Up @@ -129,7 +130,7 @@ def test_interactive_refresh_login(
""" Interactive login w/refreshable creds should not prompt user. """
login(self.profile, self.session, interactive=True)
self.session.set_credentials.assert_called_with(None, None)
self.session.create_client.assert_called_with("sts")
self.session.create_client.assert_called_with("sts", endpoint_url=None)

self.profile.get_username.assert_called()
refresh.assert_called_with(
Expand Down Expand Up @@ -160,7 +161,7 @@ def test_noninteractive_login(
""" A non interactive login should not prompt the user. """
login(self.profile, self.session, interactive=False)
self.session.set_credentials.assert_called_with(None, None)
self.session.create_client.assert_called_with("sts")
self.session.create_client.assert_called_with("sts", endpoint_url=None)

self.profile.get_username.assert_not_called()
refresh.assert_called_with(
Expand Down
1 change: 1 addition & 0 deletions src/tests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def login_cli_args(
duration=None,
http_header_factor=None,
http_header_passcode=None,
sts_endpoint_url=None,
verify_ssl_certificate=True,
# CLI only
ask_password=False,
Expand Down
32 changes: 32 additions & 0 deletions test.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# usage: source test.env
#
# Sets up a tempory directory & environment variables for testing
# aws login

disable () {
unset AWS_CONFIG_FILE
unset AWS_SHARED_CREDENTIALS_FILE
unset AWSCLI_LOGIN_ROOT
rm -rf $AWSCLI_LOGIN_ROOT

unalias aws
unalias login

unset -f disable
}

export AWSCLI_LOGIN_ROOT=`mktemp -d`
export AWS_CONFIG_FILE="$AWSCLI_LOGIN_ROOT/.aws/config"
export AWS_SHARED_CREDENTIALS_FILE="$AWSCLI_LOGIN_ROOT/.aws/credentials"

aws configure set plugins.login awscli_login.plugin
aws login configure >/dev/null <<- EOF # NOTA BENE: <<- strips tabs
https://localhost:8443/idp/profile/SAML2/SOAP/ECP
user01

push

EOF

alias login="login --sts-endpoint-url=http://localhost:5000 --verify-ssl-certificate=false"
alias aws="aws --endpoint-url=http://localhost:5000"
Loading