Skip to content

Commit c16a108

Browse files
authored
Merge pull request #17 from iann0036/feature/proxy
Feature/proxy
2 parents d88f1a2 + 63dee5c commit c16a108

630 files changed

Lines changed: 684030 additions & 685 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NOTICE

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4747
SOFTWARE.
4848

4949
---
50+
51+
3. https://github.com/elazarl/goproxy
52+
53+
54+
Copyright (c) 2012 Elazar Leibovich. All rights reserved.
55+
56+
Redistribution and use in source and binary forms, with or without
57+
modification, are permitted provided that the following conditions are
58+
met:
59+
60+
* Redistributions of source code must retain the above copyright
61+
notice, this list of conditions and the following disclaimer.
62+
* Redistributions in binary form must reproduce the above
63+
copyright notice, this list of conditions and the following disclaimer
64+
in the documentation and/or other materials provided with the
65+
distribution.
66+
* Neither the name of Elazar Leibovich. nor the names of its
67+
contributors may be used to endorse or promote products derived from
68+
this software without specific prior written permission.
69+
70+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
71+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
72+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
73+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
74+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
75+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
76+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
77+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
78+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
79+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
80+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
81+
82+
---

README.md

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# iamlive
22

3-
> Generate a basic IAM policy from AWS client-side monitoring (CSM)
3+
> Generate an IAM policy from AWS calls using client-side monitoring (CSM) or embedded proxy
44
55
![](https://raw.githubusercontent.com/iann0036/iamlive/assets/iamlive.gif)
66

@@ -30,27 +30,59 @@ To start the listener, simply run `iamlive` in a separate window to your CLI / S
3030

3131
You can optionally also include the following arguments to the `iamlive` command:
3232

33-
**--set-ini:** when set, the `.aws/config` file will be updated to use the CSM monitoring and removed when exiting (_default: false_)
33+
**--set-ini:** when set, the `.aws/config` file will be updated to use the CSM monitoring or CA bundle and removed when exiting (_default: false_)
3434

3535
**--profile:** use the specified profile when combined with `--set-ini` (_default: default_)
3636

37-
**--fails-only:** when set, only failed AWS calls will be added to the policy (_default: false_)
37+
**--fails-only:** when set, only failed AWS calls will be added to the policy, csm mode only (_default: false_)
3838

3939
**--output-file:** specify a file that will be written to on SIGHUP or exit (_default: unset_)
4040

4141
**--refresh-rate:** instead of flushing to console every API call, do it this number of seconds (_default: 0_)
4242

4343
**--sort-alphabetical:** sort actions alphabetically (_default: false_)
4444

45-
**--host:** host to listen on (_default: 127.0.0.1_)
45+
**--host:** host to listen on for CSM (_default: 127.0.0.1_)
4646

47-
_Comprehensive Example_
47+
**--mode:** _[experimental]_ the listening mode (`csm`,`proxy`) (_default: csm_)
48+
49+
**--bind-addr:** _[experimental]_ the bind address for proxy mode (_default: 127.0.0.1:10080_)
50+
51+
**--ca-bundle:** _[experimental]_ the CA certificate bundle (PEM) to use for proxy mode (_default: ~/.iamlive/ca.pem_)
52+
53+
**--ca-key:** _[experimental]_ the CA certificate key to use for proxy mode (_default: ~/.iamlive/ca.key_)
54+
55+
**--account-id:** _[experimental]_ the AWS account ID to use in policy outputs within proxy mode (_default: 123456789012_)
56+
57+
_Basic Example (CSM Mode)_
58+
59+
```
60+
iamlive --set-ini
61+
```
62+
63+
_Basic Example (Proxy Mode)_
64+
65+
```
66+
iamlive --set-ini --mode proxy
67+
```
68+
69+
_Comprehensive Example (CSM Mode)_
4870

4971
```
5072
iamlive --set-ini --profile myprofile --fails-only --output-file policy.json --refresh-rate 1 --sort-alphabetical --host 127.0.0.1
5173
```
5274

53-
### CSM Enabling
75+
_Comprehensive Example (Proxy Mode)_
76+
77+
```
78+
iamlive --set-ini --mode proxy --profile myprofile --output-file policy.json --refresh-rate 1 --sort-alphabetical --bind-addr 127.0.0.1:10080 --ca-bundle ~/.iamlive/ca.pem --ca-key ~/.iamlive/ca.key --account-id 123456789012
79+
```
80+
81+
The arguments may also be specified in an INI file located at `~/.iamlive/config`.
82+
83+
### CSM Mode
84+
85+
Client-side monitoring mode is the default behaviour and will use [metrics](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/metrics.html) delivered locally via UDP to capture policy statements with the `Action` key only (`Resource` is only available in proxy mode).
5486

5587
#### CLI
5688

@@ -76,16 +108,49 @@ export AWS_CSM_PORT=31000
76108
export AWS_CSM_HOST=127.0.0.1
77109
```
78110

111+
### Proxy Mode
112+
113+
Proxy mode will serve a local HTTP(S) server (by default at `http://127.0.0.1:10080`) that will MITM requests sent to the AWS endpoints and generate IAM policy statements with both `Action` and `Resource` keys. The CA key/certificate pair will be automatically generated and stored within `~/.iamlive/` by default.
114+
115+
#### CLI
116+
117+
To set the appropriate CA bundle in the AWS CLI, you should either use the `--set-ini` option or add the following to the relevant profile in `.aws/config`:
118+
119+
```
120+
ca_bundle = ~/.iamlive/ca.pem
121+
```
122+
123+
Alternatively, you can run the following in the window executing your CLI commands:
124+
125+
```
126+
export AWS_CA_BUNDLE=~/.iamlive/ca.pem
127+
```
128+
129+
You must also set the proxy settings for your session by running the following in the window executing your CLI commands:
130+
131+
```
132+
export HTTP_PROXY=http://127.0.0.1:10080
133+
export HTTPS_PROXY=http://127.0.0.1:10080
134+
```
135+
136+
#### SDKs
137+
138+
To enable CSM in the various AWS SDKs, you can run the following in the window executing your application prior to it starting:
139+
140+
```
141+
export HTTP_PROXY=http://127.0.0.1:10080
142+
export HTTPS_PROXY=http://127.0.0.1:10080
143+
export AWS_CA_BUNDLE=~/.iamlive/ca.pem
144+
```
145+
146+
Check the [official docs](https://docs.aws.amazon.com/credref/latest/refdocs/setting-global-ca_bundle.html) for further details on setting the CA bundle.
147+
79148
## FAQs
80149

81150
_I get a message "package embed is not in GOROOT" when attempting to build myself_
82151

83152
This project requires Go 1.16 or above to be built correctly (due to embedding feature).
84153

85-
_Can we include specifics for the Resource and Condition fields?_
86-
87-
No, the CSM protocol does not support it and cannot be changed.
88-
89154
## Acknowledgements
90155

91-
This project makes heavy use of [Parliament](https://github.com/duo-labs/parliament) and was assisted by Scott Piper's [CSM explainer](https://summitroute.com/blog/2020/05/25/client_side_monitoring/).
156+
This project makes heavy use of [Parliament](https://github.com/duo-labs/parliament) and was assisted by Scott Piper's [CSM explainer](https://summitroute.com/blog/2020/05/25/client_side_monitoring/). Thanks also to Noam Dahan's [research](https://ermetic.com/whats-new/blog/auditing-passrole-a-problematic-privilege-escalation-permission/) into missing `iam:PassRole` dependant actions.

csm.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"net"
9+
"os"
10+
"os/signal"
11+
"runtime/pprof"
12+
"strings"
13+
"syscall"
14+
15+
"github.com/mitchellh/go-homedir"
16+
"gopkg.in/ini.v1"
17+
)
18+
19+
func setINIConfigAndFileFlush() {
20+
// set ini
21+
if *setiniFlag {
22+
cfgfile, err := homedir.Expand("~/.aws/config")
23+
if err != nil {
24+
return
25+
}
26+
27+
cfg, err := ini.Load(cfgfile)
28+
if err != nil {
29+
return
30+
}
31+
32+
if *profileFlag == "default" {
33+
if *modeFlag == "csm" {
34+
cfg.Section("default").Key("csm_enabled").SetValue("true")
35+
} else if *modeFlag == "proxy" {
36+
cfg.Section("default").Key("ca_bundle").SetValue(*caBundleFlag)
37+
}
38+
} else {
39+
if *modeFlag == "csm" {
40+
cfg.Section(fmt.Sprintf("profile %s", *profileFlag)).Key("csm_enabled").SetValue("true")
41+
} else if *modeFlag == "proxy" {
42+
cfg.Section(fmt.Sprintf("profile %s", *profileFlag)).Key("ca_bundle").SetValue(*caBundleFlag)
43+
}
44+
}
45+
46+
cfg.SaveTo(cfgfile)
47+
}
48+
49+
// listen for exit, cleanup and flush
50+
sigc := make(chan os.Signal, 1)
51+
signal.Notify(sigc,
52+
syscall.SIGHUP,
53+
syscall.SIGINT,
54+
syscall.SIGTERM,
55+
syscall.SIGQUIT)
56+
go func() {
57+
for s := range sigc {
58+
// flush to file
59+
if *outputFileFlag != "" {
60+
err := ioutil.WriteFile(*outputFileFlag, getPolicyDocument(), 0644)
61+
if err != nil {
62+
log.Fatalf("Error writing policy to %s", *outputFileFlag)
63+
}
64+
}
65+
66+
if s == syscall.SIGINT || s == syscall.SIGTERM || s == syscall.SIGQUIT {
67+
if *setiniFlag {
68+
// revert ini
69+
cfgfile, err := homedir.Expand("~/.aws/config") // need to redeclare
70+
if err != nil {
71+
os.Exit(1)
72+
}
73+
74+
cfg, err := ini.Load(cfgfile)
75+
if err != nil {
76+
os.Exit(1)
77+
}
78+
79+
if *profileFlag == "default" {
80+
if *modeFlag == "csm" {
81+
cfg.Section("default").DeleteKey("csm_enabled")
82+
} else if *modeFlag == "proxy" {
83+
cfg.Section("default").DeleteKey("ca_bundle")
84+
}
85+
} else {
86+
if *modeFlag == "csm" {
87+
cfg.Section(fmt.Sprintf("profile %s", *profileFlag)).DeleteKey("csm_enabled")
88+
} else if *modeFlag == "proxy" {
89+
cfg.Section(fmt.Sprintf("profile %s", *profileFlag)).DeleteKey("ca_bundle")
90+
}
91+
}
92+
cfg.SaveTo(cfgfile)
93+
}
94+
95+
pprof.StopCPUProfile()
96+
97+
// exit
98+
os.Exit(0)
99+
}
100+
}
101+
}()
102+
}
103+
104+
func listenForEvents() {
105+
var iamMap iamMapBase
106+
107+
addr := net.UDPAddr{
108+
Port: 31000,
109+
IP: net.ParseIP(*hostFlag),
110+
}
111+
conn, err := net.ListenUDP("udp", &addr)
112+
if err != nil {
113+
panic(err)
114+
}
115+
err = conn.SetReadBuffer(1048576)
116+
if err != nil {
117+
panic(err)
118+
}
119+
defer conn.Close()
120+
121+
err = json.Unmarshal(bIAMMap, &iamMap)
122+
if err != nil {
123+
panic(err)
124+
}
125+
126+
var buf [1048576]byte
127+
for {
128+
rlen, _, err := conn.ReadFromUDP(buf[:])
129+
if err != nil {
130+
panic(err)
131+
}
132+
133+
entries := strings.Split(string(buf[0:rlen]), "\n")
134+
135+
EntryLoop:
136+
for _, entry := range entries {
137+
var e Entry
138+
139+
err := json.Unmarshal([]byte(entry), &e)
140+
if err != nil {
141+
panic(err)
142+
}
143+
144+
// checked if permissionless
145+
for _, permissionlessAction := range iamMap.SDKPermissionlessActions {
146+
if strings.ToLower(permissionlessAction) == fmt.Sprintf("%s.%s", strings.ToLower(e.Service), strings.ToLower(e.Method)) {
147+
continue EntryLoop
148+
}
149+
}
150+
151+
if e.Type == "ApiCall" {
152+
callLog = append(callLog, e)
153+
handleLoggedCall()
154+
}
155+
}
156+
}
157+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.16
44

55
require (
66
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
7+
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e
78
github.com/mitchellh/go-homedir v1.1.0
89
golang.org/dl v0.0.0-20210204224843-1557c60ec592 // indirect
910
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0pdTRcr/YEbBoxY+3GOH3gWvl4=
22
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U=
3+
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs=
4+
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
5+
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
36
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
47
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
8+
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
59
golang.org/dl v0.0.0-20210204224843-1557c60ec592 h1:GU5BB6jCTo/NbOo4bkZB5ZD2cRqqGbil8S7HfDcNSzo=
610
golang.org/dl v0.0.0-20210204224843-1557c60ec592/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
711
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=

0 commit comments

Comments
 (0)