-
Notifications
You must be signed in to change notification settings - Fork 485
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
Add UpstreamAuthority 'vault' plugin as built-in #1611
Add UpstreamAuthority 'vault' plugin as built-in #1611
Conversation
130582b
to
e1759e3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @hiyosi for the PR! This will be exciting to get merged. Please feel free to reach out if there is any question or concerns with any suggestions or comments.
The vault plugin requests to sign intermediate certificate (for signing X.509 SVIDs)to Vault PKI Engine. | ||
The plugin doesn't support `PublishJWTKeyRequest` and `PublishJWTKeyResponse`, | ||
this means that you **SHOULD NOT** use `JWT SVID` in multiple SPIRE environment with this plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The vault plugin requests to sign intermediate certificate (for signing X.509 SVIDs)to Vault PKI Engine. | |
The plugin doesn't support `PublishJWTKeyRequest` and `PublishJWTKeyResponse`, | |
this means that you **SHOULD NOT** use `JWT SVID` in multiple SPIRE environment with this plugin. | |
The vault plugin signs intermediate CA certificates for SPIRE using the Vault PKI Engine. | |
The plugin does not support the `PublishJWTKey` RPC and is therefore not appropriate for use in nested SPIRE topologies where JWT-SVIDs are in use. |
|
||
| key | type | required | description | default | | ||
|:----|:-----|:---------|:------------|:--------| | ||
| vault_addr | string | | A URL of Vault server. (e.g., https://vault.example.com:8443/) | `${VAULT_ADDR}` | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know the vault client takes a URL, but I wonder if our configuration should be a little more strict and just take a host with optional port (defaulting to 443) so that we can built the URL ourselves and force https://
? Is there ever a need for the URL to have anything other than the host and port, e.g., userinfo or a path component?
If we keep this as-is, I'd suggest the the following:
| vault_addr | string | | A URL of Vault server. (e.g., https://vault.example.com:8443/) | `${VAULT_ADDR}` | | |
| vault_addr | string | | The URL of the Vault server. (e.g., https://vault.example.com:8443/) | `${VAULT_ADDR}` | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there ever a need for the URL to have anything other than the host and port, e.g., userinfo or a path component?
I've read in the documentation(https://www.vaultproject.io/docs/auth), it doesn't seem to be necessary .
Therefore I think we can set a host and port instead of the URL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to fix it, please check.
4394dd6
| key | type | required | description | default | | ||
|:----|:-----|:---------|:------------|:--------| | ||
| vault_addr | string | | A URL of Vault server. (e.g., https://vault.example.com:8443/) | `${VAULT_ADDR}` | | ||
| pki_mount_point | string | | Name of mount point where PKI secret engine is mounted | pki | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| pki_mount_point | string | | Name of mount point where PKI secret engine is mounted | pki | | |
| pki_mount_point | string | | Name of the mount point where PKI secret engine is mounted | pki | |
|:----|:-----|:---------|:------------|:--------| | ||
| vault_addr | string | | A URL of Vault server. (e.g., https://vault.example.com:8443/) | `${VAULT_ADDR}` | | ||
| pki_mount_point | string | | Name of mount point where PKI secret engine is mounted | pki | | ||
| ca_cert_path | string | | Path to a CA certificate file that the client verifies the server certificate. Only PEM format is supported. | `${VAULT_CACERT}` | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ca_cert_path | string | | Path to a CA certificate file that the client verifies the server certificate. Only PEM format is supported. | `${VAULT_CACERT}` | | |
| ca_cert_path | string | | Path to a CA certificate file used to verify the Vault server certificate. Only PEM format is supported. | `${VAULT_CACERT}` | |
| vault_addr | string | | A URL of Vault server. (e.g., https://vault.example.com:8443/) | `${VAULT_ADDR}` | | ||
| pki_mount_point | string | | Name of mount point where PKI secret engine is mounted | pki | | ||
| ca_cert_path | string | | Path to a CA certificate file that the client verifies the server certificate. Only PEM format is supported. | `${VAULT_CACERT}` | | ||
| tls_skip_verify | string | | If true, vault client accepts any server certificates | false | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually preface the insecure flags with insecure_
:
| tls_skip_verify | string | | If true, vault client accepts any server certificates | false | | |
| insecure_skip_verify | string | | If true, the Vault server certificate is not verified (not for production use) | false | |
csr, err := ioutil.ReadFile(testReqCSR) | ||
vps.Require().NoError(err) | ||
|
||
req, err := getTestMintX509CARequest(csr) | ||
vps.Require().NoError(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do these two steps together a lot. Can we just have one helper that loads from a file and creates the request? If it is a member of the suite then it can even assert success directly instead of returning an error that each test has to assert individually:
csr, err := ioutil.ReadFile(testReqCSR) | |
vps.Require().NoError(err) | |
req, err := getTestMintX509CARequest(csr) | |
vps.Require().NoError(err) | |
req := vps.loadMintX509CARequestFromTestFile(testReqCSR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree.
I added a helper func (vps *VaultPluginSuite) loadMintX509CARequestFromTestFile(csrFile string) *upstreamauthority.MintX509CARequest { ... }
3fc46ac
s, addr, err := vps.fakeVaultServer.NewTLSServer() | ||
vps.Require().NoError(err) | ||
|
||
s.Start() | ||
defer s.Close() | ||
|
||
p := New() | ||
p.SetLogger(hclog.Default()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a lot of boilerplate for each test case do repeat. Is there a way we can DRY this up a little bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a helper function func (vps *VaultPluginSuite) newPlugin() *Plugin { ... }
3fc46ac
for i := range res.X509CaChain { | ||
cert, err := x509.ParseCertificate(res.X509CaChain[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for i := range res.X509CaChain { | |
cert, err := x509.ParseCertificate(res.X509CaChain[i]) | |
for _, certDER := range res.X509CaChain { | |
cert, err := x509.ParseCertificate(certDER) |
for i := range res.UpstreamX509Roots { | ||
upstream, err := x509.ParseCertificate(res.UpstreamX509Roots[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for i := range res.UpstreamX509Roots { | |
upstream, err := x509.ParseCertificate(res.UpstreamX509Roots[i]) | |
for _, upstreamDER := range res.UpstreamX509Roots { | |
upstream, err := x509.ParseCertificate(upstreamDER) |
spiretest.Run(t, new(VaultPluginSuite)) | ||
} | ||
|
||
type VaultPluginSuite struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The structure of these tests might align better with table-driven tests, but that's something we can improve later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with some cases are better with table driven tests.
If we should improve the tests within this PR, I think I can do.
f9e029e
to
4394dd6
Compare
@azdagron |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for addressing the comments, @hiyosi! Just a few more comments and I think this is good.
| key | type | required | description | default | | ||
|:----|:-----|:---------|:------------|:--------| | ||
| vault_host | string | | A Hostname of the Vault server or combination of Hostname and Port. | `${VAULT_HOST}` | | ||
| vault_path_prefix | string | | A URL Path Prefix for the Vault API. e.g., If the Vault places behind the reverse proxy, it is possible to customize the URL Path.. e.g., the Vault places behind the reverse proxy, it is possible to customize the URL Path base. ( <vault_path_prefix>/v1/api/...) | `${VAULT_PATH_PREFIX}` | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern about vault_addr
was that it required a URL but all that it seemed practical for people to change would be the host and port, since the scheme should be https
and I didn't see a reason to specify a path.
If deploying behind behind a reverse proxy is a normal thing for people to do, where there might be a need to have a path prefix, and considering that the vault client APIs document the configurable as being a URL then maybe the original configurable was good? 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I'll revert the commits.
In original case, Should we check the URI scheme ?
e.g., If it's not https, outputs some warn logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert: 73782ba
Logger hclog.Logger | ||
} | ||
|
||
func NewRenew(logger hclog.Logger) *Renew { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with the original code here, i.e., passing everything needed to construct the renewer in this function, I just thought maybe the logger could also be passed as a parameter instead of using a default logger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the intent of the your comment😀
I think there is no problem with passing all the argument including logger, so I'll fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix: 8e74413
2fc7301
to
73782ba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great. Thanks for this contribution, @hiyosi !
@hiyosi, would you mind squashing and rebasing this? I'm happy to merge afterwards. Thanks! |
d19f9bf
to
a0da240
Compare
I apologize, I didn't see you had updated this and merged a different PR. Can you rebase again? |
Signed-off-by: Tomoya Usami <tousami@zlab.co.jp>
7ab4b27
to
cbb5c01
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @hiyosi, thank you for this contribution. This is really great!
I have a couple of suggestions that I missed when I first looked at this. I'm commenting here but I'm happy to move this to an issue if you feel that is appropriate. Thanks again!
// Name of the mount point where PKI secret engine is mounted. (e.g., /<mount_point>/ca/pem) | ||
PKIMountPoint string `hcl:"pki_mount_point"` | ||
// Configuration for the Token authentication method | ||
TokenAuth TokenAuthConfig `hcl:"token_auth"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if having pointers to the config structs would be better. Looks like the configuration settings in all authentication methods have either default values or can be alternatively defined through environment variables, which makes empty auth configs valid. I.e.: I want to use TOKEN authentication method and I have defined the VAULT_TOKEN
environment variable. In that case I could just have token_auth { }
as a valid configuration, but currently there is no way to identify that I set an empty token_auth
config so the configuration would be rejected even if I have set the environment variable. If we use a pointer we may check if config.TokenAuth != nil
in parseAuthMethod
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@amartinezfayo
Thank you for your suggestion.
I understand the problem. I'll fix it.
return APPROLE, nil | ||
} | ||
|
||
return 0, errors.New("must be configured one of these authentication method 'Token or Cert or AppRole'") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may validate that only one of the auth methods is set?
| token_auth | struct | | Configuration for the Token authentication method | | | ||
| approle_auth | struct | | Configuration for the AppRole authentication method | | | ||
|
||
The plugin supports **Client Certificate**, **Token** and **AppRole** authentication methods. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we document any permissions required depending on the authentication method? E.g.: if a token is used, does the token need to be attached to a policy with certain capabilities?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you.
I'll try to improve the document.
Pull Request check list
Affected functionality
UpstreamAuthority plugin
Description of change
Add a new UpstreamAuthority
vault
plugin.This plugin treats the Vault PKI Secret Engine as an Upstream PKI and issues intermediate CA certificates for the SPIRE Server.
Which issue this PR fixes