Skip to content

Commit

Permalink
⭐️ snowflake provider
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-rock committed Jun 9, 2024
1 parent 465329a commit 3d4c616
Show file tree
Hide file tree
Showing 27 changed files with 6,285 additions and 2 deletions.
19 changes: 17 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ providers/build: \
providers/build/atlassian \
providers/build/cloudformation \
providers/build/shodan \
providers/build/ansible
providers/build/ansible \
providers/build/snowflake

.PHONY: providers/install
# Note we need \ to escape the target line into multiple lines
Expand Down Expand Up @@ -234,7 +235,8 @@ providers/install: \
providers/install/aws \
providers/install/cloudformation \
providers/install/shodan \
providers/install/ansible
providers/install/ansible \
providers/install/snowflake

providers/build/mock: providers/lr
./lr go providers-sdk/v1/testutils/mockprovider/resources/mockprovider.lr
Expand Down Expand Up @@ -362,6 +364,11 @@ providers/build/ansible: providers/lr
providers/install/ansible:
@$(call installProvider, providers/ansible)

providers/build/snowflake: providers/lr
@$(call buildProvider, providers/snowflake)
providers/install/snowflake:
@$(call installProvider, providers/snowflake)

providers/dist:
@$(call buildProviderDist, providers/network)
@$(call buildProviderDist, providers/os)
Expand All @@ -387,6 +394,7 @@ providers/dist:
@$(call buildProviderDist, providers/cloudformation)
@$(call buildProviderDist, providers/shodan)
@$(call buildProviderDist, providers/ansible)
@$(call buildProviderDist, providers/snowflake)

providers/bundle:
@$(call bundleProvider, providers/network)
Expand All @@ -413,6 +421,7 @@ providers/bundle:
@$(call bundleProvider, providers/cloudformation)
@$(call bundleProvider, providers/shodan)
@$(call bundleProvider, providers/ansible)
@$(call bundleProvider, providers/snowflake)

providers/test:
@$(call testProvider, providers/core)
Expand Down Expand Up @@ -440,6 +449,7 @@ providers/test:
@$(call testGoModProvider, providers/cloudformation)
@$(call testGoModProvider, providers/shodan)
@$(call testGoModProvider, providers/ansible)
@$(call testGoModProvider, providers/snowflake)

lr/test:
go test ./resources/lr/...
Expand Down Expand Up @@ -563,6 +573,11 @@ lr/docs/markdown: providers/lr
--description "The Slack resource pack lets you use MQL to query and assess the security of your Slack identities and configuration." \
--docs-file providers/slack/resources/slack.lr.manifest.yaml \
--output ../docs/docs/mql/resources/slack-pack
./lr markdown providers/slack/resources/snowflake.lr \
--pack-name "Snowflake" \
--description "The Snowflake resource pack lets you use MQL to query and assess the security of your Snowflake identities and configuration." \
--docs-file providers/snowflake/resources/snowflake.lr.manifest.yaml \
--output ../docs/docs/mql/resources/snowflake-pack
./lr markdown providers/terraform/resources/terraform.lr \
--pack-name "Terraform IaC" \
--description "The Terraform IaC resource pack lets you use MQL to query and assess the security of your Terraform HCL, plan, and state resources." \
Expand Down
122 changes: 122 additions & 0 deletions providers/snowflake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Snowflake Provider

```shell
cnquery shell snowflake
```

Required arguments:

- `--account` - The Snowflake account name.
- `--region` - The Snowflake region.
- `--user` - The Snowflake username.
- `--role` - The Snowflake role.

> The easiest way to get the account name and region is to look at the URL when you log in to the Snowflake web interface. When clicking on the account icon you can copy the account URL that included the account name and region.
**Password Authentication**

Arguments:

- `--password` - The Snowflake password.
- `--ask-pass` - Prompt for the Snowflake password.

```shell
shell snowflake --account zi12345 --region us-central1.gcp --user CHRIS --role ACCOUNTADMIN --ask-pass

Check failure on line 24 in providers/snowflake/README.md

View workflow job for this annotation

GitHub Actions / Run spell check

`ACCOUNTADMIN` is not a recognized word. (unrecognized-spelling)
```

> To create a username and password, use [Snowsight](https://docs.snowflake.com/en/user-guide/admin-user-management#using-snowsight) or using [SQL](https://docs.snowflake.com/en/user-guide/admin-user-management#using-sql).

Check failure on line 27 in providers/snowflake/README.md

View workflow job for this annotation

GitHub Actions / Run spell check

`Snowsight` is not a recognized word. (unrecognized-spelling)
**Certificate Authentication**

Arguments:

- `--private-key` - The path to the private key file.

```shell
shell snowflake --account zi12345 --region us-central1.gcp --user CHRIS --role ACCOUNTADMIN --private-key ~/.ssh/id_rsa

Check failure on line 36 in providers/snowflake/README.md

View workflow job for this annotation

GitHub Actions / Run spell check

`ACCOUNTADMIN` is not a recognized word. (unrecognized-spelling)
```

> You need to generate a RSA key pair and assign the public key to your user via [Snowsight](https://docs.snowflake.com/en/user-guide/key-pair-auth).

Check failure on line 39 in providers/snowflake/README.md

View workflow job for this annotation

GitHub Actions / Run spell check

`Snowsight` is not a recognized word. (unrecognized-spelling)
## Examples

**Retrieve all users**

```shell
cnquery> snowflake.account.users
snowflake.account.users: [
0: snowflake.user name="CHRIS"
1: snowflake.user name="DATAUSER"

Check failure on line 49 in providers/snowflake/README.md

View workflow job for this annotation

GitHub Actions / Run spell check

`DATAUSER` is not a recognized word. (unrecognized-spelling)
2: snowflake.user name="SNOWFLAKE"
]
```

**Retrieve all users that have no MFA**

```shell
cnquery> snowflake.account.users.where(extAuthnDuo == false)
snowflake.account.users.where: [
0: snowflake.user name="CHRIS"
1: snowflake.user name="DATAUSER"

Check failure on line 60 in providers/snowflake/README.md

View workflow job for this annotation

GitHub Actions / Run spell check

`DATAUSER` is not a recognized word. (unrecognized-spelling)
2: snowflake.user name="SNOWFLAKE"
]
```

**Retrieve all users that have password authentication**

```shell
cnquery> snowflake.account.users.where(hasPassword)
snowflake.account.users.where: [
0: snowflake.user name="CHRIS"
1: snowflake.user name="DATAUSER"

Check failure on line 71 in providers/snowflake/README.md

View workflow job for this annotation

GitHub Actions / Run spell check

`DATAUSER` is not a recognized word. (unrecognized-spelling)
2: snowflake.user name="SNOWFLAKE"
]

```

**Retrieve all users that have certificate authentication**

```shell
cnquery> snowflake.account.users.where(hasRsaPublicKey)
snowflake.account.users.where: [
0: snowflake.user name="CHRIS"
]
```

**Retrieve users that have not logged in for 30 days**

```shell
cnquery> snowflake.account.users.where(time.now - lastSuccessLogin > time.day * 30) { lastSuccessLogin }
snowflake.account.users.where: [
0: {
lastSuccessLogin: 366 days
}
]
```
**Check that SCIM is enabled**
```shell
cnquery> snowflake.account.securityIntegrations.where(type == /SCIM/).any(enabled == true)
[failed] [].any()
actual: []
```
**Check the retention time is greater 90 days**
```shell
cnquery> snowflake.account.parameters.one(key == "DATA_RETENTION_TIME_IN_DAYS" && value >= 90)
```
**Retrieve all databases**
```shell
cnquery> snowflake.account.databases
snowflake.account.databases: [
0: snowflake.database name="CNQUERY"
1: snowflake.database name="SNOWFLAKE"
2: snowflake.database name="SNOWFLAKE_SAMPLE_DATA"
]
```
73 changes: 73 additions & 0 deletions providers/snowflake/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package config

import (
"go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v11/providers/snowflake/provider"
)

var Config = plugin.Provider{
Name: "snowflake",
ID: "go.mondoo.com/cnquery/v11/providers/snowflake",
Version: "11.0.0",
ConnectionTypes: []string{provider.DefaultConnectionType},
Connectors: []plugin.Connector{
{
Name: "snowflake",
Use: "snowflake",
Short: "a Snowflake account",
Discovery: []string{},
Flags: []plugin.Flag{
{
Long: "user",
Type: plugin.FlagType_String,
Default: "",
Desc: "Snowflake user name",
},
{
Long: "ask-pass",
Type: plugin.FlagType_Bool,
Default: "false",
Desc: "Prompt for connection password",
ConfigEntry: "-",
},
{
Long: "password",
Short: "p",
Type: plugin.FlagType_String,
Default: "",
Desc: "Set the connection password",
Option: plugin.FlagOption_Password,
ConfigEntry: "-",
},
{
Long: "identity-file",
Short: "i",
Type: plugin.FlagType_String,
Default: "",
Desc: "Select a file from which to read the identity (private key) for public key authentication",
},
{
Long: "account",
Type: plugin.FlagType_String,
Default: "",
Desc: "Snowflake account",
},
{
Long: "region",
Type: plugin.FlagType_String,
Default: "",
Desc: "Snowflake region",
},
{
Long: "role",
Type: plugin.FlagType_String,
Default: "",
Desc: "Snowflake role",
},
},
},
},
}
121 changes: 121 additions & 0 deletions providers/snowflake/connection/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package connection

import (
"crypto/rsa"
"encoding/pem"
"errors"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/snowflakedb/gosnowflake"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/vault"
"go.mondoo.com/ranger-rpc/codes"
"go.mondoo.com/ranger-rpc/status"
"golang.org/x/crypto/ssh"
)

type SnowflakeConnection struct {
plugin.Connection
Conf *inventory.Config
asset *inventory.Asset
// Custom connection fields
client *sdk.Client
}

func NewSnowflakeConnection(id uint32, asset *inventory.Asset, conf *inventory.Config) (*SnowflakeConnection, error) {
conn := &SnowflakeConnection{
Connection: plugin.NewConnection(id, asset),
Conf: conf,
asset: asset,
}

// initialize your connection here

if len(conf.Credentials) == 0 {
return nil, status.Error(codes.InvalidArgument, "missing credentials for snowflake connection")
}

if conf.Options == nil {
conf.Options = make(map[string]string)
}

cfg := &gosnowflake.Config{
Account: conf.Options["account"],
Region: conf.Options["region"],
Role: conf.Options["role"],
}

for i := range conf.Credentials {
cred := conf.Credentials[i]
switch cred.Type {
case vault.CredentialType_password:
cfg.User = cred.User
cfg.Password = string(cred.Secret)
cfg.Authenticator = gosnowflake.AuthTypeSnowflake
case vault.CredentialType_private_key:
cfg.User = cred.User

// snowflake requires a RSA private key in PEM format
key, err := parsePrivateKey(cred.Secret, []byte(cred.Password))
if err != nil {
return nil, err
}
cfg.PrivateKey = key
cfg.Authenticator = gosnowflake.AuthTypeJwt
}
}

client, err := sdk.NewClient(cfg)
if err != nil {
return nil, err
}
conn.client = client

return conn, nil
}

func parsePrivateKey(privateKeyBytes []byte, passhrase []byte) (*rsa.PrivateKey, error) {
privateKeyBlock, _ := pem.Decode(privateKeyBytes)
if privateKeyBlock == nil {
return nil, errors.New("could not decode private key")
}

var privateKey interface{}
var err error
if privateKeyBlock.Type == "ENCRYPTED PRIVATE KEY" {
if len(passhrase) == 0 {
return nil, errors.New("private key is encrypted, but no passphrase provided")
}

privateKey, err = ssh.ParseRawPrivateKeyWithPassphrase(privateKeyBlock.Bytes, passhrase)
if err != nil {
return nil, errors.New("could not parse encrypted private key " + err.Error())
}
} else {
privateKey, err = ssh.ParseRawPrivateKey(privateKeyBytes)
if err != nil {
return nil, errors.New("could not parse private key err " + err.Error())
}
}

rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("snowflake requires a RSA private key in PEM format")
}
return rsaPrivateKey, nil
}

func (c *SnowflakeConnection) Name() string {
return "snowflake"
}

func (c *SnowflakeConnection) Asset() *inventory.Asset {
return c.asset
}

func (c *SnowflakeConnection) Client() *sdk.Client {
return c.client
}
Loading

0 comments on commit 3d4c616

Please sign in to comment.