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

Cert auth #851

Merged
merged 12 commits into from
Oct 9, 2023
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
26 changes: 25 additions & 1 deletion apis/cloud.redhat.com/v1alpha1/clowdenvironment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ import (
// +kubebuilder:validation:Enum=none;operator;local
type WebMode string

// GatewayCertMode details the mode of operation of the Gateway Cert
// +kubebuilder:validation:Enum=self-signed;acme;none
type GatewayCertMode string

// WebImages defines optional container image overrides for the web provider components
type WebImages struct {
// Mock entitlements image -- if not defined, value from operator config is used if set, otherwise a hard-coded default is used.
Expand All @@ -48,6 +52,9 @@ type WebImages struct {
// Caddy image -- if not defined, value from operator config is used if set, otherwise a hard-coded default is used.
Caddy string `json:"caddy,omitempty"`

// Caddy Gateway image -- if not defined, value from operator config is used if set, otherwise a hard-coded default is used.
CaddyGateway string `json:"caddyGateway,omitempty"`

// Mock BOP image -- if not defined, value from operator config is used if set, otherwise a hard-coded default is used.
MockBOP string `json:"mockBop,omitempty"`
}
Expand Down Expand Up @@ -86,6 +93,23 @@ type WebConfig struct {

// TLS sidecar enablement
TLS TLS `json:"tls,omitempty"`

// Gateway cert
GatewayCert GatewayCert `json:"gatewayCert,omitempty"`
}

type GatewayCert struct {
// Determines whether to enable the gateway cert, default is disabled
Enabled bool `json:"enabled,omitempty"`

// Determines the mode of certificate generation, either self-signed or acme
CertMode GatewayCertMode `json:"certMode,omitempty"`

// Determines a ConfigMap in the target namespace of the env which has ca.pem detailing the cert to use for mTLS verification
LocalCAConfigMap string `json:"localCAConfigMap,omitempty"`

// The email address used to register with Let's Encrypt for acme mode certs
EmailAddress string `json:"emailAddress,omitempty"`
}

type TLS struct {
Expand Down Expand Up @@ -767,7 +791,7 @@ func (i *ClowdEnvironment) GenerateHostname(ctx context.Context, pClient client.
domain := spec["domain"]
if domain != "" {
if random {
return fmt.Sprintf("%s-%s.%s", i.Name, randomIdent, domain)
return fmt.Sprintf("ee-%s.%s", randomIdent, domain)
}
return fmt.Sprintf("%s.%s", i.Name, domain)
}
Expand Down
16 changes: 16 additions & 0 deletions apis/cloud.redhat.com/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions config/crd/bases/cloud.redhat.com_clowdenvironments.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,31 @@ spec:
description: The URL of BOP - only used in (*_none_*/*_operator_*)
mode.
type: string
gatewayCert:
description: Gateway cert
properties:
certMode:
description: Determines the mode of certificate generation,
either self-signed or acme
enum:
- self-signed
- acme
- none
type: string
emailAddress:
description: The email address used to register with Let's
Encrypt for acme mode certs
type: string
enabled:
description: Determines whether to enable the gateway
cert, default is disabled
type: boolean
localCAConfigMap:
description: Determines a ConfigMap in the target namespace
of the env which has ca.pem detailing the cert to use
for mTLS verification
type: string
type: object
images:
description: Optional images to use for web provider components
-- only applies when running in (*_local_*) mode.
Expand All @@ -694,6 +719,11 @@ spec:
operator config is used if set, otherwise a hard-coded
default is used.
type: string
caddyGateway:
description: Caddy Gateway image -- if not defined, value
from operator config is used if set, otherwise a hard-coded
default is used.
type: string
keycloak:
description: Keycloak image -- default is 'quay.io/keycloak/keycloak:{KeycloakVersion}'
unless overridden here
Expand Down
12 changes: 12 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ rules:
- patch
- update
- watch
- apiGroups:
- cert-manager.io
resources:
- certificates
- issuers
verbs:
- create
- delete
- get
- list
- patch
- update
- apiGroups:
- cloud.redhat.com
resources:
Expand Down
1 change: 1 addition & 0 deletions controllers/cloud.redhat.com/clowdapp_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func (rm *ReconciliationMetrics) stop() {
// +kubebuilder:rbac:groups="",resources=endpoints;pods,verbs=get;list;watch
// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses;networkpolicies,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=config.openshift.io,resources=ingresses,verbs=get;list
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates;issuers,verbs=get;list;create;update;patch;delete

// ClowdAppReconciler reconciles a ClowdApp object
type ClowdAppReconciler struct {
Expand Down
1 change: 1 addition & 0 deletions controllers/cloud.redhat.com/clowderconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type ClowderConfig struct {
Images struct {
MBOP string `json:"mbop"`
Caddy string `json:"caddy"`
CaddyGateway string `json:"caddyGateway"`
Keycloak string `json:"Keycloak"`
Mocktitlements string `json:"mocktitlements"`
Envoy string `json:"envoy"`
Expand Down
16 changes: 14 additions & 2 deletions controllers/cloud.redhat.com/providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,28 @@ func createResource(cache *rc.ObjectCache, resourceIdent rc.ResourceIdent, nn ty
err = cache.Create(resourceIdent, nn, nobj)

if err != nil {
return nil, err
return nil, fmt.Errorf("error creating object: %s%s %w", nn.Name, nn.Namespace, err)
}
return nobj, nil
}

func updateResource(cache *rc.ObjectCache, resourceIdent rc.ResourceIdent, object client.Object) error {
err := cache.Update(resourceIdent, object)
psav marked this conversation as resolved.
Show resolved Hide resolved

gvks, nok, werr := cache.GetScheme().ObjectKinds(resourceIdent.GetType())

if werr != nil {
psav marked this conversation as resolved.
Show resolved Hide resolved
return werr
}

if nok {
return fmt.Errorf("this type is unknown")
}

gvk := gvks[0]

if err != nil {
return err
return fmt.Errorf("error updating object: %s %s %s %w", object.GetName(), object.GetNamespace(), gvk.Kind, err)
}
return nil
}
Expand Down
16 changes: 14 additions & 2 deletions controllers/cloud.redhat.com/providers/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import (
"github.com/RedHatInsights/rhc-osdk-utils/utils"
)

var DefaultImageCaddySideCar = "quay.io/cloudservices/crc-caddy-plugin:1c4882e"
var DefaultImageMBOP = "quay.io/cloudservices/mbop:bb071db"
var DefaultImageCaddySideCar = "quay.io/cloudservices/crc-caddy-plugin:a988cd2"
var DefaultImageCaddyGateway = DefaultImageCaddySideCar
var DefaultImageMBOP = "quay.io/cloudservices/mbop:959d00d"
var DefaultImageMocktitlements = "quay.io/cloudservices/mocktitlements:e24820c"
var DefaultKeyCloakVersion = "15.0.2"
var DefaultImageKeyCloak = fmt.Sprintf("quay.io/keycloak/keycloak:%s", DefaultKeyCloakVersion)
Expand Down Expand Up @@ -167,6 +168,17 @@ func MakeLocalDBPVC(pvc *core.PersistentVolumeClaim, nn types.NamespacedName, ba
utils.MakePVC(pvc, nn, providers.Labels{"service": "db", "app": baseResource.GetClowdName()}, capacity, baseResource)
}

// GetCaddyImage returns the caddy image to use in a given environment
func GetCaddyGatewayImage(env *crd.ClowdEnvironment) string {
if env.Spec.Providers.Web.Images.CaddyGateway != "" {
return env.Spec.Providers.Web.Images.CaddyGateway
}
if clowderconfig.LoadedConfig.Images.CaddyGateway != "" {
return clowderconfig.LoadedConfig.Images.CaddyGateway
}
return DefaultImageCaddyGateway
}

// GetCaddyImage returns the caddy image to use in a given environment
func GetCaddyImage(env *crd.ClowdEnvironment) string {
if env.Spec.Providers.Web.Images.Caddy != "" {
Expand Down
126 changes: 126 additions & 0 deletions controllers/cloud.redhat.com/providers/web/caddy_gateway_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package web

import (
"encoding/json"

crccaddy "github.com/RedHatInsights/crc-caddy-plugin"
caddy "github.com/caddyserver/caddy/v2"
caddyconfig "github.com/caddyserver/caddy/v2/caddyconfig"
caddyhttp "github.com/caddyserver/caddy/v2/modules/caddyhttp"
caddyreverseproxy "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
caddytls "github.com/caddyserver/caddy/v2/modules/caddytls"
)

type ProxyRoute struct {
Upstream string `json:"upstream"`
Path string `json:"path"`
}

func GenerateRoute(upstream ProxyRoute, warnings *[]caddyconfig.Warning) *caddyhttp.Route {
reverseProxy := caddyreverseproxy.Handler{
Upstreams: []*caddyreverseproxy.Upstream{{
Dial: upstream.Upstream,
}},
}

routings := caddyhttp.Subroute{
Routes: caddyhttp.RouteList{{
HandlersRaw: []json.RawMessage{
caddyconfig.JSONModuleObject(reverseProxy, "handler", "reverse_proxy", warnings),
},
}},
}

path := caddyhttp.MatchPath{upstream.Path}

route := caddyhttp.Route{
Group: "group2",
HandlersRaw: []json.RawMessage{
caddyconfig.JSONModuleObject(routings, "handler", "subroute", warnings),
},
MatcherSetsRaw: caddyhttp.RawMatcherSets{
caddy.ModuleMap{"path": caddyconfig.JSON(path, warnings)},
},
}

return &route
}

func GenerateConfig(hostname string, bopAddress string, whitelist []string, appRoutes []ProxyRoute) (string, error) {
var warnings []caddyconfig.Warning

host := caddyhttp.MatchHost{hostname}

crcauth := crccaddy.Middleware{
Output: "stdout",
BOP: bopAddress,
Whitelist: whitelist,
}

subRoute := caddyhttp.Subroute{
Routes: caddyhttp.RouteList{
{
HandlersRaw: []json.RawMessage{
caddyconfig.JSONModuleObject(crcauth, "handler", "crcauth", &warnings),
},
},
},
}

for _, appRoute := range appRoutes {
subRoute.Routes = append(subRoute.Routes, *GenerateRoute(appRoute, &warnings))
}

sni := []string{hostname}

appConfig := caddyhttp.App{
HTTPPort: 8888,
HTTPSPort: 9090,
Servers: map[string]*caddyhttp.Server{"srv0": {
Listen: []string{":9090"},
Routes: []caddyhttp.Route{{
Terminal: true,
MatcherSetsRaw: caddyhttp.RawMatcherSets{
caddy.ModuleMap{"host": caddyconfig.JSON(host, &warnings)},
},
Handlers: []caddyhttp.MiddlewareHandler{},
//HandlersRaw: []json.RawMessage{caddyconfig.JSON(reverseProxy, &warnings)},nbu?
HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(subRoute, "handler", "subroute", &warnings)},
}},
TLSConnPolicies: []*caddytls.ConnectionPolicy{{
MatchersRaw: caddy.ModuleMap{"sni": caddyconfig.JSON(sni, &warnings)},
CertSelection: &caddytls.CustomCertSelectionPolicy{
AnyTag: []string{"cert0"},
},
ClientAuthentication: &caddytls.ClientAuthentication{
Mode: "verify_if_given",
TrustedCACertPEMFiles: []string{"/cas/ca.pem"},
},
}, {}},
Logs: &caddyhttp.ServerLogConfig{
LoggerNames: map[string]string{"localhost.localdomain:9090": ""},
},
}},
}

fl := caddytls.FileLoader{{
Certificate: "/certs/tls.crt",
Key: "/certs/tls.key",
Tags: []string{"cert0"},
}}

tlsConfig := caddytls.TLS{
CertificatesRaw: caddy.ModuleMap{"load_files": caddyconfig.JSON(fl, &warnings)},
}

v := caddy.Config{
StorageRaw: []byte{},
AppsRaw: map[string]json.RawMessage{
"http": caddyconfig.JSON(appConfig, &warnings),
"tls": caddyconfig.JSON(tlsConfig, &warnings),
},
}

pretty, _ := json.MarshalIndent(v, "", " ")
return string(pretty), nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package web

import (
"fmt"
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestCaddyConfig(t *testing.T) {
ff, err := os.ReadFile("caddy_gateway_config_test.json")

assert.NoError(t, err)

e, _ := GenerateConfig("host", "bop", []string{"wer"}, []ProxyRoute{{
Upstream: "11",
Path: "22",
}})
fmt.Print(e)
assert.Equal(t, string(ff), e)
}
Loading
Loading