Skip to content

Commit

Permalink
feat(api): add full proxy mode to api module (#8766)
Browse files Browse the repository at this point in the history
* add full proxy mode to api module

* yarn fix

* add fix-frontend to make fix
  • Loading branch information
floreks authored Mar 8, 2024
1 parent d6bde97 commit a370d5b
Show file tree
Hide file tree
Showing 13 changed files with 72 additions and 65 deletions.
2 changes: 1 addition & 1 deletion docs/common/arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Dashboard containers accept multiple arguments that can be used to customize the
| tls-key-file | - | File containing the default x509 private key matching --tls-cert-file. |
| auto-generate-certificates | false | When set to true, Dashboard will automatically generate certificates used to serve HTTPS. |
| apiserver-host | - | The address of the Kubernetes Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8080. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and local discovery is attempted. |
| heapster-host | - | The address of the Heapster Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8082. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used. |
| sidecar-host | - | The address of the Sidecar Apiserver to connect to in the format of protocol://address:port, e.g., http://localhost:8000. If not specified, the assumption is that the binary runs inside a Kubernetes cluster and service proxy will be used. |
| metrics-provider | sidecar | Select provider type for metrics. 'none' will not check metrics. |
| metric-client-check-period | 30 | Time in seconds that defines how often configured metric client health check should be run. |
Expand All @@ -23,6 +22,7 @@ Dashboard containers accept multiple arguments that can be used to customize the
| metrics-scraper-service-name | kubernetes-dashboard-metrics-scraper | Name of the dashboard metrics scraper service. |
| disable-csrf-protection | false | Allows disabling CSRF protection. |
| csrf-key | - | Base64 encoded random 256 bytes key. Can be loaded from 'CSRF_KEY' environment variable. |
| act-as-proxy | false | Forces dashboard to work in full proxy mode, meaning that any in-cluster calls are disabled. |
| v | 1 | Number for the log level verbosity (default 1) | |

## Auth module arguments
Expand Down
2 changes: 0 additions & 2 deletions hack/scripts/conf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ RELEASE_VERSION=2.5.0
ARCH=$(uname | awk '{print tolower($0)}')

# Local cluster configuration (check start-cluster.sh script for more details).
HEAPSTER_VERSION="v1.5.4"
HEAPSTER_PORT=8082
KIND_VERSION="v0.19.0"
K8S_VERSION="v1.29.2"
KIND_BIN=${CACHE_DIR}/kind-${KIND_VERSION}
Expand Down
21 changes: 0 additions & 21 deletions hack/scripts/start-cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,9 @@
ROOT_DIR="$(cd $(dirname "${BASH_SOURCE}")/../.. && pwd -P)"
. "${ROOT_DIR}/hack/scripts/conf.sh"

function start-ci-heapster {
echo "\nRunning heapster in standalone mode"
docker run --net=host -d registry.k8s.io/heapster-amd64:${HEAPSTER_VERSION} \
--heapster-port ${HEAPSTER_PORT} \
--source=kubernetes:http://127.0.0.1:8080?inClusterConfig=false&auth=""

echo "\nWaiting for heapster to be started"
for i in {1..150}
do
HEAPSTER_STATUS=$(curl -sb -H "Accept: application/json" "127.0.0.1:${HEAPSTER_PORT}/healthz")
if [ "${HEAPSTER_STATUS}" == "ok" ]; then
break
fi
sleep 2
done
echo "\nHeapster is up and running"
}

function start-kind {
${KIND_BIN} create cluster --name="k8s-cluster-ci" --image="kindest/node:${K8S_VERSION}" --config="${ROOT_DIR}/hack/scripts/kind-config"
ensure-kubeconfig
if [ "${CI}" = true ] ; then
start-ci-heapster
fi
echo "\nKubernetes cluster is ready to use"
}

Expand Down
5 changes: 2 additions & 3 deletions hack/test-resources/kubernetes-dashboard-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,13 @@ rules:
resources: ["configmaps"]
resourceNames: ["kubernetes-dashboard-settings"]
verbs: ["get", "update"]
# Allow Dashboard to get metrics from heapster.
- apiGroups: [""]
resources: ["services"]
resourceNames: ["heapster", "dashboard-metrics-scraper"]
resourceNames: ["dashboard-metrics-scraper"]
verbs: ["proxy"]
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"]
resourceNames: ["dashboard-metrics-scraper", "http:dashboard-metrics-scraper"]
verbs: ["get"]
---
kind: ClusterRole
Expand Down
48 changes: 32 additions & 16 deletions modules/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,19 @@ func main() {
client.WithInsecureTLSSkipVerify(args.ApiServerSkipTLSVerify()),
)

versionInfo, err := client.InClusterClient().Discovery().ServerVersion()
if err != nil {
handleFatalInitError(err)
if !args.IsProxyEnabled() {
ensureAPIServerConnectionOrDie()
} else {
klog.Info("Running in proxy mode. InClusterClient connections will be disabled.")
}

klog.InfoS("Successful initial request to the apiserver", "version", versionInfo.String())

// Init integrations
integrationManager := integration.NewIntegrationManager()

switch metricsProvider := args.MetricsProvider(); metricsProvider {
case "sidecar":
integrationManager.Metric().ConfigureSidecar(args.SidecarHost()).
EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod()))
case "none":
klog.Info("Metrics provider disabled")
default:
klog.InfoS("Invalid metrics provider", "provider", metricsProvider)
klog.Info("Using default metrics provider", "provider", "sidecar")
integrationManager.Metric().ConfigureSidecar(args.SidecarHost()).
EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod()))
if !args.IsProxyEnabled() {
configureMetricsProvider(integrationManager)
} else {
klog.Info("Skipping metrics configuration. Metrics not available in proxy mode.")
}

apiHandler, err := handler.CreateHTTPAPIHandler(integrationManager)
Expand Down Expand Up @@ -109,6 +101,30 @@ func serveTLS(certificates []tls.Certificate) {
go func() { klog.Fatal(server.ListenAndServeTLS("", "")) }()
}

func ensureAPIServerConnectionOrDie() {
versionInfo, err := client.InClusterClient().Discovery().ServerVersion()
if err != nil {
handleFatalInitError(err)
}

klog.InfoS("Successful initial request to the apiserver", "version", versionInfo.String())
}

func configureMetricsProvider(integrationManager integration.Manager) {
switch metricsProvider := args.MetricsProvider(); metricsProvider {
case "sidecar":
integrationManager.Metric().ConfigureSidecar(args.SidecarHost()).
EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod()))
case "none":
klog.Info("Metrics provider disabled")
default:
klog.InfoS("Invalid metrics provider", "provider", metricsProvider)
klog.Info("Using default metrics provider", "provider", "sidecar")
integrationManager.Metric().ConfigureSidecar(args.SidecarHost()).
EnableWithRetry(integrationapi.SidecarIntegrationID, time.Duration(args.MetricClientHealthCheckPeriod()))
}
}

/**
* Handles fatal init error that prevents server from doing any work. Prints verbose error
* message and quits the server.
Expand Down
5 changes: 5 additions & 0 deletions modules/api/pkg/args/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var (
argNamespace = pflag.String("namespace", helpers.GetEnv("POD_NAMESPACE", "kubernetes-dashboard"), "Namespace to use when accessing Dashboard specific resources, i.e. metrics scraper service")
argMetricsScraperServiceName = pflag.String("metrics-scraper-service-name", "kubernetes-dashboard-metrics-scraper", "name of the dashboard metrics scraper service")
argDisableCSRFProtection = pflag.Bool("disable-csrf-protection", false, "allows disabling CSRF protection")
argIsProxyEnabled = pflag.Bool("act-as-proxy", false, "forces dashboard to work in full proxy mode, meaning that any in-cluster calls are disabled")
)

func init() {
Expand Down Expand Up @@ -147,3 +148,7 @@ func Namespace() string {
func IsCSRFProtectionEnabled() bool {
return !*argDisableCSRFProtection
}

func IsProxyEnabled() bool {
return *argIsProxyEnabled
}
2 changes: 1 addition & 1 deletion modules/api/pkg/handler/apihandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const (
ResponseLogString = "[%s] Outcoming response to %s with %d status code"
)

// APIHandler is a representation of API handler. Structure contains clientapi, Heapster clientapi and clientapi configuration.
// APIHandler is a representation of API handler. Structure contains clientapi and clientapi configuration.
type APIHandler struct {
iManager integration.Manager
}
Expand Down
7 changes: 6 additions & 1 deletion modules/web/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ clean:
coverage: coverage-frontend coverage-go

.PHONY: fix
fix: fix-go
fix: fix-frontend fix-go

.PHONY: test
test: test-go
Expand Down Expand Up @@ -62,6 +62,11 @@ coverage-go: --ensure-dir
@echo "[$(APP_NAME)] Running tests with coverage"
@go test -coverprofile=$(COVERAGE_FILE) -covermode=atomic $(PACKAGE_NAME)/...

.PHONY: fix-frontend
fix-frontend: $(PRE)
@echo "[$(APP_NAME)] Running yarn fix"
@yarn fix

.PHONY: fix-go
fix-go: $(PRE)
@echo "[$(APP_NAME)] Running lint --fix"
Expand Down
9 changes: 6 additions & 3 deletions modules/web/src/chrome/userpanel/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import {Component, ViewChild} from '@angular/core';
import {MatMenuTrigger} from '@angular/material/menu';
import {AuthService} from '@common/services/global/authentication';
import {MeService} from "@common/services/global/me";
import {MeService} from '@common/services/global/me';

@Component({
selector: 'kd-user-panel',
Expand All @@ -26,10 +26,13 @@ export class UserPanelComponent /* implements OnInit */ {
@ViewChild(MatMenuTrigger)
private readonly trigger_: MatMenuTrigger;

constructor(private readonly authService_: AuthService, private readonly _meService: MeService) {}
constructor(
private readonly authService_: AuthService,
private readonly _meService: MeService
) {}

get username(): string {
return this._meService.getUserName()
return this._meService.getUserName();
}

hasAuthHeader(): boolean {
Expand Down
2 changes: 1 addition & 1 deletion modules/web/src/common/services/global/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {CONFIG_DI_TOKEN} from '../../../index.config';
import {CsrfTokenService} from './csrftoken';
import {KdStateService} from './state';
import isEmpty from 'lodash-es/isEmpty';
import {MeService} from "@common/services/global/me";
import {MeService} from '@common/services/global/me';

@Injectable()
export class AuthService {
Expand Down
2 changes: 1 addition & 1 deletion modules/web/src/common/services/global/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {CookieService} from 'ngx-cookie-service';
import {Observable} from 'rxjs';
import {CONFIG_DI_TOKEN} from '../../../index.config';
import {AuthService} from '@common/services/global/authentication';
import {MeService} from "@common/services/global/me";
import {MeService} from '@common/services/global/me';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
Expand Down
28 changes: 15 additions & 13 deletions modules/web/src/common/services/global/me.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,46 @@
* limitations under the License.
*/

import {Injectable} from "@angular/core";
import {User} from "@api/root.api";
import {HttpClient} from "@angular/common/http";
import {interval, Observable} from "rxjs";
import {startWith, switchMap} from "rxjs/operators";
import {Injectable} from '@angular/core';
import {User} from '@api/root.api';
import {HttpClient} from '@angular/common/http';
import {interval, Observable} from 'rxjs';
import {startWith, switchMap} from 'rxjs/operators';

@Injectable()
export class MeService {
private readonly _endpoint = 'api/v1/me';
private _user: User;
private _interval = interval(30000)
private _interval = interval(30000);

constructor(private readonly _http: HttpClient) {}

init(): void {
this._interval.pipe(
startWith({} as User),
switchMap(() => this.me())
).subscribe(user => this._user = user)
this._interval
.pipe(
startWith({} as User),
switchMap(() => this.me())
)
.subscribe(user => (this._user = user));
}

me(): Observable<User> {
return this._http.get<User>(this._endpoint);
}

getUserName(): string {
return this._user?.name
return this._user?.name;
}

getUser(): User {
return this._user
return this._user;
}

reset(): void {
this._user = {} as User;
}

refresh(): void {
this.me().subscribe(user => this._user = user)
this.me().subscribe(user => (this._user = user));
}
}
4 changes: 2 additions & 2 deletions modules/web/src/common/services/global/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {ThemeService} from './theme';
import {TitleService} from './title';
import {VerberService} from './verber';
import {PinnerService} from './pinner';
import {MeService} from "@common/services/global/me";
import {MeService} from '@common/services/global/me';

@NgModule({
providers: [
Expand Down Expand Up @@ -98,7 +98,7 @@ export function init(
history: HistoryService,
theme: ThemeService,
loader: LocalConfigLoaderService,
me: MeService,
me: MeService
): Function {
return () => {
return loader.init().then(() => {
Expand Down

0 comments on commit a370d5b

Please sign in to comment.