Skip to content
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
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ account**. That is why the GCP module's base grant includes
`roles/iam.serviceAccountUser`, and why it asks for a project dedicated to Ringleader
workstations. Check what your default compute service account holds (older projects give
it `roles/editor`), or give workstations a role-less service account of their own — see
[`gcp/README.md`](gcp/README.md#every-workstation-runs-as-a-service-account).
[`gcp/README.md`](gcp/README.md#every-vm-ringleader-creates-runs-as-a-service-account).

Letting Ringleader **create** those per-user identities and **bind roles** to them is a
separate switch, `enable_workstation_identities`, and it costs real permissions: on GCP the
Expand Down Expand Up @@ -370,11 +370,17 @@ Each cloud's README lists the exact values. In short:
created) subnet id.
- **AWS** — role ARN, region, and (if created) subnet id and security group id.

Each module also prints a **governed subnet id** where it created one. That is the subnet you
name on a workstation that carries an **egress policy**. Placing a box in it is what puts it
behind the proxy. On AWS and Azure, a box with no egress policy loses its egress if it shares a
subnet the proxy steers. On those two clouds, each Ringleader namespace that runs its own proxy also
needs a governed subnet of its own. Set `additional_governed_subnets` (`ADDITIONAL_GOVERNED_SUBNETS`
The subnet, region, zone, network tags and security groups go on a CloudIdentity. That is the
Ringleader object a namespace admin writes to say how the namespace's workstations run in your
account. By default a namespace member cannot set these values on a workstation. Each cloud's README
shows a CloudIdentity.

Each module also prints a **governed subnet id** where it created one. In a namespace that runs a
proxy, a namespace admin names it in the CloudIdentity for the workstations that carry an **egress
policy**. Placing a workstation in it is what puts it behind the proxy. On AWS and Azure, a
workstation with no egress policy loses its egress if it shares a subnet the proxy steers. On those
two clouds, each Ringleader namespace that runs its own proxy also needs a governed subnet of its
own. Set `additional_governed_subnets` (`ADDITIONAL_GOVERNED_SUBNETS`
for `deploy.sh`) to create one for each further namespace. Terraform keys those ids by the labels
you gave them, the Azure `deploy.sh` prints them, and CloudFormation outputs them by slot number.

Expand Down
100 changes: 84 additions & 16 deletions aws/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ Everything else derives from those two:
## Pick a path

Use whichever your team already runs. They create the same OIDC provider, role, and
optional network, with one difference: the per-workstation instance-profile option
(`enable_workstation_identities`, below) exists only on the Terraform path.
optional network.

### CloudFormation (`aws` CLI)

Expand Down Expand Up @@ -144,6 +143,73 @@ pad's range, and the plan fails rather than guessing if they disagree. See
becomes the `Storage` object's `spec.grant`, and on the named width
**`artifact_storage_bucket`**, which becomes its `spec.bucket`

### Setting these values on a CloudIdentity

A CloudIdentity is the Ringleader object a namespace admin writes to say how that namespace's
workstations run in your account. Its `spec.defaultProviderConfig` fills in a value a workstation
leaves out, and its `spec.overrideProviderConfig` sets a value that wins over the workstation's own.
By default a namespace member cannot set the region, subnet or security groups on a workstation, so
they go here. With no `selector`, a CloudIdentity applies to every AWS workstation in its namespace:

```yaml
apiVersion: core.ringleader.dev/v1
kind: CloudIdentity
metadata:
name: aws
namespace: team-a
spec:
provider: aws
cloudAccountRef: <your CloudAccount's name>
defaultProviderConfig:
aws:
region: us-east-1
subnetId: <subnet_id>
securityGroupIds: [<security_group_id>]
```

`cloudAccountRef` names the CloudAccount, the Ringleader object that holds the `target_role_arn`
above.

Each workstation takes its settings from one CloudIdentity: the one with the highest `priority`
among those that select it. A workstation that carries an egress policy needs the group with no
egress rules (`inbound_only_security_group_id`). Where its namespace runs a proxy, it needs the
governed subnet too. For those workstations, write a second CloudIdentity with a higher `priority`
that selects them by a label you choose. Repeat in it every value the first one sets apart from the
subnet and the security group, because only one CloudIdentity applies:

```yaml
apiVersion: core.ringleader.dev/v1
kind: CloudIdentity
metadata:
name: aws-governed
namespace: team-a
spec:
provider: aws
cloudAccountRef: <your CloudAccount's name>
priority: 10
selector:
matchLabels:
egress: governed
subnet: <governed_subnet_id>
defaultProviderConfig:
aws:
region: us-east-1
securityGroupIds: [<inbound_only_security_group_id>]
```

`spec.subnet` puts every workstation this CloudIdentity selects in that subnet. It also claims the
subnet for the namespace, so Ringleader refuses another namespace's workstation there. Name the
subnet in `spec.subnet` only, because Ringleader refuses a CloudIdentity that also names one in
`defaultProviderConfig` or `overrideProviderConfig`. Never name the shared `subnet_id` in
`spec.subnet`, since the claim would refuse any workstation another namespace places in it. In a
namespace that runs no proxy, set `subnetId: <subnet_id>` under `defaultProviderConfig.aws` in place
of `spec.subnet`.

A workstation labeled `egress: governed` then takes these values. One that carries an egress policy
without the label takes the first CloudIdentity's group, which permits egress. Ringleader therefore
refuses to create such a workstation, as
[Which security group a workstation gets](#which-security-group-a-workstation-gets) describes.

## Reaching your workstations

| | needs | provided by |
Expand Down Expand Up @@ -391,9 +457,10 @@ ADDITIONAL_GOVERNED_SUBNETS=team-a=10.60.208.0/20,team-b=10.60.192.0/20 ./deploy

Each entry becomes a subnet named `ringleader-governed-<label>`, built exactly like the governed
subnet above. The label names the subnet and keys the `additional_governed_subnet_ids` output.
Ringleader never reads it, so use the name of the namespace that will use the subnet. Set each id as
`providerConfig.aws.subnetId` on that namespace's workstations that carry an egress policy, and on
no other namespace's.
Ringleader never reads it, so use the name of the namespace that will use the subnet. Name each id
as `spec.subnet` in that namespace's CloudIdentity for its workstations that carry an egress policy.
That claims the subnet for the namespace, so Ringleader refuses another namespace's workstation in
it. [Setting these values on a CloudIdentity](#setting-these-values-on-a-cloudidentity) shows one.

These subnets have no derived range, so write each CIDR out. In a first region, the six `/20`s from
`10.60.128.0/20` to `10.60.208.0/20` are free and leave the workstations subnet room to grow to a
Expand Down Expand Up @@ -561,12 +628,13 @@ missing or is `None`, the subnet id or the region is wrong. The last command the
even when something carries the group.

If the last command lists nothing, nothing carries the group now. If it lists the instance of a
workstation, that workstation has to move off the group first. Ringleader reads
`providerConfig.aws.securityGroupIds` only when it creates a workstation. So create the workstation
again with one of the two groups in
[Which security group a workstation gets](#which-security-group-a-workstation-gets) in that field.
Move anything else the command lists to a security group of its own. The commands list only what
exists now, so make sure every workstation you create later names a security group too.
workstation, that workstation has to move off the group first. Ringleader reads a workstation's
`securityGroupIds` only when it creates the workstation. So name one of the two groups in
[Which security group a workstation gets](#which-security-group-a-workstation-gets) as
`securityGroupIds` in the CloudIdentity that selects the workstation. Then create the workstation
again. Move anything else the command lists to a security group of its own. The commands list only
what exists now, so make sure every CloudIdentity for workstations in this VPC names a security
group too.

To turn the switch on, set one variable and apply again:

Expand Down Expand Up @@ -773,11 +841,11 @@ default share it, so either one's role could pass the other's workstation roles.

## Workstations hold no AWS identity by default

A workstation runs with **no instance profile** unless an administrator sets
`providerConfig.aws.iamInstanceProfile`. Without that, nothing inside the workstation can act as any
IAM principal. Attaching one needs `iam:PassRole`, which this module grants only when you
set `enable_workstation_identities` (`EnableWorkstationIdentities` on the CloudFormation path) —
read its warning first.
A workstation runs with **no instance profile** unless a namespace admin names one as
`iamInstanceProfile` in the CloudIdentity's `spec.overrideProviderConfig`. A workstation cannot name
its own. Without one, nothing inside the workstation can act as any IAM principal. Attaching one
needs `iam:PassRole`, which this module grants only while `enable_workstation_identities` is on, as
it is by default (`EnableWorkstationIdentities` on the CloudFormation path).

## Notes

Expand Down
74 changes: 66 additions & 8 deletions azure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,11 @@ first policy:
only TCP 22 and 2222, and the group has no outbound rule. Such a workstation takes no other port
from outside the VNet, even one your subnet NSG opens. Neither group admits anything the subnet
NSG refuses, because both layers have to agree.
- **A NIC NSG of your own is replaced, not merged.** A NIC carries at most one NSG. If you supply
the interface yourself (`providerConfig.azure.networkInterfaceId`) and narrowed inbound *there*,
Ringleader replaces that group when it creates the workstation, whether or not the workstation
declares `spec.egress`. With a policy, the replacement admits all inbound, so the subnet's rules
- **A NIC NSG of your own is replaced, not merged.** A NIC carries at most one NSG. A namespace
admin can supply a workstation's interface as `networkInterfaceId`, on that workstation or on a
CloudIdentity that selects only it. If you narrowed inbound on that interface's NSG, Ringleader
replaces the group when it creates the workstation, whether or not the workstation declares
`spec.egress`. With a policy, the replacement admits all inbound, so the subnet's rules
decide on their own, which **widens** inbound. Without one, it admits only TCP 22 and 2222 from
outside the VNet. Move those rules onto the subnet NSG first. Ringleader adds a rule there, but
never edits or deletes one you wrote.
Expand Down Expand Up @@ -414,9 +415,10 @@ ADDITIONAL_GOVERNED_SUBNETS=team-a=10.70.208.0/20,team-b=10.70.192.0/20 ./deploy

Each entry becomes a subnet named `governed-<label>`, built exactly like the governed subnet above.
The label names the subnet and keys the `additional_governed_subnet_ids` output. Ringleader never
reads it, so use the name of the namespace that will use the subnet. Set each id as
`providerConfig.azure.subnetId` on that namespace's workstations that carry an egress policy, and on
no other namespace's.
reads it, so use the name of the namespace that will use the subnet. Name each id as `spec.subnet`
in that namespace's CloudIdentity for its workstations that carry an egress policy. That claims the
subnet for the namespace, so Ringleader refuses another namespace's workstation in it.
[Setting these values on a CloudIdentity](#setting-these-values-on-a-cloudidentity) shows one.

These subnets have no derived prefix, so write each one out. In a first region, the six `/20`s from
`10.70.128.0/20` to `10.70.208.0/20` are free and leave the workstations subnet room to grow. A
Expand Down Expand Up @@ -893,12 +895,68 @@ turn flow logs off in a shared group only when nothing else is using the zone.
| **subscription id** | `az account show --query id -o tsv` |
| **resource group** | the one you scoped |
| **subnet id** (only if you created a network) | `terraform output handoff` |
| **governed subnet id** (only if you turned it on) | `terraform output handoff` — `providerConfig.azure.subnetId` for the workstations that carry an egress policy |
| **governed subnet id** (only if you turned it on) | `terraform output handoff`. It is the `spec.subnet` of the CloudIdentity for the workstations that carry an egress policy, where the namespace runs a proxy. See [Setting these values on a CloudIdentity](#setting-these-values-on-a-cloudidentity) |
| **additional governed subnet ids** (only if you added them) | `terraform output handoff`, or printed by `deploy.sh`. One for each further namespace that runs a proxy, used on that namespace's workstations only. See [One governed subnet per namespace that runs a proxy](#one-governed-subnet-per-namespace-that-runs-a-proxy) |
| **gateway subnet id** (only if you reserved one) | `terraform output handoff` — goes on the `Edge` as `spec.subnet`, not on a workstation; no gateway VM is built until it has one |
| **`artifact_storage_grant`** (`managed` or `named`) | `terraform output handoff` — the `Storage` object's `spec.grant`, if you want payloads in a storage account of yours |
| **`artifact_storage_account_name`** (named width only) | `terraform output handoff`, with the container Ringleader should write to. On the managed width Ringleader creates both itself |

### Setting these values on a CloudIdentity

A CloudIdentity is the Ringleader object a namespace admin writes to say how that namespace's
workstations run in your subscription. Its `spec.defaultProviderConfig` fills in a value a
workstation leaves out, and its `spec.overrideProviderConfig` sets a value that wins over the
workstation's own. By default a namespace member cannot set the subnet on a workstation, so it goes
here. With no `selector`, a CloudIdentity applies to every Azure workstation in its namespace:

```yaml
apiVersion: core.ringleader.dev/v1
kind: CloudIdentity
metadata:
name: azure
namespace: team-a
spec:
provider: azure
cloudAccountRef: <your CloudAccount's name>
defaultProviderConfig:
azure:
subnetId: <subnet id>
```

`cloudAccountRef` names the CloudAccount, the Ringleader object that holds the app client id and the
tenant id from the table above.

Each workstation takes its settings from one CloudIdentity: the one with the highest `priority`
among those that select it. Where a namespace runs a proxy, its workstations that carry an egress
policy go in the governed subnet. For those workstations, write a second CloudIdentity with a higher
`priority` that selects them by a label you choose. Repeat in it every value the first one sets
apart from the subnet, because only one CloudIdentity applies:

```yaml
apiVersion: core.ringleader.dev/v1
kind: CloudIdentity
metadata:
name: azure-governed
namespace: team-a
spec:
provider: azure
cloudAccountRef: <your CloudAccount's name>
priority: 10
selector:
matchLabels:
egress: governed
subnet: <governed subnet id>
```

`spec.subnet` puts every workstation this CloudIdentity selects in that subnet. It also claims the
subnet for the namespace, so Ringleader refuses another namespace's workstation there. Name the
subnet in `spec.subnet` only, because Ringleader refuses a CloudIdentity that also names one in
`defaultProviderConfig` or `overrideProviderConfig`. Never name the shared workstations subnet
in `spec.subnet`, since the claim would refuse any workstation another namespace places in it.

A workstation labeled `egress: governed` then goes in the governed subnet, and one without the label
takes the first CloudIdentity's subnet.

## Revoking

- **Terraform:** `terraform destroy`.
Expand Down
Loading
Loading