Skip to content
Draft
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
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ RUN if [ "${CRYPTO_LIB}" ]; then assert-fips.sh manager; fi
#RUN scan-govulncheck.sh manager
ENTRYPOINT [ "/start.sh", "/workspace/manager" ]
# Copy the controller-manager into a thin image
FROM gcr.io/distroless/static:nonroot
# FROM gcr.io/distroless/static:nonroot
FROM alpine:3.14
WORKDIR /
COPY --from=builder /workspace/manager .
# Use uid of nonroot user (65532) because kubernetes expects numeric user when applying pod security policies
Expand Down
12 changes: 3 additions & 9 deletions config/default/credentials.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ metadata:
type: Opaque
data:
credentials: ${AWS_B64ENCODED_CREDENTIALS}
---
apiVersion: v1
kind: Secret
metadata:
name: manager-bootstrap-ca-bundle
namespace: system
type: Opaque
data:
credentials: ${AWS_B64ENCODED_CABUNDLE}
config: ${AWS_B64ENCODED_CONFIG}
ca_bundle: ${AWS_B64ENCODED_CABUNDLE}
permissions_boundary: ${AWS_B64ENCODED_PERMISSIONS_BOUNDARY} # - we should move to https://github.com/kubernetes-sigs/cluster-api-provider-aws/pull/5286
3 changes: 0 additions & 3 deletions config/default/manager_credentials_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,3 @@ spec:
- name: credentials
secret:
secretName: manager-bootstrap-credentials
- name: ca-bundle
secret:
secretName: manager-bootstrap-ca-bundle
3 changes: 1 addition & 2 deletions exp/api/v1alpha3/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ package v1alpha3

import (
"testing"

fuzz "github.com/google/gofuzz"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
"k8s.io/apimachinery/pkg/runtime"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"

"sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
)
Expand Down
6 changes: 3 additions & 3 deletions pkg/cloud/services/ec2/ami.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,9 @@ func (s *Service) defaultBastionAMILookup(region string) (string, error) {

if strings.Contains(region, defaultUsGovPartitionName) {
filter := &ec2.Filter{
Name: aws.String("owner-id"),
Values: []*string{aws.String(ubuntuOwnerIDUsGov)},
}
Name: aws.String("owner-id"),
Values: []*string{aws.String(ubuntuOwnerIDUsGov)},
}
describeImageInput.Filters = append(describeImageInput.Filters, filter)
} else {
filter := &ec2.Filter{
Expand Down
2 changes: 1 addition & 1 deletion pkg/cloud/services/ec2/bastion.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
)

const (
defaultSSHKeyName = "default"
defaultSSHKeyName = "default"
defaultUsGovPartitionName = "us-gov"
)

Expand Down
23 changes: 20 additions & 3 deletions pkg/cloud/services/eks/iam/iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ limitations under the License.
package iam

import (
"context"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"net/http"
"fmt"
"net/url"
"strings"

Expand All @@ -36,6 +37,7 @@ import (
infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
"sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/converters"
iamv1 "sigs.k8s.io/cluster-api-provider-aws/iam/api/v1beta1"
"sigs.k8s.io/cluster-api-provider-aws/pkg/utils"
)

const (
Expand Down Expand Up @@ -413,6 +415,11 @@ func findStringInSlice(slice []*string, toFind string) bool {
return false
}

// "identity": {
// "oidc": {
// "issuer": "https://oidc.eks.us-east-1.amazonaws.com/id/AFDC82E69B82D086C19F12F5CB446A71"
// }
// },
const stsAWSAudience = "sts.amazonaws.com"

// CreateOIDCProvider will create an OIDC provider.
Expand All @@ -427,7 +434,7 @@ func (s *IAMService) CreateOIDCProvider(cluster *eks.Cluster) (string, error) {

thumbprint, err := fetchRootCAThumbprint(issuerURL.String())
if err != nil {
return "", err
return "", errors.Errorf("error fetching root CA thumbprint: %v", err)
}
input := iam.CreateOpenIDConnectProviderInput{
ClientIDList: aws.StringSlice([]string{stsAWSAudience}),
Expand Down Expand Up @@ -469,7 +476,17 @@ func (s *IAMService) getOIDCProviderARN(issuer string) (string, error) {
}

func fetchRootCAThumbprint(issuerURL string) (string, error) {
response, err := http.Get(issuerURL)
httpHandler, err := utils.NewHttpHandlerWithAWSCABundle(context.TODO(), "")
if err != nil {
return "", fmt.Errorf("error creating http handler: %w", err)
}

httpClient, err := httpHandler.GetHttpClient()
if err != nil {
return "", fmt.Errorf("error creating http client: %w", err)
}

response, err := httpClient.Get(issuerURL)
if err != nil {
return "", err
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/cloud/services/secretsmanager/secret_fetch_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ log::info "aws.cluster.x-k8s.io encrypted cloud-init script $0 started"
log::info "secret prefix: ${SECRET_PREFIX}"
log::info "secret count: ${CHUNKS}"

{{if .B64CABundle}}
log::info "writing AWS CA bundle to /etc/ssl/certs/aws-ca-bundle.crt.pem"
echo "{{.B64CABundle}}" | base64 -d > /etc/ssl/certs/aws-ca-bundle.crt.pem
update-ca-certificates
export AWS_CA_BUNDLE=/etc/ssl/certs/aws-ca-bundle.crt.pem
{{end}}

if test -f "${FILE}"; then
log::info "encrypted userdata already written to disk"
log::success_exit
Expand Down
7 changes: 7 additions & 0 deletions pkg/cloud/services/ssm/secret_fetch_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ log::info "aws.cluster.x-k8s.io encrypted cloud-init script $0 started"
log::info "secret prefix: ${SECRET_PREFIX}"
log::info "secret count: ${CHUNKS}"

{{if .B64CABundle}}
log::info "writing AWS CA bundle to /etc/ssl/certs/aws-ca-bundle.crt.pem"
echo "{{.B64CABundle}}" | base64 -d > /etc/ssl/certs/aws-ca-bundle.crt.pem
update-ca-certificates
export AWS_CA_BUNDLE=/etc/ssl/certs/aws-ca-bundle.crt.pem
{{end}}

if test -f "${FILE}"; then
log::info "encrypted userdata already written to disk"
log::success_exit
Expand Down
13 changes: 13 additions & 0 deletions pkg/internal/mime/mime.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ package mime

import (
"bytes"
"context"
"encoding/base64"
"fmt"
"html/template"
"mime/multipart"
"net/textproto"
"strings"

"sigs.k8s.io/cluster-api-provider-aws/pkg/utils"
)

const (
Expand Down Expand Up @@ -50,6 +54,7 @@ type scriptVariables struct {
Chunks int32
Region string
Endpoint string
B64CABundle string
}

// GenerateInitDocument renders a given template, applies MIME properties
Expand All @@ -73,6 +78,14 @@ func GenerateInitDocument(secretPrefix string, chunks int32, region string, endp
Endpoint: endpoint,
}

caBundle, err := utils.GetAWSCABundle(context.TODO(), "")
if err != nil {
return []byte{}, fmt.Errorf("failed to get AWS CA bundle: %w", err)
}
if caBundle != nil {
scriptVariables.B64CABundle = base64.StdEncoding.EncodeToString(caBundle)
}

var scriptBuf bytes.Buffer
if err := secretFetchTemplate.Execute(&scriptBuf, scriptVariables); err != nil {
return []byte{}, err
Expand Down
58 changes: 58 additions & 0 deletions pkg/utils/ca_bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package utils

import (
"context"
"fmt"
"gopkg.in/ini.v1"
"os"
)

const (
defaultProfile = "default"
)

func GetAWSCABundlePath(ctx context.Context, profile string) (string, error) {
configFile := os.Getenv("AWS_CONFIG_FILE")
if configFile == "" {
return "", nil
}

cfg, err := ini.Load(configFile)
if err != nil {
return "", fmt.Errorf("failed to load AWS config: %w", err)
}

if profile == "" {
profile = defaultProfile
}

section, err := cfg.GetSection(profile)
if err != nil {
return "", fmt.Errorf("failed to get profile %s: %w", profile, err)
}

cabundlePath := section.Key("ca_bundle")
if cabundlePath == nil || cabundlePath.String() == "" {
return "", nil
}

return cabundlePath.String(), nil
}

func GetAWSCABundle(ctx context.Context, profile string) ([]byte, error) {
cabundlePath, err := GetAWSCABundlePath(ctx, profile)
if err != nil {
return nil, fmt.Errorf("failed to get AWS CA bundle path: %w", err)
}

if cabundlePath == "" {
return nil, nil
}

cabundle, err := os.ReadFile(cabundlePath)
if err != nil {
return nil, fmt.Errorf("failed to read ca bundle file: %w", err)
}

return cabundle, nil
}
121 changes: 121 additions & 0 deletions pkg/utils/ca_bundle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package utils

import (
"context"
"os"
"testing"

. "github.com/onsi/gomega"
"gopkg.in/ini.v1"
)

func TestGetAWSCABundle(t *testing.T) {
g := NewWithT(t)

tests := []struct {
name string
profile string
setupEnv func()
setupConfig func() string
expectedError string
expectedData []byte
}{
{
name: "Should return nil if AWS_CONFIG_FILE is not set",
profile: "default",
setupEnv: func() {
os.Unsetenv("AWS_CONFIG_FILE")
},
expectedData: nil,
},
{
name: "Should return error if AWS config file cannot be loaded",
profile: "default",
setupEnv: func() {
os.Setenv("AWS_CONFIG_FILE", "invalid_path")
},
expectedError: "failed to load AWS config",
},
{
name: "Should return error if profile section is not found",
profile: "nonexistent",
setupEnv: func() {
os.Setenv("AWS_CONFIG_FILE", "test_config.ini")
},
setupConfig: func() string {
cfg := ini.Empty()
cfg.Section("default").Key("ca_bundle").SetValue("test_bundle.pem")
cfg.SaveTo("test_config.ini")
return "test_config.ini"
},
expectedError: "failed to get profile nonexistent",
},
{
name: "Should return nil if ca_bundle is not set in profile",
profile: "default",
setupEnv: func() {
os.Setenv("AWS_CONFIG_FILE", "test_config.ini")
},
setupConfig: func() string {
cfg := ini.Empty()
cfg.Section("default")
cfg.SaveTo("test_config.ini")
return "test_config.ini"
},
expectedData: nil,
},
{
name: "Should return error if ca_bundle file cannot be read",
profile: "default",
setupEnv: func() {
os.Setenv("AWS_CONFIG_FILE", "test_config.ini")
},
setupConfig: func() string {
cfg := ini.Empty()
cfg.Section("default").Key("ca_bundle").SetValue("invalid_path")
cfg.SaveTo("test_config.ini")
return "test_config.ini"
},
expectedError: "failed to read ca bundle file",
},
{
name: "Should return ca_bundle content if everything is correct",
profile: "default",
setupEnv: func() {
os.Setenv("AWS_CONFIG_FILE", "test_config.ini")
},
setupConfig: func() string {
cfg := ini.Empty()
cfg.Section("default").Key("ca_bundle").SetValue("test_bundle.pem")
cfg.SaveTo("test_config.ini")
os.WriteFile("test_bundle.pem", []byte("test content"), 0644)
return "test_config.ini"
},
expectedData: []byte("test content"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.setupEnv != nil {
tt.setupEnv()
}
if tt.setupConfig != nil {
tt.setupConfig()
}

data, err := GetAWSCABundle(context.Background(), tt.profile)
if tt.expectedError != "" {
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring(tt.expectedError))
} else {
g.Expect(err).NotTo(HaveOccurred())
}
g.Expect(data).To(Equal(tt.expectedData))

// Clean up
os.Remove("test_config.ini")
os.Remove("test_bundle.pem")
})
}
}
Loading