diff --git a/Dockerfile b/Dockerfile index 5616f1d..2d91c91 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,6 @@ RUN wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio.${VERS # https://dl.min.io/client/mc/release/linux-amd64/ RUN wget https://dl.min.io/client/mc/release/linux-amd64/mc -O /app/code/mc && chmod +x /app/code/mc -COPY env.sh start.sh /app/code/ +COPY env.sh.template start.sh /app/code/ CMD [ "/app/code/start.sh" ] diff --git a/POSTINSTALL.md b/POSTINSTALL.md index 637d4c6..cb09066 100644 --- a/POSTINSTALL.md +++ b/POSTINSTALL.md @@ -1,21 +1,19 @@ + Please use the following credentials to login: **Username**: minioadmin
**Password**: minioadmin
Please change the credentials immediately by following this [guide](https://cloudron.io/documentation/apps/minio/#admin-credentials). +
-By default, Cloudron users have `readwrite` access policy. -If you'd like to change it, you should create a respective policy by following [Minio documentation](https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html) -After that you should add the variable `MINIO_IDENTITY_OPENID_ROLE_POLICY` in /app/data/env.sh, e.g. +Please use the following credentials to login via 'Other Authentication Methods' -> 'Use Credentials': -``` -export MINIO_IDENTITY_OPENID_ROLE_POLICY="new-policy-name" -``` +**Username**: minioadmin
+**Password**: See `MINIO_ROOT_PASSWORD` in `/app/data/env.sh` Open File Manager
-Where `new-policy-name` is the policy you have created. +Cloudron users have `readwrite` access policy. See the [docs](https://cloudron.io/documentation/apps/minio/#admin-credentials) on how to change it. -Be sure to restart the app after making changes. -
+ diff --git a/env.sh b/env.sh deleted file mode 100644 index 6aad718..0000000 --- a/env.sh +++ /dev/null @@ -1,9 +0,0 @@ -# Add custom minio configuration to this file. Restart the app for changes to take effect. - -export CLOUDRON_MINIO_STARTUP_ARGS='server /app/data/data' - -# See https://docs.min.io/minio/baremetal/reference/minio-server/minio-server.html#envvar.MINIO_ROOT_USER -# You can use pwgen -1s 64 to generate usernames and passwords -export MINIO_ROOT_USER=minioadmin -export MINIO_ROOT_PASSWORD=minioadmin - diff --git a/env.sh.template b/env.sh.template new file mode 100644 index 0000000..dfdc06d --- /dev/null +++ b/env.sh.template @@ -0,0 +1,4 @@ +# Add custom minio configuration to this file. Restart the app for changes to take effect. + +export CLOUDRON_MINIO_STARTUP_ARGS='server /app/data/data' + diff --git a/start.sh b/start.sh index f8f8657..a25c947 100755 --- a/start.sh +++ b/start.sh @@ -5,7 +5,18 @@ set -eu mkdir -p /app/data/data /run/minio/config /run/minio/certs # env vars take precedence over config.yaml (https://github.com/minio/minio/blob/master/docs/distributed/CONFIG.md#things-to-know) -[[ ! -f /app/data/env.sh ]] && cp /app/code/env.sh /app/data/env.sh +if [[ ! -f /app/data/env.sh ]]; then + echo "=> First run" + cp /app/code/env.sh.template /app/data/env.sh + # minio does not show the password login by default when OIDC is setup (https://github.com/minio/minio/discussions/16928) + # we generate a dynamic password because users might forget to change the admin password (with the oidc login being so click friendly) + if [[ -n "${CLOUDRON_OIDC_ISSUER:-}" ]]; then + echo -e "export MINIO_ROOT_USER=minioadmin\nexport MINIO_ROOT_PASSWORD=$(pwgen -1s 20)\n\n" >> /app/data/env.sh + else + echo -e "export MINIO_ROOT_USER=minioadmin\nexport MINIO_ROOT_PASSWORD=minioadmin\n\n" >> /app/data/env.sh + fi +fi + source /app/data/env.sh # https://docs.min.io/minio/baremetal/reference/minio-server/minio-server.html#envvar.MINIO_SERVER_URL diff --git a/test/package-lock.json b/test/package-lock.json index 5b5ff42..1c404e8 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -12,7 +12,7 @@ "chromedriver": "^121.0.2", "expect.js": "^0.3.1", "mocha": "^10.3.0", - "selenium-webdriver": "^4.17.0", + "selenium-webdriver": "^4.18.1", "superagent": "^8.1.2" } }, @@ -1192,9 +1192,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/selenium-webdriver": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.17.0.tgz", - "integrity": "sha512-e2E+2XBlGepzwgFbyQfSwo9Cbj6G5fFfs9MzAS00nC99EewmcS2rwn2MwtgfP7I5p1e7DYv4HQJXtWedsu6DvA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.18.1.tgz", + "integrity": "sha512-uP4OJ5wR4+VjdTi5oi/k8oieV2fIhVdVuaOPrklKghgS59w7Zz3nGa5gcG73VcU9EBRv5IZEBRhPr7qFJAj5mQ==", "dependencies": { "jszip": "^3.10.1", "tmp": "^0.2.1", @@ -2408,9 +2408,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "selenium-webdriver": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.17.0.tgz", - "integrity": "sha512-e2E+2XBlGepzwgFbyQfSwo9Cbj6G5fFfs9MzAS00nC99EewmcS2rwn2MwtgfP7I5p1e7DYv4HQJXtWedsu6DvA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.18.1.tgz", + "integrity": "sha512-uP4OJ5wR4+VjdTi5oi/k8oieV2fIhVdVuaOPrklKghgS59w7Zz3nGa5gcG73VcU9EBRv5IZEBRhPr7qFJAj5mQ==", "requires": { "jszip": "^3.10.1", "tmp": "^0.2.1", diff --git a/test/package.json b/test/package.json index f59e080..2addaea 100644 --- a/test/package.json +++ b/test/package.json @@ -12,7 +12,7 @@ "chromedriver": "^121.0.2", "expect.js": "^0.3.1", "mocha": "^10.3.0", - "selenium-webdriver": "^4.17.0", + "selenium-webdriver": "^4.18.1", "superagent": "^8.1.2" } } diff --git a/test/test.js b/test/test.js index 152e0a5..597be59 100644 --- a/test/test.js +++ b/test/test.js @@ -34,7 +34,7 @@ describe('Application life cycle test', function () { const EXEC_ARGS = { cwd: path.resolve(__dirname, '..'), stdio: 'inherit' }; let browser, app; - var athenticated_by_oidc = false; + let athenticated_by_oidc = false, rootPassword; let username = process.env.USERNAME; let password = process.env.PASSWORD; @@ -57,7 +57,10 @@ describe('Application life cycle test', function () { expect(app).to.be.an('object'); } - async function login(accessKey='minioadmin', secretKey='minioadmin') { + async function login(username, password) { + await browser.manage().deleteAllCookies(); + await browser.get('about:blank'); + await browser.sleep(2000); await browser.get(`https://${app.fqdn}/login`); await browser.sleep(2000); @@ -68,8 +71,8 @@ describe('Application life cycle test', function () { await browser.sleep(2000); } await waitForElement(By.id('accessKey')); - await browser.findElement(By.id('accessKey')).sendKeys(accessKey); - await browser.findElement(By.id('secretKey')).sendKeys(secretKey); + await browser.findElement(By.id('accessKey')).sendKeys(username); + await browser.findElement(By.id('secretKey')).sendKeys(password); await browser.findElement(By.xpath('//button[@id="do-login"]')).click(); await waitForElement(By.xpath('//span[contains(text(), "Buckets")]')); await timers.setTimeout(5000); @@ -134,9 +137,27 @@ describe('Application life cycle test', function () { expect(response.body.toString('utf8')).to.contain('AccessDenied'); } + async function changeAdminCredentials() { + let data = fs.readFileSync(path.join(__dirname, '../env.sh.template'), 'utf8'); + data += '\nexport MINIO_ROOT_USER=minioakey\nexport MINIO_ROOT_PASSWORD=minioskey\n'; + fs.writeFileSync('/tmp/env.sh', data); + execSync(`cloudron push --app ${app.id} /tmp/env.sh /app/data/env.sh`, EXEC_ARGS); + execSync(`cloudron restart --app ${app.id}`, EXEC_ARGS); + await timers.setTimeout(10000); + } + + async function getAdminCredentials() { + execSync(`cloudron pull --app ${app.id} /app/data/env.sh /tmp/env.sh`, EXEC_ARGS); + const data = fs.readFileSync('/tmp/env.sh', 'utf8'); + const m = data.match(/MINIO_ROOT_PASSWORD=(.*)/); + if (!m) throw new Error('Could not detect root password'); + rootPassword = m[1].trim(); + console.log(`root password is [${rootPassword}]`); + } + xit('build app', function () { execSync('cloudron build', EXEC_ARGS); }); - // no SSO + // // no SSO it('install app (no SSO)', async function () { execSync(`cloudron install --no-sso --location ${LOCATION} --secondary-domains API_SERVER_DOMAIN=${LOCATION}-api`, EXEC_ARGS); await timers.setTimeout(10000); @@ -144,33 +165,25 @@ describe('Application life cycle test', function () { it('can get app information', getAppInfo); - it('can Admin login', login.bind(null, 'minioadmin', 'minioadmin')); + it('can admin login', login.bind(null, 'minioadmin', 'minioadmin')); it('can add bucket', addBucket); it('can logout', logout); it('does redirect', checkRedirect); it('check api', checkApi); - it('can change Admin credentials', async function () { - let data = fs.readFileSync(path.join(__dirname, '../env.sh'), 'utf8'); - data = data - .replace(/MINIO_ROOT_USER=.*/, 'MINIO_ROOT_USER=minioakey') - .replace(/MINIO_ROOT_PASSWORD=.*/, 'MINIO_ROOT_PASSWORD=minioskey'); - fs.writeFileSync('/tmp/env.sh', data); - execSync(`cloudron push --app ${app.id} /tmp/env.sh /app/data/env.sh`, EXEC_ARGS); + it('can change admin credentials', changeAdminCredentials); + it('can restart app', async function () { execSync(`cloudron restart --app ${app.id}`, EXEC_ARGS); await timers.setTimeout(10000); }); - it('can restart app', function () { execSync(`cloudron restart --app ${app.id}`, EXEC_ARGS); }); - - it('can Admin login', login.bind(null, 'minioakey', 'minioskey')); + it('can admin login', login.bind(null, 'minioakey', 'minioskey')); it('has bucket', checkBucket); it('can logout', logout); it('does redirect', checkRedirect); it('check api', checkApi); it('uninstall app', function () { execSync('cloudron uninstall --app ' + app.id, EXEC_ARGS); }); - // SSO it('install app (SSO)', async function () { execSync(`cloudron install --location ${LOCATION} --secondary-domains API_SERVER_DOMAIN=${LOCATION}-api`, EXEC_ARGS); @@ -178,8 +191,8 @@ describe('Application life cycle test', function () { }); it('can get app information', getAppInfo); - - it('can Admin login', login.bind(null, 'minioadmin', 'minioadmin')); + it('can get admin credentials', getAdminCredentials); + it('can admin login', async function () { await login('minioadmin', rootPassword); }); it('can add bucket', addBucket); it('can logout', logout); it('does redirect', checkRedirect); @@ -189,20 +202,14 @@ describe('Application life cycle test', function () { it('has bucket', checkBucket); it('can logout', logout); - it('can change Admin credentials', async function () { - let data = fs.readFileSync(path.join(__dirname, '../env.sh'), 'utf8'); - data = data - .replace(/MINIO_ROOT_USER=.*/, 'MINIO_ROOT_USER=minioakey') - .replace(/MINIO_ROOT_PASSWORD=.*/, 'MINIO_ROOT_PASSWORD=minioskey'); - fs.writeFileSync('/tmp/env.sh', data); - execSync(`cloudron push --app ${app.id} /tmp/env.sh /app/data/env.sh`, EXEC_ARGS); + it('can change admin credentials', changeAdminCredentials); + + it('can restart app', async function () { execSync(`cloudron restart --app ${app.id}`, EXEC_ARGS); await timers.setTimeout(10000); }); - it('can restart app', function () { execSync(`cloudron restart --app ${app.id}`, EXEC_ARGS); }); - - it('can Admin login', login.bind(null, 'minioakey', 'minioskey')); + it('can admin login', login.bind(null, 'minioakey', 'minioskey')); it('has bucket', checkBucket); it('can logout', logout); it('does redirect', checkRedirect); @@ -223,7 +230,7 @@ describe('Application life cycle test', function () { }); it('can get app information', getAppInfo); - it('can Admin login', login.bind(null, 'minioakey', 'minioskey')); + it('can admin login', login.bind(null, 'minioakey', 'minioskey')); it('has bucket', checkBucket); it('can logout', logout); @@ -241,7 +248,7 @@ describe('Application life cycle test', function () { }); it('can get app information', getAppInfo); - it('can Admin login', login.bind(null, 'minioakey', 'minioskey')); + it('can admin login', login.bind(null, 'minioakey', 'minioskey')); it('has bucket', checkBucket); it('can logout', logout); @@ -255,7 +262,7 @@ describe('Application life cycle test', function () { it('uninstall app', function () { execSync('cloudron uninstall --app ' + app.id, EXEC_ARGS); }); // test update - it('can install app', function () { execSync('cloudron install --appstore-id io.minio.cloudronapp --location ' + LOCATION, EXEC_ARGS); }); + it('can install app for update', function () { execSync('cloudron install --appstore-id io.minio.cloudronapp --location ' + LOCATION, EXEC_ARGS); }); it('can get app information', getAppInfo); it('can login', login.bind(null, 'minioadmin', 'minioadmin')); @@ -272,7 +279,7 @@ describe('Application life cycle test', function () { it('can configure', function () { execSync(`cloudron configure --app ${LOCATION} --location ${LOCATION} --secondary-domains API_SERVER_DOMAIN=${LOCATION}-api`, EXEC_ARGS); }); it('can get app information', getAppInfo); - it('can Admin login', login.bind(null, 'minioadmin', 'minioadmin')); + it('can admin login', login.bind(null, 'minioadmin', 'minioadmin')); it('has bucket', checkBucket); it('can logout', logout);