mirror of https://github.com/grafana/loki
operator: Configure gateway to honor the global TLS security profile (#6870)
parent
53e01a795a
commit
a068c1241d
@ -0,0 +1,74 @@ |
|||||||
|
package tlsprofile |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"github.com/ViaQ/logerr/v2/kverrors" |
||||||
|
projectconfigv1 "github.com/grafana/loki/operator/apis/config/v1" |
||||||
|
"github.com/grafana/loki/operator/internal/external/k8s" |
||||||
|
openshiftv1 "github.com/openshift/api/config/v1" |
||||||
|
"github.com/openshift/library-go/pkg/crypto" |
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors" |
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client" |
||||||
|
) |
||||||
|
|
||||||
|
// APIServerName is the apiserver resource name used to fetch it.
|
||||||
|
const APIServerName = "cluster" |
||||||
|
|
||||||
|
// GetSecurityProfileInfo gets the tls profile info to apply.
|
||||||
|
func GetSecurityProfileInfo(ctx context.Context, k k8s.Client, tlsProfileType projectconfigv1.TLSProfileType) (projectconfigv1.TLSProfileSpec, error) { |
||||||
|
var tlsProfile openshiftv1.TLSSecurityProfile |
||||||
|
|
||||||
|
if tlsProfileType != "" { |
||||||
|
tlsProfile = openshiftv1.TLSSecurityProfile{ |
||||||
|
Type: openshiftv1.TLSProfileType(tlsProfileType), |
||||||
|
} |
||||||
|
} else { |
||||||
|
tlsProfile = openshiftv1.TLSSecurityProfile{ |
||||||
|
Type: openshiftv1.TLSProfileIntermediateType, |
||||||
|
} |
||||||
|
|
||||||
|
var apiServer openshiftv1.APIServer |
||||||
|
if err := k.Get(ctx, client.ObjectKey{Name: APIServerName}, &apiServer); err != nil { |
||||||
|
if !apierrors.IsNotFound(err) { |
||||||
|
return projectconfigv1.TLSProfileSpec{}, kverrors.Wrap(err, "failed to lookup apiServer") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if apiServer.Spec.TLSSecurityProfile != nil { |
||||||
|
tlsProfile = *apiServer.Spec.TLSSecurityProfile |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tlsMinVersion, ciphers := extractInfoFromTLSProfile(&tlsProfile) |
||||||
|
return projectconfigv1.TLSProfileSpec{ |
||||||
|
MinTLSVersion: tlsMinVersion, |
||||||
|
Ciphers: ciphers, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func extractInfoFromTLSProfile(profile *openshiftv1.TLSSecurityProfile) (string, []string) { |
||||||
|
var profileType openshiftv1.TLSProfileType |
||||||
|
if profile == nil { |
||||||
|
profileType = openshiftv1.TLSProfileIntermediateType |
||||||
|
} else { |
||||||
|
profileType = profile.Type |
||||||
|
} |
||||||
|
|
||||||
|
var profileSpec *openshiftv1.TLSProfileSpec |
||||||
|
if profileType == openshiftv1.TLSProfileCustomType { |
||||||
|
if profile.Custom != nil { |
||||||
|
profileSpec = &profile.Custom.TLSProfileSpec |
||||||
|
} |
||||||
|
} else { |
||||||
|
profileSpec = openshiftv1.TLSProfiles[profileType] |
||||||
|
} |
||||||
|
|
||||||
|
// nothing found / custom type set but no actual custom spec
|
||||||
|
if profileSpec == nil { |
||||||
|
profileSpec = openshiftv1.TLSProfiles[openshiftv1.TLSProfileIntermediateType] |
||||||
|
} |
||||||
|
|
||||||
|
// need to remap all Ciphers to their respective IANA names used by Go
|
||||||
|
return string(profileSpec.MinTLSVersion), crypto.OpenSSLToIANACipherSuites(profileSpec.Ciphers) |
||||||
|
} |
||||||
@ -0,0 +1,114 @@ |
|||||||
|
package tlsprofile_test |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
|
||||||
|
projectconfigv1 "github.com/grafana/loki/operator/apis/config/v1" |
||||||
|
"github.com/grafana/loki/operator/internal/external/k8s/k8sfakes" |
||||||
|
"github.com/grafana/loki/operator/internal/handlers/internal/tlsprofile" |
||||||
|
openshiftv1 "github.com/openshift/api/config/v1" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors" |
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema" |
||||||
|
"k8s.io/apimachinery/pkg/types" |
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
apiServer = openshiftv1.APIServer{ |
||||||
|
ObjectMeta: metav1.ObjectMeta{ |
||||||
|
Name: "cluster", |
||||||
|
}, |
||||||
|
} |
||||||
|
ciphersOld = []string{ |
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", |
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", |
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", |
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", |
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", |
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", |
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", |
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", |
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", |
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", |
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", |
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", |
||||||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256", |
||||||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384", |
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA256", |
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA", |
||||||
|
"TLS_RSA_WITH_AES_256_CBC_SHA", |
||||||
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA", |
||||||
|
} |
||||||
|
ciphersIntermediate = []string{ |
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", |
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", |
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", |
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", |
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", |
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func TestGetSecurityProfileInfo(t *testing.T) { |
||||||
|
type tt struct { |
||||||
|
desc string |
||||||
|
profile projectconfigv1.TLSProfileType |
||||||
|
expected projectconfigv1.TLSProfileSpec |
||||||
|
} |
||||||
|
|
||||||
|
tc := []tt{ |
||||||
|
{ |
||||||
|
desc: "Old profile", |
||||||
|
profile: projectconfigv1.TLSProfileOldType, |
||||||
|
expected: projectconfigv1.TLSProfileSpec{ |
||||||
|
MinTLSVersion: "VersionTLS10", |
||||||
|
Ciphers: ciphersOld, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
desc: "Intermediate profile", |
||||||
|
profile: projectconfigv1.TLSProfileIntermediateType, |
||||||
|
expected: projectconfigv1.TLSProfileSpec{ |
||||||
|
MinTLSVersion: "VersionTLS12", |
||||||
|
Ciphers: ciphersIntermediate, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
desc: "Modern profile", |
||||||
|
profile: projectconfigv1.TLSProfileModernType, |
||||||
|
expected: projectconfigv1.TLSProfileSpec{ |
||||||
|
MinTLSVersion: "VersionTLS13", |
||||||
|
// Go lib crypto doesn't allow ciphers to be configured for TLS 1.3
|
||||||
|
// (Read this and weep: https://github.com/golang/go/issues/29349)
|
||||||
|
Ciphers: []string{}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
sw := &k8sfakes.FakeStatusWriter{} |
||||||
|
k := &k8sfakes.FakeClient{} |
||||||
|
|
||||||
|
k.GetStub = func(_ context.Context, name types.NamespacedName, object client.Object) error { |
||||||
|
if apiServer.Name == name.Name { |
||||||
|
k.SetClientObject(object, &apiServer) |
||||||
|
return nil |
||||||
|
} |
||||||
|
return apierrors.NewNotFound(schema.GroupResource{}, "something wasn't found") |
||||||
|
} |
||||||
|
|
||||||
|
k.StatusStub = func() client.StatusWriter { return sw } |
||||||
|
|
||||||
|
for _, tc := range tc { |
||||||
|
tc := tc |
||||||
|
t.Run(tc.desc, func(t *testing.T) { |
||||||
|
t.Parallel() |
||||||
|
info, err := tlsprofile.GetSecurityProfileInfo(context.TODO(), k, tc.profile) |
||||||
|
assert.Nil(t, err) |
||||||
|
assert.NotNil(t, info) |
||||||
|
assert.EqualValues(t, tc.expected, info) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue