Skip to content

Commit

Permalink
Adding ConfigMaps upon enabling mTLS and Endpoint Security
Browse files Browse the repository at this point in the history
  • Loading branch information
O-sura committed Feb 22, 2024
1 parent 94a941e commit 67cc331
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 42 deletions.
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.LoggerSync.Infof("API file found: " + file.Name)
// Todo: Read the apis.zip and extract the api.zip,deployments.json
}

if err != nil {
logger.LoggerSync.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.LoggerSync.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.LoggerSync.Errorf("Error while generating APK-Conf: %v", apkErr)
return nil, err
}
logger.LoggerSync.Debug("APK Conf:", apkConf)
certContainer := transformer.CertContainer{
ClientCertObj: artifact.CertMeta,
EndpointCertObj: artifact.EndpointCertMeta,
}
k8ResourceEndpoint := conf.DataPlane.K8ResourceEndpoint
crResponse, err := transformer.GenerateCRs(apkConf, artifact.Swagger, k8ResourceEndpoint)
crResponse, err := transformer.GenerateCRs(apkConf, artifact.Swagger, certContainer, k8ResourceEndpoint)
if err != nil {
logger.LoggerSync.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"`
Swagger string `json:"swagger"`
DeploymentDescriptor string `json:"deploymentDescriptor"`
ClientCerts string `json:"clientCert"`
RevisionID uint32 `json:"revisionId"`
APIJson string `json:"apiJson"`
APIFileName string `json:"apiFileName"`
EnvConfig string `json:"envConfig"`
Swagger string `json:"swagger"`
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"`
}
102 changes: 92 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), 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 @@ -433,6 +477,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 @@ -552,3 +606,31 @@ 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
cm.ObjectMeta.Namespace = "apk-integration-test"

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.Debugf("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 @@ -93,8 +93,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 @@ -127,7 +162,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

0 comments on commit 67cc331

Please sign in to comment.