Merge pull request #6857 from hashicorp/add-elasticsearch-auth

Add ElasticSearch database secrets engine
This commit is contained in:
Becca Petrin 2019-06-17 11:19:01 -07:00 committed by GitHub
commit 117d28d966
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1635 additions and 8 deletions

1
go.mod
View File

@ -74,6 +74,7 @@ require (
github.com/hashicorp/vault-plugin-auth-gcp v0.5.1
github.com/hashicorp/vault-plugin-auth-jwt v0.5.1
github.com/hashicorp/vault-plugin-auth-kubernetes v0.5.1
github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190508211750-4152192cdc0f
github.com/hashicorp/vault-plugin-secrets-ad v0.5.1
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.1
github.com/hashicorp/vault-plugin-secrets-azure v0.5.1

4
go.sum
View File

@ -275,6 +275,8 @@ github.com/hashicorp/vault-plugin-auth-jwt v0.5.1 h1:d9WLI7oF6VMtwBZwS5bbChc4kW+
github.com/hashicorp/vault-plugin-auth-jwt v0.5.1/go.mod h1:5VU7gc6/BEEFQW/viqMs3LBxI1D1cxJmKqKQEP3JUP4=
github.com/hashicorp/vault-plugin-auth-kubernetes v0.5.1 h1:q6DGb12Vw/CpZ9xDWAmpzxVRKeClFqRFgbIZ3fZcvuY=
github.com/hashicorp/vault-plugin-auth-kubernetes v0.5.1/go.mod h1:qCDsm0njdfUrnN5sFKMLjxGjZKjQf2qB6dReQ4gr4YI=
github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190508211750-4152192cdc0f h1:BYQVawXauMXQ26I3Pn1Nw9kp/aZD60xmh9ZP3jum0YM=
github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190508211750-4152192cdc0f/go.mod h1:CkOYWfeuC5nAzehBztl94S6VOn2g50h1tffpcNoWCZ8=
github.com/hashicorp/vault-plugin-secrets-ad v0.5.1 h1:BdiASUZLOvOUs317EnaUNjGxTSw0PYGQA7zJZhDKLC4=
github.com/hashicorp/vault-plugin-secrets-ad v0.5.1/go.mod h1:EH9CI8+0aWRBz8eIgGth0QjttmHWlGvn+8ZmX/ZUetE=
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.1 h1:72K91p4uLhT/jgtBq2zV5Wn8ocvny4sAN56XOcTxK1w=
@ -558,6 +560,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190410170021-cc4d4f50624c h1:OUGWoQpM/o3TxM7Fp3CEqRpaYCbg4H1hOVPnZoUtr2U=
golang.org/x/sys v0.0.0-20190410170021-cc4d4f50624c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -21,6 +21,7 @@ import (
credRadius "github.com/hashicorp/vault/builtin/credential/radius"
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
dbElastic "github.com/hashicorp/vault-plugin-database-elasticsearch"
dbCass "github.com/hashicorp/vault/plugins/database/cassandra"
dbHana "github.com/hashicorp/vault/plugins/database/hana"
dbInflux "github.com/hashicorp/vault/plugins/database/influxdb"
@ -86,12 +87,13 @@ func newRegistry() *registry {
"mysql-rds-database-plugin": dbMysql.New(credsutil.NoneLength, dbMysql.LegacyMetadataLen, dbMysql.LegacyUsernameLen),
"mysql-legacy-database-plugin": dbMysql.New(credsutil.NoneLength, dbMysql.LegacyMetadataLen, dbMysql.LegacyUsernameLen),
"postgresql-database-plugin": dbPostgres.New,
"mssql-database-plugin": dbMssql.New,
"cassandra-database-plugin": dbCass.New,
"mongodb-database-plugin": dbMongo.New,
"hana-database-plugin": dbHana.New,
"influxdb-database-plugin": dbInflux.New,
"postgresql-database-plugin": dbPostgres.New,
"mssql-database-plugin": dbMssql.New,
"cassandra-database-plugin": dbCass.New,
"mongodb-database-plugin": dbMongo.New,
"hana-database-plugin": dbHana.New,
"influxdb-database-plugin": dbInflux.New,
"elasticsearch-database-plugin": dbElastic.New,
},
logicalBackends: map[string]logical.Factory{
"ad": logicalAd.Factory,

View File

@ -0,0 +1,19 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Jetbrains IDE
.idea*
# binaries and such
bin/*
pkg/*

View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@ -0,0 +1,39 @@
# Determine this makefile's path.
# Be sure to place this BEFORE `include` directives, if any.
THIS_FILE := $(lastword $(MAKEFILE_LIST))
TEST?=$$(go list ./... | grep -v /vendor/ | grep -v /integ)
GOFMT_FILES?=$$(find . -name '*.go' | grep -v vendor)
EXTERNAL_TOOLS=\
github.com/mitchellh/gox
default: dev
# bin generates the releaseable binaries for vault-plugin-database-elasticsearch
bin: fmtcheck generate
@CGO_ENABLED=1 BUILD_TAGS='$(BUILD_TAGS)' XC_ARCH="amd64" XC_OS="linux" XC_OSARCH="linux/amd64" sh -c "'$(CURDIR)/scripts/build.sh'"
dev: fmtcheck
@CGO_ENABLED=1 BUILD_TAGS='$(BUILD_TAGS)' VAULT_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'"
# test runs the unit tests and vets the code
test: fmtcheck generate
CGO_ENABLED=1 go test -v -short -tags='$(BUILD_TAGS)' $(TEST) $(TESTARGS) -timeout=20m -parallel=1
testacc: fmtcheck generate
VAULT_ACC=1 VAULT_ADDR=http://localhost:8200 VAULT_TOKEN=root CGO_ENABLED=1 go test -v -race -tags='$(BUILD_TAGS)' $(TEST) $(TESTARGS) -timeout=20m -parallel=1
fmtcheck:
@sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'"
fmt:
gofmt -w -s $(GOFMT_FILES)
# bootstrap the build by downloading additional tools
bootstrap:
@for tool in $(EXTERNAL_TOOLS) ; do \
echo "Installing/Updating $$tool" ; \
go get -u $$tool; \
done
.PHONY: bin default generate test testacc fmt fmtcheck dev bootstrap

View File

@ -0,0 +1,138 @@
# Elasticsearch Database Secrets Engine
This plugin provides unique, short-lived credentials for Elasticsearch using native X-Pack Security.
## Getting Started
To take advantage of this plugin, you must first enable Elasticsearch's native realm of security by activating X-Pack. These
instructions will walk you through doing this using ElasticSearch 6.6.1. At the time of writing, X-Pack was a paid feature.
To use it, you may need to enable a 30-day trial with Elasticsearch, or activate a paid version.
### Enable X-Pack Security in Elasticsearch
Read [Securing the Elastic Stack](https://www.elastic.co/guide/en/elastic-stack-overview/6.6/elasticsearch-security.html) and
follow [its instructions for enabling X-Pack Security](https://www.elastic.co/guide/en/elasticsearch/reference/6.6/setup-xpack.html).
When done, verify that you've enabled X-Pack by running `$ $ES_HOME/bin/elasticsearch-setup-passwords interactive`. You'll
know it's been set up successfully if it takes you through a number of password-inputting steps.
### Recommended: Enable Encrypted Communications
This plugin communicates with Elasticsearch's security API. We recommend you enable TLS for these communications so they can be
encrypted.
To set up TLS in Elasticsearch, first read [encrypted communications](https://www.elastic.co/guide/en/elastic-stack-overview/6.6/encrypting-communications.html)
and go through its instructions on [encrypting HTTP client communications](https://www.elastic.co/guide/en/elasticsearch/reference/6.6/configuring-tls.html#tls-http).
After enabling TLS on the Elasticsearch side, you'll need to convert the .p12 certificates you generated to other formats so they can be
used by Vault. [Here is an example using OpenSSL](https://stackoverflow.com/questions/15144046/converting-pkcs12-certificate-into-pem-using-openssl)
to convert our .p12 certs to the pem format.
Also, on the instance running Elasticsearch, we needed to install our newly generated CA certificate that was originally in the .p12 format.
We did this by converting the .p12 CA cert to a pem, and then further converting that
[pem to a crt](https://stackoverflow.com/questions/13732826/convert-pem-to-crt-and-key), adding that crt to `/usr/share/ca-certificates/extra`,
and using `sudo dpkg-reconfigure ca-certificates`.
The above instructions may vary if you are not using an Ubuntu machine. Please ensure you're using the methods specific to your operating
environment. Describing every operating environment is outside the scope of these instructions.
### Create a Role for Vault
Next, in Elasticsearch, we recommend that you create a user just for Vault to use in managing secrets.
To do this, first create a role that will allow Vault the minimum privileges needed to administer users and passwords by performing a
POST to Elasticsearch. To do this, we used the `elastic` superuser whose password we created in the
`$ $ES_HOME/bin/elasticsearch-setup-passwords interactive` step.
```
$ curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"cluster": ["manage_security"]}' \
http://elastic:$PASSWORD@localhost:9200/_xpack/security/role/vault
```
Next, create a user for Vault associated with that role.
```
$ curl \
-X POST \
-H "Content-Type: application/json" \
-d @data.json \
http://elastic:$PASSWORD@localhost:9200/_xpack/security/user/vault
```
The contents of `data.json` in this example are:
```
{
"password" : "myPa55word",
"roles" : [ "vault" ],
"full_name" : "Hashicorp Vault",
"metadata" : {
"plugin_name": "Vault Plugin Secrets ElasticSearch",
"plugin_url": "https://github.com/hashicorp/vault-plugin-secrets-elasticsearch"
}
}
```
Now, Elasticsearch is configured and ready to be used with Vault.
## Example Walkthrough
Here is an example of how to successfully configure and use this secrets engine using the Vault CLI. Note that the
`plugin_name` may need to be `vault-plugin-database-elasticsearch` if you manually mounted it rather than using the
version of the plugin built in to Vault.
```
export ES_HOME=/home/somewhere/Applications/elasticsearch-6.6.1
vault secrets enable database
vault write database/config/my-elasticsearch-database \
plugin_name="elasticsearch-database-plugin" \
allowed_roles="internally-defined-role,externally-defined-role" \
username=vault \
password=myPa55word \
url=http://localhost:9200 \
ca_cert=/usr/share/ca-certificates/extra/elastic-stack-ca.crt.pem \
client_cert=$ES_HOME/config/certs/elastic-certificates.crt.pem \
client_key=$ES_HOME/config/certs/elastic-certificates.key.pem
# create and get creds with one type of role
vault write database/roles/internally-defined-role \
db_name=my-elasticsearch-database \
creation_statements='{"elasticsearch_role_definition": {"indices": [{"names":["*"], "privileges":["read"]}]}}' \
default_ttl="1h" \
max_ttl="24h"
vault read database/creds/internally-defined-role
# create and get creds with another type of role
vault write database/roles/externally-defined-role \
db_name=my-elasticsearch-database \
creation_statements='{"elasticsearch_roles": ["vault"]}' \
default_ttl="1h" \
max_ttl="24h"
vault read database/creds/externally-defined-role
# renew credentials
vault lease renew database/creds/internally-defined-role/nvJ6SveX9PN1E4BlxVWdKuX1
# revoke credentials
vault lease revoke database/creds/internally-defined-role/nvJ6SveX9PN1E4BlxVWdKuX1
# rotate root credentials
vault write -force database/rotate-root/my-elasticsearch-database
```
## Developing
The Vault plugin system is documented on the [Vault documentation site](https://www.vaultproject.io/docs/internals/plugins.html).
You will need to define a plugin directory using the `plugin_directory` configuration directive, then place the `vault-plugin-database-elasticsearch` executable generated above in the directory.
Register the plugin using
```
vault write sys/plugins/catalog/vault-plugin-database-elasticsearch \
sha256=$(sha256sum bin/vault-plugin-database-elasticsearch) \
command="vault-plugin-database-elasticsearch"
```

View File

@ -0,0 +1,231 @@
package elasticsearch
/*
This lightweight client implements only the methods needed for this secrets engine.
It consumes this API:
https://www.elastic.co/guide/en/elasticsearch/reference/6.6/security-api.html
*/
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/hashicorp/go-retryablehttp"
"github.com/hashicorp/go-rootcerts"
)
type ClientConfig struct {
Username, Password, BaseURL string
// Leave this nil to flag that TLS is not desired
TLSConfig *TLSConfig
}
// TLSConfig contains the parameters needed to configure TLS on the HTTP client
// used to communicate with Elasticsearch.
type TLSConfig struct {
// CACert is the path to a PEM-encoded CA cert file to use to verify theHTTPClient
// Elasticsearch server SSL certificate.
CACert string
// CAPath is the path to a directory of PEM-encoded CA cert files to verify
// the Elasticsearch server SSL certificate.
CAPath string
// ClientCert is the path to the certificate for Elasticsearch communication
ClientCert string
// ClientKey is the path to the private key for Elasticsearch communication
ClientKey string
// TLSServerName, if set, is used to set the SNI host when connecting via
// TLS.
TLSServerName string
// Insecure enables or disables SSL verification
Insecure bool
}
func NewClient(config *ClientConfig) (*Client, error) {
client := retryablehttp.NewClient()
if config.TLSConfig != nil {
conf := &tls.Config{
ServerName: config.TLSConfig.TLSServerName,
InsecureSkipVerify: config.TLSConfig.Insecure,
MinVersion: tls.VersionTLS12,
}
if config.TLSConfig.ClientCert != "" && config.TLSConfig.ClientKey != "" {
clientCertificate, err := tls.LoadX509KeyPair(config.TLSConfig.ClientCert, config.TLSConfig.ClientKey)
if err != nil {
return nil, err
}
conf.Certificates = append(conf.Certificates, clientCertificate)
}
if config.TLSConfig.CACert != "" || config.TLSConfig.CAPath != "" {
rootConfig := &rootcerts.Config{
CAFile: config.TLSConfig.CACert,
CAPath: config.TLSConfig.CAPath,
}
if err := rootcerts.ConfigureTLS(conf, rootConfig); err != nil {
return nil, err
}
}
client.HTTPClient.Transport = &http.Transport{TLSClientConfig: conf}
}
return &Client{
username: config.Username,
password: config.Password,
baseURL: config.BaseURL,
client: client,
}, nil
}
type Client struct {
username, password, baseURL string
client *retryablehttp.Client
}
// Role management
func (c *Client) CreateRole(ctx context.Context, name string, role map[string]interface{}) error {
endpoint := "/_xpack/security/role/" + name
method := http.MethodPost
roleBytes, err := json.Marshal(role)
if err != nil {
return err
}
req, err := http.NewRequest(method, c.baseURL+endpoint, bytes.NewReader(roleBytes))
if err != nil {
return err
}
return c.do(ctx, req, nil)
}
// GetRole returns nil, nil if role is unfound.
func (c *Client) GetRole(ctx context.Context, name string) (map[string]interface{}, error) {
endpoint := "/_xpack/security/role/" + name
method := http.MethodGet
req, err := http.NewRequest(method, c.baseURL+endpoint, nil)
if err != nil {
return nil, err
}
var roles map[string]map[string]interface{}
if err := c.do(ctx, req, &roles); err != nil {
return nil, err
}
return roles[name], nil
}
func (c *Client) DeleteRole(ctx context.Context, name string) error {
endpoint := "/_xpack/security/role/" + name
method := http.MethodDelete
req, err := http.NewRequest(method, c.baseURL+endpoint, nil)
if err != nil {
return err
}
return c.do(ctx, req, nil)
}
// User management
type User struct {
Password string `json:"password"` // Passwords must be at least 6 characters long.
Roles []string `json:"roles"`
}
func (c *Client) CreateUser(ctx context.Context, name string, user *User) error {
endpoint := "/_xpack/security/user/" + name
method := http.MethodPost
userJson, err := json.Marshal(user)
if err != nil {
return err
}
req, err := http.NewRequest(method, c.baseURL+endpoint, bytes.NewReader(userJson))
if err != nil {
return err
}
return c.do(ctx, req, nil)
}
func (c *Client) ChangePassword(ctx context.Context, name, newPassword string) error {
endpoint := "/_xpack/security/user/" + name + "/_password"
method := http.MethodPost
pwdChangeBodyJson, err := json.Marshal(map[string]string{"password": newPassword})
if err != nil {
return err
}
req, err := http.NewRequest(method, c.baseURL+endpoint, bytes.NewReader(pwdChangeBodyJson))
if err != nil {
return err
}
return c.do(ctx, req, nil)
}
func (c *Client) DeleteUser(ctx context.Context, name string) error {
endpoint := "/_xpack/security/user/" + name
method := http.MethodDelete
req, err := http.NewRequest(method, c.baseURL+endpoint, nil)
if err != nil {
return err
}
return c.do(ctx, req, nil)
}
// Low-level request handling
func (c *Client) do(ctx context.Context, req *http.Request, ret interface{}) error {
// Prepare the request.
retryableReq, err := retryablehttp.NewRequest(req.Method, req.URL.String(), req.Body)
if err != nil {
return err
}
retryableReq.SetBasicAuth(c.username, c.password)
retryableReq.Header.Add("Content-Type", "application/json")
// Execute the request.
resp, err := c.client.Do(retryableReq.WithContext(ctx))
if err != nil {
return err
}
defer resp.Body.Close()
// Read the body once so it can be retained for error output if needed.
// Since no responses are list responses, response bodies should have a small footprint
// and are very useful for debugging.
body, _ := ioutil.ReadAll(resp.Body)
// If we were successful, try to unmarshal the body if the caller wants it.
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
if ret == nil {
// No body to read out.
return nil
}
if err := json.Unmarshal(body, ret); err != nil {
// We received a success response from the ES API but the body was in an unexpected format.
return fmt.Errorf("%s; %d: %s", err, resp.StatusCode, body)
}
// Body has been successfully read out.
return nil
}
// 404 is actually another form of success in the ES API. It just means that an object we were searching
// for wasn't found.
if resp.StatusCode == 404 {
return nil
}
// We received some sort of API error. Let's return it.
return fmt.Errorf("%d: %s", resp.StatusCode, body)
}

View File

@ -0,0 +1,336 @@
package elasticsearch
import (
"context"
"encoding/json"
"errors"
"fmt"
"sync"
"time"
"github.com/hashicorp/errwrap"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/database/dbplugin"
"github.com/hashicorp/vault/sdk/database/helper/credsutil"
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
)
func New() (interface{}, error) {
db := NewElasticsearch()
return dbplugin.NewDatabaseErrorSanitizerMiddleware(db, db.SecretValues), nil
}
func Run(apiTLSConfig *api.TLSConfig) error {
dbplugin.Serve(NewElasticsearch(), api.VaultPluginTLSProvider(apiTLSConfig))
return nil
}
func NewElasticsearch() *Elasticsearch {
return &Elasticsearch{
credentialProducer: &credsutil.SQLCredentialsProducer{
DisplayNameLen: 15,
RoleNameLen: 15,
UsernameLen: 100,
Separator: "-",
},
}
}
// Elasticsearch implements dbplugin's Database interface.
type Elasticsearch struct {
// The CredentialsProducer is never mutated and thus is inherently thread-safe.
credentialProducer credsutil.CredentialsProducer
// This protects the config from races while also allowing multiple threads
// to read the config simultaneously when it's not changing.
mux sync.RWMutex
// The root credential config.
config map[string]interface{}
}
func (es *Elasticsearch) Type() (string, error) {
return "elasticsearch", nil
}
// SecretValues is used by some error-sanitizing middleware in Vault that basically
// replaces the keys in the map with the values given so they're not leaked via
// error messages.
func (es *Elasticsearch) SecretValues() map[string]interface{} {
es.mux.RLock()
defer es.mux.RUnlock()
replacements := make(map[string]interface{})
for _, secretKey := range []string{"password", "client_key"} {
vIfc, found := es.config[secretKey]
if !found {
continue
}
secretVal, ok := vIfc.(string)
if !ok {
continue
}
// So, supposing a password of "0pen5e5ame",
// this will cause that string to get replaced with "[password]".
replacements[secretVal] = "[" + secretKey + "]"
}
return replacements
}
// Init is called on `$ vault write database/config/:db-name`,
// or when you do a creds call after Vault's been restarted.
func (es *Elasticsearch) Init(ctx context.Context, config map[string]interface{}, verifyConnection bool) (map[string]interface{}, error) {
// Validate the config to provide immediate feedback to the user.
// Ensure required string fields are provided in the expected format.
for _, requiredField := range []string{"username", "password", "url"} {
raw, ok := config[requiredField]
if !ok {
return nil, fmt.Errorf(`%q must be provided`, requiredField)
}
if _, ok := raw.(string); !ok {
return nil, fmt.Errorf(`%q must be a string`, requiredField)
}
}
// Ensure optional string fields are provided in the expected format.
for _, optionalField := range []string{"ca_cert", "ca_path", "client_cert", "client_key", "tls_server_name"} {
raw, ok := config[optionalField]
if !ok {
continue
}
if _, ok = raw.(string); !ok {
return nil, fmt.Errorf(`%q must be a string`, optionalField)
}
}
// Check the one optional bool field is in the expected format.
if raw, ok := config["insecure"]; ok {
if _, ok = raw.(bool); !ok {
return nil, errors.New(`"insecure" must be a bool`)
}
}
// Test the given config to see if we can make a client.
client, err := buildClient(config)
if err != nil {
return nil, errwrap.Wrapf("couldn't make client with inbound config: {{err}}", err)
}
// Optionally, test the given config to see if we can make a successful call.
if verifyConnection {
// Whether this role is found or unfound, if we're configured correctly there will
// be no err from the call. However, if something is misconfigured, this will yield
// an error response, which will be described in the returned error.
if _, err := client.GetRole(ctx, "vault-test"); err != nil {
return nil, errwrap.Wrapf("client test of getting a role failed: {{err}}", err)
}
}
// Everything's working, write the new config to memory and storage.
es.mux.Lock()
defer es.mux.Unlock()
es.config = config
return es.config, nil
}
// CreateUser is called on `$ vault read database/creds/:role-name`
// and it's the first time anything is touched from `$ vault write database/roles/:role-name`.
// This is likely to be the highest-throughput method for this plugin.
func (es *Elasticsearch) CreateUser(ctx context.Context, statements dbplugin.Statements, usernameConfig dbplugin.UsernameConfig, _ time.Time) (string, string, error) {
username, err := es.credentialProducer.GenerateUsername(usernameConfig)
if err != nil {
return "", "", errwrap.Wrapf(fmt.Sprintf("unable to generate username for %q: {{err}}", usernameConfig), err)
}
password, err := es.credentialProducer.GeneratePassword()
if err != nil {
return "", "", errwrap.Wrapf("unable to generate password: {{err}}", err)
}
stmt, err := newCreationStatement(statements)
if err != nil {
return "", "", errwrap.Wrapf("unable to read creation_statements: {{err}}", err)
}
user := &User{
Password: password,
Roles: stmt.PreexistingRoles,
}
// Don't let anyone write the config while we're using it for our current client.
es.mux.RLock()
defer es.mux.RUnlock()
client, err := buildClient(es.config)
if err != nil {
return "", "", errwrap.Wrapf("unable to get client: {{err}}", err)
}
// If the RoleToCreate map has been populated with any data, we have one role to create.
// There can either be one RoleToCreate and no PreexistingRoles, or >= 1 PreexistingRoles
// and no RoleToCreate. They're mutually exclusive.
if len(stmt.RoleToCreate) > 0 {
// We'll simply name the role the same thing as the username, making it easy to tie back to this user.
if err := client.CreateRole(ctx, username, stmt.RoleToCreate); err != nil {
return "", "", errwrap.Wrapf(fmt.Sprintf("unable to create role name %s, role definition %q: {{err}}", username, stmt.RoleToCreate), err)
}
user.Roles = []string{username}
}
if err := client.CreateUser(ctx, username, user); err != nil {
return "", "", errwrap.Wrapf(fmt.Sprintf("unable to create user name %s, user %q: {{err}}", username, user), err)
}
return username, password, nil
}
// RenewUser gets called on `$ vault lease renew {{lease-id}}`. It automatically pushes out the amount of time until
// the database secrets engine calls RevokeUser, if appropriate.
func (es *Elasticsearch) RenewUser(_ context.Context, _ dbplugin.Statements, _ string, _ time.Time) error {
// Normally, this function would update a "VALID UNTIL" statement on a database user
// but there's no similar need here.
return nil
}
// RevokeUser is called when a lease expires.
func (es *Elasticsearch) RevokeUser(ctx context.Context, statements dbplugin.Statements, username string) error {
stmt, err := newCreationStatement(statements)
if err != nil {
return errwrap.Wrapf("unable to read creation_statements: {{err}}", err)
}
// Don't let anyone write the config while we're using it for our current client.
es.mux.RLock()
defer es.mux.RUnlock()
client, err := buildClient(es.config)
if err != nil {
return errwrap.Wrapf("unable to get client: {{err}}", err)
}
var errs error
if len(stmt.RoleToCreate) > 0 {
// If the role already doesn't exist because it was successfully deleted on a previous
// attempt to run this code, there will be no error, so it's harmless to try.
if err := client.DeleteRole(ctx, username); err != nil {
errs = multierror.Append(errs, errwrap.Wrapf(fmt.Sprintf("unable to delete role name %s: {{err}}", username), err))
}
}
// Same with the user. If it was already deleted on a previous attempt, there won't be an
// error.
if err := client.DeleteUser(ctx, username); err != nil {
errs = multierror.Append(errs, errwrap.Wrapf(fmt.Sprintf("unable to create user name %s: {{err}}", username), err))
}
return errs
}
// RotateRootCredentials doesn't require any statements from the user because it's not configurable in any
// way. We simply generate a new password and hit a pre-defined Elasticsearch REST API to rotate them.
func (es *Elasticsearch) RotateRootCredentials(ctx context.Context, _ []string) (map[string]interface{}, error) {
newPassword, err := es.credentialProducer.GeneratePassword()
if err != nil {
return nil, errwrap.Wrapf("unable to generate root password: {{err}}", err)
}
// Don't let anyone read or write the config while we're in the process of rotating the password.
es.mux.Lock()
defer es.mux.Unlock()
client, err := buildClient(es.config)
if err != nil {
return nil, errwrap.Wrapf("unable to get client: {{err}}", err)
}
if err := client.ChangePassword(ctx, es.config["username"].(string), newPassword); err != nil {
return nil, errwrap.Wrapf("unable to change password: {{}}", err)
}
es.config["password"] = newPassword
return es.config, nil
}
func (es *Elasticsearch) Close() error {
// NOOP, nothing to close.
return nil
}
// DEPRECATED, included for backward-compatibility until removal
func (es *Elasticsearch) Initialize(ctx context.Context, config map[string]interface{}, verifyConnection bool) error {
_, err := es.Init(ctx, config, verifyConnection)
return err
}
func newCreationStatement(statements dbplugin.Statements) (*creationStatement, error) {
if len(statements.Creation) == 0 {
return nil, dbutil.ErrEmptyCreationStatement
}
stmt := &creationStatement{}
if err := json.Unmarshal([]byte(statements.Creation[0]), stmt); err != nil {
return nil, errwrap.Wrapf(fmt.Sprintf("unable to unmarshal %s: {{err}}", []byte(statements.Creation[0])), err)
}
if len(stmt.PreexistingRoles) > 0 && len(stmt.RoleToCreate) > 0 {
return nil, errors.New(`"elasticsearch_roles" and "elasticsearch_role_definition" are mutually exclusive`)
}
return stmt, nil
}
type creationStatement struct {
PreexistingRoles []string `json:"elasticsearch_roles"`
RoleToCreate map[string]interface{} `json:"elasticsearch_role_definition"`
}
// buildClient is a helper method for building a client from the present config,
// which is done often.
func buildClient(config map[string]interface{}) (*Client, error) {
// We can presume these required fields are provided by strings
// because they're validated in Init.
clientConfig := &ClientConfig{
Username: config["username"].(string),
Password: config["password"].(string),
BaseURL: config["url"].(string),
}
hasTLSConf := false
tlsConf := &TLSConfig{}
// We can presume that if these are provided, they're in the expected format
// because they're also validated in Init.
if raw, ok := config["ca_cert"]; ok {
tlsConf.CACert = raw.(string)
hasTLSConf = true
}
if raw, ok := config["ca_path"]; ok {
tlsConf.CAPath = raw.(string)
hasTLSConf = true
}
if raw, ok := config["client_cert"]; ok {
tlsConf.ClientCert = raw.(string)
hasTLSConf = true
}
if raw, ok := config["client_key"]; ok {
tlsConf.ClientKey = raw.(string)
hasTLSConf = true
}
if raw, ok := config["tls_server_name"]; ok {
tlsConf.TLSServerName = raw.(string)
hasTLSConf = true
}
if raw, ok := config["insecure"]; ok {
tlsConf.Insecure = raw.(bool)
hasTLSConf = true
}
// We should only fulfill the clientConfig's TLSConfig pointer if we actually
// want the client to use TLS.
if hasTLSConf {
clientConfig.TLSConfig = tlsConf
}
client, err := NewClient(clientConfig)
if err != nil {
return nil, err
}
return client, nil
}

View File

@ -0,0 +1,16 @@
module github.com/hashicorp/vault-plugin-database-elasticsearch
go 1.12
require (
github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-retryablehttp v0.5.3
github.com/hashicorp/go-rootcerts v1.0.0
github.com/hashicorp/vault/api v1.0.1
github.com/hashicorp/vault/sdk v0.1.9
github.com/mitchellh/go-homedir v1.1.0 // indirect
golang.org/x/sys v0.0.0-20190410170021-cc4d4f50624c // indirect
google.golang.org/grpc v1.20.0 // indirect
)

View File

@ -0,0 +1,147 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.8.0 h1:z3ollgGRg8RjfJH6UVBaG54R70GFd++QOkvnJH3VSBY=
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v1.0.0 h1:/gQ1sNR8/LHpoxKRQq4PmLBuacfZb4tC93e9B30o/7c=
github.com/hashicorp/go-plugin v1.0.0/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/vault/api v1.0.1 h1:YQI4SgOlkmbEKZI8ZClo6fm9oXlBHJUlrbEtFiRPrng=
github.com/hashicorp/vault/api v1.0.1/go.mod h1:AV/+M5VPDpB90arloVX0rVDUIHkONiwz5Uza9HRtpUE=
github.com/hashicorp/vault/sdk v0.1.8/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU=
github.com/hashicorp/vault/sdk v0.1.9 h1:GkHLrt3ZU8j/ATmbLqW5P/frBCxPhCRC6nLD0kDP/yc=
github.com/hashicorp/vault/sdk v0.1.9/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190410170021-cc4d4f50624c h1:OUGWoQpM/o3TxM7Fp3CEqRpaYCbg4H1hOVPnZoUtr2U=
golang.org/x/sys v0.0.0-20190410170021-cc4d4f50624c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0 h1:DlsSIrgEBuZAUFJcta2B5i/lzeHHbnfkNFAfFXLVFYQ=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -351,7 +351,11 @@ func SetsockoptLinger(fd, level, opt int, l *Linger) (err error) {
}
func SetsockoptString(fd, level, opt int, s string) (err error) {
return setsockopt(fd, level, opt, unsafe.Pointer(&[]byte(s)[0]), uintptr(len(s)))
var p unsafe.Pointer
if len(s) > 0 {
p = unsafe.Pointer(&[]byte(s)[0])
}
return setsockopt(fd, level, opt, p, uintptr(len(s)))
}
func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) {

View File

@ -451,6 +451,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -452,6 +452,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -455,6 +455,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -453,6 +453,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -454,6 +454,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -453,6 +453,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -453,6 +453,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -454,6 +454,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -454,6 +454,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -454,6 +454,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -453,6 +453,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -452,6 +452,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

View File

@ -456,6 +456,9 @@ const (
IFA_ANYCAST = 0x5
IFA_CACHEINFO = 0x6
IFA_MULTICAST = 0x7
IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa
IFLA_UNSPEC = 0x0
IFLA_ADDRESS = 0x1
IFLA_BROADCAST = 0x2

4
vendor/modules.txt vendored
View File

@ -329,6 +329,8 @@ github.com/hashicorp/vault-plugin-auth-gcp/plugin/cache
github.com/hashicorp/vault-plugin-auth-jwt
# github.com/hashicorp/vault-plugin-auth-kubernetes v0.5.1
github.com/hashicorp/vault-plugin-auth-kubernetes
# github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190508211750-4152192cdc0f
github.com/hashicorp/vault-plugin-database-elasticsearch
# github.com/hashicorp/vault-plugin-secrets-ad v0.5.1
github.com/hashicorp/vault-plugin-secrets-ad/plugin
github.com/hashicorp/vault-plugin-secrets-ad/plugin/client
@ -643,7 +645,7 @@ golang.org/x/oauth2/jwt
golang.org/x/oauth2/jws
# golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
golang.org/x/sync/semaphore
# golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e
# golang.org/x/sys v0.0.0-20190410170021-cc4d4f50624c
golang.org/x/sys/unix
golang.org/x/sys/windows
golang.org/x/sys/cpu

View File

@ -0,0 +1,100 @@
---
layout: "api"
page_title: "Elasticsearch - Database - Secrets Engines - HTTP API"
sidebar_title: "Elasticsearch"
sidebar_current: "api-http-secret-databases-elasticsearch"
description: |-
The Elasticsearch plugin for Vault's database secrets engine generates database credentials to access Elasticsearch.
---
# Elasticsearch Database Plugin HTTP API
The Elasticsearch database plugin is one of the supported plugins for the database
secrets engine. This plugin generates credentials dynamically based on
configured roles for Elasticsearch.
## Configure Connection
In addition to the parameters defined by the [Database
Backend](/api/secret/databases/index.html#configure-connection), this plugin
has a number of parameters to further configure a connection.
| Method | Path |
| :--------------------------- | :--------------------- |
| `POST` | `/database/config/:name` |
### Parameters
- `url` `(string: <required>)` - The URL for Elasticsearch's API ("http://localhost:9200").
- `username` `(string: <required>)` - The username to be used in the connection URL ("vault").
- `password` `(string: <required>)` - The password to be used in the connection URL ("pa55w0rd").
- `ca_cert` `(string: "")` - The path to a PEM-encoded CA cert file to use to verify the Elasticsearch server's identity.
- `ca_path` `(string: "")` - The path to a directory of PEM-encoded CA cert files to use to verify the Elasticsearch server's identity.
- `client_cert` `(string: "")` - The path to the certificate for the Elasticsearch client to present for communication.
- `client_key` `(string: "")` - The path to the key for the Elasticsearch client to use for communication.
- `tls_server_name` `(string: "")` - This, if set, is used to set the SNI host when connecting via 1TLS.
- `insecure` `(bool: false)` - Not recommended. Default to false. Can be set to true to disable SSL verification.
### Sample Payload
```json
{
"plugin_name": "elasticsearch-database-plugin",
"allowed_roles": "internally-defined-role,externally-defined-role",
"url": "http://localhost:9200",
"username": "vault",
"password": "myPa55word",
"ca_cert": "/usr/share/ca-certificates/extra/elastic-stack-ca.crt.pem",
"client_cert": "$ES_HOME/config/certs/elastic-certificates.crt.pem",
"client_key": "$ES_HOME/config/certs/elastic-certificates.key.pem"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
http://127.0.0.1:8200/v1/database/config/my-elasticsearch-database
```
## Statements
Statements are configured during role creation and are used by the plugin to
determine what is sent to the database on user creation, renewing, and
revocation. For more information on configuring roles see the [Role
API](/api/secret/databases/index.html#create-role) in the database secrets engine docs.
### Parameters
The following are the statements used by this plugin. If not mentioned in this
list the plugin does not support that statement type.
- `creation_statements` `(string: <required>)` Using JSON, either defines an
`elasticsearch_role_definition` or a group of pre-existing `elasticsearch_roles`.
The object specified by the `elasticsearch_role_definition` is the JSON directly
passed through to the Elasticsearch API, so you can pass through anything shown
[here](https://www.elastic.co/guide/en/elasticsearch/reference/6.6/security-api-put-role.html).
For `elasticsearch_roles`, add the names of the roles only. They must pre-exist
in Elasticsearch. Defining roles in Vault is more secure than using pre-existing
roles because a privilege escalation could be performed by editing the roles used
out-of-band in Elasticsearch.
### Sample Creation Statements
```json
{
"elasticsearch_role_definition": {
"indices": [{
"names": ["*"],
"privileges": ["read"]
}]
}
}
```
```json
{
"elasticsearch_roles": ["pre-existing-role-in-elasticsearch"]
}
```

View File

@ -0,0 +1,176 @@
---
layout: "docs"
page_title: "Elasticsearch - Database - Secrets Engines"
sidebar_title: "Elasticsearch"
sidebar_current: "docs-secrets-databases-elasticsearch"
description: |-
Elasticsearch is one of the supported plugins for the database secrets engine. This
plugin generates database credentials dynamically based on configured roles
for Elasticsearch.
---
# Elasticsearch Database Secrets Engine
Elasticsearch is one of the supported plugins for the database secrets engine. This
plugin generates database credentials dynamically based on configured roles for
Elasticsearch.
See the [database secrets engine](/docs/secrets/databases/index.html) docs for
more information about setting up the database secrets engine.
## Getting Started
To take advantage of this plugin, you must first enable Elasticsearch's native realm of security by activating X-Pack. These
instructions will walk you through doing this using Elasticsearch 7.1.1. However, Elasticsearch 7.x.x is also supported.
At the time of writing, X-Pack was a paid feature. To use it, you may need to enable a 30-day trial with Elasticsearch,
or activate a paid version.
### Enable X-Pack Security in Elasticsearch
Read [Securing the Elastic Stack](https://www.elastic.co/guide/en/elastic-stack-overview/7.1/elasticsearch-security.html) and
follow [its instructions for enabling X-Pack Security](https://www.elastic.co/guide/en/elasticsearch/reference/7.1/setup-xpack.html).
### Enable Encrypted Communications
This plugin communicates with Elasticsearch's security API. ES requires TLS for these communications so they can be
encrypted.
To set up TLS in Elasticsearch, first read [encrypted communications](https://www.elastic.co/guide/en/elastic-stack-overview/7.1/encrypting-communications.html)
and go through its instructions on [encrypting HTTP client communications](https://www.elastic.co/guide/en/elasticsearch/reference/7.1/configuring-tls.html#tls-http).
After enabling TLS on the Elasticsearch side, you'll need to convert the .p12 certificates you generated to other formats so they can be
used by Vault. [Here is an example using OpenSSL](https://stackoverflow.com/questions/15144046/converting-pkcs12-certificate-into-pem-using-openssl)
to convert our .p12 certs to the pem format.
Also, on the instance running Elasticsearch, we needed to install our newly generated CA certificate that was originally in the .p12 format.
We did this by converting the .p12 CA cert to a pem, and then further converting that
[pem to a crt](https://stackoverflow.com/questions/13732826/convert-pem-to-crt-and-key), adding that crt to `/usr/share/ca-certificates/extra`,
and using `sudo dpkg-reconfigure ca-certificates`.
The above instructions may vary if you are not using an Ubuntu machine. Please ensure you're using the methods specific to your operating
environment. Describing every operating environment is outside the scope of these instructions.
### Set Up Passwords
When done, verify that you've enabled X-Pack by running `$ $ES_HOME/bin/elasticsearch-setup-passwords interactive`. You'll
know it's been set up successfully if it takes you through a number of password-inputting steps.
### Create a Role for Vault
Next, in Elasticsearch, we recommend that you create a user just for Vault to use in managing secrets.
To do this, first create a role that will allow Vault the minimum privileges needed to administer users and passwords by performing a
POST to Elasticsearch. To do this, we used the `elastic` superuser whose password we created in the
`$ $ES_HOME/bin/elasticsearch-setup-passwords interactive` step.
```
$ curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"cluster": ["manage_security"]}' \
http://elastic:$PASSWORD@localhost:9200/_xpack/security/role/vault
```
Next, create a user for Vault associated with that role.
```
$ curl \
-X POST \
-H "Content-Type: application/json" \
-d @data.json \
http://elastic:$PASSWORD@localhost:9200/_xpack/security/user/vault
```
The contents of `data.json` in this example are:
```
{
"password" : "myPa55word",
"roles" : [ "vault" ],
"full_name" : "Hashicorp Vault",
"metadata" : {
"plugin_name": "Vault Plugin Database Elasticsearch",
"plugin_url": "https://github.com/hashicorp/vault-plugin-database-elasticsearch"
}
}
```
Now, Elasticsearch is configured and ready to be used with Vault.
## Setup
1. Enable the database secrets engine if it is not already enabled:
```text
$ vault secrets enable database
Success! Enabled the database secrets engine at: database/
```
By default, the secrets engine will enable at the name of the engine. To
enable the secrets engine at a different path, use the `-path` argument.
1. Configure Vault with the proper plugin and connection information:
```text
$ vault write database/config/my-elasticsearch-database \
plugin_name="elasticsearch-database-plugin" \
allowed_roles="internally-defined-role,externally-defined-role" \
username=vault \
password=myPa55word \
url=http://localhost:9200 \
ca_cert=/usr/share/ca-certificates/extra/elastic-stack-ca.crt.pem \
client_cert=$ES_HOME/config/certs/elastic-certificates.crt.pem \
client_key=$ES_HOME/config/certs/elastic-certificates.key.pem
```
1. Configure a role that maps a name in Vault to a role definition in Elasticsearch.
This is considered the most secure type of role because nobody can perform
a privilege escalation by editing a role's privileges out-of-band in
Elasticsearch:
```text
$ vault write database/roles/internally-defined-role \
db_name=my-elasticsearch-database \
creation_statements='{"elasticsearch_role_definition": {"indices": [{"names":["*"], "privileges":["read"]}]}}' \
default_ttl="1h" \
max_ttl="24h"
Success! Data written to: database/roles/internally-defined-role
```
1. Alternatively, configure a role that maps a name in Vault to a pre-existing
role definition in Elasticsearch:
```text
$ vault write database/roles/externally-defined-role \
db_name=my-elasticsearch-database \
creation_statements='{"elasticsearch_roles": ["pre-existing-role-in-elasticsearch"]}' \
default_ttl="1h" \
max_ttl="24h"
Success! Data written to: database/roles/externally-defined-role
```
## Usage
After the secrets engine is configured and a user/machine has a Vault token with
the proper permission, it can generate credentials.
1. Generate a new credential by reading from the `/creds` endpoint with the name
of the role:
```text
$ vault read database/creds/my-role
Key Value
--- -----
lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6
lease_duration 1h
lease_renewable true
password 8cab931c-d62e-a73d-60d3-5ee85139cd66
username v-root-e2978cd0-
```
## API
The full list of configurable options can be seen in the [Elasticsearch database
plugin API](/api/secret/databases/elasticdb.html) page.
For more information on the database secrets engine's HTTP API please see the
[Database secrets engine API](/api/secret/databases/index.html) page.