Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync access packages #147

Merged
merged 6 commits into from
Jun 5, 2018
Merged
Show file tree
Hide file tree
Changes from 5 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
9 changes: 7 additions & 2 deletions deps/github.com/arangodb/arangosync/client/client_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ func (cc *ClientCache) createClient(source Endpoint, auth Authentication, insecu
return nil, maskAny(err)
}
ac := AuthenticationConfig{}
if auth.JWTSecret != "" {
if auth.Username != "" {
ac.UserName = auth.Username
ac.Password = auth.Password
} else if auth.JWTSecret != "" {
ac.JWTSecret = auth.JWTSecret
} else if auth.ClientToken != "" {
ac.BearerToken = auth.ClientToken
Expand Down Expand Up @@ -113,11 +116,13 @@ func NewAuthentication(tlsAuth TLSAuthentication, jwtSecret string) Authenticati
type Authentication struct {
TLSAuthentication
JWTSecret string
Username string
Password string
}

// String returns a string used to unique identify the authentication settings.
func (a Authentication) String() string {
return a.TLSAuthentication.String() + ":" + a.JWTSecret
return a.TLSAuthentication.String() + ":" + a.JWTSecret + ":" + a.Username + ":" + a.Password
Copy link

@matthewvon matthewvon Jun 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not know where String() is used. But I wonder if making the password available via this function is required, and if not required, is it a bad thing. Could we accidentally expose a real user's password to a log file or such.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used to generate a key that matches the exact settings. So with exactly the same key you are allowed to share the same client object.

}

// AuthProxy is a helper that implements github.com/arangodb-helper/go-certificates#TLSAuthentication.
Expand Down
155 changes: 126 additions & 29 deletions docs/Manual/Deployment/Kubernetes/DeploymentReplicationResource.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The ArangoDB Replication Operator creates and maintains ArangoDB
This replication specification is a `CustomResource` following
a `CustomResourceDefinition` created by the operator.

Example minimal replication definition:
Example minimal replication definition for 2 ArangoDB cluster with sync in the same Kubernetes cluster:

```yaml
apiVersion: "replication.database.arangodb.com/v1alpha"
Expand All @@ -15,17 +15,46 @@ metadata:
spec:
source:
deploymentName: cluster-a
auth:
keyfileSecretName: cluster-a-sync-auth
destination:
deploymentName: cluster-b
auth:
clientAuthSecretName: client-auth-cert
```

This definition results in:

- the arangosync `SyncMaster` in deployment `cluster-b` is called to configure a synchronization
from `cluster-a` to `cluster-b`, using the client authentication certificate stored in
`Secret` `client-auth-cert`.
from the syncmasters in `cluster-a` to the syncmasters in `cluster-b`,
using the client authentication certificate stored in `Secret` `cluster-a-sync-auth`.
To access `cluster-a`, the JWT secret found in the deployment of `cluster-a` is used.
To access `cluster-b`, the JWT secret found in the deployment of `cluster-b` is used.

Example replication definition for replicating from a source that is outside the current Kubernetes cluster
to a destination that is in the same Kubernetes cluster:

```yaml
apiVersion: "replication.database.arangodb.com/v1alpha"
kind: "ArangoDeploymentReplication"
metadata:
name: "replication-from-a-to-b"
spec:
source:
endpoint: ["https://163.172.149.229:31888", "https://51.15.225.110:31888", "https://51.15.229.133:31888"]
auth:
keyfileSecretName: cluster-a-sync-auth
tls:
caSecretName: cluster-a-sync-ca
destination:
deploymentName: cluster-b
```

This definition results in:

- the arangosync `SyncMaster` in deployment `cluster-b` is called to configure a synchronization
from the syncmasters located at the given list of endpoint URL's to the syncmasters `cluster-b`,
using the client authentication certificate stored in `Secret` `cluster-a-sync-auth`.
To access `cluster-a`, the keyfile (containing a client authentication certificate) is used.
To access `cluster-b`, the JWT secret found in the deployment of `cluster-b` is used.

## Specification reference

Expand All @@ -38,13 +67,7 @@ with sync enabled.

This cluster configured as the replication source.

### `spec.source.deploymentNamespace: string`

This setting specifies the Kubernetes namespace of an `ArangoDeployment` resource specified in `spec.source.deploymentName`.

If this setting is empty, the namespace of the `ArangoDeploymentReplication` is used.

### `spec.source.masterEndpoints: []string`
### `spec.source.endpoint: []string`

This setting specifies zero or more master endpoint URL's of the source cluster.

Expand All @@ -53,12 +76,23 @@ that is reachable from the Kubernetes cluster the `ArangoDeploymentReplication`

Specifying this setting and `spec.source.deploymentName` at the same time is not allowed.

### `spec.source.auth.jwtSecretName: string`
### `spec.source.auth.keyfileSecretName: string`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the user facing an upgrade issue if they previously used .jwtSecretName? Same question for .deploymentNamespace versus .endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was never part of a released version and was only just introduced in master, so I doubt it will cause problems.


This setting specifies the name of a `Secret` containing a JWT `token` used to authenticate
This setting specifies the name of a `Secret` containing a client authentication certificate called `tls.keyfile` used to authenticate
with the SyncMaster at the specified source.

This setting is required, unless `spec.source.deploymentName` has been set.
If `spec.source.auth.userSecretName` has not been set,
the client authentication certificate found in the secret with this name is also used to configure
the synchronization and fetch the synchronization status.

This setting is required.

### `spec.source.auth.userSecretName: string`

This setting specifies the name of a `Secret` containing a `username` & `password` used to authenticate
with the SyncMaster at the specified source in order to configure synchronization and fetch synchronization status.

The user identified by the username must have write access in the `_system` database of the source ArangoDB cluster.

### `spec.source.tls.caSecretName: string`

Expand All @@ -74,13 +108,7 @@ with sync enabled.

This cluster configured as the replication destination.

### `spec.destination.deploymentNamespace: string`

This setting specifies the Kubernetes namespace of an `ArangoDeployment` resource specified in `spec.destination.deploymentName`.

If this setting is empty, the namespace of the `ArangoDeploymentReplication` is used.

### `spec.destination.masterEndpoints: []string`
### `spec.destination.endpoint: []string`

This setting specifies zero or more master endpoint URL's of the destination cluster.

Expand All @@ -89,12 +117,27 @@ that is reachable from the Kubernetes cluster the `ArangoDeploymentReplication`

Specifying this setting and `spec.destination.deploymentName` at the same time is not allowed.

### `spec.destination.auth.jwtSecretName: string`
### `spec.destination.auth.keyfileSecretName: string`

This setting specifies the name of a `Secret` containing a JWT `token` used to authenticate
This setting specifies the name of a `Secret` containing a client authentication certificate called `tls.keyfile` used to authenticate
with the SyncMaster at the specified destination.

This setting is required, unless `spec.destination.deploymentName` has been set.
If `spec.destination.auth.userSecretName` has not been set,
the client authentication certificate found in the secret with this name is also used to configure
the synchronization and fetch the synchronization status.

This setting is required, unless `spec.destination.deploymentName` or `spec.destination.auth.userSecretName` has been set.

Specifying this setting and `spec.destination.userSecretName` at the same time is not allowed.

### `spec.destination.auth.userSecretName: string`

This setting specifies the name of a `Secret` containing a `username` & `password` used to authenticate
with the SyncMaster at the specified destination in order to configure synchronization and fetch synchronization status.

The user identified by the username must have write access in the `_system` database of the destination ArangoDB cluster.

Specifying this setting and `spec.destination.keyfileSecretName` at the same time is not allowed.

### `spec.destination.tls.caSecretName: string`

Expand All @@ -103,8 +146,62 @@ the TLS connection created by the SyncMaster at the specified destination.

This setting is required, unless `spec.destination.deploymentName` has been set.

### `spec.auth.clientAuthSecretName: string`
## Authentication details

The authentication settings in a `ArangoDeploymentReplication` resource are used for two distinct purposes.

The first use is the authentication of the syncmasters at the destination with the syncmasters at the source.
This is always done using a client authentication certificate which is found in a `tls.keyfile` field
in a secret identified by `spec.source.auth.keyfileSecretName`.

The second use is the authentication of the ArangoDB Replication operator with the syncmasters at the source
or destination. These connections are made to configure synchronization, stop configuration and fetch the status
of the configuration.
The method used for this authentication is derived as follows (where `X` is either `source` or `destination`):

- If `spec.X.userSecretName` is set, the username + password found in the `Secret` identified by this name is used.
- If `spec.X.keyfileSecretName` is set, the client authentication certificate (keyfile) found in the `Secret` identifier by this name is used.
- If `spec.X.deploymentName` is set, the JWT secret found in the deployment is used.

## Creating client authentication certificate keyfiles

The client authentication certificates needed for the `Secrets` identified by `spec.source.auth.keyfileSecretName` & `spec.destination.auth.keyfileSecretName`
are normal ArangoDB keyfiles that can be created by the `arangosync create client-auth keyfile` command.
In order to do so, you must have access to the client authentication CA of the source/destination.

If the client authentication CA at the source/destination also contains a private key (`ca.key`), the ArangoDeployment operator
can be used to create such a keyfile for you, without the need to have `arangosync` installed locally.
Read the following paragraphs for instructions on how to do that.

## Creating and using access packages

An access package is a YAML file that contains:

- A client authentication certificate, wrapped in a `Secret` in a `tls.keyfile` data field.
- A TLS certificate authority public key, wrapped in a `Secret` in a `ca.crt` data field.

The format of the access package is such that it can be inserted into a Kubernetes cluster using the standard `kubectl` tool.

To create an access package that can be used to authenticate with the ArangoDB SyncMasters of an `ArangoDeployment`,
add a name of a non-existing `Secret` to the `spec.sync.externalAccess.accessPackageSecretNames` field of the `ArangoDeployment`.
In response, a `Secret` is created in that Kubernetes cluster, with the given name, that contains a `accessPackage.yaml` data field
that contains a Kubernetes resource specification that can be inserted into the other Kubernetes cluster.

The process for creating and using an access package to authentication at the source cluster is as follows:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"package to authentication" -> "package for authentication"


- Edit the `ArangoDeployment` resource of the source cluster, set `spec.sync.externalAccess.accessPackageSecretNames` to `["my-access-package"]`
- Wait for the `ArangoDeployment` operator to create a `Secret` named `my-access-package`.
- Extract the access package from the Kubernetes source cluster using:

```bash
kubectl get secret my-access-package --template='{{index .data "accessPackage.yaml"}}' | base64 -D > accessPackage.yaml
```

- Insert the secrets found in the access package in the Kubernetes destination cluster using:

```bash
kubectl apply -f accessPackage.yaml
```

This setting specifies the name of a `Secret` containing a client authentication certificate,
used to authenticate the SyncMaster in the destination cluster with the SyncMaster in the
source cluster.
As a result, the destination Kubernetes cluster will have 2 additional `Secrets`. One containing a client authentication certificate
formatted ad a keyfile. Another containing the public key of the TLS CA certificate of the source cluster.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling or grammar or typo: "One containing a client authentication certificate formatted ad a keyfile." ad -> and ? If so, better wording would be "One contains a ...". Second sentence would then be "Another contains ..."

13 changes: 13 additions & 0 deletions docs/Manual/Deployment/Kubernetes/DeploymentResource.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,19 @@ If not set, this setting defaults to:
- If `spec.sync.externalAccess.loadBalancerIP` is set, it defaults to `https://<load-balancer-ip>:<8629>`.
- Otherwise it defaults to `https://<sync-service-dns-name>:<8629>`.

### `spec.sync.externalAccess.accessPackageSecretNames: []string`

This setting specifies the names of zero of more `Secrets` that will be created by the deployment
operator containing "access packages". An access package contains those `Secrets` that are needed
to access the SyncMasters of this `ArangoDeployment`.

By removing a name from this setting, the corresponding `Secret` is also deleted.
Note that to remove all access packages, leave an empty array in place (`[]`).
Completely removing the setting results in not modifying the list.

See [the `ArangoDeploymentReplication` specification](./DeploymentReplicationResource.md) for more information
on access packages.

### `spec.sync.auth.jwtSecretName: string`

This setting specifies the name of a kubernetes `Secret` that contains
Expand Down
18 changes: 17 additions & 1 deletion pkg/apis/deployment/v1alpha/sync_external_access_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,27 @@ import (
"net"
"net/url"
"strconv"

"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
)

// SyncExternalAccessSpec holds configuration for the external access provided for the sync deployment.
type SyncExternalAccessSpec struct {
ExternalAccessSpec
MasterEndpoint []string `json:"masterEndpoint,omitempty"`
MasterEndpoint []string `json:"masterEndpoint,omitempty"`
AccessPackageSecretNames []string `json:accessPackageSecretNames,omitempty"`
}

// GetMasterEndpoint returns the value of masterEndpoint.
func (s SyncExternalAccessSpec) GetMasterEndpoint() []string {
return s.MasterEndpoint
}

// GetAccessPackageSecretNames returns the value of accessPackageSecretNames.
func (s SyncExternalAccessSpec) GetAccessPackageSecretNames() []string {
return s.AccessPackageSecretNames
}

// ResolveMasterEndpoint returns the value of `--master.endpoint` option passed to arangosync.
func (s SyncExternalAccessSpec) ResolveMasterEndpoint(syncServiceHostName string, syncServicePort int) []string {
if len(s.MasterEndpoint) > 0 {
Expand All @@ -61,6 +69,11 @@ func (s SyncExternalAccessSpec) Validate() error {
return maskAny(fmt.Errorf("Failed to parse master endpoint '%s': %s", ep, err))
}
}
for _, name := range s.AccessPackageSecretNames {
if err := k8sutil.ValidateResourceName(name); err != nil {
return maskAny(fmt.Errorf("Invalid name '%s' in accessPackageSecretNames: %s", name, err))
}
}
return nil
}

Expand All @@ -75,6 +88,9 @@ func (s *SyncExternalAccessSpec) SetDefaultsFrom(source SyncExternalAccessSpec)
if s.MasterEndpoint == nil && source.MasterEndpoint != nil {
s.MasterEndpoint = append([]string{}, source.MasterEndpoint...)
}
if s.AccessPackageSecretNames == nil && source.AccessPackageSecretNames != nil {
s.AccessPackageSecretNames = append([]string{}, source.AccessPackageSecretNames...)
}
}

// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec.
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,11 @@ func (in *SyncExternalAccessSpec) DeepCopyInto(out *SyncExternalAccessSpec) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.AccessPackageSecretNames != nil {
in, out := &in.AccessPackageSecretNames, &out.AccessPackageSecretNames
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}

Expand Down
68 changes: 0 additions & 68 deletions pkg/apis/replication/v1alpha/authentication_spec.go

This file was deleted.

Loading