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
31 changes: 2 additions & 29 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,11 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

name: PR
---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.
**Describe the PR**

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
67 changes: 38 additions & 29 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ on:
push:
branches: [ master, main ]

permissions:
contents: write
statuses: write
checks: write
pull-requests: write

jobs:
set-version:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
container:
image: mcr.microsoft.com/dotnet/sdk:6.0
outputs:
Expand All @@ -27,47 +33,50 @@ jobs:
- name: Set SemVer Version
uses: gittools/actions/gitversion/execute@v1
id: gitversion

pr:
runs-on: ubuntu-latest
container:
image: golang:1.24-bookworm
runs-on: ubuntu-22.04
needs: set-version
env:
REVISION: $GITHUB_SHA
SEMVER: ${{ needs.set-version.outputs.semVer }}
steps:
- uses: actions/checkout@v4
- name: install deps

- uses: ensono/actions/eirctl-setup@v0.3.1
with:
version: latest
isPrerelease: false

- name: prep-git
run: |
# Chromium dependencies
apt-get update && apt-get install -y jq git \
zip unzip \
libnss3 \
libxss1 \
libasound2 \
libxtst6 \
libgtk-3-0 \
libgbm1 \
ca-certificates
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config user.email ${{ github.actor }}-ci@gha.org
git config user.name ${{ github.actor }}
- name: make test

- name: Run linters
run: |
echo 'eirctl run vuln:check'

- name: Unit Tests
run: |
make REVISION=$GITHUB_SHA test
- name: Publish Junit style Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
eirctl run unit:test:run

- name: Publish Test Report
uses: mikepenz/action-junit-report@v5
if: success() || failure()
with:
report_paths: '**/report-junit.xml'
- name: Analyze with SonarCloud
# You can pin the exact commit or the version.
uses: SonarSource/sonarcloud-github-action@master
report_paths: '.coverage/report-junit.xml'
commit: ${{ github.sha }}
fail_on_failure: true
check_name: aws-cli-auth Unit Tests

- name: Analyze with SonarCloud
uses: SonarSource/sonarqube-scan-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret)
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args:
projectBaseDir: .
args: >
-Dsonar.projectVersion=${{ needs.set-version.outputs.semVer }}
-Dsonar.go.coverage.reportPaths=/github/workspace/.coverage/out
-Dsonar.go.tests.reportPaths=/github/workspace/.coverage/report-junit.xml
24 changes: 14 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,35 @@ jobs:
run: |
echo "REVISION -> $GITHUB_SHA"
echo "VERSION -> $GITVERSION_SEMVER"

release:
runs-on: ubuntu-latest
container:
image: golang:1.24-bookworm
env:
FOO: Bar
needs: set-version
env:
SEMVER: ${{ needs.set-version.outputs.semVer }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: install deps

- uses: ensono/actions/eirctl-setup@v0.3.1
with:
version: latest
isPrerelease: false

- name: git-deps
run: |
apt-get update && apt-get install jq git -y
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config user.email ${{ github.actor }}-ci@gha.org
git config user.name ${{ github.actor }}
- name: release library
run: |
make GIT_TAG=${SEMVER} REVISION=$GITHUB_SHA tag
- name: release binary
VERSION=${SEMVER} REVISION=$GITHUB_SHA eirctl run tag

- name: Build binary
run: |
make REVISION=$GITHUB_SHA GIT_TAG=${SEMVER} PAT=${{ secrets.GITHUB_TOKEN }} cross-build
eirctl run pipeline bin:release --set Version=${SEMVER} --set Revision=$GITHUB_SHA

- name: Release
uses: softprops/action-gh-release@v2.2.1
with:
Expand All @@ -72,4 +76,4 @@ jobs:
generate_release_notes: true
token: ${{ secrets.GITHUB_TOKEN }}
files: ./dist/*
prerelease: false
prerelease: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ vendor/

.ignore*
local/
.deps/
.cache/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ This tool deals with IdP logins via SAML, both into an AWS account directly or v

If, however, you need to support a non standard user journeys enforced by your IdP i.e. a sub company selection within your organization login portal, or a selection screen for different MFA providers - PingID or RSA HardToken etc.... you cannot reliably automate the flow or it would have to be too specific.

As such this approach uses [go-rod](https://github.com/go-rod/rod) library to uniformly allow the user to complete any and all auth steps and selections in a managed browser session up to the point of where the SAMLResponse is to be sent to AWS ACS service `https://signin.aws.amazon.com/saml`.
As such this approach uses [go-rod](https://github.com/go-rod/rod) library to uniformly allow the user to complete any and all auth steps and selections in a managed browser session up to the point of where the SAMLResponse is to be sent to AWS ACS service `https://signin.aws.amazon.com/saml`.

Capturing this via hijack request and posting to AWS STS service to exchange this for the temporary credentials.

Expand Down
21 changes: 20 additions & 1 deletion aws-cli-auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,29 @@ package main

import (
"context"
"log"
"os"
"os/signal"
"syscall"

"github.com/DevLabFoundry/aws-cli-auth/cmd"
)

func main() {
cmd.Execute(context.Background())
ctx, stop := signal.NotifyContext(context.Background(), []os.Signal{os.Interrupt, syscall.SIGTERM, os.Kill}...)
defer stop()

go func() {
<-ctx.Done()
stop()
// log.Printf("\x1b[31minterrupted: %s\x1b[0m", ctx.Err())
os.Exit(0)
}()

c := cmd.New()
c.WithSubCommands(cmd.SubCommands()...)

if err := c.Execute(ctx); err != nil {
log.Fatalf("\x1b[31m%s\x1b[0m", err)
}
}
93 changes: 93 additions & 0 deletions cmd/awscliauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package cmd

import (
"context"
"fmt"
"os"
"path"

"github.com/DevLabFoundry/aws-cli-auth/internal/credentialexchange"
"github.com/spf13/cobra"
)

var (
Version string = "0.0.1"
Revision string = "1111aaaa"
)

type Root struct {
Cmd *cobra.Command
// ChannelOut io.Writer
// ChannelErr io.Writer
// viperConf *viper.Viper
rootFlags *rootCmdFlags
Datadir string
}

type rootCmdFlags struct {
cfgSectionName string
storeInProfile bool
killHangingProcess bool
roleChain []string
verbose bool
duration int
}

func New() *Root {
rf := &rootCmdFlags{}
r := &Root{
rootFlags: rf,
Cmd: &cobra.Command{
Use: "aws-cli-auth",
Short: "CLI tool for retrieving AWS temporary credentials",
Long: `CLI tool for retrieving AWS temporary credentials using SAML providers, or specified method of retrieval - i.e. force AWS_WEB_IDENTITY.
Useful in situations like CI jobs or containers where multiple env vars might be present.
Stores them under the $HOME/.aws/credentials file under a specified path or returns the crednetial_process payload for use in config`,
Version: fmt.Sprintf("%s-%s", Version, Revision),
SilenceUsage: true,
SilenceErrors: true,
},
}

r.Cmd.PersistentFlags().StringSliceVarP(&rf.roleChain, "role-chain", "", []string{}, "If specified it will assume the roles from the base credentials, in order they are specified in")
r.Cmd.PersistentFlags().BoolVarP(&rf.storeInProfile, "store-profile", "s", false, `By default the credentials are returned to stdout to be used by the credential_process.
Set this flag to instead store the credentials under a named profile section. You can then reference that profile name via the CLI or for use in an SDK`)
r.Cmd.PersistentFlags().StringVarP(&rf.cfgSectionName, "cfg-section", "", "", "Config section name in the default AWS credentials file. To enable priofi")
// When specifying store in profile the config section name must be provided
r.Cmd.MarkFlagsRequiredTogether("store-profile", "cfg-section")
r.Cmd.PersistentFlags().IntVarP(&rf.duration, "max-duration", "d", 900, `Override default max session duration, in seconds, of the role session [900-43200].
NB: This cannot be higher than the 3600 as the API does not allow for AssumeRole for sessions longer than an hour`)
r.Cmd.PersistentFlags().BoolVarP(&rf.verbose, "verbose", "v", false, "Verbose output")
_ = r.dataDirInit()
return r
}

// SubCommands is a standalone Builder helper
//
// IF you are making your sub commands public, you can just pass them directly `WithSubCommands`
func SubCommands() []func(*Root) {
return []func(*Root){
newSamlCmd,
newClearCmd,
newSpecificIdentityCmd,
}
}

func (r *Root) WithSubCommands(iocFuncs ...func(rootCmd *Root)) {
for _, fn := range iocFuncs {
fn(r)
}
}

func (r *Root) Execute(ctx context.Context) error {
return r.Cmd.ExecuteContext(ctx)
}

func (r *Root) dataDirInit() error {
datadir := path.Join(credentialexchange.HomeDir(), fmt.Sprintf(".%s-data", credentialexchange.SELF_NAME))
if _, err := os.Stat(datadir); err != nil {
return os.MkdirAll(datadir, 0755)
}
r.Datadir = datadir
return nil
}
Loading