diff --git a/Dockerfile.aro-poc b/Dockerfile.aro-poc new file mode 100644 index 00000000000..9424ac6bb8a --- /dev/null +++ b/Dockerfile.aro-poc @@ -0,0 +1,10 @@ +# Build container for POC usage +ARG REGISTRY +FROM ${REGISTRY}/ubi8/ubi-minimal +RUN microdnf update && microdnf clean all +RUN curl -o /etc/pki/ca-trust/source/anchors/AMEROOT_ameroot.crt http://crl.microsoft.com/pkiinfra/certs/AMEROOT_ameroot.crt && update-ca-trust +COPY ./aro /usr/local/bin/ +ENTRYPOINT ["aro"] +# Endpoint used for healthz. We may also use this endpoint for HTTP handler (configurable) +EXPOSE 8080/tcp +USER 1000 \ No newline at end of file diff --git a/Makefile b/Makefile index aca342d263d..008060d4ab1 100644 --- a/Makefile +++ b/Makefile @@ -260,3 +260,9 @@ vendor: hack/update-go-module-dependencies.sh .PHONY: admin.kubeconfig aks.kubeconfig aro az clean client deploy dev-config.yaml discoverycache generate image-aro image-aro-multistage image-fluentbit image-proxy lint-go runlocal-rp proxy publish-image-aro publish-image-aro-multistage publish-image-fluentbit publish-image-proxy secrets secrets-update e2e.test tunnel test-e2e test-go test-python vendor build-all validate-go unit-test-go coverage-go validate-fips + +poc-pkg: + helm package poc/pkg + +poc-build-deploy: + poc/hack/build-deploy.sh $(alias) \ No newline at end of file diff --git a/cmd/aro/main.go b/cmd/aro/main.go index 6a7ece861b8..a01182af813 100644 --- a/cmd/aro/main.go +++ b/cmd/aro/main.go @@ -5,104 +5,37 @@ package main import ( "context" - "flag" "fmt" - "math/rand" - "net/http" _ "net/http/pprof" "os" - "strings" - "time" "github.com/Azure/ARO-RP/pkg/env" utillog "github.com/Azure/ARO-RP/pkg/util/log" _ "github.com/Azure/ARO-RP/pkg/util/scheme" - "github.com/Azure/ARO-RP/pkg/util/version" + "github.com/spf13/pflag" ) -func usage() { - fmt.Fprint(flag.CommandLine.Output(), "usage:\n") - fmt.Fprintf(flag.CommandLine.Output(), " %s dbtoken\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s deploy config.yaml location\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s gateway\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s mirror [release_image...]\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s monitor\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s portal\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s rp\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s operator {master,worker}\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), " %s update-versions\n", os.Args[0]) - flag.PrintDefaults() +var ( + serverPort string + enableMISE bool +) + +func init() { + pflag.StringVar(&serverPort, "server-port", "8080", "port to service http requests") + pflag.BoolVar(&enableMISE, "enable-mise", false, "enable MISE authentication for http requests") } func main() { - rand.Seed(time.Now().UnixNano()) + pflag.Parse() - flag.Usage = usage - flag.Parse() - - ctx := context.Background() - audit := utillog.GetAuditEntry() log := utillog.GetLogger() - go func() { - log.Warn(http.ListenAndServe("localhost:6060", nil)) - }() - - log.Printf("starting, git commit %s", version.GitCommit) - - var err error - switch strings.ToLower(flag.Arg(0)) { - case "dbtoken": - checkArgs(1) - err = dbtoken(ctx, log) - case "deploy": - checkArgs(3) - err = deploy(ctx, log) - case "gateway": - checkArgs(1) - err = gateway(ctx, log) - case "mirror": - checkMinArgs(1) - err = mirror(ctx, log) - case "monitor": - checkArgs(1) - err = monitor(ctx, log) - case "rp": - checkArgs(1) - err = rp(ctx, log, audit) - case "portal": - checkArgs(1) - err = portal(ctx, log, audit) - case "operator": - checkArgs(2) - err = operator(ctx, log) - case "update-versions": - checkArgs(1) - err = updateOCPVersions(ctx, log) - default: - usage() - os.Exit(2) - } - - if err != nil { + ctx := context.Background() + if err := rpPoc(ctx, log); err != nil { log.Fatal(err) } } -func checkArgs(required int) { - if len(flag.Args()) != required { - usage() - os.Exit(2) - } -} - -func checkMinArgs(required int) { - if len(flag.Args()) < required { - usage() - os.Exit(2) - } -} - func DBName(isLocalDevelopmentMode bool) (string, error) { if !isLocalDevelopmentMode { return "ARO", nil diff --git a/cmd/aro/poc.go b/cmd/aro/poc.go new file mode 100644 index 00000000000..83efc5e16ea --- /dev/null +++ b/cmd/aro/poc.go @@ -0,0 +1,40 @@ +package main + +import ( + "context" + "os" + "os/signal" + "syscall" + + "github.com/Azure/ARO-RP/pkg/poc" + "github.com/sirupsen/logrus" +) + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +func rpPoc(ctx context.Context, log *logrus.Entry) error { + log.Print("********** ARO-RP on AKS PoC **********") + ctx, shutdown := context.WithCancel(ctx) + defer shutdown() + go handleSigterm(log, shutdown) + + config := poc.FrontendConfig{ + Port: serverPort, + EnableMISE: enableMISE, + } + + frontEnd := poc.NewFrontend(log, config) + + return frontEnd.Run(ctx) +} + +func handleSigterm(log *logrus.Entry, shutdown context.CancelFunc) { + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGTERM) + <-signals + + log.Print("received SIGTERM. Terminating...") + + shutdown() +} diff --git a/pkg/poc/frontend.go b/pkg/poc/frontend.go new file mode 100644 index 00000000000..4ce27cf88e2 --- /dev/null +++ b/pkg/poc/frontend.go @@ -0,0 +1,113 @@ +package poc + +import ( + "context" + "fmt" + "log" + "net/http" + "strings" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "github.com/sirupsen/logrus" +) + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +type FrontendConfig struct { + Port string + // TODO(jonachang) delete this in production + EnableMISE bool +} + +type frontend struct { + logger *logrus.Entry + port string + router chi.Router +} + +func NewFrontend(logger *logrus.Entry, config FrontendConfig) frontend { + var router chi.Router + if config.EnableMISE { + router = getMiseRouter() + } else { + router = getNonMiseRouter() + } + + return frontend{ + logger: logger, + port: config.Port, + router: router, + } +} + +func (f *frontend) Run(ctx context.Context) error { + router := f.router + server := &http.Server{ + Addr: ":" + f.port, + Handler: router, + ErrorLog: log.New(f.logger.Writer(), "", 0), + } + + go func() { + f.logger.Info("Starting http server...") + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + f.logger.Fatalf("Server listen/serve error: %s", err) + } + }() + + <-ctx.Done() + + f.logger.Info("Stopping http server") + err := server.Shutdown(context.Background()) + if err != nil { + f.logger.Errorf("Server shutdown error: %s", err) + } + + return err +} + +func getBaseRouter() chi.Router { + r := chi.NewRouter() + r.Use(middleware.Logger) + r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Write([]byte("ok")) + }) + return r +} + +func getMiseRouter() chi.Router { + r := getBaseRouter() + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + miseToken := extractAuthBearerToken(r.Header) + miseRespCode, miseRespBody, err := authenticateWithMISE(r.Context(), miseToken, r.Method) + if err != nil { + err = fmt.Errorf("unable to perform authentication with MISE: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if miseRespCode != http.StatusOK { + err = fmt.Errorf("MISE authentication failed with code %d and body %s", miseRespCode, miseRespBody) + http.Error(w, err.Error(), miseRespCode) + return + } + w.Write([]byte("****** Welcome to ARO-RP on AKS PoC mise ******")) + }) + return r +} + +func getNonMiseRouter() chi.Router { + r := getBaseRouter() + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("****** Welcome to ARO-RP on AKS PoC no mise ******")) + }) + return r +} + +func extractAuthBearerToken(h http.Header) string { + auth := h.Get("Authorization") + token := strings.TrimPrefix(auth, "Bearer ") + return strings.TrimSpace(token) +} diff --git a/pkg/poc/miseAuthentication.go b/pkg/poc/miseAuthentication.go new file mode 100644 index 00000000000..c9ea3130730 --- /dev/null +++ b/pkg/poc/miseAuthentication.go @@ -0,0 +1,62 @@ +package poc + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" +) + +type miseRequestData struct { + MiseURL string + OriginalURI string + OriginalMethod string + Token string +} + +const ( + miseURL = "http://localhost:5000/ValidateRequest" + originURI = "https://server/endpoint" +) + +func authenticateWithMISE(ctx context.Context, token, requestMethod string) (int, string, error) { + + requestData := miseRequestData{ + MiseURL: miseURL, + OriginalURI: originURI, + OriginalMethod: requestMethod, + Token: token, + } + + req, err := createMiseHTTPRequest(ctx, requestData) + if err != nil { + return 0, "", err + } + + // TODO(jonachang): need to cache the client when in production. + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return 0, "", err + } + defer resp.Body.Close() + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return 0, "", fmt.Errorf("error reading response body: %w", err) + } + + return resp.StatusCode, string(bodyBytes), nil +} + +func createMiseHTTPRequest(ctx context.Context, data miseRequestData) (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodPost, data.MiseURL, bytes.NewBuffer(nil)) + if err != nil { + return nil, err + } + req.Header.Set("Original-URI", data.OriginalURI) + req.Header.Set("Original-Method", data.OriginalMethod) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", data.Token)) + return req, nil +} diff --git a/poc/README.md b/poc/README.md new file mode 100644 index 00000000000..c9a5544eed8 --- /dev/null +++ b/poc/README.md @@ -0,0 +1,18 @@ +# Feature +This project is part of the [migration tasks](https://dev.azure.com/msazure/AzureRedHatOpenShift/_wiki/wikis/AzureRedHatOpenShift.wiki/515448/Migration) to migrate the RP application from VMSS to the AKS Cluster and follow the [design](https://dev.azure.com/msazure/AzureRedHatOpenShift/_wiki/wikis/AzureRedHatOpenShift.wiki/567165/Mock-RP-Design). + +# Pipelines (Build & Release) +- [RP Infrastructure - INT](https://dev.azure.com/msazure/AzureRedHatOpenShift/_build?definitionId=321081) + +# Resources +- Deployment: + - Set up the cluster and deploy the RP application. +- [Istio](https://dev.azure.com/msazure/AzureRedHatOpenShift/_wiki/wikis/AzureRedHatOpenShift.wiki/499024/Istio): + - Follow [AKS Instruction](https://learn.microsoft.com/en-us/azure/aks/istio-about) to implement the Istio addon for the AKS cluster. + - Use it to create an Istio service mesh and gateway for the RP application. +- [TLS Certificate](https://learn.microsoft.com/en-us/azure/aks/ingress-tls?tabs=azure-cli): + - Use the [CSI driver](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver) to synchronize the Key Vault certificate to AKS. + - Create a dummy pod in the `aks-istio-ingress` namespace to synchronize the certificate using the CSI driver. +- [MISE](https://identitydivision.visualstudio.com/DevEx/_git/MISE?path=%2Fdocs%2FContainer.md&_a=preview): + - Currently, follow the [sidecar pattern](https://dev.azure.com/msazure/AzureRedHatOpenShift/_wiki/wikis/AzureRedHatOpenShift.wiki/595474/MISE-istio-external-authorization-V.S.-side-car-pattern) to implement MISE. + - To enable MISE authentication, edit the `values.yaml`'s `MISE_AUTH_ENABLED` attribute and `enableMISE` in `pkg>poc>miseAuthentication.go` to be either true or false. \ No newline at end of file diff --git a/poc/hack/README.md b/poc/hack/README.md new file mode 100644 index 00000000000..50022381b0e --- /dev/null +++ b/poc/hack/README.md @@ -0,0 +1,8 @@ +# Scripts for ARO-RP on AKS POC + +## build-deploy.sh +- Used to build and deploy ARO-RP from local environment to the test AKS cluster +- Run this via the Makefile. +- In the root ARO-RP directory, run `make alias=*your_alias* poc-build-deploy` +- This will upload your local RP image to the ACR as `dev/*your_alias*:latest` +- This will create a namespaces `*your_alias*-dev` where the local RP build will be deployed \ No newline at end of file diff --git a/poc/hack/build-deploy.sh b/poc/hack/build-deploy.sh new file mode 100755 index 00000000000..638ed74355c --- /dev/null +++ b/poc/hack/build-deploy.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +set -euxo pipefail + +ACR_NAME="aropocsvc.azurecr.io" +CLUSTER_NAME="aro-rp-aks-poc" +DOCKERFILE="Dockerfile.aro-poc" +REGISTRY="REGISTRY=registry.access.redhat.com" +RESOURCE_GROUP="rp-aks-poc" +SUB="0cc1cafa-578f-4fa5-8d6b-ddfd8d82e6ea" + +azure_login() { + accountShow=$(az account show) + if [ -z "$accountShow" ]; then + az login + fi + az account set --subscription "$SUB" +} + +build_container() { + env GOOS=linux GOARCH=amd64 go build -o aro ./cmd/aro + docker build --file "$DOCKERFILE" --tag "$DOCKERTAG" --build-arg="$REGISTRY" . +} + +build_pkg() { + helm package poc/pkg +} + +clean() { + rm -f ./aro > /dev/null 2>&1 + docker rmi $DOCKERTAG:latest > /dev/null 2>&1 + rm -f ./pkg-*.tgz > /dev/null 2>&1 +} + +deploy_pkg() { + local namespace="$1-dev" + local release="$1-dev" + + helm_list=$(helm list -q -f $release --namespace $namespace) + if [[ $helm_list == *"$release"* ]]; then + helm uninstall $release --namespace $namespace + fi + + # Create namespace if it doesn't exist + kubectl create namespace $namespace --dry-run=client -o yaml | kubectl apply -f - + + # Enable automatic sidecar injection + # https://learn.microsoft.com/en-us/azure/aks/istio-deploy-addon + kubectl label namespace $namespace istio.io/rev=asm-1-17 + + helm install $release ./pkg-0.1.0.tgz \ + --set image.repository=$DOCKERTAG \ + --set image.tag=latest \ + --namespace $namespace +} + +get_kubeconfig() { + az aks get-credentials --resource-group "$RESOURCE_GROUP" --name "$CLUSTER_NAME" --admin +} + +push_container() { + az acr login --name "$ACR_NAME" + docker push "$DOCKERTAG:latest" +} + +# Begin script execution +alias="$1" +if [ -z "$alias" ]; then + echo "Usage: $0 " + exit 1 +fi + +DOCKERTAG="$ACR_NAME/dev/$alias" + +# Build stage +build_container +build_pkg + +# Deploy stage +azure_login +push_container +get_kubeconfig +deploy_pkg $alias + +clean \ No newline at end of file diff --git a/poc/pkg/.helmignore b/poc/pkg/.helmignore new file mode 100644 index 00000000000..0e8a0eb36f4 --- /dev/null +++ b/poc/pkg/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/poc/pkg/Chart.yaml b/poc/pkg/Chart.yaml new file mode 100644 index 00000000000..de8e329d87e --- /dev/null +++ b/poc/pkg/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: pkg +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/poc/pkg/templates/NOTES.txt b/poc/pkg/templates/NOTES.txt new file mode 100644 index 00000000000..a808eb9b5de --- /dev/null +++ b/poc/pkg/templates/NOTES.txt @@ -0,0 +1 @@ +ARO-RP on AKS POC \ No newline at end of file diff --git a/poc/pkg/templates/_helpers.tpl b/poc/pkg/templates/_helpers.tpl new file mode 100644 index 00000000000..e2ba03ea73e --- /dev/null +++ b/poc/pkg/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "pkg.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "pkg.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "pkg.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "pkg.labels" -}} +helm.sh/chart: {{ include "pkg.chart" . }} +{{ include "pkg.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "pkg.selectorLabels" -}} +app.kubernetes.io/name: {{ include "pkg.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "pkg.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "pkg.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/poc/pkg/templates/deployment.yaml b/poc/pkg/templates/deployment.yaml new file mode 100644 index 00000000000..8be3e37fc54 --- /dev/null +++ b/poc/pkg/templates/deployment.yaml @@ -0,0 +1,87 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "pkg.fullname" . }} + labels: + {{- include "pkg.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "pkg.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "pkg.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "pkg.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - aro + args: + - "--server-port={{ .Values.image.containerPort }}" + - "--enable-mise={{ .Values.MISE.MISE_AUTH_ENABLED }}" + ports: + - name: http + containerPort: {{ .Values.image.containerPort }} + protocol: TCP + livenessProbe: + httpGet: + path: /healthz + port: http + readinessProbe: + httpGet: + path: /healthz + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.MISE.MISE_AUTH_ENABLED }} + - name: {{ .Values.MISE.container.name }} + image: {{ .Values.MISE.container.image }} + env: + - name: {{ .Values.MISE.container.env.MISE_APPSETTINGS_LOCATION.name }} + value: {{ .Values.MISE.container.env.MISE_APPSETTINGS_LOCATION.value }} + ports: + - containerPort: {{ .Values.MISE.container.ports.containerPort }} + name: http + volumeMounts: + - name: {{ .Values.MISE.container.volumes.name }} + mountPath: {{ .Values.MISE.container.volumes.mountPath }} + readOnly: true + {{- end }} + volumes: + - name: {{ .Values.MISE.container.volumes.name }} + configMap: + name: {{ .Values.MISE.container.volumes.configMap.name }} + items: + - key: {{ .Values.MISE.container.volumes.configMap.items.key }} + path: {{ .Values.MISE.container.volumes.configMap.items.path }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/poc/pkg/templates/hpa.yaml b/poc/pkg/templates/hpa.yaml new file mode 100644 index 00000000000..b82709970de --- /dev/null +++ b/poc/pkg/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "pkg.fullname" . }} + labels: + {{- include "pkg.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "pkg.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/poc/pkg/templates/istio-gateway.yaml b/poc/pkg/templates/istio-gateway.yaml new file mode 100644 index 00000000000..a3e5c1da22e --- /dev/null +++ b/poc/pkg/templates/istio-gateway.yaml @@ -0,0 +1,17 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: {{ .Values.istio.gateway.name }} +spec: + selector: + istio: {{ .Values.istio.name }} + servers: + - port: + number: {{ .Values.istio.gateway.port }} + name: https + protocol: HTTPS + tls: + mode: {{ .Values.istio.gateway.tlsMode }} + credentialName: {{ .Values.keyVault.tlsCertificateName }} + hosts: + - "*" \ No newline at end of file diff --git a/poc/pkg/templates/istio-virtualService.yaml b/poc/pkg/templates/istio-virtualService.yaml new file mode 100644 index 00000000000..e062cd99cef --- /dev/null +++ b/poc/pkg/templates/istio-virtualService.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: {{ .Values.istio.virtualService.name }} +spec: + hosts: + - "*" + gateways: + - {{ .Values.istio.gateway.name }} + http: + - match: + - uri: + prefix: / + route: + - destination: + host: {{ .Values.service.name }} + port: + number: {{ .Values.service.port }} \ No newline at end of file diff --git a/poc/pkg/templates/mise-configMap.yaml b/poc/pkg/templates/mise-configMap.yaml new file mode 100644 index 00000000000..29b2321a9cb --- /dev/null +++ b/poc/pkg/templates/mise-configMap.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +data: + # Follow the instructuions here to create appsettings.json. + # https://identitydivision.visualstudio.com/DevEx/_git/Microsoft-IdentityModel-S2S?path=/docs/JsonConfigurations.md&_a=preview + appsettings.json: | + { + "AzureAd": { + "Instance": "{{ .Values.MISE.configMap.AzureAd.Instance }}", + "ClientId": "{{ .Values.MISE.configMap.AzureAd.ClientId }}", + "TenantId": "{{ .Values.MISE.configMap.AzureAd.TenantId }}", + "Audience": "{{ .Values.MISE.configMap.AzureAd.Audience }}", + "Logging": { + "LogLevel": "{{ .Values.MISE.configMap.AzureAd.LogLevel }}" + }, + "InboundPolicies": + [ + { + "Label": "{{ .Values.MISE.configMap.AzureAd.InboundPolicies.Label }}", + "AuthenticationSchemes": ["{{ .Values.MISE.configMap.AzureAd.InboundPolicies.AuthenticationSchemes }}"], + "TokenTypes": ["{{ .Values.MISE.configMap.AzureAd.InboundPolicies.TokenTypes }}"], + "Authority": "{{ .Values.MISE.configMap.AzureAd.InboundPolicies.Authority }}", + "ValidApplicationIds": ["{{ .Values.MISE.configMap.AzureAd.InboundPolicies.ValidApplicationIds }}"], + }, + { + "Label": "{{ .Values.MISE.configMap.AzureAd.InboundPolicies.Internal.Label }}", + "AuthenticationSchemes": ["{{ .Values.MISE.configMap.AzureAd.InboundPolicies.Internal.AuthenticationSchemes }}"], + "TokenTypes": ["{{ .Values.MISE.configMap.AzureAd.InboundPolicies.Internal.TokenTypes }}"], + "Authority": "{{ .Values.MISE.configMap.AzureAd.InboundPolicies.Internal.Authority }}", + "ValidApplicationIds": ["{{ .Values.MISE.configMap.AzureAd.InboundPolicies.Internal.ValidApplicationIds }}"], + } + ], + }, + "AllowedHosts": "{{ .Values.MISE.configMap.AllowedHosts }}", + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "{{ .Values.MISE.configMap.Kestrel.Endpoints.Url }}" + } + } + }, + "Logging": { + "LogLevel": { + "Default": "{{ .Values.MISE.configMap.Logging.LogLevel.Default }}", + "Microsoft": "{{ .Values.MISE.configMap.Logging.LogLevel.Microsoft }}", + "Microsoft.Hosting.Lifetime": "{{ .Values.MISE.configMap.Logging.LogLevel.Microsoft_Hosting_Lifetime }}", + } + } + } +kind: ConfigMap +metadata: + creationTimestamp: null + name: {{ .Values.MISE.container.volumes.configMap.name }} diff --git a/poc/pkg/templates/service.yaml b/poc/pkg/templates/service.yaml new file mode 100644 index 00000000000..56942a1cdff --- /dev/null +++ b/poc/pkg/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.service.name }} + labels: + {{- include "pkg.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "pkg.selectorLabels" . | nindent 4 }} diff --git a/poc/pkg/templates/serviceaccount.yaml b/poc/pkg/templates/serviceaccount.yaml new file mode 100644 index 00000000000..9fee42ca189 --- /dev/null +++ b/poc/pkg/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "pkg.serviceAccountName" . }} + labels: + {{- include "pkg.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/poc/pkg/templates/tests/test-connection.yaml b/poc/pkg/templates/tests/test-connection.yaml new file mode 100644 index 00000000000..ba311934b6b --- /dev/null +++ b/poc/pkg/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "pkg.fullname" . }}-test-connection" + labels: + {{- include "pkg.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "pkg.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/poc/pkg/templates/tls-cert-deployment.yaml b/poc/pkg/templates/tls-cert-deployment.yaml new file mode 100644 index 00000000000..b0bd32bf69d --- /dev/null +++ b/poc/pkg/templates/tls-cert-deployment.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.istio.tlsDeployment.name }} + namespace: {{ .Values.AKSCluster.istoIngressNamespace }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Values.istio.tlsDeployment.name }} + template: + metadata: + labels: + app: {{ .Values.istio.tlsDeployment.name }} + spec: + containers: + - name: {{ .Values.istio.tlsDeployment.name }} + image: {{ .Values.istio.tlsDeployment.image }} + env: + - name: TITLE + value: "Welcome TLS certificate sync deployment" + volumeMounts: + - name: secrets-store-inline + mountPath: "/mnt/secrets-store" + readOnly: true + volumes: + - name: secrets-store-inline + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ .Values.keyVault.name }} \ No newline at end of file diff --git a/poc/pkg/templates/tls-secretProvider.yaml b/poc/pkg/templates/tls-secretProvider.yaml new file mode 100644 index 00000000000..6e0b26f2cb7 --- /dev/null +++ b/poc/pkg/templates/tls-secretProvider.yaml @@ -0,0 +1,25 @@ +apiVersion: secrets-store.csi.x-k8s.io/v1alpha1 +kind: SecretProviderClass +metadata: + name: {{ .Values.keyVault.name }} + namespace: {{ .Values.AKSCluster.istoIngressNamespace }} +spec: + provider: azure + secretObjects: + - secretName: {{ .Values.keyVault.tlsCertificateName }} + type: "kubernetes.io/tls" + data: + - objectName: {{ .Values.keyVault.tlsCertificateName }} + key: tls.key + - objectName: {{ .Values.keyVault.tlsCertificateName }} + key: tls.crt + parameters: + keyvaultName: {{ .Values.keyVault.name }} + useVMManagedIdentity: "true" + userAssignedIdentityID: {{ .Values.AKSCluster.userAssignedIdentityId }} + objects: | + array: + - | + objectName: {{ .Values.keyVault.tlsCertificateName }} + objectType: secret + tenantId: {{ .Values.AKSCluster.tenantId }} \ No newline at end of file diff --git a/poc/pkg/values.yaml b/poc/pkg/values.yaml new file mode 100644 index 00000000000..7c57386cb4f --- /dev/null +++ b/poc/pkg/values.yaml @@ -0,0 +1,146 @@ +# Default values for pkg. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + pullPolicy: Always + containerPort: 8080 + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + name: rp-poc-svc + type: ClusterIP + port: 8080 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# Use for Istio service mesh +istio: + name: aks-istio-ingressgateway-external + gateway: + name: rp-poc-gateway + port: 443 + # TLS Mode: https://istio.io/latest/docs/reference/config/networking/gateway/#ServerTLSSettings-TLSmode + tlsMode: SIMPLE + virtualService: + name: rp-poc-virtualservice + tlsDeployment: + name: tls-sync-deployment + image: mcr.microsoft.com/azuredocs/aks-helloworld:v1 + container: tls-sync-container + +AKSCluster: + # AKS Istio addon requires the TLS cert secret to be in this namespace + istoIngressNamespace: aks-istio-ingress + userAssignedIdentityId: "" + tenantId: 72f988bf-86f1-41af-91ab-2d7cd011db47 + +keyVault: + name: aro-rp-aks-kv + tlsCertificateName: aks-rp-certificate + +MISE: + MISE_AUTH_ENABLED: true + container: + name: mise + image: aropocsvc.azurecr.io/mise-sidecar:dev + env: + MISE_APPSETTINGS_LOCATION: + name: MISE_APPSETTINGS_LOCATION + value: "/etc/mise/appsettings.json" + ports: + containerPort: 5000 + volumes: + name: config + mountPath: /etc/mise + configMap: + name: miseconfig + items: + key: appsettings.json + path: appsettings.json + configMap: + AzureAd: + Instance: https://login.microsoftonline.com + # Use for telemetry headed to AAD on OpenID configuration requests. + # https://identitydivision.visualstudio.com/DevEx/_wiki/wikis/DevEx.wiki/56/ServiceAuthLibrary-SAL- + # TODO(jonachang) use ARO-RP first party principal when going to production + ClientId: "" + TenantId: 72f988bf-86f1-41af-91ab-2d7cd011db47 + Audience: https://management.azure.com + # Possible log levels: Trace, Debug, Information, Warning, Error, Critical, None + LogLevel: Debug + InboundPolicies: + Label: aro-rp-arm-policy + AuthenticationSchemes: Bearer + TokenTypes: AppToken + Authority: https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0 + ValidApplicationIds: "" + Internal: + Label: aro-rp-internal-policy + AuthenticationSchemes: Bearer + TokenTypes: AppToken + Authority: https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0 + ValidApplicationIds: "" + AllowedHosts: "*" + Kestrel: + Endpoints: + Url: http://localhost:5000 + metatata: + name: miseconfig + Logging: + LogLevel: + Default: Debug + Microsoft: Debug + Microsoft_Hosting_Lifetime: Debug \ No newline at end of file