mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Use Conjur API's built in JWT functions (#3771)
* Use Conjur API's built in JWT functions Signed-off-by: Shlomo Heigh <shlomo.heigh@cyberark.com> * docs: clarify that all Conjur types are supported Signed-off-by: Shlomo Heigh <shlomo.heigh@cyberark.com> * docs: add link to Conjur blog post Signed-off-by: Shlomo Heigh <shlomo.heigh@cyberark.com> --------- Signed-off-by: Shlomo Heigh <shlomo.heigh@cyberark.com>
This commit is contained in:
parent
1707de3d5a
commit
a1722cbfaa
6 changed files with 70 additions and 129 deletions
|
@ -2,6 +2,11 @@
|
|||
|
||||
A list of blogs written by people all over the community. Feel free to let us know if you are writing about ESO at some place! We would be happy to mention you here!
|
||||
|
||||
## [Enhancing Kubernetes Security and Flexibility with the CyberArk Conjur and ESO Integration](https://developer.cyberark.com/blog/enhancing-kubernetes-security-and-flexibility-with-the-cyberark-conjur-and-eso-integration/)
|
||||
|
||||
[@szh](https://github.com/szh) Writes about using ESO with CyberArk Conjur. He includes detailed steps on how to
|
||||
set up a local environment with Docker Desktop and how to deploy ESO and Conjur OSS on it.
|
||||
|
||||
## [Comparing External Secrets Operator with Secret Storage CSI as Kubernetes External Secrets is Deprecated](https://mixi-developers.mixi.co.jp/compare-eso-with-secret-csi-402bf37f20bc)
|
||||
|
||||
@riddle writes about choosing ESO when comparing with Secret Store CSI Driver in their specific use case. They show us the relevant differences between the projects when looking at their scenario and requirements while integrating with ArgoCD. [Comparing External Secrets Operator with Secret Storage CSI as Kubernetes External Secrets is Deprecated](https://mixi-developers.mixi.co.jp/compare-eso-with-secret-csi-402bf37f20bc)
|
||||
|
|
|
@ -6,7 +6,9 @@ This section describes how to set up the Conjur provider for External Secrets Op
|
|||
|
||||
Before installing the Conjur provider, you need:
|
||||
|
||||
* A running Conjur Server, with:
|
||||
* A running Conjur Server ([OSS](https://github.com/cyberark/conjur),
|
||||
[Enterprise](https://www.cyberark.com/products/secrets-manager-enterprise/), or
|
||||
[Cloud](https://www.cyberark.com/products/multi-cloud-secrets/)), with:
|
||||
* An accessible Conjur endpoint (for example: `https://myapi.example.com`).
|
||||
* Your configured Conjur authentication info (such as `hostid`, `apikey`, or JWT service ID). For more information on configuring Conjur, see [Policy statement reference](https://docs.cyberark.com/conjur-open-source/Latest/en/Content/Operations/Policy/policy-statement-ref.htm).
|
||||
* Support for your authentication method (`apikey` is supported by default, `jwt` requires additional configuration).
|
||||
|
|
|
@ -16,14 +16,9 @@ package conjur
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cyberark/conjur-api-go/conjurapi"
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
|
@ -88,31 +83,3 @@ func (c *Client) getJwtFromServiceAccountTokenRequest(ctx context.Context, servi
|
|||
}
|
||||
return tokenResponse.Status.Token, nil
|
||||
}
|
||||
|
||||
// newClientFromJwt creates a new Conjur client using the given JWT Auth Config.
|
||||
func (c *Client) newClientFromJwt(ctx context.Context, config conjurapi.Config, jwtAuth *esv1beta1.ConjurJWT) (SecretsClient, error) {
|
||||
jwtToken, getJWTError := c.getJWTToken(ctx, jwtAuth)
|
||||
if getJWTError != nil {
|
||||
return nil, getJWTError
|
||||
}
|
||||
|
||||
client, clientError := c.clientAPI.NewClientFromJWT(config, jwtToken, jwtAuth.ServiceID, jwtAuth.HostID)
|
||||
if clientError != nil {
|
||||
return nil, clientError
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// newHTTPSClient creates a new HTTPS client with the given cert.
|
||||
func newHTTPSClient(cert []byte) (*http.Client, error) {
|
||||
pool := x509.NewCertPool()
|
||||
ok := pool.AppendCertsFromPEM(cert)
|
||||
if !ok {
|
||||
return nil, errors.New("can't append Conjur SSL cert")
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12},
|
||||
}
|
||||
return &http.Client{Transport: tr, Timeout: time.Second * 10}, nil
|
||||
}
|
||||
|
|
|
@ -78,48 +78,9 @@ func (c *Client) GetConjurClient(ctx context.Context) (SecretsClient, error) {
|
|||
}
|
||||
|
||||
if prov.Auth.APIKey != nil {
|
||||
config.Account = prov.Auth.APIKey.Account
|
||||
conjUser, secErr := resolvers.SecretKeyRef(
|
||||
ctx,
|
||||
c.kube,
|
||||
c.StoreKind,
|
||||
c.namespace, prov.Auth.APIKey.UserRef)
|
||||
if secErr != nil {
|
||||
return nil, fmt.Errorf(errBadServiceUser, secErr)
|
||||
}
|
||||
conjAPIKey, secErr := resolvers.SecretKeyRef(
|
||||
ctx,
|
||||
c.kube,
|
||||
c.StoreKind,
|
||||
c.namespace,
|
||||
prov.Auth.APIKey.APIKeyRef)
|
||||
if secErr != nil {
|
||||
return nil, fmt.Errorf(errBadServiceAPIKey, secErr)
|
||||
}
|
||||
|
||||
conjur, newClientFromKeyError := c.clientAPI.NewClientFromKey(config,
|
||||
authn.LoginPair{
|
||||
Login: conjUser,
|
||||
APIKey: conjAPIKey,
|
||||
},
|
||||
)
|
||||
|
||||
if newClientFromKeyError != nil {
|
||||
return nil, fmt.Errorf(errConjurClient, newClientFromKeyError)
|
||||
}
|
||||
c.client = conjur
|
||||
return conjur, nil
|
||||
return c.conjurClientFromAPIKey(ctx, config, prov)
|
||||
} else if prov.Auth.Jwt != nil {
|
||||
config.Account = prov.Auth.Jwt.Account
|
||||
|
||||
conjur, clientFromJwtError := c.newClientFromJwt(ctx, config, prov.Auth.Jwt)
|
||||
if clientFromJwtError != nil {
|
||||
return nil, fmt.Errorf(errConjurClient, clientFromJwtError)
|
||||
}
|
||||
|
||||
c.client = conjur
|
||||
|
||||
return conjur, nil
|
||||
return c.conjurClientFromJWT(ctx, config, prov)
|
||||
} else {
|
||||
// Should not happen because validate func should catch this
|
||||
return nil, errors.New("no authentication method provided")
|
||||
|
@ -150,3 +111,59 @@ func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
|
|||
func (c *Client) Close(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) conjurClientFromAPIKey(ctx context.Context, config conjurapi.Config, prov *esv1beta1.ConjurProvider) (SecretsClient, error) {
|
||||
config.Account = prov.Auth.APIKey.Account
|
||||
conjUser, secErr := resolvers.SecretKeyRef(
|
||||
ctx,
|
||||
c.kube,
|
||||
c.StoreKind,
|
||||
c.namespace, prov.Auth.APIKey.UserRef)
|
||||
if secErr != nil {
|
||||
return nil, fmt.Errorf(errBadServiceUser, secErr)
|
||||
}
|
||||
conjAPIKey, secErr := resolvers.SecretKeyRef(
|
||||
ctx,
|
||||
c.kube,
|
||||
c.StoreKind,
|
||||
c.namespace,
|
||||
prov.Auth.APIKey.APIKeyRef)
|
||||
if secErr != nil {
|
||||
return nil, fmt.Errorf(errBadServiceAPIKey, secErr)
|
||||
}
|
||||
|
||||
conjur, newClientFromKeyError := c.clientAPI.NewClientFromKey(config,
|
||||
authn.LoginPair{
|
||||
Login: conjUser,
|
||||
APIKey: conjAPIKey,
|
||||
},
|
||||
)
|
||||
|
||||
if newClientFromKeyError != nil {
|
||||
return nil, fmt.Errorf(errConjurClient, newClientFromKeyError)
|
||||
}
|
||||
c.client = conjur
|
||||
return conjur, nil
|
||||
}
|
||||
|
||||
func (c *Client) conjurClientFromJWT(ctx context.Context, config conjurapi.Config, prov *esv1beta1.ConjurProvider) (SecretsClient, error) {
|
||||
config.AuthnType = "jwt"
|
||||
config.Account = prov.Auth.Jwt.Account
|
||||
config.JWTHostID = prov.Auth.Jwt.HostID
|
||||
config.ServiceID = prov.Auth.Jwt.ServiceID
|
||||
|
||||
jwtToken, getJWTError := c.getJWTToken(ctx, prov.Auth.Jwt)
|
||||
if getJWTError != nil {
|
||||
return nil, getJWTError
|
||||
}
|
||||
|
||||
config.JWTContent = jwtToken
|
||||
|
||||
conjur, clientError := c.clientAPI.NewClientFromJWT(config)
|
||||
if clientError != nil {
|
||||
return nil, fmt.Errorf(errConjurClient, clientError)
|
||||
}
|
||||
|
||||
c.client = conjur
|
||||
return conjur, nil
|
||||
}
|
||||
|
|
|
@ -15,15 +15,8 @@ limitations under the License.
|
|||
package conjur
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cyberark/conjur-api-go/conjurapi"
|
||||
"github.com/cyberark/conjur-api-go/conjurapi/authn"
|
||||
"github.com/cyberark/conjur-api-go/conjurapi/response"
|
||||
)
|
||||
|
||||
// SecretsClient is an interface for the Conjur client.
|
||||
|
@ -36,7 +29,7 @@ type SecretsClient interface {
|
|||
// SecretsClientFactory is an interface for creating a Conjur client.
|
||||
type SecretsClientFactory interface {
|
||||
NewClientFromKey(config conjurapi.Config, loginPair authn.LoginPair) (SecretsClient, error)
|
||||
NewClientFromJWT(config conjurapi.Config, jwtToken string, jwtServiceID, jwtHostID string) (SecretsClient, error)
|
||||
NewClientFromJWT(config conjurapi.Config) (SecretsClient, error)
|
||||
}
|
||||
|
||||
// ClientAPIImpl is an implementation of the ClientAPI interface.
|
||||
|
@ -47,49 +40,6 @@ func (c *ClientAPIImpl) NewClientFromKey(config conjurapi.Config, loginPair auth
|
|||
}
|
||||
|
||||
// NewClientFromJWT creates a new Conjur client from a JWT token.
|
||||
// cannot use the built-in function "conjurapi.NewClientFromJwt" because it requires environment variables
|
||||
// see: https://github.com/cyberark/conjur-api-go/blob/b698692392a38e5d38b8440f32ab74206544848a/conjurapi/client.go#L130
|
||||
func (c *ClientAPIImpl) NewClientFromJWT(config conjurapi.Config, jwtToken, jwtServiceID, jwtHostID string) (SecretsClient, error) {
|
||||
jwtTokenString := fmt.Sprintf("jwt=%s", jwtToken)
|
||||
|
||||
var httpClient *http.Client
|
||||
if config.IsHttps() {
|
||||
cert, err := config.ReadSSLCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpClient, err = newHTTPSClient(cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
httpClient = &http.Client{Timeout: time.Second * 10}
|
||||
}
|
||||
|
||||
var authnJwtURL string
|
||||
// If a hostID is provided, it must be included in the URL
|
||||
if jwtHostID != "" {
|
||||
authnJwtURL = strings.Join([]string{config.ApplianceURL, "authn-jwt", jwtServiceID, config.Account, url.PathEscape(jwtHostID), "authenticate"}, "/")
|
||||
} else {
|
||||
authnJwtURL = strings.Join([]string{config.ApplianceURL, "authn-jwt", jwtServiceID, config.Account, "authenticate"}, "/")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", authnJwtURL, strings.NewReader(jwtTokenString))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
tokenBytes, err := response.DataResponse(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conjurapi.NewClientFromToken(config, string(tokenBytes))
|
||||
func (c *ClientAPIImpl) NewClientFromJWT(config conjurapi.Config) (SecretsClient, error) {
|
||||
return conjurapi.NewClientFromJwt(config)
|
||||
}
|
||||
|
|
|
@ -709,7 +709,7 @@ func (c *ConjurMockAPIClient) NewClientFromKey(_ conjurapi.Config, _ authn.Login
|
|||
return &fake.ConjurMockClient{}, nil
|
||||
}
|
||||
|
||||
func (c *ConjurMockAPIClient) NewClientFromJWT(_ conjurapi.Config, _, _, _ string) (SecretsClient, error) {
|
||||
func (c *ConjurMockAPIClient) NewClientFromJWT(_ conjurapi.Config) (SecretsClient, error) {
|
||||
return &fake.ConjurMockClient{}, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue