-
Notifications
You must be signed in to change notification settings - Fork 18
Added functionalities for integrating Vault or an SSM alternative into Aether Core #443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds comprehensive Vault/SSM integration functionality to Aether Core, including a full-featured vanilla JavaScript web UI for managing 5G network components. The changes enable secure key management (K4 encryption keys), subscriber management with pagination and filtering, and improved device group and network slice validations.
Key changes:
- Complete web UI implementation with vanilla JavaScript modules for managing device groups, network slices, gNB/UPF inventory, K4 keys, and subscribers
- K4 key management API with encryption support (AES, DES, DES3)
- Pagination and filtering for subscriber list endpoint
- Enhanced subscriber synchronization processes
- Admin operations for SSM sync, health checks, and K4 key rotation
Reviewed changes
Copilot reviewed 143 out of 147 changed files in this pull request and generated 24 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/frontend_files/index.html | Complete web console UI with navigation, sections for all entities, modals, and notifications |
| ui/frontend_files/styles.css | Comprehensive styling with animations, responsive design, and custom components |
| ui/frontend_files/app.js | Main application entry point with global state management and event handlers |
| ui/frontend_files/modules/baseManager.js | Base class providing common CRUD operations for all entity managers |
| ui/frontend_files/modules/deviceGroups.js | Device group management with IMSI configuration and QoS settings |
| ui/frontend_files/modules/networkSlices.js | Network slice management with gNodeB, UPF, and application filtering rules |
| ui/frontend_files/modules/gnbInventory.js | gNB (5G base station) inventory management |
| ui/frontend_files/modules/upfInventory.js | UPF (User Plane Function) inventory management |
| ui/frontend_files/modules/k4.js | K4 encryption key management with support for multiple key types |
| ui/frontend_files/modules/subscribers.js | Subscriber management with pagination, filtering, and detailed authentication views |
| ui/frontend_files/modules/uiManager.js | UI navigation and section management |
| ui/frontend_files/modules/modalManager.js | Modal dialog management for create/edit operations |
| ui/frontend_files/modules/notifications.js | Toast notification system for user feedback |
| ui/frontend_files/modules/objectsModels/*.js | JavaScript equivalents of Go data models |
| ui/frontend_files/manifest.json | PWA manifest for web app configuration |
| ui/frontend_files/favicon.* | Application icons |
| configmodels/model_subs_data.go | Added K4 SNO and encryption algorithm fields to subscriber override data |
| configmodels/model_slice_site_info.go | Updated UPF field type to use 'any' instead of 'interface{}' |
| VERSION | Version bumped to 2.0.2-dev |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 'gnb-inventory': 'gnbInventory', | ||
| 'gnb-details': 'gnbInventory', // Same manager for details | ||
| 'upf-inventory': 'upfInventory', | ||
| 'subscribers': 'subscribers', // identificador genérico |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spanish comment found in code. The comment "identificador genérico" (generic identifier) should be translated to English for consistency with the rest of the codebase.
| // El atributo 'onclick' contiene el nombre de la sección, así que lo usamos como selector. | ||
| const navLink = document.querySelector(`.nav-link[onclick="showSection('${section}')"]`); | ||
| if (navLink) { | ||
| navLink.classList.add('active'); | ||
| } | ||
|
|
||
| // Update app state | ||
| app.currentSection = section; | ||
|
|
||
| // Load data for the section | ||
| this.loadSectionData(section); | ||
| } | ||
|
|
||
| loadSectionData(section) { | ||
| // Lógica para la sección de suscriptores (página original combinada) | ||
| if (section === 'subscribers') { | ||
| app.managers.k4Manager.loadData(); | ||
| app.managers.subscriberManager.renderForm(); | ||
| } else if (section === 'k4-keys') { | ||
| // Load K4 keys list | ||
| app.managers.k4Manager.loadData(); | ||
| } else if (section === 'subscribers-list') { | ||
| // Load subscribers list | ||
| app.managers.subscriberListManager.loadData(); | ||
| } else if (section === 'device-groups') { | ||
| // Load device groups list | ||
| const managerKey = this.sections[section]; | ||
| if (managerKey && app.managers[managerKey]) { | ||
| app.managers[managerKey].loadData(); | ||
| } | ||
| } else if (section === 'network-slices') { | ||
| // Load network slices list | ||
| const managerKey = this.sections[section]; | ||
| if (managerKey && app.managers[managerKey]) { | ||
| app.managers[managerKey].loadData(); | ||
| } | ||
| } else if (section === 'gnb-inventory') { | ||
| // Load gNB inventory list | ||
| const managerKey = this.sections[section]; | ||
| if (managerKey && app.managers[managerKey]) { | ||
| app.managers[managerKey].loadData(); | ||
| } | ||
| } else if (section === 'device-group-details' || section === 'gnb-details' || section === 'network-slice-details' || section === 'k4-details' || section === 'subscriber-details') { | ||
| // Don't reload data for details views as they're already loaded | ||
| return; | ||
| } else { | ||
| const managerKey = this.sections[section]; | ||
| if (managerKey && app.managers[managerKey]) { | ||
| app.managers[managerKey].loadData(); | ||
| } | ||
| } | ||
| } |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spanish comments found in code. Comments should be in English for consistency with the rest of the codebase. Lines 43, 57, 85, and others contain Spanish text.
| if (!container) { | ||
| return; | ||
| } |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixed tabs and spaces detected for indentation. The code uses tabs at the beginning of lines 200-202 while the rest of the file uses spaces. This inconsistency should be fixed to maintain uniform code formatting.
| let html = this.renderListControls(); | ||
|
|
||
| if (!subscribers || subscribers.length === 0) { | ||
| html += ` | ||
| <div class="alert alert-info"> | ||
| <i class="fas fa-info-circle me-2"></i> | ||
| No subscribers found | ||
| </div> | ||
| `; | ||
| container.innerHTML = html; | ||
| this.bindListControls(); | ||
| return; | ||
| } | ||
|
|
||
| html += '<div class="table-responsive"><table class="table table-striped">'; | ||
| html += '<thead><tr><th>UE ID (IMSI)</th><th>PLMN ID</th><th>Actions</th></tr></thead><tbody>'; | ||
|
|
||
| subscribers.forEach(subscriber => { | ||
| const ueId = subscriber.ueId || 'N/A'; | ||
| const plmnId = subscriber.plmnID || 'N/A'; | ||
|
|
||
| html += ` | ||
| <tr class="subscriber-row" onclick="showSubscriberDetails('${ueId}')" style="cursor: pointer;"> | ||
| <td><strong>${ueId}</strong></td> | ||
| <td><code>${plmnId}</code></td> | ||
| <td onclick="event.stopPropagation();"> | ||
| <button class="btn btn-sm btn-outline-primary me-1" | ||
| onclick="editItem('${this.type}', '${ueId}')"> | ||
| <i class="fas fa-edit"></i> Edit | ||
| </button> | ||
| <button class="btn btn-sm btn-outline-danger" | ||
| onclick="deleteItem('${this.type}', '${ueId}')"> | ||
| <i class="fas fa-trash"></i> Delete | ||
| </button> | ||
| </td> | ||
| </tr> | ||
| `; | ||
| }); | ||
|
|
||
|
|
||
| html += '</tbody></table></div>'; | ||
| container.innerHTML = html; | ||
| this.bindListControls(); |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixed tabs and spaces detected for indentation. Lines 204-246 use tabs for indentation while the rest of the file uses spaces. This inconsistency should be fixed to maintain uniform code formatting throughout the file.
| if err != nil { | ||
| return nil, err | ||
| } | ||
| err = mClient.Client.Disconnect(context.Background()) |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mClient may be nil at this dereference because errConnect may not have been checked.
| * JavaScript equivalent of Go structs from model_subs_data.go | ||
| */ | ||
|
|
||
| import { FlowRule } from "./model_flow_rule"; |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import FlowRule.
| renderEditableDetails(sliceData) { | ||
| const siteInfo = sliceData['site-info'] || {}; | ||
| const plmn = siteInfo.plmn || {}; | ||
| const gNodeBs = siteInfo.gNodeBs || []; |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable gNodeBs.
| const siteInfo = sliceData['site-info'] || {}; | ||
| const plmn = siteInfo.plmn || {}; | ||
| const gNodeBs = siteInfo.gNodeBs || []; | ||
| const upf = siteInfo.upf || {}; |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable upf.
| const plmn = siteInfo.plmn || {}; | ||
| const gNodeBs = siteInfo.gNodeBs || []; | ||
| const upf = siteInfo.upf || {}; | ||
| const deviceGroups = sliceData['site-device-group'] || []; |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable deviceGroups.
| RUN go install github.com/go-task/task/v3/cmd/task@latest | ||
|
|
||
| WORKDIR $GOPATH/src/webconsole | ||
|
|
||
| COPY go.mod . | ||
| COPY go.sum . | ||
| COPY Taskfile.yml . | ||
|
|
||
| RUN task mod-start | ||
|
|
||
|
|
||
| COPY . . | ||
| RUN make all && \ | ||
| CGO_ENABLED=0 go build -a -installsuffix nocgo -o webconsole -x server.go | ||
|
|
||
| FROM alpine:3.23@sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375 AS webui | ||
| ARG BUILD_UI=true | ||
| RUN if [ "$BUILD_UI" = "true" ]; then \ | ||
| task webconsole-ui; \ | ||
| else \ | ||
| task all; \ | ||
| fi |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Dockerfile installs and executes the task build tool via go install github.com/go-task/task/v3/cmd/task@latest, which pulls arbitrary mutable code at build time (@latest) and then runs it as part of the build pipeline. If the upstream module or its supply chain is compromised, an attacker could execute code during image builds with access to build secrets and artifacts. To reduce this risk, pin task to a specific, audited version (e.g., a fixed tag or commit) and treat it as a normal dependency rather than fetching @latest dynamically in the build stage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 148 out of 155 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| func updateSubscriberInDeviceGroups(imsi string) (int, error) { | ||
| func updateSubscriberInDeviceGroupsWhenDeleteSub(imsi string) (int, error) { |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function name is inconsistent with casing conventions. Consider renaming to updateSubscriberInDeviceGroupsWhenDeleteSubscriber or updateSubscriberInDeviceGroupsOnDelete for clarity.
| func updateSubscriberInDeviceGroupsWhenDeleteSub(imsi string) (int, error) { | |
| func updateSubscriberInDeviceGroupsWhenDeleteSubscriber(imsi string) (int, error) { |
| logger.AppLog.Errorln("K4 Key DeK4DataDelete Error:", err) | ||
| return err | ||
| } | ||
| logger.AppLog.Debugf("successfully processed K4 key DeK4DataDelete for SNO: %s", k4Sno) |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected 'DeK4DataDelete' to 'Delete' in error message.
| logger.AppLog.Errorln("K4 Key DeK4DataDelete Error:", err) | |
| return err | |
| } | |
| logger.AppLog.Debugf("successfully processed K4 key DeK4DataDelete for SNO: %s", k4Sno) | |
| logger.AppLog.Errorln("K4 Key Delete Error:", err) | |
| return err | |
| } | |
| logger.AppLog.Debugf("successfully processed K4 key delete for SNO: %s", k4Sno) |
| logger.AppLog.Errorln("K4 Key DeK4DataDelete Error:", err) | ||
| return err | ||
| } | ||
| logger.AppLog.Debugf("successfully processed K4 key DeK4DataDelete for SNO: %s", k4Sno) |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected 'DeK4DataDelete' to 'delete' in success message.
| logger.AppLog.Errorln("K4 Key DeK4DataDelete Error:", err) | |
| return err | |
| } | |
| logger.AppLog.Debugf("successfully processed K4 key DeK4DataDelete for SNO: %s", k4Sno) | |
| logger.AppLog.Errorln("K4 Key delete Error:", err) | |
| return err | |
| } | |
| logger.AppLog.Debugf("successfully processed K4 key delete for SNO: %s", k4Sno) |
| this.uplink = 0; // uplink data rate in bps | ||
| this.downlink = 0; // downlink data rate in bps | ||
| this.bitrateUnit = ""; // data rate unit for uplink and downlink | ||
| this.traffiClass = ""; // QCI/QFI for the traffic |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'traffiClass' to 'trafficClass'.
| this.traffiClass = ""; // QCI/QFI for the traffic | |
| this.trafficClass = ""; // QCI/QFI for the traffic |
| return http.StatusBadRequest, err | ||
| } | ||
|
|
||
| var errorOccured bool |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'errorOccured' to 'errorOccurred'.
| var errorOccured bool | ||
| wg := sync.WaitGroup{} | ||
|
|
||
| // delete IMSI's that are removed | ||
| dimsis := getDeletedImsisList(&deviceGroup, prevDevGroup) | ||
| for _, imsi := range dimsis { | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| err := removeSubscriberEntriesRelatedToDeviceGroups(slice.SiteInfo.Plmn.Mcc, slice.SiteInfo.Plmn.Mnc, imsi) | ||
| if err != nil { | ||
| logger.ConfigLog.Errorln(err) | ||
| errorOccured = true | ||
| } | ||
| }() | ||
| } | ||
| wg.Wait() | ||
|
|
||
| if errorOccured { |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable name should be 'errorOccurred'.
| var errorOccured bool | |
| wg := sync.WaitGroup{} | |
| // delete IMSI's that are removed | |
| dimsis := getDeletedImsisList(&deviceGroup, prevDevGroup) | |
| for _, imsi := range dimsis { | |
| wg.Add(1) | |
| go func() { | |
| defer wg.Done() | |
| err := removeSubscriberEntriesRelatedToDeviceGroups(slice.SiteInfo.Plmn.Mcc, slice.SiteInfo.Plmn.Mnc, imsi) | |
| if err != nil { | |
| logger.ConfigLog.Errorln(err) | |
| errorOccured = true | |
| } | |
| }() | |
| } | |
| wg.Wait() | |
| if errorOccured { | |
| var ( | |
| errorOccurred bool | |
| mu sync.Mutex | |
| ) | |
| wg := sync.WaitGroup{} | |
| // delete IMSI's that are removed | |
| dimsis := getDeletedImsisList(&deviceGroup, prevDevGroup) | |
| for _, imsi := range dimsis { | |
| wg.Add(1) | |
| go func(imsi string) { | |
| defer wg.Done() | |
| err := removeSubscriberEntriesRelatedToDeviceGroups(slice.SiteInfo.Plmn.Mcc, slice.SiteInfo.Plmn.Mnc, imsi) | |
| if err != nil { | |
| logger.ConfigLog.Errorln(err) | |
| mu.Lock() | |
| errorOccurred = true | |
| mu.Unlock() | |
| } | |
| }(imsi) | |
| } | |
| wg.Wait() | |
| mu.Lock() | |
| hasError := errorOccurred | |
| mu.Unlock() | |
| if hasError { |
| } | ||
| wg.Wait() | ||
|
|
||
| if errorOccured { |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable name should be 'errorOccurred'.
| <div class="card border-success"> | ||
| <div class="card-body"> | ||
| <h5 class="card-title"> | ||
| <i class="fas fa-heartbeat me-2 text-success"></i>Check K4 Life |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The term 'K4 Life' is ambiguous. Consider renaming to 'K4 Health Check' or 'K4 Key Lifecycle' for clarity.
| <i class="fas fa-heartbeat me-2 text-success"></i>Check K4 Life | |
| <i class="fas fa-heartbeat me-2 text-success"></i>K4 Health Check |
…ject#439) Signed-off-by: PedroVhGit <pedrovh040110@gmail.com>
…mec-project#441) Signed-off-by: PedroVhGit <pedrovh040110@gmail.com>
…o Aether Core. - API for managing K4 (encryption keys) - Modules for handling the client and login in both options - Login to Vault with AppRole, Kubernetes, and certificates - Optimization of the subscriber synchronization process when a network slice or device group was updated, and also when a subscriber was deleted - Sync and health check modules for Vault and SSM - Web UI with vanilla JS - Implementation of pagination and filtering for the get users endpoint - Improvements to device group and network slice validations - Tests for the integrated functionalities - New models see info about the ssm here: https://github.com/networkgcorefullcode/ssm Signed-off-by: PedroVhGit <pedrovh040110@gmail.com>
- Updated logging calls in `handlers_user_account.go` to replace `logger.DbLog` with `logger.AppLog` for better clarity and consistency. - Enhanced error handling and logging in user account creation, deletion, and password change functions. Add slice request parsing and validation helper function - Introduced `parseAndValidateSliceRequest` function in `slice_helpers.go` to handle JSON binding and validation for network slice requests. - Implemented comprehensive validation for required fields and application filtering rules within the slice request. Update tests to reflect changes in logging and slice validation - Modified test cases in `slice_helpers_batch_test.go` and `slice_operations_test.go` to accommodate new logging structure and slice validation logic. - Ensured that error messages are consistent with the new validation rules. Refactor subscriber helpers for improved error logging - Changed logging in `subscriber_helpers.go` to utilize `logger.AppLog` for all error messages related to subscriber authentication data operations. - Enhanced rollback error handling during subscriber data updates and deletions. Clean up unused code and improve readability - Removed redundant code and comments in `validators.go` related to slice request validation, centralizing the logic in the new helper function. - Simplified mock structures in `server_test.go` for better clarity and maintainability. Signed-off-by: PedroVhGit <pedrovh040110@gmail.com>
Signed-off-by: PedroVhGit <pedrovh040110@gmail.com>
- Improved error handling in key rotation and health check functions to return errors from VaultSyncInitDefault. - Cleaned up code by removing unnecessary blank lines and comments. - Enhanced logging to provide better insights into failures during Vault operations. - Updated test cases to ensure proper error handling and response validation. - Consolidated mutex declarations for better readability. - Added SPDX license headers to several files for compliance. - Fixed minor issues in user account handling and subscriber configuration APIs. Signed-off-by: PedroVhGit <pedrovh040110@gmail.com>
gab-arrobo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @vhPedroGitHub, thanks for your contribution. There are some changes that I think should not be there such as the changes inside the .github directory. It seems that the version of webconsole you are using is a bit old. So, please rebase your PR.
Also, as per the PR description, you mentioned that a (vanilla) UI was added. It would be great to see how it looks like and what capabilities it supports. Would it be possible that you add some screen captures providing details. Also, given that this is a kind of big PR, it would take us some time to go over. So, it would be great if you can attend an Aether TST meeting (next one is this coming Tuesday) for give a talk about the changes. Or we can have a call through Slack (please join the Aether community in Slack)
| WebUILog *zap.SugaredLogger | ||
| ContextLog *zap.SugaredLogger | ||
| GinLog *zap.SugaredLogger | ||
| GrpcLog *zap.SugaredLogger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not using GRPC (for the webconsole) anymore
| WebUILog = log.Sugar().With("component", "WebUI", "category", "WebUI") | ||
| ContextLog = log.Sugar().With("component", "WebUI", "category", "Context") | ||
| GinLog = log.Sugar().With("component", "WebUI", "category", "GIN") | ||
| GrpcLog = log.Sugar().With("component", "WebUI", "category", "GRPC") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not using GRPC anymore. Why was it added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these changes are not correct. It seems to me that you are using an older version of the webconsole. The Latest version has to have the permissions you are removing. Please revert these changes
|
@thakurajayL @sureshmarikkannu, any thoughts/comments about this PR? |
|
Hi @vhPedroGitHub, any update about this PR? Thanks! |
|
Hi @gab-arrobo . I'm on exams right now; when I finish, I'll answer the comments and update the PR. |
@vhPedroGitHub, thanks for the update. Another important aspect would be to break the PR into smaller PRs to make it easier for us to review them. |
Hi. My team are working on these features. I hope this is helpful
look here: https://github.com/networkgcorefullcode
see info about the ssm here: https://github.com/networkgcorefullcode/ssm
We have updated udm, sd-core-helm-charts and OnRamp, and we have run simulations to test the new functionality
Signed-off-by: Pedro Valdes pedrovh040110@gmail.com