$(DOMAIN_CONF_SOURCES) \
$(SECRET_CONF_SOURCES) \
$(CPU_CONF_SOURCES) \
+ $(SECURITY_DRIVER_SOURCES) \
$(NWFILTER_PARAM_CONF_SOURCES)
+if WITH_SECDRIVER_SELINUX
+libvirt_lxc_SOURCES += $(SECURITY_DRIVER_SELINUX_SOURCES)
+endif
+if WITH_SECDRIVER_APPARMOR
+libvirt_lxc_SOURCES += $(SECURITY_DRIVER_APPARMOR_SOURCES)
+endif
libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(AM_LDFLAGS)
libvirt_lxc_LDADD = $(CAPNG_LIBS) $(YAJL_LIBS) \
$(LIBXML_LIBS) $(NUMACTL_LIBS) $(THREAD_LIBS) \
if WITH_DTRACE
libvirt_lxc_LDADD += probes.o
endif
+if WITH_SECDRIVER_SELINUX
+libvirt_lxc_LDADD += $(SELINUX_LIBS)
+endif
libvirt_lxc_CFLAGS = \
$(LIBPARTED_CFLAGS) \
$(NUMACTL_CFLAGS) \
libvirt_lxc_CFLAGS += $(BLKID_CFLAGS)
libvirt_lxc_LDADD += $(BLKID_LIBS)
endif
+if WITH_SECDRIVER_SELINUX
+libvirt_lxc_CFLAGS += $(SELINUX_CFLAGS)
+endif
endif
endif
EXTRA_DIST += $(LXC_CONTROLLER_SOURCES)
let value_sep = del /[ \t]*=[ \t]*/ " = "
let indent = del /[ \t]*/ ""
+ let array_sep = del /,[ \t\n]*/ ", "
+ let array_start = del /\[[ \t\n]*/ "[ "
+ let array_end = del /\]/ "]"
+
+ let str_val = del /\"/ "\"" . store /[^\"]*/ . del /\"/ "\""
let bool_val = store /0|1/
+ let int_val = store /[0-9]+/
+ let str_array_element = [ seq "el" . str_val ] . del /[ \t\n]*/ ""
+ let str_array_val = counter "el" . array_start . ( str_array_element . ( array_sep . str_array_element ) * ) ? . array_end
+ let str_entry (kw:string) = [ key kw . value_sep . str_val ]
let bool_entry (kw:string) = [ key kw . value_sep . bool_val ]
-
+ let int_entry (kw:string) = [ key kw . value_sep . int_val ]
+ let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]
(* Config entry grouped by function - same order as example config *)
let log_entry = bool_entry "log_with_libvirtd"
+ | str_entry "security_driver"
+ | bool_entry "security_default_confined"
+ | bool_entry "security_require_confined"
(* Each enty in the config is one of the following three ... *)
let entry = log_entry
# This is disabled by default, uncomment below to enable it.
#
# log_with_libvirtd = 1
+
+
+# The default security driver is SELinux. If SELinux is disabled
+# on the host, then the security driver will automatically disable
+# itself. If you wish to disable QEMU SELinux security driver while
+# leaving SELinux enabled for the host in general, then set this
+# to 'none' instead.
+#
+# security_driver = "selinux"
+
+# If set to non-zero, then the default security labeling
+# will make guests confined. If set to zero, then guests
+# will be unconfined by default. Defaults to 0.
+# security_default_confined = 1
+
+# If set to non-zero, then attempts to create unconfined
+# guests will be blocked. Defaults to 0.
+# security_require_confined = 1
/* Functions */
-virCapsPtr lxcCapsInit(void)
+virCapsPtr lxcCapsInit(lxc_driver_t *driver)
{
struct utsname utsname;
virCapsPtr caps;
/* LXC Requires an emulator in the XML */
virCapabilitiesSetEmulatorRequired(caps);
+ if (driver) {
+ /* Security driver data */
+ const char *doi, *model;
+
+ doi = virSecurityManagerGetDOI(driver->securityManager);
+ model = virSecurityManagerGetModel(driver->securityManager);
+ if (STRNEQ(model, "none")) {
+ if (!(caps->host.secModel.model = strdup(model)))
+ goto no_memory;
+ if (!(caps->host.secModel.doi = strdup(doi)))
+ goto no_memory;
+ }
+
+ VIR_DEBUG("Initialized caps for security driver \"%s\" with "
+ "DOI \"%s\"", model, doi);
+ } else {
+ VIR_INFO("No driver, not initializing security driver");
+ }
+
return caps;
+no_memory:
+ virReportOOMError();
+
error:
virCapabilitiesFree(caps);
return NULL;
virConfPtr conf;
virConfValuePtr p;
+ driver->securityDefaultConfined = false;
+ driver->securityRequireConfined = false;
+
/* Set the container configuration directory */
if ((driver->configDir = strdup(LXC_CONFIG_DIR)) == NULL)
goto no_memory;
if (!conf)
goto done;
+#define CHECK_TYPE(name,typ) if (p && p->type != (typ)) { \
+ lxcError(VIR_ERR_INTERNAL_ERROR, \
+ "%s: %s: expected type " #typ, \
+ filename, (name)); \
+ virConfFree(conf); \
+ return -1; \
+ }
+
p = virConfGetValue(conf, "log_with_libvirtd");
- if (p) {
- if (p->type != VIR_CONF_LONG)
- VIR_WARN("lxcLoadDriverConfig: invalid setting: log_with_libvirtd");
- else
- driver->log_libvirtd = p->l;
+ CHECK_TYPE ("log_with_libvirtd", VIR_CONF_LONG);
+ if (p) driver->log_libvirtd = p->l;
+
+ p = virConfGetValue (conf, "security_driver");
+ CHECK_TYPE ("security_driver", VIR_CONF_STRING);
+ if (p && p->str) {
+ if (!(driver->securityDriverName = strdup(p->str))) {
+ virReportOOMError();
+ virConfFree(conf);
+ return -1;
+ }
}
+ p = virConfGetValue (conf, "security_default_confined");
+ CHECK_TYPE ("security_default_confined", VIR_CONF_LONG);
+ if (p) driver->securityDefaultConfined = p->l;
+
+ p = virConfGetValue (conf, "security_require_confined");
+ CHECK_TYPE ("security_require_confined", VIR_CONF_LONG);
+ if (p) driver->securityRequireConfined = p->l;
+
+
+#undef CHECK_TYPE
+
virConfFree(conf);
done:
# include "capabilities.h"
# include "threads.h"
# include "cgroup.h"
+# include "security/security_manager.h"
# include "configmake.h"
# define LXC_CONFIG_DIR SYSCONFDIR "/libvirt/lxc"
virDomainEventStatePtr domainEventState;
+ char *securityDriverName;
+ bool securityDefaultConfined;
+ bool securityRequireConfined;
+ virSecurityManagerPtr securityManager;
+
/* Mapping of 'char *uuidstr' -> virConnectPtr
* of guests which will be automatically killed
* when the virConnectPtr is closed*/
};
int lxcLoadDriverConfig(lxc_driver_t *driver);
-virCapsPtr lxcCapsInit(void);
+virCapsPtr lxcCapsInit(lxc_driver_t *driver);
# define lxcError(code, ...) \
virReportErrorHelper(VIR_FROM_LXC, code, __FILE__, \
typedef struct __lxc_child_argv lxc_child_argv_t;
struct __lxc_child_argv {
virDomainDefPtr config;
+ virSecurityManagerPtr securityDriver;
unsigned int nveths;
char **veths;
int monitor;
goto cleanup;
}
+ VIR_DEBUG("Setting up security labeling");
+ if (virSecurityManagerSetProcessLabel(argv->securityDriver, vmDef) < 0)
+ goto cleanup;
+
ret = 0;
cleanup:
VIR_FREE(ttyPath);
* Returns PID of container on success or -1 in case of error
*/
int lxcContainerStart(virDomainDefPtr def,
+ virSecurityManagerPtr securityDriver,
unsigned int nveths,
char **veths,
int control,
int cflags;
int stacksize = getpagesize() * 4;
char *stack, *stacktop;
- lxc_child_argv_t args = { def, nveths, veths, control,
+ lxc_child_argv_t args = { def, securityDriver,
+ nveths, veths, control,
ttyPaths, nttyPaths, handshakefd};
/* allocate a stack for the container */
# define LXC_CONTAINER_H
# include "lxc_conf.h"
+# include "security/security_manager.h"
enum {
LXC_CONTAINER_FEATURE_NET = (1 << 0),
int lxcContainerWaitForContinue(int control);
int lxcContainerStart(virDomainDefPtr def,
+ virSecurityManagerPtr securityDriver,
unsigned int nveths,
char **veths,
int control,
static int
lxcControllerRun(virDomainDefPtr def,
+ virSecurityManagerPtr securityDriver,
unsigned int nveths,
char **veths,
int monitor,
goto cleanup;
if ((container = lxcContainerStart(def,
+ securityDriver,
nveths,
veths,
control[1],
{ "veth", 1, NULL, 'v' },
{ "console", 1, NULL, 'c' },
{ "handshakefd", 1, NULL, 's' },
+ { "security", 1, NULL, 'S' },
{ "help", 0, NULL, 'h' },
{ 0, 0, 0, 0 },
};
int *ttyFDs = NULL;
size_t nttyFDs = 0;
+ virSecurityManagerPtr securityDriver = NULL;
if (setlocale(LC_ALL, "") == NULL ||
bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
while (1) {
int c;
- c = getopt_long(argc, argv, "dn:v:m:c:s:h",
+ c = getopt_long(argc, argv, "dn:v:m:c:s:h:S:",
options, NULL);
if (c == -1)
}
break;
+ case 'S':
+ if (!(securityDriver = virSecurityManagerNew(optarg, false, false, false))) {
+ fprintf(stderr, "Cannot create security manager '%s'",
+ optarg);
+ goto cleanup;
+ }
+ break;
+
case 'h':
case '?':
fprintf(stderr, "\n");
fprintf(stderr, " -c FD, --console FD\n");
fprintf(stderr, " -v VETH, --veth VETH\n");
fprintf(stderr, " -s FD, --handshakefd FD\n");
+ fprintf(stderr, " -S NAME, --security NAME\n");
fprintf(stderr, " -h, --help\n");
fprintf(stderr, "\n");
goto cleanup;
}
}
+ if (securityDriver == NULL) {
+ if (!(securityDriver = virSecurityManagerNew("none", false, false, false))) {
+ fprintf(stderr, "%s: cannot initialize nop security manager", argv[0]);
+ goto cleanup;
+ }
+ }
+
if (name == NULL) {
fprintf(stderr, "%s: missing --name argument for configuration\n", argv[0]);
virEventRegisterDefaultImpl();
- if ((caps = lxcCapsInit()) == NULL)
+ if ((caps = lxcCapsInit(NULL)) == NULL)
goto cleanup;
if ((configFile = virDomainConfigFile(LXC_STATE_DIR,
goto cleanup;
}
- rc = lxcControllerRun(def, nveths, veths, monitor, client,
+ rc = lxcControllerRun(def, securityDriver,
+ nveths, veths, monitor, client,
ttyFDs, nttyFDs, handshakefd);
-
cleanup:
if (def)
virPidFileDelete(LXC_STATE_DIR, def->name);
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
+ if (virSecurityManagerVerify(driver->securityManager, def) < 0)
+ goto cleanup;
+
if ((dupVM = virDomainObjIsDuplicate(&driver->domains, def, 0)) < 0)
goto cleanup;
return -1;
}
- if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ if (virSecurityManagerSetSocketLabel(driver->securityManager, vm->def) < 0) {
+ VIR_ERROR(_("Failed to set security context for monitor for %s"),
+ vm->def->name);
+ goto error;
+ }
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+
+ if (virSecurityManagerClearSocketLabel(driver->securityManager, vm->def) < 0) {
+ VIR_ERROR(_("Failed to clear security context for monitor for %s"),
+ vm->def->name);
+ goto error;
+ }
+
+ if (fd < 0) {
virReportSystemError(errno, "%s",
_("Failed to create client socket"));
goto error;
return -1;
}
+ virSecurityManagerRestoreAllLabel(driver->securityManager,
+ vm->def, false);
+ virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
+ /* Clear out dynamically assigned labels */
+ if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
+ VIR_FREE(vm->def->seclabel.model);
+ VIR_FREE(vm->def->seclabel.label);
+ VIR_FREE(vm->def->seclabel.imagelabel);
+ }
+
if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) == 0) {
rc = virCgroupKillPainfully(group);
if (rc < 0) {
virCommandAddArgFormat(cmd, "%d", ttyFDs[i]);
virCommandPreserveFD(cmd, ttyFDs[i]);
}
+
+ if (driver->securityDriverName)
+ virCommandAddArgPair(cmd, "--security", driver->securityDriverName);
+
virCommandAddArg(cmd, "--handshake");
virCommandAddArgFormat(cmd, "%d", handshakefd);
virCommandAddArg(cmd, "--background");
virReportOOMError();
goto cleanup;
}
+
+ /* If you are using a SecurityDriver with dynamic labelling,
+ then generate a security label for isolation */
+ VIR_DEBUG("Generating domain security label (if required)");
+ if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DEFAULT)
+ vm->def->seclabel.type = VIR_DOMAIN_SECLABEL_NONE;
+
+ if (virSecurityManagerGenLabel(driver->securityManager, vm->def) < 0) {
+ virDomainAuditSecurityLabel(vm, false);
+ goto cleanup;
+ }
+ virDomainAuditSecurityLabel(vm, true);
+
+ VIR_DEBUG("Setting domain security labels");
+ if (virSecurityManagerSetAllLabel(driver->securityManager,
+ vm->def, NULL) < 0)
+ goto cleanup;
+
for (i = 0 ; i < vm->def->nconsoles ; i++)
ttyFDs[i] = -1;
if (rc != 0) {
VIR_FORCE_CLOSE(priv->monitor);
virDomainConfVMNWFilterTeardown(vm);
+
+ virSecurityManagerRestoreAllLabel(driver->securityManager,
+ vm->def, false);
+ virSecurityManagerReleaseLabel(driver->securityManager, vm->def);
+ /* Clear out dynamically assigned labels */
+ if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
+ VIR_FREE(vm->def->seclabel.model);
+ VIR_FREE(vm->def->seclabel.label);
+ VIR_FREE(vm->def->seclabel.imagelabel);
+ }
}
for (i = 0 ; i < nttyFDs ; i++)
VIR_FORCE_CLOSE(ttyFDs[i]);
VIR_DOMAIN_XML_INACTIVE)))
goto cleanup;
+ if (virSecurityManagerVerify(driver->securityManager, def) < 0)
+ goto cleanup;
+
if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0)
goto cleanup;
}
+static int lxcDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr seclabel)
+{
+ lxc_driver_t *driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ lxcDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ memset(seclabel, 0, sizeof(*seclabel));
+
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ lxcError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ goto cleanup;
+ }
+
+ if (!virDomainVirtTypeToString(vm->def->virtType)) {
+ lxcError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown virt type in domain definition '%d'"),
+ vm->def->virtType);
+ goto cleanup;
+ }
+
+ /*
+ * Theoretically, the pid can be replaced during this operation and
+ * return the label of a different process. If atomicity is needed,
+ * further validation will be required.
+ *
+ * Comment from Dan Berrange:
+ *
+ * Well the PID as stored in the virDomainObjPtr can't be changed
+ * because you've got a locked object. The OS level PID could have
+ * exited, though and in extreme circumstances have cycled through all
+ * PIDs back to ours. We could sanity check that our PID still exists
+ * after reading the label, by checking that our FD connecting to the
+ * LXC monitor hasn't seen SIGHUP/ERR on poll().
+ */
+ if (virDomainObjIsActive(vm)) {
+ if (virSecurityManagerGetProcessLabel(driver->securityManager,
+ vm->def, vm->pid, seclabel) < 0) {
+ lxcError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Failed to get security label"));
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ lxcDriverUnlock(driver);
+ return ret;
+}
+
+static int lxcNodeGetSecurityModel(virConnectPtr conn,
+ virSecurityModelPtr secmodel)
+{
+ lxc_driver_t *driver = conn->privateData;
+ int ret = 0;
+
+ lxcDriverLock(driver);
+ memset(secmodel, 0, sizeof(*secmodel));
+
+ /* NULL indicates no driver, which we treat as
+ * success, but simply return no data in *secmodel */
+ if (driver->caps->host.secModel.model == NULL)
+ goto cleanup;
+
+ if (!virStrcpy(secmodel->model, driver->caps->host.secModel.model,
+ VIR_SECURITY_MODEL_BUFLEN)) {
+ lxcError(VIR_ERR_INTERNAL_ERROR,
+ _("security model string exceeds max %d bytes"),
+ VIR_SECURITY_MODEL_BUFLEN - 1);
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (!virStrcpy(secmodel->doi, driver->caps->host.secModel.doi,
+ VIR_SECURITY_DOI_BUFLEN)) {
+ lxcError(VIR_ERR_INTERNAL_ERROR,
+ _("security DOI string exceeds max %d bytes"),
+ VIR_SECURITY_DOI_BUFLEN-1);
+ ret = -1;
+ goto cleanup;
+ }
+
+cleanup:
+ lxcDriverUnlock(driver);
+ return ret;
+}
+
+
static int
lxcDomainEventRegister(virConnectPtr conn,
virConnectDomainEventCallback callback,
lxcMonitorEvent,
vm, NULL)) < 0)
goto error;
+
+ if (virSecurityManagerReserveLabel(driver->securityManager,
+ vm->def, vm->pid) < 0)
+ goto error;
} else {
vm->def->id = -1;
VIR_FORCE_CLOSE(priv->monitor);
}
+static int
+lxcSecurityInit(lxc_driver_t *driver)
+{
+ virSecurityManagerPtr mgr = virSecurityManagerNew(driver->securityDriverName,
+ false,
+ driver->securityDefaultConfined,
+ driver->securityRequireConfined);
+ if (!mgr)
+ goto error;
+
+ driver->securityManager = mgr;
+
+ return 0;
+
+error:
+ VIR_ERROR(_("Failed to initialize security drivers"));
+ virSecurityManagerFree(mgr);
+ return -1;
+}
+
+
static int lxcStartup(int privileged)
{
char *ld;
if (lxcLoadDriverConfig(lxc_driver) < 0)
goto cleanup;
- if ((lxc_driver->caps = lxcCapsInit()) == NULL)
+ if (lxcSecurityInit(lxc_driver) < 0)
+ goto cleanup;
+
+ if ((lxc_driver->caps = lxcCapsInit(lxc_driver)) == NULL)
goto cleanup;
lxc_driver->caps->privateDataAllocFunc = lxcDomainObjPrivateAlloc;
lxcProcessAutoDestroyShutdown(lxc_driver);
virCapabilitiesFree(lxc_driver->caps);
+ virSecurityManagerFree(lxc_driver->securityManager);
VIR_FREE(lxc_driver->configDir);
VIR_FREE(lxc_driver->autostartDir);
VIR_FREE(lxc_driver->stateDir);
.domainGetBlkioParameters = lxcDomainGetBlkioParameters, /* 0.9.8 */
.domainGetInfo = lxcDomainGetInfo, /* 0.4.2 */
.domainGetState = lxcDomainGetState, /* 0.9.2 */
+ .domainGetSecurityLabel = lxcDomainGetSecurityLabel, /* 0.9.10 */
+ .nodeGetSecurityModel = lxcNodeGetSecurityModel, /* 0.9.10 */
.domainGetXMLDesc = lxcDomainGetXMLDesc, /* 0.4.2 */
.listDefinedDomains = lxcListDefinedDomains, /* 0.4.2 */
.numOfDefinedDomains = lxcNumDefinedDomains, /* 0.4.2 */
# This is disabled by default, uncomment below to enable it.
#
log_with_libvirtd = 1
+security_driver = \"selinux\"
"
test Libvirtd_lxc.lns get conf =
{ "#comment" = "This is disabled by default, uncomment below to enable it." }
{ "#comment" = "" }
{ "log_with_libvirtd" = "1" }
+{ "security_driver" = "selinux" }