diff --git a/cmd/fleetctl/fleetctl/apply_test.go b/cmd/fleetctl/fleetctl/apply_test.go index 3d32bf9f1d..249ad5a201 100644 --- a/cmd/fleetctl/fleetctl/apply_test.go +++ b/cmd/fleetctl/fleetctl/apply_test.go @@ -40,6 +40,15 @@ import ( "github.com/urfave/cli/v2" ) +var testSAMLIDPMetadataURL = getTestSAMLIDPMetadataURL() + +func getTestSAMLIDPMetadataURL() string { + if port := os.Getenv("FLEET_SAML_IDP_HTTP_PORT"); port != "" { + return "http://localhost:" + port + "/simplesaml/saml2/idp/metadata.php" + } + return "http://localhost:9080/simplesaml/saml2/idp/metadata.php" +} + var userRoleSpecList = []*fleet.User{ { UpdateCreateTimestamps: fleet.UpdateCreateTimestamps{ @@ -3619,7 +3628,7 @@ spec: }, { desc: "missing required sso entity_id", - spec: ` + spec: fmt.Sprintf(` apiVersion: v1 kind: config spec: @@ -3628,13 +3637,13 @@ spec: entity_id: "" issuer_uri: "http://localhost:8080/simplesaml/saml2/idp/SSOService.php" idp_name: "SimpleSAML" - metadata_url: "http://localhost:9080/simplesaml/saml2/idp/metadata.php" -`, + metadata_url: "%s" +`, testSAMLIDPMetadataURL), wantErr: `422 Validation Failed: required`, }, { desc: "missing required sso idp_name", - spec: ` + spec: fmt.Sprintf(` apiVersion: v1 kind: config spec: @@ -3643,8 +3652,8 @@ spec: entity_id: "https://localhost:8080" issuer_uri: "http://localhost:8080/simplesaml/saml2/idp/SSOService.php" idp_name: "" - metadata_url: "http://localhost:9080/simplesaml/saml2/idp/metadata.php" -`, + metadata_url: "%s" +`, testSAMLIDPMetadataURL), wantErr: `422 Validation Failed: required`, }, { diff --git a/docker-compose.yml b/docker-compose.yml index f32e4378cf..17e42eeaad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: # This is required by Percona XtraDB server. CLUSTER_NAME: fleet ports: - - "3306:3306" + - "${FLEET_MYSQL_PORT:-3306}:3306" mysql_test: image: ${FLEET_MYSQL_IMAGE:-mysql:8.0.44} @@ -76,7 +76,7 @@ services: environment: *mysql-default-environment ports: # ports 3308 and 3309 are used by the main and replica MySQL containers in tools/mysql-replica-testing/docker-compose.yml - - "3310:3306" + - "${FLEET_MYSQL_REPLICA_TEST_PORT:-3310}:3306" tmpfs: - /var/lib/mysql:rw,noexec,nosuid - /tmpfs @@ -85,15 +85,15 @@ services: mailhog: image: mailhog/mailhog:latest ports: - - "8025:8025" - - "1025:1025" + - "${FLEET_MAILHOG_WEB_PORT:-8025}:8025" + - "${FLEET_MAILHOG_SMTP_PORT:-1025}:1025" # SMTP server with Basic Authentication. mailpit: image: axllent/mailpit:latest ports: - - "8026:8025" - - "1026:1025" + - "${FLEET_MAILPIT_WEB_PORT:-8026}:8025" + - "${FLEET_MAILPIT_SMTP_PORT:-1026}:1025" volumes: - ./tools/mailpit/auth.txt:/auth.txt command: ["--smtp-auth-file=/auth.txt", "--smtp-auth-allow-insecure=true"] @@ -102,8 +102,8 @@ services: smtp4dev_test: image: rnwood/smtp4dev:v3 ports: - - "8028:80" - - "1027:25" + - "${FLEET_SMTP4DEV_WEB_PORT:-8028}:80" + - "${FLEET_SMTP4DEV_SMTP_PORT:-1027}:25" volumes: - ./tools/smtp4dev:/certs environment: @@ -114,7 +114,7 @@ services: redis: image: redis:6 ports: - - "6379:6379" + - "${FLEET_REDIS_PORT:-6379}:6379" saml_idp: image: fleetdm/docker-idp:latest @@ -122,15 +122,15 @@ services: - ./tools/saml/users.php:/var/www/simplesamlphp/config/authsources.php - ./tools/saml/config.php:/var/www/simplesamlphp/metadata/saml20-sp-remote.php ports: - - "9080:8080" - - "9443:8443" + - "${FLEET_SAML_IDP_HTTP_PORT:-9080}:8080" + - "${FLEET_SAML_IDP_HTTPS_PORT:-9443}:8443" # CAdvisor container allows monitoring other containers. Useful for # development. cadvisor: image: gcr.io/cadvisor/cadvisor:latest ports: - - "5678:8080" + - "${FLEET_CADVISOR_PORT:-5678}:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - /sys:/sys:ro @@ -139,7 +139,7 @@ services: prometheus: image: prom/prometheus:latest ports: - - "9090:9090" + - "${FLEET_PROMETHEUS_PORT:-9090}:9090" volumes: - ./tools/app/prometheus.yml:/etc/prometheus/prometheus.yml @@ -148,8 +148,8 @@ services: localstack: image: localstack/localstack ports: - - "4566:4566" - - "4571:4571" + - "${FLEET_LOCALSTACK_PORT:-4566}:4566" + - "${FLEET_LOCALSTACK_LEGACY_PORT:-4571}:4571" environment: - SERVICES=firehose,kinesis,s3,iam,sts,secretsmanager @@ -157,8 +157,8 @@ services: s3: image: rustfs/rustfs:1.0.0-alpha.85 ports: - - "9000:9000" - - "9001:9001" + - "${FLEET_S3_PORT:-9000}:9000" + - "${FLEET_S3_CONSOLE_PORT:-9001}:9001" environment: - RUSTFS_ADDRESS=0.0.0.0:9000 - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001 diff --git a/server/datastore/s3/testing_utils.go b/server/datastore/s3/testing_utils.go index f15dd092cd..35784f66b5 100644 --- a/server/datastore/s3/testing_utils.go +++ b/server/datastore/s3/testing_utils.go @@ -15,9 +15,17 @@ import ( const ( accessKeyID = "locals3" secretAccessKey = "locals3" - testEndpoint = "http://localhost:9000" ) +var testEndpoint = getTestEndpoint() + +func getTestEndpoint() string { + if port := os.Getenv("FLEET_S3_PORT"); port != "" { + return "http://localhost:" + port + } + return "http://localhost:9000" +} + func SetupTestSoftwareInstallerStore(tb testing.TB, bucket, prefix string) *SoftwareInstallerStore { store := setupTestStore(tb, bucket, prefix, NewSoftwareInstallerStore) tb.Cleanup(func() { cleanupStore(tb, store.s3store) }) diff --git a/server/mail/mail_test.go b/server/mail/mail_test.go index 110072f9f5..11545aa85f 100644 --- a/server/mail/mail_test.go +++ b/server/mail/mail_test.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "os" + "strconv" "testing" "time" @@ -18,6 +19,35 @@ import ( "github.com/stretchr/testify/require" ) +var testMailpitSMTPPort = getTestMailpitSMTPPort() +var testMailpitWebURL = getTestMailpitWebURL() +var testSMTP4DevSMTPPort = getTestSMTP4DevSMTPPort() + +func getTestMailpitSMTPPort() uint { + if port := os.Getenv("FLEET_MAILPIT_SMTP_PORT"); port != "" { + if p, err := strconv.ParseUint(port, 10, 32); err == nil && p > 0 { + return uint(p) + } + } + return 1026 +} + +func getTestMailpitWebURL() string { + if port := os.Getenv("FLEET_MAILPIT_WEB_PORT"); port != "" { + return "http://127.0.0.1:" + port + } + return "http://127.0.0.1:8026" +} + +func getTestSMTP4DevSMTPPort() uint { + if port := os.Getenv("FLEET_SMTP4DEV_SMTP_PORT"); port != "" { + if p, err := strconv.ParseUint(port, 10, 32); err == nil && p > 0 { + return uint(p) + } + } + return 1027 +} + var testFunctions = [...]func(*testing.T, fleet.MailService){ testSMTPPlainAuth, testSMTPPlainAuthInvalidCreds, @@ -37,7 +67,7 @@ func TestCanSendMail(t *testing.T) { SMTPEnableTLS: false, SMTPVerifySSLCerts: false, SMTPEnableStartTLS: false, - SMTPPort: 1026, + SMTPPort: testMailpitSMTPPort, SMTPServer: "localhost", SMTPSenderAddress: "test@example.com", } @@ -49,8 +79,8 @@ func TestCanSendMail(t *testing.T) { } func TestMail(t *testing.T) { - // This mail test requires mailhog unauthenticated running on localhost:1025 - // and mailpit running on localhost:1026. + // This mail test requires mailhog and mailpit (ports read from env vars + // FLEET_MAILPIT_SMTP_PORT, FLEET_SMTP4DEV_SMTP_PORT, FLEET_MAILPIT_WEB_PORT). if _, ok := os.LookupEnv("MAIL_TEST"); !ok { t.Skip("Mail tests are disabled") } @@ -78,7 +108,7 @@ func testSMTPPlainAuth(t *testing.T, mailer fleet.MailService) { SMTPEnableTLS: false, SMTPVerifySSLCerts: false, SMTPEnableStartTLS: false, - SMTPPort: 1026, + SMTPPort: testMailpitSMTPPort, SMTPServer: "localhost", SMTPSenderAddress: "test@example.com", }, @@ -104,7 +134,7 @@ func testSMTPPlainAuthInvalidCreds(t *testing.T, mailer fleet.MailService) { SMTPEnableTLS: false, SMTPVerifySSLCerts: false, SMTPEnableStartTLS: false, - SMTPPort: 1026, + SMTPPort: testMailpitSMTPPort, SMTPServer: "localhost", SMTPSenderAddress: "test@example.com", }, @@ -130,7 +160,7 @@ func testSMTPSkipVerify(t *testing.T, mailer fleet.MailService) { SMTPEnableTLS: true, SMTPVerifySSLCerts: false, SMTPEnableStartTLS: true, - SMTPPort: 1027, + SMTPPort: testSMTP4DevSMTPPort, SMTPServer: "localhost", SMTPSenderAddress: "test@example.com", }, @@ -153,7 +183,7 @@ func testSMTPNoAuthWithTLS(t *testing.T, mailer fleet.MailService) { SMTPEnableTLS: true, SMTPVerifySSLCerts: true, SMTPEnableStartTLS: true, - SMTPPort: 1027, + SMTPPort: testSMTP4DevSMTPPort, SMTPServer: "localhost", SMTPSenderAddress: "test@example.com", }, @@ -181,7 +211,7 @@ func testSMTPDomain(t *testing.T, mailer fleet.MailService) { SMTPEnableTLS: false, SMTPVerifySSLCerts: false, SMTPEnableStartTLS: false, - SMTPPort: 1026, + SMTPPort: testMailpitSMTPPort, SMTPServer: "localhost", SMTPDomain: "custom.domain.example.com", SMTPSenderAddress: randomAddress, @@ -221,7 +251,7 @@ type MailpitMessages struct { } func getLastRawMailpitMessageFrom(t *testing.T, address string) string { - res, err := http.Get("http://127.0.0.1:8026/api/v1/messages") + res, err := http.Get(testMailpitWebURL + "/api/v1/messages") require.NoError(t, err) var messages MailpitMessages @@ -236,7 +266,7 @@ func getLastRawMailpitMessageFrom(t *testing.T, address string) string { } require.NotNilf(t, messageID, "could not find message from %s in mailpit", address) - res, err = http.Get(fmt.Sprintf("http://127.0.0.1:8026/api/v1/message/%s/raw", messageID)) + res, err = http.Get(fmt.Sprintf("%s/api/v1/message/%s/raw", testMailpitWebURL, messageID)) require.NoError(t, err) rawMail, err := io.ReadAll(res.Body) @@ -258,7 +288,7 @@ func testMailTest(t *testing.T, mailer fleet.MailService) { SMTPEnableTLS: true, SMTPVerifySSLCerts: true, SMTPEnableStartTLS: true, - SMTPPort: 1027, + SMTPPort: testSMTP4DevSMTPPort, SMTPServer: "localhost", SMTPSenderAddress: "test@example.com", }, diff --git a/server/platform/mysql/testing_utils/testing_utils.go b/server/platform/mysql/testing_utils/testing_utils.go index ed48e85246..a48c6103ec 100644 --- a/server/platform/mysql/testing_utils/testing_utils.go +++ b/server/platform/mysql/testing_utils/testing_utils.go @@ -20,9 +20,19 @@ const ( TestUsername = "root" TestPassword = "toor" TestReplicaDatabaseSuffix = "_replica" - TestReplicaAddress = "localhost:3310" ) +var TestReplicaAddress = getTestReplicaAddress() + +// getTestReplicaAddress returns the MySQL replica test server address from environment variable +// FLEET_MYSQL_REPLICA_TEST_PORT or defaults to localhost:3310 +func getTestReplicaAddress() string { + if port := os.Getenv("FLEET_MYSQL_REPLICA_TEST_PORT"); port != "" { + return "localhost:" + port + } + return "localhost:3310" +} + var TestAddress = getTestAddress() // getTestAddress returns the MySQL test server address from environment variable diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 0fa44d1167..768de98b2b 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -4868,7 +4868,7 @@ func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() { t := s.T() acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -4876,10 +4876,10 @@ func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() { "enable_sso": true, "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php", + "metadata_url": "%s", "enable_jit_provisioning": false } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) require.False(t, acResp.SSOSettings.EnableJITProvisioning) @@ -4895,15 +4895,15 @@ func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() { // enable JIT provisioning acResp = appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "sso_settings": { "enable_sso": true, "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php", + "metadata_url": "%s", "enable_jit_provisioning": true } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) require.True(t, acResp.SSOSettings.EnableJITProvisioning) @@ -21075,7 +21075,7 @@ func (s *integrationEnterpriseTestSuite) TestSSOIdPInitiatedLogin() { t := s.T() acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -21085,9 +21085,9 @@ func (s *integrationEnterpriseTestSuite) TestSSOIdPInitiatedLogin() { "enable_sso_idp_login": true, "entity_id": "sso.test.com", "idp_name": "SimpleSAML", - "metadata_url": "http://127.0.0.1:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) body := s.LoginSSOUserIDPInitiated("sso_user2", "user123#", "sso.test.com") diff --git a/server/service/integration_mdm_dep_test.go b/server/service/integration_mdm_dep_test.go index 2d5ca1344a..c1a7baba96 100644 --- a/server/service/integration_mdm_dep_test.go +++ b/server/service/integration_mdm_dep_test.go @@ -95,18 +95,18 @@ func (s *integrationMDMTestSuite) TestDEPEnrollReleaseDeviceGlobal() { // setup IdP so that AccountConfiguration profile is sent after DEP enrollment var acResp appConfigResponse - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "mdm": { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotEmpty(t, acResp.MDM.EndUserAuthentication) t.Cleanup(func() { s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ @@ -264,13 +264,13 @@ func (s *integrationMDMTestSuite) TestDEPEnrollReleaseDeviceTeam() { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true } } - }`, "fleet_ade_test", tm.Name, tm.Name, tm.Name)), http.StatusOK, &acResp) + }`, "fleet_ade_test", tm.Name, tm.Name, tm.Name, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotEmpty(t, acResp.MDM.EndUserAuthentication) t.Cleanup(func() { s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index c25996b88f..66428ab207 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -4819,15 +4819,15 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() { // setup test data var acResp appConfigResponse - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "mdm": { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotEmpty(t, acResp.MDM.EndUserAuthentication) tm, err := s.ds.NewTeam(context.Background(), &fleet.Team{Name: "team1"}) @@ -5205,18 +5205,18 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() { var acResp appConfigResponse var errResp validationErrResp var teamResp teamResponse - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "mdm": { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotEmpty(t, acResp.MDM.EndUserAuthentication) // can't clear IdP settings while end user authentication is enabled (global) @@ -5258,15 +5258,15 @@ func (s *integrationMDMTestSuite) TestMDMMacOSSetup() { // can't clear IdP settings while end user authentication is enabled on a team // 1. configure IdP globally acResp = appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "mdm": { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotEmpty(t, acResp.MDM.EndUserAuthentication) require.False(t, acResp.MDM.MacOSSetup.EnableEndUserAuthentication) @@ -6275,7 +6275,7 @@ func (s *integrationMDMTestSuite) TestSSO() { // "mdm.test.com" entity ID is defined in `tools/saml/config.php`. acResp = appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -6283,14 +6283,14 @@ func (s *integrationMDMTestSuite) TestSSO() { "end_user_authentication": { "entity_id": "mdm.test.com", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true, "lock_end_user_info": true } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) s.runWorker() require.Contains(t, lastSubmittedProfile.URL, acResp.ServerSettings.ServerURL+"/mdm/sso") @@ -7074,7 +7074,7 @@ func (s *integrationMDMTestSuite) setUpMDMSSO(t *testing.T, lockEndUserInfo bool // set the SSO fields acResp = appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -7082,18 +7082,18 @@ func (s *integrationMDMTestSuite) setUpMDMSSO(t *testing.T, lockEndUserInfo bool "end_user_authentication": { "entity_id": "mdm.test.com", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true, - "lock_end_user_info": `+fmt.Sprintf("%t", lockEndUserInfo)+` + "lock_end_user_info": %t } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL, lockEndUserInfo)), http.StatusOK, &acResp) wantSettings := fleet.SSOProviderSettings{ EntityID: "mdm.test.com", IDPName: "SimpleSAML", - MetadataURL: "http://localhost:9080/simplesaml/saml2/idp/metadata.php", + MetadataURL: testSAMLIDPMetadataURL, } assert.Equal(t, wantSettings, acResp.MDM.EndUserAuthentication.SSOProviderSettings) @@ -11102,18 +11102,18 @@ func (s *integrationMDMTestSuite) TestCustomConfigurationWebURL() { // configure end-user authentication globally acResp = appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "mdm": { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) // assign the DEP profile and assert that contains the right values for the URL configurationWebURLShouldBeEmpty = false @@ -11167,18 +11167,18 @@ func (s *integrationMDMTestSuite) TestCustomConfigurationWebURL() { // try to enable end user auth again, it fails because configuration_web_url is set acResp = appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "mdm": { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true } } - }`), http.StatusUnprocessableEntity, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusUnprocessableEntity, &acResp) // create a team via spec teamSpecs := map[string]any{ @@ -11207,18 +11207,18 @@ func (s *integrationMDMTestSuite) TestCustomConfigurationWebURL() { err = s.ds.DeleteMDMAppleSetupAssistant(context.Background(), nil) require.NoError(t, err) acResp = appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "mdm": { "end_user_authentication": { "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" }, "macos_setup": { "enable_end_user_authentication": true } } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) // enable end user auth teamSpecs = map[string]any{ @@ -19943,7 +19943,7 @@ func (s *integrationMDMTestSuite) TestBYODEnrollmentWithIdPEnabled() { res = s.DoRawNoAuth("GET", "/enroll", nil, http.StatusSeeOther, "enroll_secret", "idp") location := res.Header.Get("Location") require.NotEmpty(t, location) - require.True(t, strings.HasPrefix(location, "http://localhost:9080/simplesaml/")) + require.True(t, strings.HasPrefix(location, testSAMLIDPBaseURL+"/simplesaml/")) res = s.LoginMDMSSOUser("sso_user", "user123#") require.Equal(t, http.StatusSeeOther, res.StatusCode) @@ -19958,7 +19958,7 @@ func (s *integrationMDMTestSuite) TestBYODEnrollmentWithIdPEnabled() { map[string]string{"Cookie": shared_mdm.BYODIdpCookieName + "=abc"}, "enroll_secret", "idp", "enrollment_reference", "not_matching!") location = res.Header.Get("Location") require.NotEmpty(t, location) - require.True(t, strings.HasPrefix(location, "http://localhost:9080/simplesaml/")) + require.True(t, strings.HasPrefix(location, testSAMLIDPBaseURL+"/simplesaml/")) // requesting the /enroll page again and simulating the BYOD IdP cookie being // set renders the download profile page when there is a matching enrollment @@ -19974,7 +19974,7 @@ func (s *integrationMDMTestSuite) TestBYODEnrollmentWithIdPEnabled() { res = s.DoRawNoAuth("GET", "/enroll", nil, http.StatusSeeOther, "enroll_secret", "no-such-secret") location = res.Header.Get("Location") require.NotEmpty(t, location) - require.True(t, strings.HasPrefix(location, "http://localhost:9080/simplesaml/")) + require.True(t, strings.HasPrefix(location, testSAMLIDPBaseURL+"/simplesaml/")) } func (s *integrationMDMTestSuite) TestIOSiPadOSRefetch() { @@ -21425,7 +21425,7 @@ func (s *integrationMDMTestSuite) TestTechnicianPermissions() { // Set SMTP, agent options, and SSO settings, and check they are not available for Technicians. acSetup := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "smtp_settings": { "enable_smtp": true, "sender_address": "test@example.com", @@ -21439,7 +21439,7 @@ func (s *integrationMDMTestSuite) TestTechnicianPermissions() { "enable_sso": true, "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php", + "metadata_url": "%s", "enable_jit_provisioning": false }, "agent_options": { @@ -21455,7 +21455,7 @@ func (s *integrationMDMTestSuite) TestTechnicianPermissions() { } } } - }`), http.StatusOK, &acSetup) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acSetup) t.Cleanup(func() { acSetup := appConfigResponse{} s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ diff --git a/server/service/integration_sso_test.go b/server/service/integration_sso_test.go index 5c10c1b312..0145a05d54 100644 --- a/server/service/integration_sso_test.go +++ b/server/service/integration_sso_test.go @@ -61,15 +61,15 @@ func (s *integrationSSOTestSuite) TestGetSSOSettings() { t := s.T() acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "sso_settings": { "enable_sso": true, "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php", + "metadata_url": "%s", "enable_jit_provisioning": false } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) // double-check the settings @@ -167,7 +167,7 @@ func (s *integrationSSOTestSuite) TestSSOLogin() { t := s.T() acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -175,9 +175,9 @@ func (s *integrationSSOTestSuite) TestSSOLogin() { "enable_sso": true, "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) // Register current number of activities. @@ -270,7 +270,7 @@ func (s *integrationSSOTestSuite) TestSSOLoginDisallowedWithPremiumRoles() { t := s.T() acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -278,9 +278,9 @@ func (s *integrationSSOTestSuite) TestSSOLoginDisallowedWithPremiumRoles() { "enable_sso": true, "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) user, err := s.ds.UserByEmail(t.Context(), "sso_user2@example.com") @@ -344,14 +344,14 @@ func (s *integrationSSOTestSuite) TestPerformRequiredPasswordResetWithSSO() { // enable SSO acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "sso_settings": { "enable_sso": true, "entity_id": "https://localhost:8080", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) // perform a required password change using the non-SSO user, works @@ -403,8 +403,8 @@ func (s *integrationSSOTestSuite) TestSSOLoginWithMetadata() { t := s.T() acResp := appConfigResponse{} - metadata, err := json.Marshal([]byte(` - + metadata, err := json.Marshal(fmt.Appendf(nil, ` + @@ -420,11 +420,11 @@ func (s *integrationSSOTestSuite) TestSSOLoginWithMetadata() { - + urn:oasis:names:tc:SAML:2.0:nameid-format:transient - + -`)) +`, testSAMLIDPMetadataURL, testSAMLIDPSLOURL, testSAMLIDPSSOURL)) require.NoError(t, err) s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { @@ -461,7 +461,7 @@ func (s *integrationSSOTestSuite) TestSSOLoginNoEntityID() { t := s.T() acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -469,9 +469,9 @@ func (s *integrationSSOTestSuite) TestSSOLoginNoEntityID() { "enable_sso": true, "entity_id": "localhost", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) ac, err := s.ds.AppConfig(context.Background()) @@ -506,7 +506,7 @@ func (s *integrationSSOTestSuite) TestSSOLoginSAMLResponseTampered() { } acResp := appConfigResponse{} - s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ + s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(fmt.Sprintf(`{ "server_settings": { "server_url": "https://localhost:8080" }, @@ -514,9 +514,9 @@ func (s *integrationSSOTestSuite) TestSSOLoginSAMLResponseTampered() { "enable_sso": true, "entity_id": "sso.test.com", "idp_name": "SimpleSAML", - "metadata_url": "http://localhost:9080/simplesaml/saml2/idp/metadata.php" + "metadata_url": "%s" } - }`), http.StatusOK, &acResp) + }`, testSAMLIDPMetadataURL)), http.StatusOK, &acResp) require.NotNil(t, acResp) // Create sso_user2@example.com if it doesn't exist (because this is @@ -588,9 +588,9 @@ func (s *integrationSSOTestSuite) TestSSOLoginSAMLResponseTampered() { func (s *integrationSSOTestSuite) TestSSOServerURL() { t := s.T() - // Use the test metadata instead of trying to fetch from localhost:9080 - testMetadata := ` - + // Use the test metadata instead of trying to fetch from the SAML IDP + testMetadata := fmt.Sprintf(` + @@ -599,11 +599,11 @@ func (s *integrationSSOTestSuite) TestSSOServerURL() { - + urn:oasis:names:tc:SAML:2.0:nameid-format:transient - + -` +`, testSAMLIDPMetadataURL, testSAMLIDPSLOURL, testSAMLIDPSSOURL) // Configure SSO with a specific SSO server URL and inline metadata acResp := appConfigResponse{} diff --git a/server/service/mail_test.go b/server/service/mail_test.go index 488a2b6d74..d7bcc949cc 100644 --- a/server/service/mail_test.go +++ b/server/service/mail_test.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "os" + "strconv" "testing" "github.com/fleetdm/fleet/v4/server/fleet" @@ -18,6 +19,25 @@ import ( "gopkg.in/guregu/null.v3" ) +var testMailpitSMTPPort = getTestMailpitSMTPPort() +var testMailpitWebURL = getTestMailpitWebURL() + +func getTestMailpitSMTPPort() uint { + if port := os.Getenv("FLEET_MAILPIT_SMTP_PORT"); port != "" { + if p, err := strconv.ParseUint(port, 10, 32); err == nil && p > 0 { + return uint(p) + } + } + return 1026 +} + +func getTestMailpitWebURL() string { + if port := os.Getenv("FLEET_MAILPIT_WEB_PORT"); port != "" { + return "http://127.0.0.1:" + port + } + return "http://127.0.0.1:8026" +} + type notTestFoundError struct{} func (e *notTestFoundError) Error() string { @@ -40,7 +60,7 @@ func (e *notTestFoundError) Is(other error) bool { } func TestMailService(t *testing.T) { - // This mail test requires mailpit running on localhost:1026. + // This mail test requires mailpit (ports read from env vars FLEET_MAILPIT_SMTP_PORT, FLEET_MAILPIT_WEB_PORT). if _, ok := os.LookupEnv("MAIL_TEST"); !ok { t.Skip("Mail tests are disabled") } @@ -61,7 +81,7 @@ func TestMailService(t *testing.T) { SMTPPassword: "mailpit-password", SMTPEnableTLS: false, SMTPVerifySSLCerts: false, - SMTPPort: 1026, + SMTPPort: testMailpitSMTPPort, SMTPServer: "localhost", SMTPSenderAddress: "foobar@example.com", }, @@ -101,7 +121,7 @@ func TestMailService(t *testing.T) { ctx = test.UserContext(ctx, test.UserAdmin) // (1) Modifying the app config `sender_address` field to trigger a test e-mail send. - _, err := svc.ModifyAppConfig(ctx, []byte(`{ + _, err := svc.ModifyAppConfig(ctx, fmt.Appendf(nil, `{ "org_info": { "org_name": "Acme" }, @@ -117,15 +137,15 @@ func TestMailService(t *testing.T) { "password": "mailpit-password", "enable_ssl_tls": false, "verify_ssl_certs": false, - "port": 1026, + "port": %d, "server": "127.0.0.1", "sender_address": "foobar_updated@example.com" } -}`), fleet.ApplySpecOptions{}) +}`, testMailpitSMTPPort), fleet.ApplySpecOptions{}) require.NoError(t, err) getLastMailPitMessage := func() map[string]interface{} { - resp, err := http.Get("http://localhost:8026/api/v1/messages?limit=1") + resp, err := http.Get(testMailpitWebURL + "/api/v1/messages?limit=1") require.NoError(t, err) defer resp.Body.Close() b, err := io.ReadAll(resp.Body) diff --git a/server/service/testing_client.go b/server/service/testing_client.go index 4196a4f260..7f4ed2190e 100644 --- a/server/service/testing_client.go +++ b/server/service/testing_client.go @@ -38,6 +38,19 @@ import ( "github.com/stretchr/testify/suite" ) +// testSAMLIDPBaseURL is the SAML IDP base URL, read from FLEET_SAML_IDP_HTTP_PORT (defaults to http://localhost:9080). +var testSAMLIDPBaseURL = getTestSAMLIDPBaseURL() +var testSAMLIDPMetadataURL = testSAMLIDPBaseURL + "/simplesaml/saml2/idp/metadata.php" +var testSAMLIDPSSOURL = testSAMLIDPBaseURL + "/simplesaml/saml2/idp/SSOService.php" +var testSAMLIDPSLOURL = testSAMLIDPBaseURL + "/simplesaml/saml2/idp/SingleLogoutService.php" + +func getTestSAMLIDPBaseURL() string { + if port := os.Getenv("FLEET_SAML_IDP_HTTP_PORT"); port != "" { + return "http://localhost:" + port + } + return "http://localhost:9080" +} + type withDS struct { s *suite.Suite ds *mysql.Datastore @@ -455,7 +468,7 @@ func (ts *withServer) LoginSSOUserIDPInitiated(username, password, entityID stri res := ts.loginSSOUserIDPInitiated( username, password, "/api/v1/fleet/sso", - fmt.Sprintf("http://127.0.0.1:9080/simplesaml/saml2/idp/SSOService.php?spentityid=%s", entityID), + fmt.Sprintf("%s?spentityid=%s", testSAMLIDPSSOURL, entityID), http.StatusOK, ) defer res.Body.Close()