feat(docker): Support for Bearer token to access the Docker registry (#10400)

This commit is contained in:
Niels Basjes 2021-06-16 22:22:32 +02:00 committed by GitHub
parent 5c21d44055
commit d4a22c7f3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 1 deletions

View file

@ -206,6 +206,53 @@ module.exports = {
}; };
``` ```
#### Google Container Registry
Assume you are running GitLab CI in the Google Cloud, and you are storing your Docker images in the Google Container Registry (GCR).
Access to the GCR uses Bearer token based authentication.
This token can be obtained by running `gcloud auth print-access-token`, which requires the Google Cloud SDK to be installed.
The token expires after 60 minutes so you cannot store it in a variable for subsequent builds (like you can with `RENOVATE_TOKEN`).
When running Renovate in this context the Google access token must be retrieved and injected into the `hostRules` configuration just before Renovate is started.
_This documentation gives **a few hints** on **a possible way** to achieve this end result._
The basic approach is that you create a custom image and then run Renovate as one of the stages of your project.
To make this run independent of any user you should use a [`Project Access Token`](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html) (with Scopes: `api`, `read_api` and `write_repository`) for the project and use this as the `RENOVATE_TOKEN` variable for Gitlab CI.
See also the [renovate-runner repository on GitLab](https://gitlab.com/renovate-bot/renovate-runner) where `.gitlab-ci.yml` configuration examples can be found.
To get access to the token a custom Renovate Docker image is needed that includes the Google Cloud SDK.
The Dockerfile to create such an image can look like this:
```Dockerfile
FROM renovate/renovate:25.40.1
# Include the "Docker tip" which you can find here https://cloud.google.com/sdk/docs/install
# under "Installation" for "Debian/Ubuntu"
RUN ...
```
For Renovate to access the Google Container Registry (GCR) it needs the current Google Access Token.
The configuration fragment to do that looks something like this:
```js
hostRules: [
{
matchHost: 'eu.gcr.io',
token: 'MyReallySecretTokenThatExpiresAfter60Minutes',
},
];
```
One way to provide the short-lived Google Access Token to Renovate is by generating these settings into a `config.js` file from within the `.gitlab-ci.yml` right before starting Renovate:
```yaml
script:
- 'echo "module.exports = { hostRules: [ { matchHost: ''eu.gcr.io'', token: ''"$(gcloud auth print-access-token)"'' } ] };" > config.js'
- renovate $RENOVATE_EXTRA_FLAGS
```
#### ChartMuseum #### ChartMuseum
Maybe you're running your own ChartMuseum server to host your private Helm Charts. Maybe you're running your own ChartMuseum server to host your private Helm Charts.

View file

@ -1,3 +1,4 @@
import * as httpMock from '../../../test/http-mock';
import { getName, mocked } from '../../../test/util'; import { getName, mocked } from '../../../test/util';
import * as _hostRules from '../../util/host-rules'; import * as _hostRules from '../../util/host-rules';
import * as dockerCommon from './common'; import * as dockerCommon from './common';
@ -70,4 +71,48 @@ describe(getName(), () => {
`); `);
}); });
}); });
describe('getAuthHeaders', () => {
beforeEach(() => {
httpMock
.scope('https://my.local.registry')
.get('/v2/')
.reply(401, '', { 'www-authenticate': 'Authenticate you must' });
hostRules.hosts.mockReturnValue([]);
});
it('returns "authType token" if both provided', async () => {
hostRules.find.mockReturnValue({
authType: 'some-authType',
token: 'some-token',
});
const headers = await dockerCommon.getAuthHeaders(
'https://my.local.registry',
'https://my.local.registry/prefix'
);
expect(headers).toMatchInlineSnapshot(`
Object {
"authorization": "some-authType some-token",
}
`);
});
it('returns "Bearer token" if only token provided', async () => {
hostRules.find.mockReturnValue({
token: 'some-token',
});
const headers = await dockerCommon.getAuthHeaders(
'https://my.local.registry',
'https://my.local.registry/prefix'
);
expect(headers).toMatchInlineSnapshot(`
Object {
"authorization": "Bearer some-token",
}
`);
});
});
}); });

View file

@ -81,12 +81,20 @@ export async function getAuthHeaders(
'base64' 'base64'
); );
opts.headers = { authorization: `Basic ${auth}` }; opts.headers = { authorization: `Basic ${auth}` };
} else if (opts.token) {
const authType = opts.authType ?? 'Bearer';
logger.trace(
`Using ${authType} token for Docker registry ${registryHost}`
);
opts.headers = { authorization: `${authType} ${opts.token}` };
return opts.headers;
} }
delete opts.username; delete opts.username;
delete opts.password; delete opts.password;
delete opts.token;
if (authenticateHeader.scheme.toUpperCase() === 'BASIC') { if (authenticateHeader.scheme.toUpperCase() === 'BASIC') {
logger.debug(`Using Basic auth for docker registry ${dockerRepository}`); logger.trace(`Using Basic auth for docker registry ${registryHost}`);
await http.get(apiCheckUrl, opts); await http.get(apiCheckUrl, opts);
return opts.headers; return opts.headers;
} }