Skip to content

Commit 6efe068

Browse files
Merge branch 'main' into cmyers_wazuh-int
2 parents 6ceb068 + c56076b commit 6efe068

File tree

8 files changed

+80
-11
lines changed

8 files changed

+80
-11
lines changed

create-a-container/bin/create-container.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ async function main() {
251251
cores: 4,
252252
features: 'nesting=1,keyctl=1,fuse=1',
253253
memory: 4096,
254-
net0: 'name=eth0,ip=dhcp,bridge=vmbr0,host-managed=1',
254+
net0: `name=eth0,ip=dhcp,bridge=${node.networkBridge}`,
255255
searchdomain: site.internalDomain,
256256
swap: 0,
257257
onboot: 1,
@@ -301,7 +301,7 @@ async function main() {
301301
cores: 4,
302302
features: 'nesting=1,keyctl=1,fuse=1',
303303
memory: 4096,
304-
net0: 'name=eth0,ip=dhcp,bridge=vmbr0',
304+
net0: `name=eth0,ip=dhcp,bridge=${node.networkBridge}`,
305305
searchdomain: site.internalDomain,
306306
swap: 0,
307307
onboot: 1,
@@ -351,6 +351,16 @@ async function main() {
351351
// Use user entrypoint if specified, otherwise keep default
352352
const finalEntrypoint = container.entrypoint || defaultEntrypoint;
353353

354+
// OCI images that aren't system containers (entrypoint != /sbin/init) need
355+
// host-managed networking since they lack their own DHCP/networking stack
356+
if (isDocker && finalEntrypoint !== '/sbin/init') {
357+
const net0 = defaultConfig['net0'] || `name=eth0,ip=dhcp,bridge=${node.networkBridge}`;
358+
console.log('Non-init container detected, enabling host-managed networking');
359+
await client.updateLxcConfig(node.name, vmid, {
360+
net0: `${net0},host-managed=1`
361+
});
362+
}
363+
354364
// Build config to apply
355365
const envConfig = {};
356366
if (finalEntrypoint) {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
/** @type {import('sequelize-cli').Migration} */
4+
module.exports = {
5+
async up(queryInterface, Sequelize) {
6+
await queryInterface.addColumn('Nodes', 'networkBridge', {
7+
type: Sequelize.STRING(255),
8+
allowNull: false,
9+
defaultValue: 'vmbr0',
10+
after: 'volumeStorage'
11+
});
12+
},
13+
14+
async down(queryInterface) {
15+
await queryInterface.removeColumn('Nodes', 'networkBridge');
16+
}
17+
};

create-a-container/models/node.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ module.exports = (sequelize, DataTypes) => {
9090
type: DataTypes.STRING(255),
9191
allowNull: false,
9292
defaultValue: 'local-lvm'
93+
},
94+
networkBridge: {
95+
type: DataTypes.STRING(255),
96+
allowNull: false,
97+
defaultValue: 'vmbr0'
9398
}
9499
}, {
95100
sequelize,

create-a-container/models/site.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,27 @@ module.exports = (sequelize, DataTypes) => {
2222
as: 'externalDomains'
2323
});
2424
}
25+
26+
/**
27+
* Returns all external domains sorted so that domains whose default site is
28+
* this site appear first (in id order), followed by all other domains (also
29+
* in id order).
30+
* @returns {Promise<Array>} Sorted array of ExternalDomain instances
31+
*/
32+
async getSortedExternalDomains() {
33+
const { ExternalDomain } = sequelize.models;
34+
const all = await ExternalDomain.findAll({ order: [['id', 'ASC']] });
35+
const defaults = [];
36+
const others = [];
37+
for (const domain of all) {
38+
if (domain.siteId === this.id) {
39+
defaults.push(domain);
40+
} else {
41+
others.push(domain);
42+
}
43+
}
44+
return [...defaults, ...others];
45+
}
2546
}
2647
Site.init({
2748
name: DataTypes.STRING,

create-a-container/routers/containers.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,8 @@ router.get('/new', requireAuth, async (req, res) => {
111111
return res.redirect('/sites');
112112
}
113113

114-
// Get all external domains (global)
115-
const externalDomains = await ExternalDomain.findAll({
116-
order: [['name', 'ASC']]
117-
});
114+
// Get all external domains: default domains for this site first (by id), then others (by id)
115+
const externalDomains = await site.getSortedExternalDomains();
118116

119117
if (isApi) {
120118
return res.json({
@@ -243,7 +241,8 @@ router.get('/:id/edit', requireAuth, async (req, res) => {
243241
return res.redirect(`/sites/${siteId}/containers`);
244242
}
245243

246-
const externalDomains = await ExternalDomain.findAll({ order: [['name', 'ASC']] });
244+
// Get all external domains: default domains for this site first (by id), then others (by id)
245+
const externalDomains = await site.getSortedExternalDomains();
247246

248247
return res.render('containers/form', {
249248
site,

create-a-container/routers/nodes.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ router.post('/', async (req, res) => {
117117
return res.redirect('/sites');
118118
}
119119

120-
const { name, ipv4Address, apiUrl, tokenId, secret, tlsVerify, imageStorage, volumeStorage } = req.body;
120+
const { name, ipv4Address, apiUrl, tokenId, secret, tlsVerify, imageStorage, volumeStorage, networkBridge } = req.body;
121121

122122
await Node.create({
123123
name,
@@ -128,6 +128,7 @@ router.post('/', async (req, res) => {
128128
tlsVerify: tlsVerify === '' || tlsVerify === null ? null : tlsVerify === 'true',
129129
imageStorage: imageStorage || 'local',
130130
volumeStorage: volumeStorage || 'local-lvm',
131+
networkBridge: networkBridge || 'vmbr0',
131132
siteId
132133
});
133134

@@ -215,6 +216,7 @@ router.post('/import', async (req, res) => {
215216
tlsVerify: tlsVerify === '' || tlsVerify === null ? null : tlsVerify === 'true',
216217
imageStorage,
217218
volumeStorage,
219+
networkBridge: 'vmbr0',
218220
siteId
219221
};
220222
}));
@@ -265,7 +267,7 @@ router.put('/:id', async (req, res) => {
265267
return res.redirect(`/sites/${siteId}/nodes`);
266268
}
267269

268-
const { name, ipv4Address, apiUrl, tokenId, secret, tlsVerify, imageStorage, volumeStorage } = req.body;
270+
const { name, ipv4Address, apiUrl, tokenId, secret, tlsVerify, imageStorage, volumeStorage, networkBridge } = req.body;
269271

270272
const updateData = {
271273
name,
@@ -274,7 +276,8 @@ router.put('/:id', async (req, res) => {
274276
tokenId: tokenId || null,
275277
tlsVerify: tlsVerify === '' || tlsVerify === null ? null : tlsVerify === 'true',
276278
imageStorage: imageStorage || 'local',
277-
volumeStorage: volumeStorage || 'local-lvm'
279+
volumeStorage: volumeStorage || 'local-lvm',
280+
networkBridge: networkBridge || 'vmbr0'
278281
};
279282

280283
// Only update secret if a new value was provided

create-a-container/views/nodes/form.ejs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@
130130
<div class="form-text">Storage for container root filesystems</div>
131131
</div>
132132

133+
<div class="mb-3">
134+
<label for="networkBridge" class="form-label">Network Bridge</label>
135+
<input
136+
type="text"
137+
class="form-control"
138+
id="networkBridge"
139+
name="networkBridge"
140+
value="<%= node && node.networkBridge ? node.networkBridge : 'vmbr0' %>"
141+
placeholder="e.g., vmbr0"
142+
>
143+
<div class="form-text">Proxmox network bridge used when creating containers</div>
144+
</div>
145+
133146
<div class="d-flex justify-content-between">
134147
<a href="/sites/<%= site.id %>/nodes" class="btn btn-secondary" aria-label="Cancel and return to nodes list">Cancel</a>
135148
<button type="submit" class="btn btn-primary">

mie-opensource-landing/docs/developers/database-schema.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ erDiagram
4747
boolean disableTlsVerification
4848
string imageStorage "default: local"
4949
string volumeStorage "default: local-lvm"
50+
string networkBridge "default: vmbr0"
5051
int siteId FK
5152
}
5253
@@ -173,7 +174,7 @@ erDiagram
173174
Top-level organizational unit. Has many Nodes. Has many ExternalDomains (as default site). `externalIp` is the public IP used as the target for Cloudflare DNS A records when cross-site HTTP services are created.
174175

175176
### Node
176-
Proxmox VE server within a site. `name` must match Proxmox hostname (unique). `imageStorage` defaults to `'local'` (CT templates). `volumeStorage` defaults to `'local-lvm'` (container rootfs). Belongs to Site, has many Containers.
177+
Proxmox VE server within a site. `name` must match Proxmox hostname (unique). `imageStorage` defaults to `'local'` (CT templates). `volumeStorage` defaults to `'local-lvm'` (container rootfs). `networkBridge` defaults to `'vmbr0'` (Proxmox bridge used in container net0 config). Belongs to Site, has many Containers.
177178

178179
### Container
179180
LXC container on a Proxmox node. Unique composite index on `(nodeId, containerId)`. `hostname`, `macAddress`, `ipv4Address` globally unique. Belongs to Node and optionally to a Job.

0 commit comments

Comments
 (0)