Ignore selinux security opts when comparing services

The moby engine v20.x.y adds some selinux [security configurations](https://docs.docker.com/engine/reference/run/#security-configuration)
depending on the [container configuration](https://github.com/moby/moby/blob/master/daemon/create.go#L214).
This would cause the supervisor to enter a service restart loop as the
current and target service configurations will never match. The
supervisor now ignores selinux specific security options since those are
not supported by balenaOS.

Closes: #1890
Change-type: patch
This commit is contained in:
Felipe Lalanne 2022-02-23 18:03:44 -03:00
parent 2df5b2f388
commit 1b54ce8bfd
2 changed files with 70 additions and 1 deletions

View File

@ -28,6 +28,8 @@ import { EnvVarObject } from '../types';
const SERVICE_NETWORK_MODE_REGEX = /service:\s*(.+)/;
const CONTAINER_NETWORK_MODE_REGEX = /container:\s*(.+)/;
const unsupportedSecurityOpt = (opt: string) => /label=.*/.test(opt);
export type ServiceStatus =
| 'Stopping'
| 'Stopped'
@ -383,6 +385,18 @@ export class Service {
}
delete config.tmpfs;
if (config.securityOpt != null) {
const unsupported = (config.securityOpt || []).filter(
unsupportedSecurityOpt,
);
if (unsupported.length > 0) {
log.warn(`Ignoring unsupported security options: ${unsupported}`);
config.securityOpt = (config.securityOpt || []).filter(
(opt) => !unsupportedSecurityOpt(opt),
);
}
}
// Normalise the config before passing it to defaults
ComposeUtils.normalizeNullValues(config);
@ -577,7 +591,14 @@ export class Service {
groupAdd: container.HostConfig.GroupAdd || [],
pid: container.HostConfig.PidMode || '',
pidsLimit: container.HostConfig.PidsLimit || 0,
securityOpt: container.HostConfig.SecurityOpt || [],
securityOpt: (container.HostConfig.SecurityOpt || []).filter(
// The docker engine v20+ adds selinux security options depending
// on the container configuration. Ignore those in the target state
// comparison as selinux is not supported by balenaOS so those options
// will not have any effect.
// https://github.com/moby/moby/blob/master/daemon/create.go#L214
(opt: string) => !unsupportedSecurityOpt(opt),
),
usernsMode: container.HostConfig.UsernsMode || '',
ipc: container.HostConfig.IpcMode || '',
macAddress: (container.Config as any).MacAddress || '',

View File

@ -1031,4 +1031,52 @@ describe('compose/service', () => {
.be.false;
});
});
describe('Security options', () => {
it('ignores selinux security options on the target state', async () => {
const service = await Service.fromComposeObject(
{
appId: 123,
serviceId: 123,
serviceName: 'test',
securityOpt: [
'label=user:USER',
'label=user:ROLE',
'seccomp=unconfined',
],
},
{ appName: 'test' } as any,
);
expect(service.config)
.to.have.property('securityOpt')
.that.deep.equals(['seccomp=unconfined']);
});
it('ignores selinux security options on the current state', async () => {
const mockContainer = createContainer({
Id: 'deadbeef',
Name: 'main_431889_572579',
Config: {
Labels: {
'io.resin.app-id': '1011165',
'io.resin.architecture': 'armv7hf',
'io.resin.service-id': '43697',
'io.resin.service-name': 'main',
'io.resin.supervised': 'true',
},
},
HostConfig: {
SecurityOpt: ['label=disable', 'seccomp=unconfined'],
},
});
const service = Service.fromDockerContainer(
await mockContainer.inspect(),
);
expect(service.config)
.to.have.property('securityOpt')
.that.deep.equals(['seccomp=unconfined']);
});
});
});