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

Adding ConfigMaps upon enabling mTLS #1081

Merged
merged 1 commit into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions apim-apk-agent/internal/utils/apis_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ func FetchAPIsOnEvent(conf *config.Config, apiUUID *string, k8sClient client.Cli
logger.LoggerUtils.Debugf("API file found: " + file.Name)
// Todo: Read the apis.zip and extract the api.zip,deployments.json
}

if err != nil {
logger.LoggerUtils.Errorf("Error while reading zip: %v", err)
return nil, err
Expand Down Expand Up @@ -108,13 +107,18 @@ func FetchAPIsOnEvent(conf *config.Config, apiUUID *string, k8sClient client.Cli
logger.LoggerUtils.Errorf("Error while decoding the API Project Artifact: %v", decodingError)
return nil, err
}
apkConf, apiUUID, revisionID, apkErr := transformer.GenerateAPKConf(artifact.APIJson, artifact.ClientCerts)
apkConf, apiUUID, revisionID, apkErr := transformer.GenerateAPKConf(artifact.APIJson, artifact.CertArtifact)
if apkErr != nil {
logger.LoggerUtils.Errorf("Error while generating APK-Conf: %v", apkErr)
return nil, err
}
logger.LoggerUtils.Debug("APK Conf:", apkConf)
certContainer := transformer.CertContainer{
ClientCertObj: artifact.CertMeta,
EndpointCertObj: artifact.EndpointCertMeta,
}
k8ResourceEndpoint := conf.DataPlane.K8ResourceEndpoint
crResponse, err := transformer.GenerateCRs(apkConf, artifact.Schema, k8ResourceEndpoint)
crResponse, err := transformer.GenerateCRs(apkConf, artifact.Schema, certContainer, k8ResourceEndpoint)
if err != nil {
logger.LoggerUtils.Errorf("Error occured in receiving the updated CRDs: %v", err)
return nil, err
Expand Down
41 changes: 34 additions & 7 deletions apim-apk-agent/pkg/transformer/api_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,38 @@ type APIYaml struct {
// APIArtifact represents the artifact details of an API, including api details, environment configuration,
// Swagger definition, deployment descriptor, and revision ID extracted from the API Project Zip.
type APIArtifact struct {
APIJson string `json:"apiJson"`
APIFileName string `json:"apiFileName"`
EnvConfig string `json:"envConfig"`
Schema string `json:"schema"`
DeploymentDescriptor string `json:"deploymentDescriptor"`
ClientCerts string `json:"clientCert"`
RevisionID uint32 `json:"revisionId"`
APIJson string `json:"apiJson"`
APIFileName string `json:"apiFileName"`
EnvConfig string `json:"envConfig"`
Schema string `json:"schema"`
DeploymentDescriptor string `json:"deploymentDescriptor"`
CertArtifact CertificateArtifact `json:"certArtifact"`
RevisionID uint32 `json:"revisionId"`
CertMeta CertMetadata `json:"certMeta"`
EndpointCertMeta EndpointCertMetadata `json:"endpintCertMeta"`
}

// CertificateArtifact stores the parsed file content created inside the API project zip upon enabling certificate aided security options
type CertificateArtifact struct {
ClientCerts string `json:"clientCert"`
EndpointCerts string `json:"endpointCert"`
}

// CertMetadata marks the availability of the cert files provided by the client and their contents
type CertMetadata struct {
CertAvailable bool `json:"certAvailable"`
ClientCertFiles map[string]string `json:"clientCertFiles"`
}

// EndpointCertMetadata marks the availability of the endpoint certificates and stores the cert contents
type EndpointCertMetadata struct {
CertAvailable bool `json:"certAvailable"`
EndpointCertFiles map[string]string `json:"endpointCertFiles"`
}

// CertContainer acts as a wrapper to hold onto all the certificate details for both endpoint and client-side security configs
// belong to a particular API Project
type CertContainer struct {
ClientCertObj CertMetadata
EndpointCertObj EndpointCertMetadata
}
52 changes: 32 additions & 20 deletions apim-apk-agent/pkg/transformer/apk_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@

package transformer

// EndpointCertificate struct stores the the alias and the name for a particular endpoint security configuration
type EndpointCertificate struct {
Name string `yaml:"secretName"`
Key string `yaml:"secretKey"`
}

// EndpointConfiguration stores the data related to endpoints and their related
type EndpointConfiguration struct {
Endpoint string `yaml:"endpoint,omitempty"`
EndCertificate EndpointCertificate `yaml:"certificate,omitempty"`
}

// AdditionalProperty stores the custom properties set by the user for a particular API
type AdditionalProperty struct {
Name string `yaml:"name"`
Expand All @@ -43,14 +55,14 @@ type AuthConfiguration struct {
}

// Endpoint represents an API endpoint.
type Endpoint struct {
Endpoint string `yaml:"endpoint,omitempty"`
}
// type Endpoint struct {
// Endpoint string `yaml:"endpoint,omitempty"`
// }

// EndpointConfiguration holds production and sandbox endpoints.
type EndpointConfiguration struct {
Production *Endpoint `yaml:"production,omitempty"`
Sandbox *Endpoint `yaml:"sandbox,omitempty"`
// EndpointConfigurations holds production and sandbox endpoints.
type EndpointConfigurations struct {
Production *EndpointConfiguration `yaml:"production,omitempty"`
Sandbox *EndpointConfiguration `yaml:"sandbox,omitempty"`
}

// OperationPolicies organizes request and response policies for an API operation.
Expand Down Expand Up @@ -81,17 +93,17 @@ type VHost struct {

// API represents an main API type definition
type API struct {
Name string `yaml:"name,omitempty"`
ID string `yaml:"id,omitempty"`
Version string `yaml:"version,omitempty"`
Context string `yaml:"basePath,omitempty"`
Type string `yaml:"type,omitempty"`
DefaultVersion bool `yaml:"defaultVersion"`
DefinitionPath string `yaml:"definitionPath,omitempty"`
EndpointConfigurations *EndpointConfiguration `yaml:"endpointConfigurations,omitempty"`
Operations *[]Operation `yaml:"operations,omitempty"`
Authentication *[]AuthConfiguration `yaml:"authentication,omitempty"`
CorsConfig *CORSConfiguration `yaml:"corsConfiguration,omitempty"`
AdditionalProperties *[]AdditionalProperty `yaml:"additionalProperties,omitempty"`
SubscriptionValidation bool `yaml:"subscriptionValidation,omitempty"`
Name string `yaml:"name,omitempty"`
ID string `yaml:"id,omitempty"`
Version string `yaml:"version,omitempty"`
Context string `yaml:"basePath,omitempty"`
Type string `yaml:"type,omitempty"`
DefaultVersion bool `yaml:"defaultVersion"`
DefinitionPath string `yaml:"definitionPath,omitempty"`
EndpointConfigurations *EndpointConfigurations `yaml:"endpointConfigurations,omitempty"`
Operations *[]Operation `yaml:"operations,omitempty"`
Authentication *[]AuthConfiguration `yaml:"authentication,omitempty"`
CorsConfig *CORSConfiguration `yaml:"corsConfiguration,omitempty"`
AdditionalProperties *[]AdditionalProperty `yaml:"additionalProperties,omitempty"`
SubscriptionValidation bool `yaml:"subscriptionValidation,omitempty"`
}
31 changes: 31 additions & 0 deletions apim-apk-agent/pkg/transformer/endpoint_cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package transformer

// EndpointCert holds the data belongs to a single endpoint certificate configuration
type EndpointCert struct {
Alias string `json:"alias"`
Endpoint string `json:"endpoint"`
Certificate string `json:"certificate"`
TenantID int `json:"tenantId"`
}

// EndpointCertDescriptor contains data related to one or more endpoint certificates for an API
type EndpointCertDescriptor struct {
EndpointCertData []EndpointCert `json:"data"`
}
101 changes: 91 additions & 10 deletions apim-apk-agent/pkg/transformer/transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"

"io"
"mime/multipart"
Expand All @@ -49,7 +50,7 @@ import (
)

// GenerateAPKConf will Generate the mapped .apk-conf file for a given API Project zip
func GenerateAPKConf(APIJson string, clientCerts string) (string, string, uint32, error) {
func GenerateAPKConf(APIJson string, certArtifact CertificateArtifact) (string, string, uint32, error) {

apk := &API{}

Expand Down Expand Up @@ -94,19 +95,31 @@ func GenerateAPKConf(APIJson string, clientCerts string) (string, string, uint32

apk.Operations = &apkOperations

apk.EndpointConfigurations = &EndpointConfiguration{
// For private PPDPs, we need to treat the token type to be SANDBOX as it is tested by developers.
Sandbox: &Endpoint{
Endpoint: apiYamlData.EndpointConfig.SandboxEndpoints.URL},
Production: &Endpoint{
Endpoint: apiYamlData.EndpointConfig.ProductionEndpoints.URL},
//Adding Endpoint-certificate configurations to the conf
var endpointCertList EndpointCertDescriptor
endCertAvailable := false

if certArtifact.EndpointCerts != "" {
certErr := json.Unmarshal([]byte(certArtifact.EndpointCerts), &endpointCertList)
if certErr != nil {
logger.LoggerTransformer.Errorf("Error while unmarshalling endpoint_cert.json content: ", apiYamlError)
return "", "null", 0, certErr
}
endCertAvailable = true
}

sandboxURL := apiYamlData.EndpointConfig.SandboxEndpoints.URL
prodURL := apiYamlData.EndpointConfig.ProductionEndpoints.URL
endpointRes := getEndpointConfigs(sandboxURL, prodURL, endCertAvailable, endpointCertList)

apk.EndpointConfigurations = &endpointRes

//Adding client-certificate configurations to the conf
var certList CertDescriptor
certAvailable := false

if clientCerts != "" {
certErr := json.Unmarshal([]byte(clientCerts), &certList)
if certArtifact.ClientCerts != "" {
certErr := json.Unmarshal([]byte(certArtifact.ClientCerts), &certList)
if certErr != nil {
logger.LoggerTransformer.Errorf("Error while unmarshalling client_cert.json content: ", apiYamlError)
return "", "null", 0, certErr
Expand Down Expand Up @@ -235,9 +248,40 @@ func mapAuthConfigs(authHeader string, secSchemes []string, certAvailable bool,
return authConfigs
}

// getEndpointConfigs will map the endpoints and there security configurations and returns them
// TODO: Currently the APK-Conf does not support giving multiple certs for a particular endpoint.
// After fixing this, the following logic should be changed to map multiple cert configs
func getEndpointConfigs(sandboxURL string, prodURL string, endCertAvailable bool, endpointCertList EndpointCertDescriptor) EndpointConfigurations {
var sandboxEndpointConf, prodEndpointConf EndpointConfiguration
sandboxEndpointConf.Endpoint = sandboxURL
prodEndpointConf.Endpoint = prodURL
if endCertAvailable {
for _, endCert := range endpointCertList.EndpointCertData {
if endCert.Endpoint == sandboxURL {
sandboxEndpointConf.EndCertificate = EndpointCertificate{
Name: endCert.Alias,
Key: endCert.Certificate,
}
}
if endCert.Endpoint == prodURL {
prodEndpointConf.EndCertificate = EndpointCertificate{
Name: endCert.Alias,
Key: endCert.Certificate,
}
}
}
}

epconfigs := EndpointConfigurations{
Sandbox: &sandboxEndpointConf,
Production: &prodEndpointConf,
}
return epconfigs
}

// GenerateCRs takes the .apk-conf, api definition, vHost and the organization for a particular API and then generate and returns
// the relavant CRD set as a zip
func GenerateCRs(apkConf string, apiDefinition string, k8ResourceGenEndpoint string) (*K8sArtifacts, error) {
func GenerateCRs(apkConf string, apiDefinition string, certContainer CertContainer, k8ResourceGenEndpoint string) (*K8sArtifacts, error) {
k8sArtifact := K8sArtifacts{HTTPRoutes: make(map[string]*gwapiv1b1.HTTPRoute), GQLRoutes: make(map[string]*dpv1alpha2.GQLRoute), Backends: make(map[string]*dpv1alpha1.Backend), Scopes: make(map[string]*dpv1alpha1.Scope), Authentication: make(map[string]*dpv1alpha2.Authentication), APIPolicies: make(map[string]*dpv1alpha2.APIPolicy), InterceptorServices: make(map[string]*dpv1alpha1.InterceptorService), ConfigMaps: make(map[string]*corev1.ConfigMap), Secrets: make(map[string]*corev1.Secret), RateLimitPolicies: make(map[string]*dpv1alpha1.RateLimitPolicy)}
if apkConf == "" {
logger.LoggerTransformer.Error("Empty apk-conf parameter provided. Unable to generate CRDs.")
Expand Down Expand Up @@ -441,6 +485,16 @@ func GenerateCRs(apkConf string, apiDefinition string, k8ResourceGenEndpoint str
logger.LoggerSync.Errorf("[!]Unknown Kind parsed from the YAML File: %v", kind)
}
}
// Create ConfigMap to store the cert data if mTLS has enabled
if certContainer.ClientCertObj.CertAvailable {
createConfigMaps(certContainer.ClientCertObj.ClientCertFiles, &k8sArtifact)
}

// Create ConfigMap to store the cert data if endpoint security has enabled
if certContainer.EndpointCertObj.CertAvailable {
createConfigMaps(certContainer.EndpointCertObj.EndpointCertFiles, &k8sArtifact)
}

return &k8sArtifact, nil
}

Expand Down Expand Up @@ -563,3 +617,30 @@ func generateSHA1Hash(input string) string {
h.Write([]byte(input))
return hex.EncodeToString(h.Sum(nil))
}

// createConfigMaps returns a marshalled yaml of ConfigMap kind after adding the given values
func createConfigMaps(certFiles map[string]string, k8sArtifact *K8sArtifacts) {
for confKey, confValue := range certFiles {
pathSegments := strings.Split(confKey, ".")
configName := pathSegments[0]

//TODO: Have to take the version, namespace as parameters instead of hardcoding
cm := corev1.ConfigMap{}
cm.APIVersion = "v1"
cm.Kind = "ConfigMap"
cm.ObjectMeta.Name = configName

if cm.ObjectMeta.Labels == nil {
cm.ObjectMeta.Labels = make(map[string]string)
}

if cm.Data == nil {
cm.Data = make(map[string]string)
}
cm.Data[confKey] = confValue
certConfigMap := &cm

logger.LoggerTransformer.Debug("New ConfigMap Data: %v", *certConfigMap)
k8sArtifact.ConfigMaps[certConfigMap.ObjectMeta.Name] = certConfigMap
}
}
39 changes: 37 additions & 2 deletions apim-apk-agent/pkg/transformer/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,43 @@ func readZipFile(file *zip.File) (*APIArtifact, error) {
if err != nil {
return nil, err
}
apiArtifact.ClientCerts = string(certificateJSON)
apiArtifact.CertArtifact.ClientCerts = string(certificateJSON)
apiArtifact.CertMeta.CertAvailable = true
}

if strings.Contains(file.Name, "endpoint_certificates.json") {
endpointCertificateJSON, err := ReadContent(file)
if err != nil {
return nil, err
}
apiArtifact.CertArtifact.EndpointCerts = string(endpointCertificateJSON)
apiArtifact.EndpointCertMeta.CertAvailable = true
}

if strings.Contains(file.Name, ".crt") {
certificateData, err := ReadContent(file)
if err != nil {
return nil, err
}
//NOTE:There is an issue in reading the certificate content. Even the same logic is been used, the
// .crt files in the Client-certificates gets parsed in base64 encoded version while the cert files in
// Endpoint-certtificate folder gets parsed as original value
pathSegments := strings.Split(file.Name, "/")
if strings.Contains(file.Name, "Client-certificates") {
if apiArtifact.CertMeta.ClientCertFiles == nil {
apiArtifact.CertMeta.ClientCertFiles = make(map[string]string)
}
apiArtifact.CertMeta.ClientCertFiles[pathSegments[len(pathSegments)-1]] = string(certificateData)
}
if strings.Contains(file.Name, "Endpoint-certificates") {
if apiArtifact.EndpointCertMeta.EndpointCertFiles == nil {
apiArtifact.EndpointCertMeta.EndpointCertFiles = make(map[string]string)
}
apiArtifact.EndpointCertMeta.EndpointCertFiles[pathSegments[len(pathSegments)-1]] = string(certificateData)
}

}

}
return apiArtifact, nil
}
Expand Down Expand Up @@ -138,7 +173,7 @@ func readAPIZipFile(file *zip.File, apiArtifact *APIArtifact) error {
}

if strings.Contains(file.Name, "client_certificates.json") {
apiArtifact.ClientCerts = string(content)
apiArtifact.CertArtifact.ClientCerts = string(content)
}

return nil
Expand Down
Loading