fix(compute): add null check before hashing admin password

Change-Id: If45bf4a0bb8a3cdc6d11a5db818609e58f2983ef
Signed-off-by: Hüseyin Altun <huseyin.altun@tubitak.gov.tr>
This commit is contained in:
huseyin.altun
2025-06-12 13:15:01 +00:00
committed by Hüseyin Altun
parent 60743e8853
commit 4c2a8c70fb
6 changed files with 39 additions and 7 deletions

View File

@@ -83,6 +83,7 @@
"react-fast-compare": "^3.0.1",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"sha512-crypt-ts": "^0.1.26",
"uuid": "^8.3.2"
},
"devDependencies": {

View File

@@ -0,0 +1,4 @@
---
features:
- |
feat: Hash instance passwords for cloud-init

View File

@@ -24,6 +24,7 @@ import { isEmpty, isFinite } from 'lodash';
import {
getUserData,
canCreateIronicByEndpoint,
hashPasswordForCloudInit,
} from 'resources/nova/instance';
import { ironicOriginEndpoint } from 'client/client/constants';
import Notify from 'components/Notify';
@@ -332,6 +333,9 @@ export class CreateIronic extends StepAction {
getUserData(server.adminPass, userData, username || 'root')
);
}
if (server.adminPass) {
server.adminPass = hashPasswordForCloudInit(server.adminPass);
}
return {
server,
};

View File

@@ -21,7 +21,7 @@ import globalServerStore from 'stores/nova/instance';
import globalProjectStore from 'stores/keystone/project';
import classnames from 'classnames';
import { isEmpty, isFinite, isString } from 'lodash';
import { getUserData } from 'resources/nova/instance';
import { getUserData, hashPasswordForCloudInit } from 'resources/nova/instance';
import { getAllDataDisks } from 'resources/cinder/snapshot';
import { getGiBValue } from 'utils/index';
import Notify from 'components/Notify';
@@ -748,6 +748,9 @@ export class StepCreate extends StepAction {
getUserData(server.adminPass, userData, username || 'root')
);
}
if (server.adminPass) {
server.adminPass = hashPasswordForCloudInit(server.adminPass);
}
const body = {
server,
};

View File

@@ -22,6 +22,7 @@ import PopActionEvent from 'src/components/Popover/PopActionEvent';
import lockSvg from 'asset/image/lock.svg';
import unlockSvg from 'asset/image/unlock.svg';
import { isEmpty } from 'lodash';
import { sha512 } from 'sha512-crypt-ts';
const lockIcon = (
<Tooltip
@@ -217,7 +218,7 @@ const passwordAndUserData =
'Content-Disposition: attachment; filename="passwd-script.txt" \n' +
'\n' +
'#!/bin/sh\n' +
"echo 'USER_NAME:USER_PASSWORD' | chpasswd\n" +
"echo 'USER_NAME:USER_PASSWORD' | chpasswd --encrypted \n" +
'\n' +
'--===============2309984059743762475==\n' +
'Content-Type: text/x-shellscript; charset="us-ascii" \n' +
@@ -250,7 +251,7 @@ const onlyPassword =
'Content-Disposition: attachment; filename="passwd-script.txt" \n' +
'\n' +
'#!/bin/sh\n' +
"echo 'USER_NAME:USER_PASSWORD' | chpasswd\n" +
"echo 'USER_NAME:USER_PASSWORD' | chpasswd --encrypted \n" +
'\n' +
'--===============2309984059743762475==--';
@@ -264,18 +265,22 @@ const onlyUserData =
'Content-Transfer-Encoding: 7bit\n' +
'Content-Disposition: attachment; filename="init-shell.txt" \n' +
'\n' +
'USER_DATA\n' +
'USER_DATA | chpasswd --encrypted\n' +
'\n' +
'--===============2309984059743762475==--';
export const getUserData = (password, userData, username = 'root') => {
const hashedPassword = hashPasswordForCloudInit(password);
if (password && userData) {
let str = passwordAndUserData.replace(/USER_PASSWORD/g, password);
let str = passwordAndUserData.replace(/USER_PASSWORD/g, hashedPassword);
str = str.replace(/USER_NAME/g, username);
return str.replace(/USER_DATA/g, userData);
}
if (password) {
const str = onlyPassword.replace(/USER_PASSWORD/g, password);
const str = onlyPassword.replace(/USER_PASSWORD/g, hashedPassword);
return str.replace(/USER_NAME/g, username);
}
return onlyUserData.replace(/USER_DATA/g, userData);
@@ -583,3 +588,17 @@ export const isBootFromVolume = (item) => {
}
return !item.image;
};
export const hashPasswordForCloudInit = (password) => {
const hashedPassword = sha512.crypt(password, randomSalt());
return hashedPassword;
};
const randomSalt = () => {
const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./';
const length = 8 + Math.random() * 8;
let result = '';
for (let i = length; i > 0; --i) {
result += alphabet[Math.floor(Math.random() * alphabet.length)];
}
return result;
}

View File

@@ -18,6 +18,7 @@ import client from 'client';
import Base from 'stores/base';
import { mapperRule } from 'resources/neutron/security-group-rule';
import { RecycleBinStore } from '../skyline/recycle-server';
import { hashPasswordForCloudInit } from 'src/resources/nova/instance';
export class ServerStore extends Base {
@observable
@@ -380,7 +381,7 @@ export class ServerStore extends Base {
async changePassword({ id, password }) {
const body = {
changePassword: {
adminPass: password,
adminPass: hashPasswordForCloudInit(password),
},
};
return this.operation({ body, id });