Skip to content

Commit

Permalink
Refining Documentation
Browse files Browse the repository at this point in the history
And replacing `whitelist` with `allowlist` in more places.

Signed-off-by: Geoffrey Wiseman <geoffrey.wiseman@codiform.com>
  • Loading branch information
geoffreywiseman committed Jun 14, 2023
1 parent 40c6ac4 commit f20b120
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 83 deletions.
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
## [1.0.0]

### Added
- `--list` lists whitelisted CIDR blocks
- `--add-current` adds your current external ip in CIDR form to the whitelist
- `--remove-current` to remove your current external ip in CIDR form from the whitelist
- `--add` to add manually-specified CIDR blocks to the whitelist
- `--remove` to remove manual CIDR blocks from the whitelist
- `--list` lists allowlist CIDR blocks
- `--add-current` adds your current external ip in CIDR form to the allowlist
- `--remove-current` to remove your current external ip in CIDR form from the allowlist
- `--add` to add manually-specified CIDR blocks to the allowlist
- `--remove` to remove manual CIDR blocks from the allowlist
42 changes: 2 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,53 +24,15 @@ you may have additional work ahead of you.

Now that Python2 is largely a relic of the past, I'm focused on supporting Python 3 only. The current CI build is for Python 3.9+.

## Usage
## Usage

If you want usage help at the command line, try:

```bash
awswl --help
```

You can list the IP address blocks that are authorized, including which ip address is current:

```bash
awswl --list
```

Authorize your current IP Address:

```bash
awswl --add-current
```

Remove authorization for your current IP:

```bash
awswl --remove-current
```

Authorize a manually-specified CIDR block:

```bash
awswl --add 192.168.0.0/24
```

Remove authorization for a manually-specified CIDR block:

```bash
awswl --remove 192.168.0.0/24
```

For each of these commands, you need to tell awswl which security group to use, which you can do
with the ``--sgid`` command-line option or using an environment variable.


## Integration

In order to get your current ip address, ``--list``, ``--add-current`` and ``--remove-current`` will make a request to ``checkip.amazonaws.org``. Because it's another AWS service, seems less likely to be a privacy concern for anyone.

I may [add a switch](https://github.com/geoffreywiseman/awswl/issues/3) to disable that for the anyone who isn't fond of `awswl` making an additional network request, so if that's a concern for you, feel free to vote for it.
There's more detailed usage documentation in the documentation, which you can read on [github](docs/usage.md) or [readthedocs](https://awswl.readthedocs.io/en/latest/usage/).


## Environment
Expand Down
10 changes: 5 additions & 5 deletions awswl/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

def parse_args(args):
parser = argparse.ArgumentParser(
description='Maintains a list of whitelisted CIDR blocks granted SSH access to '
description='Maintains a list of allowlisted CIDR blocks granted SSH access to '
'AWS via a security group.'
)
parser.add_argument(
Expand All @@ -16,12 +16,12 @@ def parse_args(args):
)
parser.add_argument(
'--add-current', action='append_const', dest='actions', const='cmd_add_current',
help='Adds the current IP address to the whitelist.'
help='Adds the current IP address to the allowlist.'
)
parser.add_argument(
'--remove-current', action='append_const', dest='actions',
const='cmd_remove_current',
help='Remove the current IP address from the whitelist.'
help='Remove the current IP address from the allowlist.'
)
parser.add_argument(
'--version', action='append_const', dest='actions', const='cmd_version',
Expand All @@ -42,11 +42,11 @@ def parse_args(args):
)
parser.add_argument(
'--add', action='append', dest='add_blocks',
help="Adds a manually-specified CIDR block from the whitelist."
help="Adds a manually-specified CIDR block from the allowlist."
)
parser.add_argument(
'--remove', action='append', dest='remove_blocks',
help="Removes a manually-specified CIDR block from the whitelist."
help="Removes a manually-specified CIDR block from the allowlist."
)

return parser.parse_args(args)
42 changes: 21 additions & 21 deletions awswl/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ def add_cidr(options, description, cidr):
}
]
)
print("Added {0} ({1}) to whitelist.".format(description, cidr))
print("Added {0} ({1}) to allowlist.".format(description, cidr))
except ClientError as e:
if e.response['Error']['Code'] == "InvalidPermission.Duplicate":
cap_desc = description[0].capitalize() + description[1:]
print("{0} is already whitelisted.".format(cap_desc))
print("{0} is already allowlisted.".format(cap_desc))
else:
print("Unexpected error")
print(e)
Expand All @@ -94,26 +94,27 @@ def cmd_remove_current(options):
def remove_cidr(options, description, cidr):
try:
security_group = get_security_group(options)
security_group.revoke_ingress(
# FIXME: Workaround for Moto issue: https://github.com/spulec/moto/issues/1522
# CidrIp=cidr,
# IpProtocol='tcp',
# FromPort=options.ssh_port,
# ToPort=options.ssh_port
IpPermissions=[
{
'IpRanges': [{'CidrIp': cidr}],
'IpProtocol': 'tcp',
'FromPort': options.ssh_port,
'ToPort': options.ssh_port,
}
]
)
print("Removed {0} ({1}) from whitelist.".format(description, cidr))
if security_group:
security_group.revoke_ingress(
# FIXME: Workaround for Moto issue: https://github.com/spulec/moto/issues/1522
# CidrIp=cidr,
# IpProtocol='tcp',
# FromPort=options.ssh_port,
# ToPort=options.ssh_port
IpPermissions=[
{
'IpRanges': [{'CidrIp': cidr}],
'IpProtocol': 'tcp',
'FromPort': options.ssh_port,
'ToPort': options.ssh_port,
}
]
)
print("Removed {0} ({1}) from allowlist.".format(description, cidr))
except ClientError as e:
if e.response['Error']['Code'] == "InvalidPermission.NotFound":
cap_desc = description[0].capitalize() + description[1:]
print("{0} does not seem to be whitelisted.".format(cap_desc))
print("{0} does not seem to be allowlisted.".format(cap_desc))
else:
print(e)

Expand Down Expand Up @@ -153,10 +154,9 @@ def get_security_group(options: Namespace):
print(f"Using security group {name} ({sgid}).\n")
return ec2.SecurityGroup(sgid)
else:
print(f"Found multiple security group {groups[0]} matching name: ")
print(f"Found {len(groups)} security groups matching name: ")
for group in groups:
print(f"- {group[0]} ({group[1]})")
print(f"Found multiple security groups matching name {options.sg_name}: {groups}\n")


def get_matching_security_groups(sg_name):
Expand Down
26 changes: 20 additions & 6 deletions awswl/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

def main():
args = sys.argv[1:]
if not args:
args = ['--help']
options = cli.parse_args(args)
version = get_distribution('awswl').version
options.version = version
Expand Down Expand Up @@ -36,17 +38,29 @@ def has_action(options: Namespace):
not options.remove_blocks


def only_version(options: Namespace):
if options.add_blocks:
return False
if options.remove_blocks:
return False
if any(act != 'cmd_version' for act in options.actions):
return False
return True


def validate_options(options):
if not options.sgid and not options.sg_name:
if has_action(options):
print(
"You must specify a security group id (--sgid option, AWSWL_SGID env var) "
"or security group name (--sg-name, AWSWL_SGNAME) for awswl to use."
"You haven't asked AWSWL to do anything. "
"Try `awswl --help` to get started?"
)
return False
elif has_action(options):
if only_version(options):
return True
if not options.sgid and not options.sg_name:
print(
"You haven't asked AWSWL to do anything. "
"Try `awswl --help` to get started?"
"You must specify a security group id (--sgid option, AWSWL_SGID env var) "
"or security group name (--sg-name, AWSWL_SGNAME) for awswl to use."
)
return False
return True
6 changes: 5 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ In case you're encountering AWSWL without having considered the other options, y

## Project Name

I named this project when "whitelist" was the most common word for this sort of action. These days, "allowlist" is preferred and is more clear, but renaming the pypy package and cli command will break links, so for now I'm referring to it as "allowlist" even though the repository and cli are named `awswl` instead of `awsal`.
I named this project when "whitelist" was the traditional and most common word for this sort of thing. In the interim, there's been some good progress on more inclusive language and now I'd say that "allowlist" is both preferred and more clear.

Renaming the pypy package and cli command will break links, so for now I'm referring to it as "allowlist" even though the repository and cli are named `awswl` instead of `awsal`.

I may get around to renaming the repository, package and cli -- if you feel strongly about it, drop me a line or file an issue.
111 changes: 111 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Usage

## Common Cases

These are the most common operations. I'm going to use made-up ip addresses in these listings, so don't read too deeply into the specifics.

### Listing IP Addresses in the Security Group

Often, I'll want to check if my current ip address is in a security group:

❯ awswl --sgid sg-0123456abc --list
The following CIDR blocks are authorized for SSH:
- 192.168.0.0/16
- 172.16.0.0/21
- 8.8.8.8/32

These IP addresses are made up, but if my current external ip address were listed, it should be marked with `(current)`. This is what it would look like if I were in the list:

❯ awswl --sg-name "mycorp-prod-bastion" --list
The following CIDR blocks are authorized for SSH:
- 192.168.0.0/16
- 172.16.0.0/21
- 8.8.8.8/32
- 1.2.3.4/32 (current)

### Adding My Current External IP

If I want to give myself access to a security group, I could `--add-current`:

❯ awswl --sgid sg-0123456abc --add-current
Added current external IP address as a CIDR block (1.2.3.4/32) to allowlist.

### Removing My Current External IP

If I'm working in an environment temporarily, I might want to revoke access as soon as I'm done, using `--remove-current`

❯ awswl --sg-name "myorg-jump-host" --remove-current
Removed current external IP address as a CIDR block (4.3.2.1/32) from allowlist.

### Adding or Removing a Custom CIDR

Although I usually want my current external ip address, there are certainly cases where you might want to allow-list a custom CIDR block:

❯ awswl --sgid sg-0123456abc --add 8.8.8.8/28
Added specified CIDR block (8.8.8.0/28) to allowlist.

❯ awswl --sg-name "*beta-extern*" --remove 8.8.8.8/28
Removed specified CIDR block (8.8.8.0/28) from allowlist.

## Required Metadata
There's a bunch of required metadata to do this properly.

### AWS Credentials
In order to modify AWS security groups, you need valid AWS credentials for the API calls required.

AWS AllowList is built in Python using boto, which can use an AWS Credentials file or environment variables.

Boto's support for environment variables works fine with `aws-vault` and likely other similar tools. If you've tested `awswl` with a different aws authentication approach, let me know and I can list it here.

There's no attempt to capture or record credentials -- the awsal code doesn't actually interact with the credentials at all, that's all done by `boto`, but do feel free to look over the source to assuage any privacy concerns.

### AWS Region

The desired AWS region can be supplied in an environment variables as well, `AWS_REGION`, although it might also be in your AWS profile or supplied by whatever tool you might use to manage AWS Credentials.

### Security Group (ID, Name)

In order to modify a security group, AWS AllowList needs to know which security group to modify:

- If you know the security group id, you can specify it as a CLI option, `--sgid`.
- If for a given project you often need a particular security group, you could specify the security group id in an environment variable, `AWSWL_SGID`, and store that in something like [direnv](https://direnv.net).
- If you know the full name of the security group you can specify that as a CLI option, `--sg-name`.
- Security group name also supports wildcards, so if the full name is difficult but a partial name is easy, you can use something like `--sg-name "*beta-bastion*"`
- If the wildcards match more than one group, you'll get an error, which will list all the matching security groups with their ids, so you may be able to use that output to specify `--sgid`.

### SSH Port

If you want to modify a port other than the default SSH port, you can specify the `--ssh-port` CLI option.

### Current IP Address

In order to get your current ip address, ``--list``, ``--add-current`` and ``--remove-current`` will make a request to ``checkip.amazonaws.org``. Because it's another AWS service, seems less likely to be a privacy concern for anyone.

I may [add a switch](https://github.com/geoffreywiseman/awswl/issues/3) to disable that for the anyone who isn't fond of `awswl` making an additional network request, so if that's a concern for you, feel free to vote for it.

## Help and Version

If you want to get usage help at the command line, use `--help`:

❯ awswl --help
usage: awswl [-h] [--list] [--add-current] [--remove-current] [--version] [--sgid SGID] [--sg-name SG_NAME] [--ssh-port SSH_PORT] [--add ADD_BLOCKS] [--remove REMOVE_BLOCKS]

Maintains a list of allowlisted CIDR blocks granted SSH access to AWS via a security group.

options:
-h, --help show this help message and exit
--list Lists the ip addresses in the security group with SSH access.
--add-current Adds the current IP address to the allowlist.
--remove-current Remove the current IP address from the allowlist.
--version Print the current version of awswl.
--sgid SGID The security group to use for SSH access.
--sg-name SG_NAME The name of the security group to use (wildcards allowed).
--ssh-port SSH_PORT The port used for SSH. By default this is port 22, but some people prefer to access SSH over another port.
--add ADD_BLOCKS Adds a manually-specified CIDR block from the allowlist.
--remove REMOVE_BLOCKS
Removes a manually-specified CIDR block from the allowlist.

To get the current version, `--version`:

❯ awswl --version
awswl v1.1.0
5 changes: 4 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
site_name: AWSWL


nav:
- Usage: 'usage.md'
- Alternatives: 'alternatives.md'
- Edge Cases: 'edgecases.md'
8 changes: 4 additions & 4 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ def security_group_fixture():
mock.start()
ec2 = boto3.resource('ec2')
sg = ec2.create_security_group(
Description='Security Group for SSH Whitelisting',
GroupName='SSH Whitelist',
Description='Security Group for SSH allowlisting',
GroupName='SSH allowlist',
VpcId='vpc-123'
)
yield sg
Expand Down Expand Up @@ -172,7 +172,7 @@ def test_remove_current_indicates_notfound(mock_stdout, exip_method, region,
opt = options(security_group)
commands.cmd_remove_current(opt)
assert \
"Current external IP address as a CIDR block does not seem to be whitelisted." \
"Current external IP address as a CIDR block does not seem to be allowlisted." \
in mock_stdout.getvalue()


Expand All @@ -198,5 +198,5 @@ def test_remove_removes_specified(mock_stdout, region, security_group):
def test_remove_specified_indicates_notfound(mock_stdout, region, security_group):
opt = options(security_group)
commands.cmd_remove(opt, '192.0.2.1/32')
assert "Specified CIDR block does not seem to be whitelisted." \
assert "Specified CIDR block does not seem to be allowlisted." \
in mock_stdout.getvalue()

0 comments on commit f20b120

Please sign in to comment.