Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 12c17ee
Showing
9 changed files
with
988 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
*.log | ||
*.log.* | ||
*-collector | ||
.idea | ||
.DS_Store | ||
*.env | ||
*.state | ||
secrets.yml | ||
*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Accept the Go version for the image to be set as a build argument. | ||
# Default to Go 1.12 | ||
ARG GO_VERSION=1.13 | ||
|
||
# First stage: build the executable. | ||
FROM golang:${GO_VERSION}-alpine AS builder | ||
|
||
# Enable Go Modules | ||
ENV GO111MODULE=on | ||
|
||
# Install dependencies | ||
RUN apk --no-cache add build-base git bzr mercurial gcc ca-certificates | ||
|
||
# Create the user and group files that will be used in the running container to | ||
# run the process as an unprivileged user. | ||
RUN mkdir /user && \ | ||
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \ | ||
echo 'nobody:x:65534:' > /user/group | ||
|
||
# Create collector log directory | ||
RUN mkdir -p /var/log/collector | ||
|
||
# Set the working directory outside $GOPATH to enable the support for modules. | ||
WORKDIR /src | ||
|
||
# Copy Go Module config | ||
COPY go.mod . | ||
COPY go.sum . | ||
|
||
# Download Go Modules | ||
RUN go mod download | ||
|
||
# Import the code from the context. | ||
COPY . . | ||
|
||
# Build the executable to `/app`. Mark the build as statically linked. | ||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o app . | ||
|
||
# Final stage: the running container. | ||
FROM scratch AS final | ||
|
||
# Import the user and group files from the first stage. | ||
COPY --from=builder /user/group /user/passwd /etc/ | ||
|
||
# Import the Certificate-Authority certificates for enabling HTTPS. | ||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ | ||
|
||
# Import the compiled executable from the first stage. | ||
COPY --from=builder src/app /app | ||
|
||
# Import logging directory from first stage. | ||
COPY --chown=nobody:nobody --from=builder /var/log/collector /var/log/collector | ||
|
||
# Perform any further action as an unprivileged user. | ||
USER nobody:nobody | ||
|
||
# Run the compiled binary. | ||
ENTRYPOINT ["/app"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"github.com/rfizzle/collector-helpers/outputs" | ||
flag "github.com/spf13/pflag" | ||
"github.com/spf13/viper" | ||
"log" | ||
"strings" | ||
) | ||
|
||
func setupCliFlags() error { | ||
viper.SetEnvPrefix("GSC") | ||
viper.AutomaticEnv() | ||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) | ||
|
||
flag.String("state-path", "google.state", "state file path") | ||
flag.Int("schedule", 30, "time in seconds to collect") | ||
flag.String("google-credentials", "", "google service account creds file path") | ||
flag.String("impersonated-user", "", "user to impersonate for API access") | ||
flag.BoolP("verbose", "v", false, "verbose logging") | ||
outputs.InitCLIParams() | ||
flag.Parse() | ||
err := viper.BindPFlags(flag.CommandLine) | ||
|
||
if err != nil { | ||
log.Fatalf("Failed parsing flags: %v", err.Error()) | ||
} | ||
|
||
// Check parameters | ||
if err := checkRequiredParams(); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func checkRequiredParams() error { | ||
if viper.GetString("state-path") == "" { | ||
return errors.New("missing State File Path param (--state-path)") | ||
} | ||
|
||
if viper.GetString("google-credentials") == "" { | ||
return errors.New("missing Google Credentials param (--google-credentials)") | ||
} | ||
|
||
if viper.GetString("impersonated-user") == "" { | ||
return errors.New("missing Impersonate User param (--impersonated-user)") | ||
} | ||
|
||
if err := outputs.ValidateCLIParams(); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package client | ||
|
||
type GoogleServiceAccountCredentials struct { | ||
Type string `json:"type"` | ||
ProjectId string `json:"project_id"` | ||
PrivateKeyId string `json:"private_key_id"` | ||
PrivateKey string `json:"private_key"` | ||
ClientEmail string `json:"client_email"` | ||
ClientId string `json:"client_id"` | ||
AuthUri string `json:"auth_uri"` | ||
TokenUri string `json:"token_uri"` | ||
AuthProviderX509CertUrl string `json:"auth_provider_x509_cert_url"` | ||
ClientX509CertUrl string `json:"client_x509_cert_url"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package client | ||
|
||
import ( | ||
"encoding/json" | ||
adminreports "google.golang.org/api/admin/reports/v1" | ||
) | ||
|
||
func convertActivityTypeToInterface(items []*adminreports.Activity) []string { | ||
var data []string | ||
for _, val := range items { | ||
// Convert item to json byte array | ||
plain, _ := json.Marshal(val) | ||
|
||
// Add string to array | ||
data = append(data, string(plain)) | ||
} | ||
|
||
return data | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"github.com/tidwall/pretty" | ||
"golang.org/x/oauth2/google" | ||
"golang.org/x/oauth2/jwt" | ||
adminreports "google.golang.org/api/admin/reports/v1" | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
) | ||
|
||
func BuildClient(credentialFilePath, impersonationEmail string) (*http.Client, error) { | ||
// Open our jsonFile | ||
credentialJsonFile, err := os.Open(credentialFilePath) | ||
|
||
// if we os.Open returns an error then handle it | ||
if err != nil { | ||
return nil, errors.New("error reading credential file") | ||
} | ||
|
||
// Read credential file | ||
byteValue, err := ioutil.ReadAll(credentialJsonFile) | ||
|
||
// Handle credential file read issues | ||
if err != nil { | ||
return nil, errors.New("error reading json from credential file") | ||
} | ||
|
||
// Define creds | ||
var creds GoogleServiceAccountCredentials | ||
|
||
// unmarshal into object | ||
err = json.Unmarshal(byteValue, &creds) | ||
|
||
// return if error during unmarshal | ||
if err != nil { | ||
return nil, errors.New("error parsing json from credential file to struct") | ||
} | ||
|
||
conf := &jwt.Config{ | ||
Email: creds.ClientEmail, | ||
// The contents of your RSA private key or your PEM file | ||
// that contains a private key. | ||
// If you have a p12 file instead, you | ||
// can use `openssl` to export the private key into a pem file. | ||
// | ||
// $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes | ||
// | ||
// The field only supports PEM containers with no passphrase. | ||
// The openssl command will convert p12 keys to passphrase-less PEM containers. | ||
PrivateKey: []byte(creds.PrivateKey), | ||
Scopes: []string{ | ||
"https://www.googleapis.com/auth/admin.reports.audit.readonly", | ||
"https://www.googleapis.com/auth/admin.reports.usage.readonly", | ||
}, | ||
TokenURL: google.JWTTokenURL, | ||
// If you would like to impersonate a user, you can | ||
// create a transport with a subject. The following GET | ||
// request will be made on the behalf of user@example.com. | ||
// Optional. | ||
Subject: impersonationEmail, | ||
} | ||
// Initiate an http.Client, the following GET request will be | ||
// authorized and authenticated on the behalf of user@example.com. | ||
return conf.Client(context.Background()), nil | ||
} | ||
|
||
func ActivitiesList(service *adminreports.Service, eventType, timestamp string, resultsChannel chan<- string) (int, error) { | ||
count := 0 | ||
response, err := service.Activities.List("all", eventType).StartTime(timestamp).MaxResults(1000).Do() | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
// Return if there are no new results | ||
if len(response.Items) == 0 { | ||
return 0, nil | ||
} else { | ||
// Convert to the activity type | ||
tmpData := convertActivityTypeToInterface(response.Items) | ||
count += len(tmpData) | ||
for _, event := range tmpData { | ||
// Ugly print the json into a single lined string | ||
resultsChannel <- string(pretty.Ugly([]byte(event))) | ||
} | ||
} | ||
|
||
// Handle paged responses | ||
for response.NextPageToken != "" { | ||
response, err := service.Activities.List("all", eventType).StartTime(timestamp).MaxResults(1000).PageToken(response.NextPageToken).Do() | ||
if err != nil { | ||
return 0, err | ||
} | ||
tmpData := convertActivityTypeToInterface(response.Items) | ||
count += len(tmpData) | ||
for _, event := range tmpData { | ||
// Ugly print the json into a single lined string | ||
resultsChannel <- string(pretty.Ugly([]byte(event))) | ||
} | ||
} | ||
|
||
return count, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module github.com/rfizzle/gsuite-collector | ||
|
||
go 1.14 | ||
|
||
require ( | ||
github.com/rfizzle/collector-helpers v0.1.0 | ||
github.com/spf13/pflag v1.0.5 | ||
github.com/spf13/viper v1.7.1 | ||
github.com/tidwall/pretty v1.0.1 | ||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d | ||
google.golang.org/api v0.30.0 | ||
) |
Oops, something went wrong.