Skip to content

Commit

Permalink
Add password authentication (#32)
Browse files Browse the repository at this point in the history
* add options to command line

* minor fix

* add authentication option to cassandra

* remove cqlshrc file and use default credentials instead

* add readme and change naming

* aff indent

* remove mod files

---------

Co-authored-by: victorr <[email protected]>
Co-authored-by: aharonh <[email protected]>
  • Loading branch information
3 people authored Apr 22, 2024
1 parent bdb22f5 commit be86452
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 107 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@
# dep/glide
vendor/
bin/
rebuild.sh
go.mod
go.sum
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ TRAVIS := $(shell printenv TRAVIS)
all: bootstrap build docker push

fmt:
go fmt ./pkg/... ./cmd/...
go fmt -mod=mod ./pkg/... ./cmd/...

vet:
go vet ./pkg/... ./cmd/...
go vet -mod=mod ./pkg/... ./cmd/...

# Build cain binary
build: fmt vet
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(LDFLAGS) -o bin/cain cmd/cain.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod=mod -ldflags $(LDFLAGS) -o bin/cain cmd/cain.go

# Build cain docker image
docker: fmt vet
Expand Down
72 changes: 53 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,18 @@ Usage:
cain backup [flags]
Flags:
-b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75)
--cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data")
-c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra")
--dst string destination to backup to. Example: s3://bucket/cassandra. Overrides $CAIN_DST
-k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE
-n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default")
-p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1)
-l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra")
-a, --authentication use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION
-b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75)
--cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data")
-u, --cassandra-username string cassandra username. Overrides $CAIN_CASSANDRA_USERNAME (default "cain")
-c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra")
--dst string destination to backup to. Example: s3://bucket/cassandra. Overrides $CAIN_DST
-h, --help help for backup
-k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE
-n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default")
--nodetool-credentials-file string path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE (default "/home/cassandra/.nodetool/credentials")
-p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1)
-l, --selector string
```

#### Examples
Expand All @@ -77,6 +81,19 @@ cain backup \
--dst s3://db-backup/cassandra
```

Backup to AWS S3 with Cassandra authentication enabled

```
cain backup \
-n default \
-l release=cassandra \
-k keyspace \
--dst s3://db-backup/cassandra
-a
-u cain
--nodetool-credentials-file /home/cassandra/.nodetool/credentials
```

Backup to Azure Blob Storage

```
Expand Down Expand Up @@ -105,17 +122,21 @@ Usage:
cain restore [flags]
Flags:
-b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75)
--cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data")
-c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra")
-k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE
-n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default")
-p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1)
-s, --schema string schema version to restore (optional). Overrides $CAIN_SCHEMA
-l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra")
--src string source to restore from. Example: s3://bucket/cassandra/namespace/cluster-name. Overrides $CAIN_SRC
-t, --tag string tag to restore. Overrides $CAIN_TAG
--user-group string user and group who should own restored files. Overrides $CAIN_USER_GROUP (default "cassandra:cassandra")
-a, --authentication use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION
-b, --buffer-size float in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE (default 6.75)
--cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data")
-u, --cassandra-username string cassandra username. Overrides $CAIN_CASSANDRA_USERNAME (default "cain")
-c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra")
-h, --help help for restore
-k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE
-n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default")
-f, --nodetool-credentials-file string path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE (default "/home/cassandra/.nodetool/credentials")
-p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1)
-s, --schema string schema version to restore (optional). Overrides $CAIN_SCHEMA
-l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra")
--src string source to restore from. Example: s3://bucket/cassandra/namespace/cluster-name. Overrides $CAIN_SRC
-t, --tag string tag to restore. Overrides $CAIN_TAG
--user-group string user and group who should own restored files. Overrides $CAIN_USER_GROUP (default "cassandra:cassandra")
```

#### Examples
Expand Down Expand Up @@ -211,6 +232,7 @@ Since Cain uses [Skbn](https://github.com/nuvo/skbn), adding support for additio

| Cain version | Skbn version |
|--------------|--------------|
| 0.5.2 | 0.4.2 |
| 0.5.1 | 0.4.2 |
| 0.5.0 | 0.4.1 |
| 0.4.2 | 0.4.1 |
Expand Down Expand Up @@ -239,6 +261,18 @@ Skbn uses the default AWS [credentials chain](https://docs.aws.amazon.com/sdk-fo

Skbn uses `AZURE_STORAGE_ACCOUNT` and `AZURE_STORAGE_ACCESS_KEY` environment variables for authentication.

### Cassandra Credentials
When Authentication is enabled Cain will look for default credentials
for `cqlsh` in `/home/cassandra/.cassandra/credentials`
if you use authentication please make sure the cassandra
container has this file and the username and password are correct.

For `nodetool` authentications default credentials are in:
`/home/cassandra/.nodetool/credentials` can be overridden by
setting the `--nodetool-credentials-file` flag.
When this flag is used, the username for the nodetool
authentication must be provided as well .

## Examples

1. [Helm example](/examples/helm)
Expand Down
96 changes: 56 additions & 40 deletions cmd/cain.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,17 @@ func NewRootCmd(args []string) *cobra.Command {
}

type backupCmd struct {
namespace string
selector string
container string
keyspace string
dst string
parallel int
bufferSize float64
cassandraDataDir string
namespace string
selector string
container string
keyspace string
dst string
parallel int
bufferSize float64
cassandraDataDir string
authentication bool
cassandraUsername string
nodetoolCredentialsFile string

out io.Writer
}
Expand All @@ -73,14 +76,17 @@ func NewBackupCmd(out io.Writer) *cobra.Command {
},
Run: func(cmd *cobra.Command, args []string) {
options := cain.BackupOptions{
Namespace: b.namespace,
Selector: b.selector,
Container: b.container,
Keyspace: b.keyspace,
Dst: b.dst,
Parallel: b.parallel,
BufferSize: b.bufferSize,
CassandraDataDir: b.cassandraDataDir,
Namespace: b.namespace,
Selector: b.selector,
Container: b.container,
Keyspace: b.keyspace,
Dst: b.dst,
Parallel: b.parallel,
BufferSize: b.bufferSize,
CassandraDataDir: b.cassandraDataDir,
Authentication: b.authentication,
CassandraUsername: b.cassandraUsername,
NodetoolCredentialsFile: b.nodetoolCredentialsFile,
}
if _, err := cain.Backup(options); err != nil {
log.Fatal(err)
Expand All @@ -97,22 +103,27 @@ func NewBackupCmd(out io.Writer) *cobra.Command {
f.IntVarP(&b.parallel, "parallel", "p", utils.GetIntEnvVar("CAIN_PARALLEL", 1), "number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL")
f.Float64VarP(&b.bufferSize, "buffer-size", "b", utils.GetFloat64EnvVar("CAIN_BUFFER_SIZE", 6.75), "in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE")
f.StringVar(&b.cassandraDataDir, "cassandra-data-dir", utils.GetStringEnvVar("CAIN_CASSANDRA_DATA_DIR", "/var/lib/cassandra/data"), "cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR")

f.BoolVarP(&b.authentication, "authentication", "a", utils.GetBoolEnvVar("CAIN_AUTHENTICATION", false), "use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION")
f.StringVarP(&b.cassandraUsername, "cassandra-username", "u", utils.GetStringEnvVar("CAIN_CASSANDRA_USERNAME", "cain"), "cassandra username. Overrides $CAIN_CASSANDRA_USERNAME")
f.StringVar(&b.nodetoolCredentialsFile, "nodetool-credentials-file", utils.GetStringEnvVar("CAIN_NODETOOL_CREDENTIALS_FILE", "/home/cassandra/.nodetool/credentials"), "path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE")
return cmd
}

type restoreCmd struct {
src string
keyspace string
tag string
schema string
namespace string
selector string
container string
parallel int
bufferSize float64
userGroup string
cassandraDataDir string
src string
keyspace string
tag string
schema string
namespace string
selector string
container string
parallel int
bufferSize float64
userGroup string
cassandraDataDir string
authentication bool
cassandraUsername string
nodetoolCredentialsFile string

out io.Writer
}
Expand Down Expand Up @@ -142,17 +153,20 @@ func NewRestoreCmd(out io.Writer) *cobra.Command {
},
Run: func(cmd *cobra.Command, args []string) {
options := cain.RestoreOptions{
Src: r.src,
Keyspace: r.keyspace,
Tag: r.tag,
Schema: r.schema,
Namespace: r.namespace,
Selector: r.selector,
Container: r.container,
Parallel: r.parallel,
BufferSize: r.bufferSize,
UserGroup: r.userGroup,
CassandraDataDir: r.cassandraDataDir,
Src: r.src,
Keyspace: r.keyspace,
Tag: r.tag,
Schema: r.schema,
Namespace: r.namespace,
Selector: r.selector,
Container: r.container,
Parallel: r.parallel,
BufferSize: r.bufferSize,
UserGroup: r.userGroup,
CassandraDataDir: r.cassandraDataDir,
Authentication: r.authentication,
CassandraUsername: r.cassandraUsername,
NodetoolCredentialsFile: r.nodetoolCredentialsFile,
}
if err := cain.Restore(options); err != nil {
log.Fatal(err)
Expand All @@ -172,7 +186,9 @@ func NewRestoreCmd(out io.Writer) *cobra.Command {
f.Float64VarP(&r.bufferSize, "buffer-size", "b", utils.GetFloat64EnvVar("CAIN_BUFFER_SIZE", 6.75), "in memory buffer size (MB) to use for files copy (buffer per file). Overrides $CAIN_BUFFER_SIZE")
f.StringVar(&r.userGroup, "user-group", utils.GetStringEnvVar("CAIN_USER_GROUP", "cassandra:cassandra"), "user and group who should own restored files. Overrides $CAIN_USER_GROUP")
f.StringVar(&r.cassandraDataDir, "cassandra-data-dir", utils.GetStringEnvVar("CAIN_CASSANDRA_DATA_DIR", "/var/lib/cassandra/data"), "cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR")

f.BoolVarP(&r.authentication, "authentication", "a", utils.GetBoolEnvVar("CAIN_AUTHENTICATION", false), "use authentication for nodetool and clqsh. Overrides $CAIN_AUTHENTICATION")
f.StringVarP(&r.cassandraUsername, "cassandra-username", "u", utils.GetStringEnvVar("CAIN_CASSANDRA_USERNAME", "cain"), "cassandra username. Overrides $CAIN_CASSANDRA_USERNAME")
f.StringVarP(&r.nodetoolCredentialsFile, "nodetool-credentials-file", "f", utils.GetStringEnvVar("CAIN_NODETOOL_CREDENTIALS_FILE", "/home/cassandra/.nodetool/credentials"), "path to nodetool credentials file. Overrides $CAIN_NODETOOL_CREDENTIALS_FILE")
return cmd
}

Expand Down
82 changes: 58 additions & 24 deletions pkg/cain/cain.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@ import (
"github.com/nuvo/skbn/pkg/skbn"
)

// Authentication options if cassandra cluster uses authentication
type Credentials struct {
enabled bool
username string
nodetoolCredentialsFile string
}

// BackupOptions are the options to pass to Backup
type BackupOptions struct {
Namespace string
Selector string
Container string
Keyspace string
Dst string
Parallel int
BufferSize float64
CassandraDataDir string
Namespace string
Selector string
Container string
Keyspace string
Dst string
Parallel int
BufferSize float64
CassandraDataDir string
Authentication bool
CassandraUsername string
NodetoolCredentialsFile string
}

// Backup performs backup
Expand Down Expand Up @@ -46,15 +56,26 @@ func Backup(o BackupOptions) (string, error) {
if err := utils.TestK8sDirectory(k8sClient, pods, o.Namespace, o.Container, o.CassandraDataDir); err != nil {
return "", err
}
creds := Credentials{
enabled: o.Authentication,
username: o.CassandraUsername,
nodetoolCredentialsFile: o.NodetoolCredentialsFile,
}
if o.Authentication {
log.Println("Testing existence of nodetool credentials file")
if err := utils.TestK8sDirectory(k8sClient, pods, o.Namespace, o.Container, o.NodetoolCredentialsFile); err != nil {
return "", err
}
}

log.Println("Backing up schema")
dstBasePath, err := BackupKeyspaceSchema(k8sClient, dstClient, o.Namespace, pods[0], o.Container, o.Keyspace, dstPrefix, dstPath)
dstBasePath, err := BackupKeyspaceSchema(k8sClient, dstClient, o.Namespace, pods[0], o.Container, o.Keyspace, dstPrefix, dstPath, creds)
if err != nil {
return "", err
}

log.Println("Taking snapshots")
tag := TakeSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace)
tag := TakeSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, creds)

log.Println("Calculating paths. This may take a while...")
fromToPathsAllPods, err := utils.GetFromAndToPathsFromK8s(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, tag, dstBasePath, o.CassandraDataDir)
Expand All @@ -68,25 +89,28 @@ func Backup(o BackupOptions) (string, error) {
}

log.Println("Clearing snapshots")
ClearSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, tag)
ClearSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, tag, creds)

log.Println("All done!")
return tag, nil
}

// RestoreOptions are the options to pass to Restore
type RestoreOptions struct {
Src string
Keyspace string
Tag string
Schema string
Namespace string
Selector string
Container string
Parallel int
BufferSize float64
UserGroup string
CassandraDataDir string
Src string
Keyspace string
Tag string
Schema string
Namespace string
Selector string
Container string
Parallel int
BufferSize float64
UserGroup string
CassandraDataDir string
Authentication bool
CassandraUsername string
NodetoolCredentialsFile string
}

// Restore performs restore
Expand All @@ -110,7 +134,17 @@ func Restore(o RestoreOptions) error {
if err := utils.TestK8sDirectory(k8sClient, existingPods, o.Namespace, o.Container, o.CassandraDataDir); err != nil {
return err
}

creds := Credentials{
enabled: o.Authentication,
username: o.CassandraUsername,
nodetoolCredentialsFile: o.NodetoolCredentialsFile,
}
if o.Authentication {
log.Println("Testing existence of nodetool credentials file")
if err := utils.TestK8sDirectory(k8sClient, existingPods, o.Namespace, o.Container, o.NodetoolCredentialsFile); err != nil {
return err
}
}
log.Println("Getting current schema")
_, sum, err := DescribeKeyspaceSchema(k8sClient, o.Namespace, existingPods[0], o.Container, o.Keyspace)
if err != nil {
Expand Down Expand Up @@ -163,7 +197,7 @@ func Restore(o RestoreOptions) error {
}

log.Println("Refreshing tables")
RefreshTables(k8sClient, o.Namespace, o.Container, o.Keyspace, podsToBeRestored, tablesToRefresh)
RefreshTables(k8sClient, o.Namespace, o.Container, o.Keyspace, podsToBeRestored, tablesToRefresh, creds)

log.Println("All done!")
return nil
Expand Down
Loading

0 comments on commit be86452

Please sign in to comment.