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

Add UpstreamAuthority 'vault' plugin as built-in #1611

Merged
merged 1 commit into from
Jun 10, 2020

Conversation

hiyosi
Copy link
Contributor

@hiyosi hiyosi commented Jun 3, 2020

Pull Request check list

  • Commit conforms to CONTRIBUTING.md?
  • Proper tests/regressions included?
  • Documentation updated?

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

@hiyosi hiyosi force-pushed the builtin-vault-upstream-authority branch 3 times, most recently from 130582b to e1759e3 Compare June 3, 2020 05:53
Copy link
Member

@azdagron azdagron left a 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.

Comment on lines 3 to 5
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.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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}` |
Copy link
Member

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:

Suggested change
| 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}` |

Copy link
Contributor Author

@hiyosi hiyosi Jun 8, 2020

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.

Copy link
Contributor Author

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 |
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
| 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}` |
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
| 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 |
Copy link
Member

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_:

Suggested change
| 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 |

Comment on lines 184 to 188
csr, err := ioutil.ReadFile(testReqCSR)
vps.Require().NoError(err)

req, err := getTestMintX509CARequest(csr)
vps.Require().NoError(err)
Copy link
Member

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:

Suggested change
csr, err := ioutil.ReadFile(testReqCSR)
vps.Require().NoError(err)
req, err := getTestMintX509CARequest(csr)
vps.Require().NoError(err)
req := vps.loadMintX509CARequestFromTestFile(testReqCSR)

Copy link
Contributor Author

@hiyosi hiyosi Jun 8, 2020

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

Comment on lines 91 to 98
s, addr, err := vps.fakeVaultServer.NewTLSServer()
vps.Require().NoError(err)

s.Start()
defer s.Close()

p := New()
p.SetLogger(hclog.Default())
Copy link
Member

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?

Copy link
Contributor Author

@hiyosi hiyosi Jun 8, 2020

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

Comment on lines 194 to 195
for i := range res.X509CaChain {
cert, err := x509.ParseCertificate(res.X509CaChain[i])
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
for i := range res.X509CaChain {
cert, err := x509.ParseCertificate(res.X509CaChain[i])
for _, certDER := range res.X509CaChain {
cert, err := x509.ParseCertificate(certDER)

Comment on lines 200 to 201
for i := range res.UpstreamX509Roots {
upstream, err := x509.ParseCertificate(res.UpstreamX509Roots[i])
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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 {
Copy link
Member

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.

Copy link
Contributor Author

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.

@hiyosi hiyosi force-pushed the builtin-vault-upstream-authority branch 3 times, most recently from f9e029e to 4394dd6 Compare June 8, 2020 08:02
@hiyosi
Copy link
Contributor Author

hiyosi commented Jun 8, 2020

@azdagron
Awesome! Thank you for your review!!
I fixed and commit you pointed out. Please check it again.
(I committed with fixup! , so they should be rebased before merging.)

Copy link
Member

@azdagron azdagron left a 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}` |
Copy link
Member

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? 😬

Copy link
Contributor Author

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.

Copy link
Contributor Author

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 {
Copy link
Member

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.

Copy link
Contributor Author

@hiyosi hiyosi Jun 8, 2020

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fix: 8e74413

@hiyosi hiyosi force-pushed the builtin-vault-upstream-authority branch from 2fc7301 to 73782ba Compare June 9, 2020 00:44
azdagron
azdagron previously approved these changes Jun 9, 2020
Copy link
Member

@azdagron azdagron left a 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 !

@azdagron
Copy link
Member

azdagron commented Jun 9, 2020

@hiyosi, would you mind squashing and rebasing this? I'm happy to merge afterwards. Thanks!

@hiyosi hiyosi force-pushed the builtin-vault-upstream-authority branch 2 times, most recently from d19f9bf to a0da240 Compare June 9, 2020 22:47
@azdagron
Copy link
Member

azdagron commented Jun 9, 2020

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>
@hiyosi hiyosi force-pushed the builtin-vault-upstream-authority branch from 7ab4b27 to cbb5c01 Compare June 9, 2020 23:32
@azdagron azdagron merged commit bfb201f into spiffe:master Jun 10, 2020
@hiyosi hiyosi deleted the builtin-vault-upstream-authority branch June 10, 2020 02:47
Copy link
Member

@amartinezfayo amartinezfayo left a 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"`
Copy link
Member

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.

Copy link
Contributor Author

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'")
Copy link
Member

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.
Copy link
Member

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?

Copy link
Contributor Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants