From 161f0744aa7ffd4b5f93836c614cdfbfb2a2a441 Mon Sep 17 00:00:00 2001 From: Jan Eitzinger Date: Mon, 28 Apr 2025 09:54:22 +0200 Subject: [PATCH] fix: enforce apiAllowedIPs config option Fixes #385 --- ReleaseNotes.md | 14 ++++ cmd/cc-backend/init.go | 12 +++ configs/config-demo.json | 3 + configs/config.json | 106 ++++++++++++++----------- internal/api/api_test.go | 3 + internal/importer/importer_test.go | 3 + internal/repository/userConfig_test.go | 3 + pkg/schema/schemas/config.schema.json | 3 +- pkg/schema/validate_test.go | 26 +++--- 9 files changed, 113 insertions(+), 60 deletions(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 3e3939d..860f62a 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -6,6 +6,20 @@ This is a bug fix release of `cc-backend`, the API backend and frontend implementation of ClusterCockpit. For release specific notes visit the [ClusterCockpit Documentation](https://clusterockpit.org/docs/release/). +## Breaking changes + +The option `apiAllowedIPs` is now a required configuration attribute in +`config.json`. This option restricts access to the admin API. + +To retain the previous behavior that the API is per default accessible from +everywhere set: + +```json + "apiAllowedIPs": [ + "*" + ] +``` + ## Breaking changes for minor release 1.4.x - You need to perform a database migration. Depending on your database size the diff --git a/cmd/cc-backend/init.go b/cmd/cc-backend/init.go index f899ec1..0a5b836 100644 --- a/cmd/cc-backend/init.go +++ b/cmd/cc-backend/init.go @@ -32,6 +32,18 @@ const configString = ` "jwts": { "max-age": "2000h" }, + "apiAllowedIPs": [ + "*" + ], + "enable-resampling": { + "trigger": 30, + "resolutions": [ + 600, + 300, + 120, + 60 + ] + }, "clusters": [ { "name": "name", diff --git a/configs/config-demo.json b/configs/config-demo.json index e8d4570..9425bd2 100644 --- a/configs/config-demo.json +++ b/configs/config-demo.json @@ -17,6 +17,9 @@ 60 ] }, + "apiAllowedIPs": [ + "*" + ], "emission-constant": 317, "clusters": [ { diff --git a/configs/config.json b/configs/config.json index d5b8ada..f946b20 100644 --- a/configs/config.json +++ b/configs/config.json @@ -1,50 +1,62 @@ { - "addr": "0.0.0.0:443", - "ldap": { - "url": "ldaps://test", - "user_base": "ou=people,ou=hpc,dc=test,dc=de", - "search_dn": "cn=hpcmonitoring,ou=roadm,ou=profile,ou=hpc,dc=test,dc=de", - "user_bind": "uid={username},ou=people,ou=hpc,dc=test,dc=de", - "user_filter": "(&(objectclass=posixAccount))" - }, - "https-cert-file": "/etc/letsencrypt/live/url/fullchain.pem", - "https-key-file": "/etc/letsencrypt/live/url/privkey.pem", - "user": "clustercockpit", - "group": "clustercockpit", - "archive": { - "kind": "file", - "path": "./var/job-archive" - }, - "validate": true, - "clusters": [ - { - "name": "test", - "metricDataRepository": { - "kind": "cc-metric-store", - "url": "http://localhost:8082", - "token": "eyJhbGciOiJF-E-pQBQ" - }, - "filterRanges": { - "numNodes": { - "from": 1, - "to": 64 - }, - "duration": { - "from": 0, - "to": 86400 - }, - "startTime": { - "from": "2022-01-01T00:00:00Z", - "to": null - } - } + "addr": "0.0.0.0:443", + "ldap": { + "url": "ldaps://test", + "user_base": "ou=people,ou=hpc,dc=test,dc=de", + "search_dn": "cn=hpcmonitoring,ou=roadm,ou=profile,ou=hpc,dc=test,dc=de", + "user_bind": "uid={username},ou=people,ou=hpc,dc=test,dc=de", + "user_filter": "(&(objectclass=posixAccount))" + }, + "https-cert-file": "/etc/letsencrypt/live/url/fullchain.pem", + "https-key-file": "/etc/letsencrypt/live/url/privkey.pem", + "user": "clustercockpit", + "group": "clustercockpit", + "archive": { + "kind": "file", + "path": "./var/job-archive" + }, + "validate": false, + "apiAllowedIPs": [ + "*" + ], + "clusters": [ + { + "name": "test", + "metricDataRepository": { + "kind": "cc-metric-store", + "url": "http://localhost:8082", + "token": "eyJhbGciOiJF-E-pQBQ" + }, + "filterRanges": { + "numNodes": { + "from": 1, + "to": 64 + }, + "duration": { + "from": 0, + "to": 86400 + }, + "startTime": { + "from": "2022-01-01T00:00:00Z", + "to": null } - ], - "jwts": { - "cookieName": "", - "validateUser": false, - "max-age": "2000h", - "trustedIssuer": "" - }, - "short-running-jobs-duration": 300 + } + } + ], + "jwts": { + "cookieName": "", + "validateUser": false, + "max-age": "2000h", + "trustedIssuer": "" + }, + "enable-resampling": { + "trigger": 30, + "resolutions": [ + 600, + 300, + 120, + 60 + ] + }, + "short-running-jobs-duration": 300 } diff --git a/internal/api/api_test.go b/internal/api/api_test.go index c47bd4d..e67813c 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -45,6 +45,9 @@ func setup(t *testing.T) *api.RestApi { "jwts": { "max-age": "2m" }, + "apiAllowedIPs": [ + "*" + ], "clusters": [ { "name": "testcluster", diff --git a/internal/importer/importer_test.go b/internal/importer/importer_test.go index 4e839cf..209b6be 100644 --- a/internal/importer/importer_test.go +++ b/internal/importer/importer_test.go @@ -45,6 +45,9 @@ func setup(t *testing.T) *repository.JobRepository { "jwts": { "max-age": "2m" }, + "apiAllowedIPs": [ + "*" + ], "clusters": [ { "name": "testcluster", diff --git a/internal/repository/userConfig_test.go b/internal/repository/userConfig_test.go index c01bb5c..cd15c9d 100644 --- a/internal/repository/userConfig_test.go +++ b/internal/repository/userConfig_test.go @@ -25,6 +25,9 @@ func setupUserTest(t *testing.T) *UserCfgRepo { "jwts": { "max-age": "2m" }, + "apiAllowedIPs": [ + "*" + ], "clusters": [ { "name": "testcluster", diff --git a/pkg/schema/schemas/config.schema.json b/pkg/schema/schemas/config.schema.json index f372fc1..c844174 100644 --- a/pkg/schema/schemas/config.schema.json +++ b/pkg/schema/schemas/config.schema.json @@ -492,6 +492,7 @@ }, "required": [ "jwts", - "clusters" + "clusters", + "apiAllowedIPs" ] } diff --git a/pkg/schema/validate_test.go b/pkg/schema/validate_test.go index 2dc97c1..f4943f0 100644 --- a/pkg/schema/validate_test.go +++ b/pkg/schema/validate_test.go @@ -14,17 +14,20 @@ func TestValidateConfig(t *testing.T) { "jwts": { "max-age": "2m" }, - "clusters": [ - { - "name": "testcluster", - "metricDataRepository": { - "kind": "cc-metric-store", - "url": "localhost:8082"}, - "filterRanges": { - "numNodes": { "from": 1, "to": 64 }, - "duration": { "from": 0, "to": 86400 }, - "startTime": { "from": "2022-01-01T00:00:00Z", "to": null } - }}] + "apiAllowedIPs": [ + "*" + ], + "clusters": [ + { + "name": "testcluster", + "metricDataRepository": { + "kind": "cc-metric-store", + "url": "localhost:8082"}, + "filterRanges": { + "numNodes": { "from": 1, "to": 64 }, + "duration": { "from": 0, "to": 86400 }, + "startTime": { "from": "2022-01-01T00:00:00Z", "to": null } + }}] }`) if err := Validate(Config, bytes.NewReader(json)); err != nil { @@ -33,7 +36,6 @@ func TestValidateConfig(t *testing.T) { } func TestValidateJobMeta(t *testing.T) { - } func TestValidateCluster(t *testing.T) {