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

⚠️ Serve catalog over HTTPS #263

Merged
merged 6 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ release:
header: |
## Installation
```bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml
kubectl wait --for=condition=Available --namespace=cert-manager deployment/cert-manager-webhook --timeout=60s
kubectl apply -f https://github.com/operator-framework/catalogd/releases/download/{{ .Tag }}/catalogd.yaml
kubectl wait --for=condition=Available --namespace=catalogd-system deployment/catalogd-controller-manager --timeout=60s
```
16 changes: 10 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ clean: ## Remove binaries and test artifacts
.PHONY: generate
generate: $(CONTROLLER_GEN) ## Generate code and manifests.
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/base/crd/bases

.PHONY: fmt
fmt: ## Run go fmt against code.
Expand Down Expand Up @@ -152,20 +152,24 @@ kind-load: $(KIND) ## Load the built images onto the local cluster
$(KIND) load docker-image $(IMAGE) --name $(KIND_CLUSTER_NAME)

.PHONY: install
install: build-container kind-load deploy wait ## Install local catalogd
install: build-container kind-load cert-manager deploy wait ## Install local catalogd

.PHONY: deploy
deploy: $(KUSTOMIZE) ## Deploy Catalogd to the K8s cluster specified in ~/.kube/config.
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMAGE)
$(KUSTOMIZE) build config/default | kubectl apply -f -
cd config/base/manager && $(KUSTOMIZE) edit set image controller=$(IMAGE)
$(KUSTOMIZE) build config/overlays/cert-manager | kubectl apply -f -

.PHONY: undeploy
undeploy: $(KUSTOMIZE) ## Undeploy Catalogd from the K8s cluster specified in ~/.kube/config.
$(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=true -f -
$(KUSTOMIZE) build config/overlays/cert-manager | kubectl delete --ignore-not-found=true -f -

wait:
kubectl wait --for=condition=Available --namespace=$(CATALOGD_NAMESPACE) deployment/catalogd-controller-manager --timeout=60s

.PHONY: cert-manager
cert-manager:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/${CERT_MGR_VERSION}/cert-manager.yaml
kubectl wait --for=condition=Available --namespace=cert-manager deployment/cert-manager-webhook --timeout=60s
##@ Release

export ENABLE_RELEASE_PIPELINE ?= false
Expand All @@ -175,7 +179,7 @@ release: $(GORELEASER) ## Runs goreleaser for catalogd. By default, this will ru
$(GORELEASER) $(GORELEASER_ARGS)

quickstart: $(KUSTOMIZE) generate ## Generate the installation release manifests and scripts
$(KUSTOMIZE) build config/default | sed "s/:devel/:$(GIT_VERSION)/g" > catalogd.yaml
$(KUSTOMIZE) build config/overlays/cert-manager | sed "s/:devel/:$(GIT_VERSION)/g" > catalogd.yaml

.PHONY: demo-update
demo-update:
Expand Down
2 changes: 1 addition & 1 deletion Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ load('../tilt-support/Tiltfile', 'deploy_repo')

repo = {
'image': 'quay.io/operator-framework/catalogd',
'yaml': 'config/default',
'yaml': 'config/overlays/cert-manager',
'binaries': {
'manager': 'catalogd-controller-manager',
},
Expand Down
2 changes: 1 addition & 1 deletion api/core/v1alpha1/catalog_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
)

func TestPollIntervalCELValidationRules(t *testing.T) {
validators := fieldValidatorsFromFile(t, "../../../config/crd/bases/catalogd.operatorframework.io_catalogs.yaml")
validators := fieldValidatorsFromFile(t, "../../../config/base/crd/bases/catalogd.operatorframework.io_catalogs.yaml")
pth := "openAPIV3Schema.properties.spec"
validator, found := validators["v1alpha1"][pth]
assert.True(t, found)
Expand Down
40 changes: 37 additions & 3 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ limitations under the License.
package main

import (
"crypto/tls"
"flag"
"fmt"
"net"
"net/http"
"net/url"
"os"
Expand Down Expand Up @@ -71,9 +73,12 @@ func main() {
catalogdVersion bool
systemNamespace string
catalogServerAddr string
httpExternalAddr string
externalAddr string
cacheDir string
gcInterval time.Duration
certFile string
keyFile string
listener net.Listener
)
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
Expand All @@ -83,10 +88,12 @@ func main() {
"Enabling this will ensure there is only one active controller manager.")
flag.StringVar(&systemNamespace, "system-namespace", "", "The namespace catalogd uses for internal state, configuration, and workloads")
flag.StringVar(&catalogServerAddr, "catalogs-server-addr", ":8083", "The address where the unpacked catalogs' content will be accessible")
flag.StringVar(&httpExternalAddr, "http-external-address", "http://catalogd-catalogserver.catalogd-system.svc", "The external address at which the http server is reachable.")
flag.StringVar(&externalAddr, "external-address", "catalogd-catalogserver.catalogd-system.svc", "The external address at which the http(s) server is reachable.")
flag.StringVar(&cacheDir, "cache-dir", "/var/cache/", "The directory in the filesystem that catalogd will use for file based caching")
flag.BoolVar(&catalogdVersion, "version", false, "print the catalogd version and exit")
flag.DurationVar(&gcInterval, "gc-interval", 12*time.Hour, "interval in which garbage collection should be run against the catalog content cache")
flag.StringVar(&certFile, "tls-cert", "", "The certificate file used for serving catalog contents over HTTPS")
flag.StringVar(&keyFile, "tls-key", "", "The key file used for serving catalog contents over HTTPS")
opts := zap.Options{
Development: true,
}
Expand Down Expand Up @@ -143,14 +150,40 @@ func main() {
os.Exit(1)
}

baseStorageURL, err := url.Parse(fmt.Sprintf("%s/catalogs/", httpExternalAddr))
if certFile != "" && keyFile != "" {
trgeiger marked this conversation as resolved.
Show resolved Hide resolved
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
setupLog.Error(err, "unable to load certificate key pair")
os.Exit(1)
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS13,
}
trgeiger marked this conversation as resolved.
Show resolved Hide resolved
listener, err = tls.Listen("tcp", catalogServerAddr, config)
if err != nil {
setupLog.Error(err, "unable to create HTTPS server listener")
os.Exit(1)
}
externalAddr = "https://" + externalAddr
} else {
listener, err = net.Listen("tcp", catalogServerAddr)
if err != nil {
setupLog.Error(err, "unable to create HTTP server listener")
os.Exit(1)
}
externalAddr = "http://" + externalAddr
}

baseStorageURL, err := url.Parse(fmt.Sprintf("%s/catalogs/", externalAddr))
if err != nil {
setupLog.Error(err, "unable to create base storage URL")
os.Exit(1)
}

localStorage = storage.LocalDir{RootDir: storeDir, BaseURL: baseStorageURL}
shutdownTimeout := 30 * time.Second

catalogServer := server.Server{
Kind: "catalogs",
Server: &http.Server{
Expand All @@ -162,6 +195,7 @@ func main() {
WriteTimeout: 5 * time.Minute,
},
ShutdownTimeout: &shutdownTimeout,
Listener: listener,
}

if err := mgr.Add(&catalogServer); err != nil {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,3 @@ resources:
- ../crd
- ../rbac
- ../manager

Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ spec:
args:
- --leader-elect
- --metrics-bind-address=127.0.0.1:8080
- --http-external-address=http://catalogd-catalogserver.catalogd-system.svc
- --external-address=catalogd-catalogserver.catalogd-system.svc
image: controller:latest
name: manager
volumeMounts:
Expand Down
File renamed without changes.
65 changes: 65 additions & 0 deletions config/base/rbac/role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups:
- catalogd.operatorframework.io
resources:
- catalogs
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- catalogd.operatorframework.io
resources:
- catalogs/finalizers
verbs:
- update
- apiGroups:
- catalogd.operatorframework.io
resources:
- catalogs/status
verbs:
- get
- patch
- update
- apiGroups:
- ""
resources:
- pods
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope for this PR, but seeing this I don't think we need these permissions on pods anymore. we don't use a pod to unpack the catalog contents anymore and probably forgot to come back and cleanup these permissions. I'll create an issue for this.

verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- pods/log
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: manager-role
namespace: system
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
File renamed without changes.
28 changes: 28 additions & 0 deletions config/overlays/cert-manager/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Adds namespace to all resources.
namespace: catalogd-system

# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: catalogd-

# the following config is for teaching kustomize how to do var substitution
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/crd
- ../../base/rbac
- ../../base/manager
- resources

patches:
- target:
kind: Service
name: catalogserver
path: patches/catalogserver_service_port.yaml
- target:
kind: Deployment
name: controller-manager
path: patches/manager_deployment_certs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- op: replace
path: /spec/ports/0/port
value: 443
- op: replace
path: /spec/ports/0/name
value: https
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- op: add
path: /spec/template/spec/volumes/-
value: {"name":"catalogserver-certs", "secret":{"secretName":"catalogd-catalogserver-cert"}}
- op: add
path: /spec/template/spec/containers/1/volumeMounts/-
value: {"name":"catalogserver-certs", "mountPath":"/var/certs"}
- op: add
path: /spec/template/spec/containers/1/args/-
value: "--tls-cert=/var/certs/tls.crt"
- op: add
path: /spec/template/spec/containers/1/args/-
value: "--tls-key=/var/certs/tls.key"
14 changes: 14 additions & 0 deletions config/overlays/cert-manager/resources/certificate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: catalogserver-cert
namespace: system
spec:
secretName: catalogd-catalogserver-cert
dnsNames:
- localhost
- catalogd-catalogserver.catalogd-system.svc
issuerRef:
kind: Issuer
name: catalogd-catalogserver-ca-issuer
37 changes: 37 additions & 0 deletions config/overlays/cert-manager/resources/issuer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: catalogserver-selfsigned-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: catalogserver-ca
namespace: system
spec:
isCA: true
secretName: catalogd-catalogserver-ca
dnsNames:
- catalogd.io
duration: 2160h # 90d
renewBefore: 360h # 15d
privateKey:
rotationPolicy: Always
algorithm: ECDSA
size: 256
issuerRef:
name: catalogd-catalogserver-selfsigned-issuer
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: catalogserver-ca-issuer
namespace: system
spec:
ca:
secretName: catalogd-catalogserver-ca
3 changes: 3 additions & 0 deletions config/overlays/cert-manager/resources/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resources:
- certificate.yaml
- issuer.yaml
3 changes: 2 additions & 1 deletion internal/third_party/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var (
_ manager.LeaderElectionRunnable = (*Server)(nil)
)

// Server is a general purpose HTTP server Runnable for a manager.
// Server is a general purpose HTTP(S) server Runnable for a manager.
// It is used to serve some internal handlers for health probes and profiling,
// but it can also be used to run custom servers.
type Server struct {
trgeiger marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -118,5 +118,6 @@ func (s *Server) serve() error {
if s.Listener != nil {
return s.Server.Serve(s.Listener)
}

return s.Server.ListenAndServe()
}
4 changes: 2 additions & 2 deletions test/e2e/unpack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ var _ = Describe("Catalog Unpacking", func() {
name := strings.Split(url.Hostname(), ".")[0]
port := url.Port()
// the ProxyGet() call below needs an explicit port value, so if
// value from url.Port() is empty, we assume port 80.
// value from url.Port() is empty, we assume port 443.
if port == "" {
port = "80"
port = "443"
}
resp := kubeClient.CoreV1().Services(ns).ProxyGet(url.Scheme, name, port, url.Path, map[string]string{})
rc, err := resp.Stream(ctx)
Expand Down
Loading