Skip to content

posts๐Ÿ”—

Simplify Aws Developer Security With Leapp

Security Is Necessary Work

Security can add overhead, get in the way, complicate development, generally can cause challenges to development efforts. The balance of security and usability is one that all development organizations have to deal with.

I could be ultra-secure by requiring carrier pigeon multi-factor authentication for my team, but I'm not sure that would be well received.

security steps

It's also critical and the responsibility of each developer, not just the security team.

Let's take a few simple examples where a local developer can compromise security in AWS by simply taking an easy path.

  • Builds a docker container and uploads to Docker Hub, for a generic tool, but includes embedded AWS credentials for a linter in it.
  • Uses IAM long-lived keys to use a tool to generate infrastructure as code and commits these to the repository due to not excluding the .env file in the .gitignore.
  • Uses IAM access keys that are highly privileged as the easy path (over narrowing permission scope), puts these in a vendor site that integrates with AWS, and forgets about it. Vendor has data compromised and the AWS keys are exposed.

There are so many ways that using long-lived IAM credentials can be abused.

Assume Roles

It's recommended to use Role Assumption instead of IAM Access keys when possible.

This means all the tooling will leverage AWS Security Token Service and use short-lived ephemeral scoped credentials.

This limits both the blast radius and time of any compromise.

Security As The Path Of Least Resistance

The problem with this path, is one of effort.

  • If using AWS SSO: You'd have to
    • login to your portal
    • open target account section
    • get programmatic credentials for the account
    • paste them into your ~/.aws/credentials file
  • If not using SSO, you'd have to use the CLI or appropriate sdk to generate your credentials on demand and then set.

The key is that all of these approaches aren't easy as the default.

It's easier to just use an IAM key and move on rather than battle with cli, sdks, and other options.

Tools like aws-vault are fantastic in helping with this, but aren't intuitive, work the same way in different platforms, and overall still provide a barrier to making it the easiest path.

Leapp

๐Ÿš€ Leapp - One step away from your Cloud

leapp ui

I came across this app by chance back in March when I was just starting at my new job at $Work and was looking for a tool to help simplify things over aws-vault. Since I was benefiting from AWS SSO, I was looking for a way to simplify switching roles with SSO, while still maintaining compatibility with the lowest common denominator of the lowly ~/.aws/credentials file that I knew worked with Terraform, PowerShell, and other tools equally.

The result? Shipped code on day 3!

What It Provides

Leapp allows the path of least resistance to best practices with AWS Credentials (as well as Azure). With AWS SSO, you get the benefit of the portal accounts automatically populated as well. Role assumption becomes the easy path.

How It Works

In my scenario, using AWS SSO provides a more complicated route that demonstrates the ease of use.

  • Open App
  • Click on Account & Role I want to assume, say "Dev Account - AWS Admininstrator"
  • Pop-up for SSO process using whatever SSO process is setup
  • Enter name and password
  • Enter MFA
  • Auth dialogue proceeds to confirmation screen of signing in CLI

aws ccli sso confirmation

  • Done!

The result:

~/.aws/credentials has the [default] profile containing my STS credentials. This token rotates every n1 minutes producing ephemeral credentials.

credentials file changed

Enhancements

Named Profiles

The 0.6.0 release that should be out by Jun 30th (I'm using pre-release preview) contained some nice enhancements to this.

In prior versions, [default] profile was just replaced with whatever account you selected.

Now, named profiles are supported as well, so you could have multiple assumed roles open at the same time for calling with named profiles.

SSM Sesssion Launcher

There's a built-in session launcher, allowing searching of EC2 instances and the instance terminal launch of an aws ssm start-session command against the target.

leapp AWS ssm launcher

Future

The future for Leapp is an interesting question.

They are active in engaging for feedback, so this is built with actual user feedback, not in a bubble. I had an hour call with one of the developers talking about authentication approaches that helped me appreciate how much they want to engage their uses in a true Agile development pattern. Not only did I learn a bunch, but ended up being able to provide a few ideas for future consideration. Great discussion with @andreacavagna01. Give Andrea a follow on :(fab fa-twitter): twitter and :(fab fa-github): GitHub!

I can see a larger market than developers if a company rolled it out to users, as it would simplify access by analysts, developers, security team members, and others.

It's not:

  • An "essential" service
  • "Necessary" in a strict sense, because things work fine without this tool.

Instead it offers:

  • A usability improvement to developers authentication workflow.
  • The easy path is a secure path

For this reason, I'm interested to see how adoption by enterprises and organizations will occur. I've observed that solving painpoints can both incentive a company if painful enough to adopt new approaches, but also bypass improvements as they aren't "essential" and things already work, albeit unoptimally.

For me, I'm adding it to my AWS essentials, since now I can just focus on work without and improve my credentials handling without it intruding. I'll continue recommending this to folks, and keep up with what improvements continue to be released from this team.

Keep up with the Leapp roadmap here: GitHub Leapp Roadmap

Resources

Resource
Access management for AWS resources - AWS Identity and Access Management
Resources to learn more about IAM - AWS Identity and Access Managemen
Using IAM roles - AWS Identity and Access Management

  1. I believe 10-20 right now 

Go R1 Day 47

progress

  • worked with lefthook to run golang-lint tools and discovered how vast the configuration for this was, simplifying many of the other linting tools that was running seperately.
  • created some goyek tasks for bringing up and down docker compose stacks.
  • learned that file.Close() shouldn't be used with defer to avoid unsafe file handling operations.
// taskComposeDestroy tears down the docker stack including volumes
// this is using goyek task framework for Make alternative
func taskComposeDestroy() goyek.Task {
  return goyek.Task{
    Name:  "compose-destroy",
    Usage: "remove stack with prejudice",
    Action: func(tf *goyek.TF) {
      dcBase := filepath.Join(BuildRoot, "docker", "docker-compose.myservice.yml")
      dcMySQL := filepath.Join(BuildRoot, "docker", "docker-compose.mysql.yml")
      compose := tf.Cmd("docker", "compose", "-f", dcBase, "-f", dcMySQL, "down", "--volumes", "--remove-orphans")
      if err := compose.Run(); err != nil {
        tf.Fatalf(":heavy_exclamation_mark: docker-compose down failed: [%v]", err)
      }
      tf.Log(":white_check_mark: docker-compose ran successfully")
    },
  }
}

Go R1 Day 46

progress

  • At $work I focused on dockertest.
  • Modified the provided test statements to ensure autoremoval of container occurred on failure.
  • Had packet issues I couldn't figure out this time, so shelved for now. packets.go:37: unexpected eof
  • At home, I played with bubbleteam a bit, and decided while an epic TUI interface, the framework is far too involved for what I want to mess around with at this time. For instance, it doesn't provide multi-select, instead much of that is manually written, requiring a lot of effort. I'll look at another framework or go-prompt again, and just use something that provides selections out of the box.
  • Further refined some goyek build statements, running docker compose multi-file based merging of docker-compose files enabled.

Understanding The Basics of SQL Server Security

Confusing

As I've worked with folks using other database engines, I've realized that Microsoft SQL Server has some terminology and handling that is a bit confusing. Here's my attempt to clarify the basics for myself and others needing a quick overview. This is not comprehensive coverage of security architecture, which is a very complex topic, more just terminology.

Terminology

Note that it's best to consider SQL Server as it's own operating system, not just a standard application running. It has its own memory manage, cpu optimization, user security model, and more. It's helpful in understanding why a Server Login != Instance Login by reviewing common terminology. I've noticed that among other open-source tools like MySQL, it's much more common to hear terms like "Database Server", which in my mind mix up for non-dbas the actual scope being talked about.

Term Definition
Server The operating system
Instance The SQL Server Instance that can contain 1 or many databases
Database The database inside the instance.

This can be 1 or many.

Term Definition
Server Login Windows or Linux user at the Operating System level
SQL Login Login created inside SQL Server, using SQL statement. This is internal to SQL Server and not part of the Server OS.
Database User A database user is created and linked to the Instance SQL Login
Server Role Roles for Instance level permissions, such sysadmin (sa), SecurityAdmin, and others. These do not grant database-level permissions, other than sa having global rights.
Database Role A defined role that grants read, write, or other permissions inside the database.

Here's a quick visual I threw together to reinforce the concept.

Yes, I'm a talented comic artist and take commissions. ๐Ÿ˜€

sql-login-database-architecture

Best Practice

When managing user permissions at a database level, it's best to leverage Active Directory (AD) groups. Once this is done, you'd create roles. The members of those roles would be the AD Groups.

No Active Directory

SQL Logins and corresponding database users must be created if active directory groups aren't being used.

Survey Said

I did a quick Twitter survey and validated that Active Directory Groups are definitely the most common way to manage.

Go R1 Day 45

progress

  • Worked through merging flags and the "run" approach from Mat Ryer.
  • Used ff for parsing.

The resulting logic seems correct with main being very simple with:

package main
import(
    "io"
    "flag"
    "os"
    "github.com/peterbourgon/ff/v3"
    "github.com/peterbourgon/ff/v3/ffcli"
    "github.com/peterbourgon/ff/v3/fftoml"
)
const (
 // exitFail is the exit code if the program
 // fails.
 exitFail           = 1
)

// main configuration from Matt Ryer with minimal logic, passing to run, to allow easier CLI tests
func main() {
 if err := run(os.Args, os.Stdout); err != nil {
  fmt.Fprintf(os.Stderr, "%s\n", err)
  os.Exit(exitFail)
 }
}

The run function then handles the actual parsing:

func run(args []string, stdout io.Writer) error {
 if len(args) == 0 {
  return errors.New("no arguments")
 }
  flags := flag.NewFlagSet(args[0], flag.ExitOnError)
 flag.BoolVar(&debug, "debug", false, "sets log level to debug")

if err := ff.Parse(flags, args,
  ff.WithEnvVarNoPrefix(),
  ff.WithConfigFileFlag("config"),
  ff.WithConfigFileParser(fftoml.Parser),
 ); err != nil {
  return err
 }
 if debug {
  logLevel = "debug"
 }
  // proceed with initialization of logger and other tools
  return nil

I like this approach, as the examples by Mat show how you can end up testing the inputs and variations on flags as well. The example from his blog post showed how easy it became with:

err := run([]string{"program", "-v", "-debug=true", "-another=2"})

Go R1 Day 44

progress

  • Generated tests for Gorilla Mux and realized need more context and reading on how to properly test Gorilla Mux to avoid excessively mocking.
  • Reviewed two different paradigms for struct methods, using parameters and using global configuration objects.

Go R1 Day 43

progress

  • Modified forked SharedBrain repo to use yaml parser instead of toml.
  • Modified tests handle invalid casting of interface, which was causing a panic.
otherDate, ok := otherDateInt.(time.Time)
if !ok {
  log.Printf("[time.Parse] probable invalid date format %s", plainFilename)
}
  • Improved tests to align to markdown standard formatting.
  • FOSS license scanned on 4 repos to test compliance of licensing for badge.
  • Use goyek templates to build out initial go based build actions.

Go R1 Day 40

progress

  • Learned a bit more modules vs packages, including internal package special behavior.
  • Configured Azure DevOps remote module source with package level imports.
  • Successfully migrated a utility logger for setup of Zerolog into a seperate remote module for importing.
  • Setup 50% test coverage for logger using gotest test generator.
  • Used Is for test setup. (this would be a cool use of gotest templates test generation)
  • Modified sharedbrain forked repo to generate yaml front matter with dashes.

Go R1 Day 41

progress

  • Enabled Go code coverage for tests in VSCode
  • go install github.com/jpoles1/gopherbadger@master to install tooling for generating code coverage badge for readme.
  • Set ![gopherbadger-tag-do-not-edit]() in the readme, and then this gets replaced with a code coverage percentage badge.
  • Generate the required code coverage reports using: go test -coverprofile cover.out followed by go tool cover -html=cover.out -o coverage.html for a visual report.