Advanced RBAC: Governing Access to Processes and Accounts, Part 1

Overview

This two-part example demonstrates advanced usage of Fugue’s role-based access control feature. It focuses on how to create and manage an S3 bucket using a combination of process-level RBAC, account-level RBAC, and RBAC metadata, along with IAM policy.

Prerequisites

It’s not necessary to have completed the Using the RBAC Feature walkthrough first, but you might find it a helpful resource for RBAC basics.

You will, however, need to have the Fugue CLI set up and the Conductor installed before you can run this example. If you haven’t done so yet, it just takes a few quick steps.

You’ll also need to be running the paid version of Fugue, not Free Fugue, since the paid version offers the full RBAC feature.

What We’ll Do In This Example

Here in part Part 1, we’ll write a composition declaring an S3 bucket and an RBAC policy composition governing accounts and processes. We’ll also show how to apply the policy to the Conductor, enable a new user, and switch users.

In part Part 2, we’ll run a composition as one user, check the process status as a second user, and kill the process as a third user. We’ll also explain how rules interact at the process and account levels.

What We’ll Have When We’re Done

We’ll have a composition that creates an S3 bucket with an IAM policy restricting access via AWS account ID. We’ll also have an RBAC policy composition that determines who can manage the process. And of course, we’ll have the S3 bucket itself.

You’ll also have implemented multiple layers of access control:

  • Access to an AWS resource (S3 bucket) is governed through IAM policy in the composition.
  • Access to the composition’s process is governed through RBAC at the process level and account level.
  • Access to the RBAC metadata is also governed through RBAC.

How Long It Will Take

About 30 minutes.

Download

You can download the source code for this example from Github:

If you executed init during the Quick Setup, download the compositions to the directory where you ran the command.

Get editor plug-ins here.

Let’s Go!

Your Assignment

Let’s say you’re the leader of the SecOps team at your place of work. You’ve been given a handful of tasks:

  • Create and maintain an S3 bucket with an IAM policy that gives full access to select SecOps employees.
  • Ensure only members of SecOps can create or modify the bucket or its IAM policy.
  • Ensure Bob, an analyst, has permission to check the status of the bucket process.

You’ll need to write two Ludwig files: a composition declaring a bucket with policy, and an RBAC policy composition governing access to Fugue.

Then, you’ll need to attach the RBAC policy to the Conductor, enable the new users, and execute process management commands as those users.

We’ll walk you through each of these steps. Along the way, we’ll explain how RBAC works at the account level and process level.

Keep reading, or jump ahead to a specific step:

Writing the Bucket Composition

We’ll start with the first task on the list: Writing a composition that declares an S3 bucket with a typed IAM policy. We’ll name the composition S3BucketWithPolicy.lw.

The composition is fairly straightforward. We include the composition keyword; import the Fugue.AWS, Fugue.AWS.S3, and Fugue.AWS.IAM.Typed libraries; and use the S3.Bucket.new constructor to declare an S3 bucket according to these specifications:

  • The bucket’s region is N. Virginia, Us-east-1.
  • A canned ACL grants read-only permissions to AWS’s Authenticated Users group.
  • An inline typed IAM policy grants full permissions to the AWS account IDs listed (just yours, once you replace the dummy ID).
Writing the S3 bucket composition.

Writing the S3 bucket composition.

Here’s the finished product:

composition

import Fugue.AWS as AWS
import Fugue.AWS.S3 as S3
import Fugue.AWS.IAM.Typed as Typed

bucketRepo: S3.Bucket.new {
  name: "bucket-name-goes-here",
  region: AWS.Us-east-1,
  acl: S3.AuthenticatedRead,
  tags: [AWS.tag("Application", "Artifact repository")],
  policy: Typed.Policy.toString(policyDocument)
}

policyDocument: Typed.Policy.new {
  version: Typed.Policy.V2012-10-17,
  statements: [
    Typed.Policy.allow {
      sid: "bucket-name-goes-here",
      principals: [ Typed.Policy.aws ["arn:aws:iam::xxxxxxxxxxxx:root"] ],
      actions: ["s3:*"],
      resources: ["arn:aws:s3:::bucket-name-goes-here/*"]
    }
  ]
}

Action required! Open S3BucketWithPolicy.lw in your favorite text editor and make the following changes:

  • Replace bucket-name-goes-here with a unique bucket name on lines 8, 19, and 22.
  • Replace xxxxxxxxxxxx with your AWS account ID on line 20.

Note

Going over each line of the composition is out of the scope of this example, but if you want more information about writing Ludwig, check out Writing Typed IAM Policies, Writing Compositions, or Ludwig Syntax Reference.

Writing the RBAC Policy

As you may recall from Using the RBAC Feature, an RBAC policy composition consists of rules that permit users to execute specific Fugue commands on specific accounts.

Our policy will declare the following:

  • Two users, alice and bob
  • One default Fugue-managed AWS account
  • A rule that gives alice read/write permissions on RBAC metadata, so she can manage users and RBAC policy
  • A rule that allows the SecOps team to create a new process in the default account
  • A rule that allows the SecOps team to execute all process-management commands on a specific process
  • A rule that allows bob to check the status of a specific process
Writing the RBAC policy.

Writing the RBAC policy.

Once again, we’ll start the policy composition as we would any other composition – with the composition keyword. We’ll also import the Fugue.System.Policy library.

composition

import Fugue.System.Policy as Policy

Declaring the Account

Next, we declare the Fugue-managed AWS account that this policy applies to. When you execute fugue account list, you should see a table of accounts, including the default fugue account. The NamedAccount constructor refers to an account by its human-friendly name, and the Account constructor refers to an account by its account ID (which might look like this: fugue-1507311378131).

NamedAccount is a little more convenient because you don’t need to look up the full account ID, so we’ll use this constructor to refer to the default fugue account (named default in the policy).

default: Policy.NamedAccount {accountName: "fugue"}

Declaring Users

Now, we create a list of users. Each RBAC user is declared in a top-level binding. We’ll declare alice and bob:

alice: Policy.User {userId: "alice"}
bob: Policy.User {userId: "bob"}

secOps: [alice]

In the fourth line above, we create another top-level binding, secOps, to represent a list of users on the SecOps team. alice is currently its only member, but having a separate list of SecOps team members makes it easy to add new members later. Instead of modifying every single SecOps rule, you simply add the user to the secOps list, and they are granted the appropriate permissions.

Declaring a Process

The next step is declaring the specific process you want to govern access to. In this case, it’s the process that Fugue will create once we run the bucket composition. We’ve decided to give it an alias of bucketProcess, so we can use the AliasedProcess constructor to refer to it. We’ll be sure to use this alias later when we run the composition.

bucketProcess: Policy.AliasedProcess {alias: "bucketProcess"}

Declaring RBAC Metadata Rules

A quick refresher on rules: A rule is the keystone type of the Fugue RBAC system. Rules are made up of principals, actions, and subjects. A principal is an entity that can be authorized to perform an operation, like a user. An action is an operation a principal can perform, like a Fugue command. A subject is the target of the action, like an account or a process. Access for non-root users is implicitly denied.

For our first set of rules, we’ve given the alice user full permissions for interacting with the RBAC metadata. This means that alice can attach or detach the RBAC policy, list RBAC users, generate user secrets, etc.

The rbacRules function returns a set of rules that governs access to the RBAC metadata:

aliceFullRbacPerms: Policy.rbacRules {
  principals: [alice],
  actions: Policy.allRbacActions
}

We’ve called the binding aliceFullRbacPerms so we know what the rules do at a glance.

The principals field takes a list of principals. In this case, there’s only alice.

Because the rbacRules function automatically sets the subject to the RBAC metadata, the function omits the subject field.

The actions field takes a list of actions. In this case, we’ve used the value allRbacActions, which provides full read/write access to the RBAC policy on the Conductor.

(Wondering why we consider this a set of rules, since there’s just one binding? The rbacRules function provides a quick way to generate multiple rules. It returns a rule that says alice can attach the RBAC policy; a rule that alice can detach the RBAC policy; and so on. Each rule still represents one principal, one subject, and one action.)

Declaring Account Run Rules

The singleAccountRules function returns a set of rules that allows SecOps to use the run command in the default account:

secOpsRunDefaultAccount: Policy.singleAccountRules {
  principals: secOps,
  account: default,
  actions: [Policy.AccountRunProcess]
}

We’ve called the binding secOpsRunDefaultAccount.

The principals field takes a list of principals. In this case, we’ve used secOps, which is a list of users (just alice at the moment).

The account field takes a single account name. In this case, we’ve used default.

The actions field takes a list of account actions. In this case, we’ve used AccountRunProcess, which permits the use of fugue run in an account.

Declaring Process Action Rules

Process-level rules are more fine-grained than account-level rules. Rather than governing access to entire accounts, they govern access to specific processes in specific accounts. The singleProcessRules function returns a set of rules that gives SecOps full access to the bucketProcess process:

secOpsFullPermsBucketProcess: Policy.singleProcessRules {
  principals: secOps,
  process: bucketProcess,
  actions: Policy.allProcessActions
}

We’ve called the binding secOpsFullPermsBucketProcess.

The principals field takes a list of principals. Again, we’ve used secOps.

The process field takes a single process name. In this case, we’ve used bucketProcess.

The actions field takes a list of actions. In this case, we’ve used the value allProcessActions, which provides full read/modify/delete permissions on the given process.

Declaring the Status-Only Rule

The last thing our RBAC policy needs to do is enable status-only access to bucketProcess for bob, the analyst. We use the Rule constructor to return a single rule:

bobStatusOnlyBucketProcess: Policy.Rule {
  principal: bob,
  subject: Policy.ProcessType(bucketProcess),
  action: Policy.ProcessAction(Policy.ProcessGetStatus)
}

We’ve called the binding bobStatusOnlyBucketProcess.

The principal field takes a single principal. In this case, we’ve used bob.

The subject field takes a single subject. In this case, the ProcessType constructor specifies bucketProcess.

The action field takes a single action. In this case, the ProcessAction constructor specifies ProcessGetStatus.

This is currently the only rule for bob, and as such, it is the only action he is permitted to take.

Applying the Policy

Now that we’ve written the RBAC policy composition, we need to attach it to the Conductor so it takes effect. We’ll use the fugue policy rbac-attach command:

fugue policy rbac-attach BucketRBACPolicy.lw

We’ll see output like this:

Compiling Ludwig file BucketRBACPolicy.lw ...
[ OK ] Successfully compiled. No errors.

Uploading policy to S3 ...
[ OK ] Successfully uploaded.

Requesting the Conductor set new policy ...
[ DONE ] Policy uploaded and applied.

We can confirm that the policy has successfully been attached by listing the Fugue RBAC users with the fugue policy list-users command:

fugue policy list-users

Three users are listed in the output:

Fugue User list for main-user/xxxxxxxxxxxx - Thu Dec 14 2017 2:23pm

User Name   Enabled    Created     Updated
----------  ---------  ----------  -----------
alice       no         2:17pm      2:17pm
bob         no         2:17pm      2:17pm
root        yes        Oct 6 2017  Oct 24 2017

[ HELP ] To enable a user, run "fugue policy generate-secret <user_id>".

The root user is enabled by default. It’s the one you’re using right now, and as you might expect, it’s a superuser. The other two users are alice and bob. Neither has been enabled yet, so let’s generate a secret, or access token, for alice. (Don’t worry – we’ll get to bob later!)

Applying the RBAC policy and switching users.

Applying the RBAC policy and switching users.

Enabling and Switching to a New User

The user secret is what authenticates a user and enables them to operate Fugue. We’ll enable alice by generating a secret, add her user information to the credentials file, and switch the active user from root to alice.

We can generate a secret for alice by using the fugue policy generate-secret command:

fugue policy generate-secret alice

You’ll see output like this:

====================
User Credential Details:

[alice]
user = alice
secret = ybzXWo37ung5K5KetdQv7JhO5/pCZNEXAMPLEEXAMPLE

[ HELP ] You can copy/paste these values into your credentials file /Users/main-user/projects/credentials
====================
[ DONE ] Secret successfully generated.

[alice] is the name of the credential profile, user is the username, and secret is the user secret. Your secret will be a different string, of course!

Note: This is the part where you, the admin, would normally vend the alice credentials to Alice herself. For this exercise, you’re going to switch back and forth between all three users, so you need to store all three sets of credentials in your credentials file.

As the CLI output notes, you can copy and paste alice‘s set of credentials into the credentials file, which is typically located in the directory where you ran fugue install.

However, there’s a handy shortcut. You can use the fugue user set –profile <profile> <user> <secret> command to switch the active user from root to alice and automatically add alice‘s credentials to the credentials file under a specified profile. Since we need the active user to be alice for the next step anyway, we’ll save ourselves the trouble of copying and pasting.

Note: Executing fugue user set <user> <secret> without the --profile option will overwrite the default (root) credentials, which are listed under the [default-<AWS account>-<region>] profile.

Warning

Do not lose your root credentials. They cannot be replaced with the policy generate-secret command. If you have lost your root credentials, see the reset-secret command or contact support@fugue.co.

Let’s switch to alice and save her credentials in a profile named alice. Copy the secret from the output of the last command and execute the fugue user set command, substituting the secret below for your own:

fugue user set --profile alice alice ybzXWo37ung5K5KetdQv7JhO5/pCZNEXAMPLEEXAMPLE

The credentials file can contain any number of profiles. But because a profile can only contain one set of credentials at a time, the CLI will warn you that the operation will overwrite anything currently in the alice profile. We don’t have an alice profile yet, so enter y. If you configured Fugue with init, you’ll see output like this:

[ INFO ] This operation will overwrite profile 'alice' in /Users/main-user/projects/credentials

[ WARN ] Are you sure you want to proceed? [y/N]: y
Found existing fugue.yaml.old file in /Users/main-user/projects .
Deleting existing fugue.yaml.old ...
[ OK ] Existing fugue.yaml.old file deleted.

Found existing fugue.yaml file in /Users/main-user/projects .
Renaming existing fugue.yaml file to fugue.yaml.old ...
[ OK ] Existing fugue.yaml file renamed.

[ DONE ] The user has been set to 'alice'

Note: You won’t see the six lines containing fugue.yaml if you configured Fugue with environment variables; you’ll just see the INFO, WARN, and DONE lines.

If you examine your credentials file now (cat credentials), you’ll see alice‘s new creds in addition to root creds:

[alice]
user = alice
secret = ybzXWo37ung5K5KetdQv7JhO5/pCZNEXAMPLEEXAMPLE

[default-xxxxxxxxxxxx-us-east-1]
user = root
secret = E6a5iZ9jbxoWgGIid65i6zC4XXzhvcEXAMPLEEXAMPLE

Continue to Part 2

Great work so far! You learned how to write an RBAC policy composition declaring rules at the account level and the process level, and you also learned how to apply the policy and enable RBAC users. There’s more to do, though, so take a breather and head over to Part 2 when you’re ready!