refs #871 #872 adds validations to dont enter in another directory in pxe and pxe-templates like etc passwd
parent
a005dec399
commit
653f8d6061
|
@ -871,8 +871,8 @@ public function getBootFiles(): JsonResponse
|
|||
*/
|
||||
|
||||
|
||||
public function getBootFile(string $mac): Response
|
||||
{
|
||||
public function getBootFile(string $mac): Response
|
||||
{
|
||||
// Ruta donde están alojados los archivos de arranque
|
||||
$directory = '/opt/ogboot/tftpboot/ipxe_scripts';
|
||||
|
||||
|
@ -880,16 +880,30 @@ public function getBootFile(string $mac): Response
|
|||
$mac = $this->validateAndFormatMac($mac ?? null);
|
||||
$fileName = "01-" . $mac;
|
||||
$filePath = "$directory/$fileName";
|
||||
$this->logger->info('FilePath: ' . $filePath);
|
||||
|
||||
// Normalizar la ruta del archivo para evitar accesos fuera del directorio permitido
|
||||
$realFilePath = realpath($directory) . '/' . $fileName;
|
||||
|
||||
// Verificar que el archivo está dentro del directorio permitido
|
||||
if (strpos($realFilePath, realpath($directory)) !== 0) {
|
||||
return new JsonResponse(
|
||||
[
|
||||
'error' => 'UNAUTHORIZED_ACCESS',
|
||||
'message' => 'Intento de acceso no autorizado fuera del directorio de arranque.'
|
||||
],
|
||||
Response::HTTP_FORBIDDEN
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar si el archivo existe
|
||||
if (!file_exists($filePath)) {
|
||||
if (!file_exists($realFilePath)) {
|
||||
return new JsonResponse(
|
||||
['error' => 'NOT_FOUND', 'message' => 'No boot file found for the specified MAC address.'],
|
||||
Response::HTTP_NOT_FOUND
|
||||
);
|
||||
}
|
||||
|
||||
$content = file_get_contents($filePath);
|
||||
$content = file_get_contents($realFilePath);
|
||||
$templateName = 'unknown'; // Valor por defecto si no se encuentra la plantilla
|
||||
$contentLines = explode("\n", $content);
|
||||
|
||||
|
@ -954,93 +968,42 @@ public function getBootFile(string $mac): Response
|
|||
['success' => 'Boot file retrieved successfully', 'message' => $result],
|
||||
Response::HTTP_OK
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @Route("/ogboot/v1/pxes", name="create_boot_file", methods={"POST"})
|
||||
* @Route("/ogboot/v1/pxes", methods={"POST"})
|
||||
* @OA\Post(
|
||||
* path="/ogboot/v1/pxes",
|
||||
* summary="Create a new PXE boot file",
|
||||
* description="Creates a PXE boot file based on the provided template and parameters. All parameters except 'template_name' and 'mac' are optional.",
|
||||
* summary="Crear archivo PXE",
|
||||
* description="Crea un archivo de arranque PXE con la MAC y plantilla proporcionadas.",
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="template_name", type="string", description="Template name (required)", example="pxe"),
|
||||
* @OA\Property(property="mac", type="string", description="MAC address (required)", example="00:50:56:22:11:12"),
|
||||
* @OA\Property(property="lang", type="string", description="Language (optional)", example="es_ES.UTF-8"),
|
||||
* @OA\Property(property="ip", type="string", description="IP address (optional)", example="192.168.2.11"),
|
||||
* @OA\Property(property="server_ip", type="string", description="Server IP address (optional)", example="192.168.2.1"),
|
||||
* @OA\Property(property="router", type="string", description="Router (optional)", example="192.168.2.1"),
|
||||
* @OA\Property(property="netmask", type="string", description="Netmask (optional)", example="255.255.255.0"),
|
||||
* @OA\Property(property="computer_name", type="string", description="Computer name (optional)", example="pc11"),
|
||||
* @OA\Property(property="netiface", type="string", description="Network interface (optional)", example="eth0"),
|
||||
* @OA\Property(property="group", type="string", description="Group (optional)", example="Aula_virtual"),
|
||||
* @OA\Property(property="ogrepo", type="string", description="Repository IP (optional)", example="192.168.2.1"),
|
||||
* @OA\Property(property="oglive", type="string", description="Live server IP (optional)", example="192.168.2.1"),
|
||||
* @OA\Property(property="oglog", type="string", description="Log server IP (optional)", example="192.168.2.1"),
|
||||
* @OA\Property(property="ogshare", type="string", description="Share server IP (optional)", example="192.168.2.1"),
|
||||
* @OA\Property(property="oglivedir", type="string", description="Live directory (optional)", example="ogLive"),
|
||||
* @OA\Property(property="ogprof", type="string", description="Is professor (optional)", example="false"),
|
||||
* @OA\Property(property="hardprofile", type="string", description="Hardware profile (optional)", example=""),
|
||||
* @OA\Property(property="ogntp", type="string", description="NTP server (optional)", example=""),
|
||||
* @OA\Property(property="ogdns", type="string", description="DNS server (optional)", example=""),
|
||||
* @OA\Property(property="ogproxy", type="string", description="Proxy server (optional)", example=""),
|
||||
* @OA\Property(property="ogunit", type="string", description="Unit directory (optional)", example=""),
|
||||
* @OA\Property(property="resolution", type="string", description="Screen resolution (optional)", example="788")
|
||||
* @OA\Property(property="mac", type="string", example="00:50:56:22:11:12"),
|
||||
* @OA\Property(property="template_name", type="string", example="mi_plantilla.ipxe"),
|
||||
* @OA\Property(property="server_ip", type="string", example="192.168.2.1"),
|
||||
* @OA\Property(property="oglivedir", type="string", example="ogLive")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="PXE boot file created successfully",
|
||||
* description="Plantilla creada exitosamente",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="string", example="Plantilla creada con éxito"),
|
||||
* @OA\Property(property="message", type="string")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="Invalid input",
|
||||
* @OA\JsonContent(
|
||||
* oneOf={
|
||||
* @OA\Schema(
|
||||
* @OA\Property(property="error", type="string", example="Missing required fields"),
|
||||
* @OA\Property(property="details", type="string", example="MAC and template_name are required")
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* @OA\Property(property="error", type="string", example="Invalid MAC address"),
|
||||
* @OA\Property(property="details", type="string", example="MAC address contains invalid characters")
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* @OA\Property(property="error", type="string", example="Template not found"),
|
||||
* @OA\Property(property="details", type="string", example="The specified template does not exist")
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=404,
|
||||
* description="Template not found",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="error", type="string", example="Template not found")
|
||||
* )
|
||||
* description="Entrada inválida o datos requeridos faltantes"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Internal server error",
|
||||
* @OA\JsonContent(
|
||||
* oneOf={
|
||||
* @OA\Schema(
|
||||
* @OA\Property(property="error", type="string", example="Failed to create PXE boot file"),
|
||||
* @OA\Property(property="details", type="string", example="Error writing to file system")
|
||||
* ),
|
||||
* @OA\Schema(
|
||||
* @OA\Property(property="error", type="string", example="Failed to read template"),
|
||||
* @OA\Property(property="details", type="string", example="Could not access the template file")
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* description="Error interno del servidor al crear el archivo PXE"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
@ -1058,6 +1021,14 @@ public function createBootFile(Request $request): JsonResponse
|
|||
return new JsonResponse(['error' => 'Missing required fields: mac and template_name'], Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Validación adicional para asegurarnos de que no hay caracteres inseguros
|
||||
if (!preg_match('/^[a-zA-Z0-9._-]+$/', $templateName) || strpos($templateName, '..') !== false) {
|
||||
return new JsonResponse(
|
||||
['error' => 'INVALID_TEMPLATE_NAME', 'message' => 'Nombre de la plantilla no válido o intento de acceso no autorizado.'],
|
||||
Response::HTTP_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
// Parámetros opcionales
|
||||
$parameters = [
|
||||
'LANG' => $data['lang'] ?? 'es_ES.UTF-8',
|
||||
|
@ -1081,18 +1052,17 @@ public function createBootFile(Request $request): JsonResponse
|
|||
'resolution' => $data['resolution'] ?? '788'
|
||||
];
|
||||
|
||||
# $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates';
|
||||
$templateDir = $this->tftpbootDir . '/ipxe_scripts/templates';
|
||||
$templatePath = $templateDir . '/' . $templateName;
|
||||
|
||||
// Verificar si la plantilla existe
|
||||
if (!file_exists($templatePath)) {
|
||||
return new JsonResponse(['error' => 'Template not found'], Response::HTTP_NOT_FOUND);
|
||||
return new JsonResponse(['error' => 'TEMPLATE_NOT_FOUND', 'message' => 'No se encontró la plantilla especificada'], Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$templateContent = file_get_contents($templatePath);
|
||||
if ($templateContent === false) {
|
||||
return new JsonResponse(['error' => 'Failed to read template'], Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
return new JsonResponse(['error' => 'FAILED_TO_READ_TEMPLATE', 'message' => 'Error al leer la plantilla'], Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
// Construcción de los argumentos del kernel
|
||||
|
@ -1123,10 +1093,8 @@ public function createBootFile(Request $request): JsonResponse
|
|||
|
||||
// Insertar el comentario con el nombre de la plantilla después de #!ipxe
|
||||
if (strpos($pxeContent, '#!ipxe') === 0) {
|
||||
// Añadir el comentario después de #!ipxe
|
||||
$pxeContent = "#!ipxe\n#Template: $templateName\n" . substr($pxeContent, 6);
|
||||
} else {
|
||||
// Si no encuentra #!ipxe, agregarlo al principio seguido del comentario
|
||||
$pxeContent = "#!ipxe\n#Template: $templateName\n" . $pxeContent;
|
||||
}
|
||||
|
||||
|
@ -1134,20 +1102,21 @@ public function createBootFile(Request $request): JsonResponse
|
|||
$pxeFileName = '01-' . $mac;
|
||||
|
||||
// Ruta para guardar el archivo PXE
|
||||
#$pxeFilePath = '/opt/ogboot/tftpboot/ipxe_scripts/' . $pxeFileName;
|
||||
$pxeFilePath = $this->tftpbootDir . '/ipxe_scripts' . $pxeFileName;
|
||||
$pxeFilePath = $this->tftpbootDir . '/ipxe_scripts/' . $pxeFileName;
|
||||
|
||||
// Crear el archivo PXE
|
||||
if (file_put_contents($pxeFilePath, $pxeContent) === false) {
|
||||
return new JsonResponse(['error' => 'Failed to create PXE boot file'], Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
return new JsonResponse(['error' => 'FAILED_TO_CREATE_PXE_FILE', 'message' => 'Error al crear el archivo PXE'], Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
// Retornar la plantilla creada en el formato solicitado
|
||||
return new JsonResponse([
|
||||
'success' => 'Plantilla creada con éxito',
|
||||
'success' => 'PXE file created successfully',
|
||||
'message' => $pxeContent
|
||||
], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
|
||||
function validateAndFormatMac($mac)
|
||||
{
|
||||
// Convertir a minúsculas para un formato uniforme
|
||||
|
@ -1391,65 +1360,114 @@ public function getAllTemplates(): JsonResponse
|
|||
* description="La plantilla de arranque se creó exitosamente.",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="message", type="string", example="Plantilla creada exitosamente"),
|
||||
* @OA\Property(property="template", type="string", example="mi_plantilla.ipxe")
|
||||
* @OA\Property(property="success", type="string", example="Plantilla creada con éxito"),
|
||||
* @OA\Property(
|
||||
* property="message",
|
||||
* type="object",
|
||||
* @OA\Property(property="template_name", type="string", example="mi_plantilla.ipxe"),
|
||||
* @OA\Property(property="template_content", type="string", example="#!ipxe\nset timeout 0\nset timeout-style hidden\nset ISODIR __OGLIVE__\nset default 0\nset kernelargs __INFOHOST__\n:try_iso\nkernel http://__SERVERIP__/tftpboot/${ISODIR}/ogvmlinuz ${kernelargs} || goto fallback\ninitrd http://__SERVERIP__/tftpboot/${ISODIR}/oginitrd.img\nboot\n\n:fallback\nset ISODIR ogLive\nkernel http://__SERVERIP__/tftpboot/${ISODIR}/ogvmlinuz ${kernelargs}\ninitrd http://__SERVERIP__/tftpboot/${ISODIR}/oginitrd.img\nboot\n")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="La solicitud no pudo ser procesada debido a un error en los datos proporcionados en el cuerpo de la solicitud."
|
||||
* description="Datos no válidos. Faltan los parámetros 'name_template' o 'content_template'.",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="error", type="string", example="INVALID_INPUT"),
|
||||
* @OA\Property(property="message", type="string", example="Faltan datos requeridos: name_template y content_template son necesarios.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=403,
|
||||
* description="Intento de acceso no autorizado fuera del directorio de plantillas.",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="error", type="string", example="UNAUTHORIZED_ACCESS"),
|
||||
* @OA\Property(property="message", type="string", example="Intento de acceso no autorizado fuera del directorio de plantillas.")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=500,
|
||||
* description="Ocurrió un error al crear la plantilla de arranque."
|
||||
* description="Ocurrió un error al crear la plantilla de arranque.",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="error", type="string", example="FILE_CREATION_ERROR"),
|
||||
* @OA\Property(property="message", type="string", example="Ocurrió un error al crear la plantilla de arranque.")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function createTemplate(Request $request)
|
||||
|
||||
public function createTemplate(Request $request): JsonResponse
|
||||
{
|
||||
$data = json_decode($request->getContent(), true);
|
||||
|
||||
if (!isset($data['name_template']) || !isset($data['content_template'])) {
|
||||
return new Response('La solicitud no pudo ser procesada debido a un error en los datos proporcionados en el cuerpo de la solicitud.', 400);
|
||||
return new JsonResponse([
|
||||
'error' => 'INVALID_INPUT',
|
||||
'message' => 'Faltan datos requeridos: name_template y content_template son necesarios.'
|
||||
], JsonResponse::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$nameTemplate = $data['name_template'];
|
||||
$contentTemplate = $data['content_template'];
|
||||
#$templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates';
|
||||
$templateDir = $this->tftpbootDir . '/ipxe_scripts/templates';
|
||||
|
||||
// Validar el nombre de la plantilla
|
||||
if (!preg_match('/^[a-zA-Z0-9._-]+$/', $nameTemplate)) {
|
||||
return new Response('Nombre de la plantilla no válido.', 400);
|
||||
return new JsonResponse([
|
||||
'error' => 'INVALID_TEMPLATE_NAME',
|
||||
'message' => 'El nombre de la plantilla contiene caracteres no válidos.'
|
||||
], JsonResponse::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Construir la ruta completa del archivo
|
||||
$filePath = $templateDir . '/' . $nameTemplate;
|
||||
|
||||
// Normalizar la ruta del archivo para evitar accesos fuera del directorio permitido
|
||||
$realFilePath = realpath($templateDir) . '/' . $nameTemplate;
|
||||
|
||||
// Verificar que el archivo está dentro del directorio permitido
|
||||
if (strpos($realFilePath, realpath($templateDir)) !== 0) {
|
||||
return new JsonResponse([
|
||||
'error' => 'UNAUTHORIZED_ACCESS',
|
||||
'message' => 'Intento de acceso no autorizado fuera del directorio de plantillas.'
|
||||
], JsonResponse::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
$contentTemplate = str_replace("\\n", "\n", $contentTemplate);
|
||||
|
||||
try {
|
||||
// Intentar crear la plantilla
|
||||
file_put_contents($filePath, $contentTemplate);
|
||||
file_put_contents($realFilePath, $contentTemplate);
|
||||
|
||||
// Comprobar si el archivo se ha creado correctamente
|
||||
if (!file_exists($filePath)) {
|
||||
if (!file_exists($realFilePath)) {
|
||||
throw new \Exception('El archivo no se pudo crear.');
|
||||
}
|
||||
|
||||
// Verificar si el contenido escrito coincide con el contenido esperado
|
||||
$writtenContent = file_get_contents($filePath);
|
||||
$writtenContent = file_get_contents($realFilePath);
|
||||
if ($writtenContent !== $contentTemplate) {
|
||||
throw new \Exception('El contenido del archivo no coincide con el contenido esperado.');
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return new Response('Ocurrió un error al crear la plantilla de arranque. ' . $e->getMessage(), 500);
|
||||
return new JsonResponse([
|
||||
'error' => 'FILE_CREATION_ERROR',
|
||||
'message' => 'Ocurrió un error al crear la plantilla de arranque: ' . $e->getMessage()
|
||||
], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
// Retornar el nombre de la plantilla y su contenido en el formato solicitado
|
||||
return new Response(json_encode([
|
||||
return new JsonResponse([
|
||||
'success' => 'Plantilla creada con éxito',
|
||||
'message' => [
|
||||
'template_name' => $nameTemplate,
|
||||
'template_content' => $contentTemplate
|
||||
]), 200, ['Content-Type' => 'application/json']);
|
||||
]
|
||||
], JsonResponse::HTTP_OK);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1532,3 +1550,4 @@ public function deleteTemplate($name): Response
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue