Skip to content

Commit 28d96f2

Browse files
committed
changes
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent c4224f3 commit 28d96f2

6 files changed

Lines changed: 122 additions & 45 deletions

File tree

plugins/logs-web-server/src/main/java/org/apache/cloudstack/logsws/LogsWebSessionApiService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,4 @@ public interface LogsWebSessionApiService extends PluggableService {
3131
ListResponse<LogsWebSessionResponse> listLogsWebSessions(ListLogsWebSessionsCmd cmd);
3232
LogsWebSessionResponse createLogsWebSession(CreateLogsWebSessionCmd cmd) throws CloudRuntimeException;
3333
boolean deleteLogsWebSession(DeleteLogsWebSession cmd) throws CloudRuntimeException;
34-
LogsWebSessionResponse createLogsWebSessionResponse(long logsEndpointId);
3534
}

plugins/logs-web-server/src/main/java/org/apache/cloudstack/logsws/LogsWebSessionApiServiceImpl.java

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,23 @@
4747

4848
import com.cloud.api.ApiServlet;
4949
import com.cloud.domain.Domain;
50+
import com.cloud.exception.InternalErrorException;
5051
import com.cloud.exception.InvalidParameterValueException;
52+
import com.cloud.exception.PermissionDeniedException;
5153
import com.cloud.user.Account;
5254
import com.cloud.user.AccountService;
5355
import com.cloud.user.DomainService;
5456
import com.cloud.utils.Pair;
57+
import com.cloud.utils.component.ManagerBase;
5558
import com.cloud.utils.db.Filter;
5659
import com.cloud.utils.db.SearchBuilder;
5760
import com.cloud.utils.db.SearchCriteria;
61+
import com.cloud.utils.db.Transaction;
62+
import com.cloud.utils.db.TransactionCallbackWithException;
5863
import com.cloud.utils.exception.CloudRuntimeException;
5964
import com.cloud.utils.net.NetUtils;
6065

61-
public class LogsWebSessionApiServiceImpl implements LogsWebSessionApiService {
66+
public class LogsWebSessionApiServiceImpl extends ManagerBase implements LogsWebSessionApiService {
6267

6368
@Inject
6469
LogsWebSessionManager logsWSManager;
@@ -72,6 +77,9 @@ public class LogsWebSessionApiServiceImpl implements LogsWebSessionApiService {
7277
@Override
7378
public ListResponse<LogsWebSessionResponse> listLogsWebSessions(ListLogsWebSessionsCmd cmd) {
7479
final Long id = cmd.getId();
80+
if (!accountService.isRootAdmin(CallContext.current().getCallingAccountId())) {
81+
throw new PermissionDeniedException("Invalid request");
82+
}
7583
List<LogsWebSessionResponse> responsesList = new ArrayList<>();
7684
SearchBuilder<LogsWebSessionVO> sb = logsWebSessionDao.createSearchBuilder();
7785
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
@@ -82,24 +90,34 @@ public ListResponse<LogsWebSessionResponse> listLogsWebSessions(ListLogsWebSessi
8290

8391
Filter searchFilter = new Filter(LogsWebSessionVO.class, "id", true, cmd.getStartIndex(),
8492
cmd.getPageSizeVal());
85-
Pair<List<LogsWebSessionVO>, Integer> webhooksAndCount = logsWebSessionDao.searchAndCount(sc, searchFilter);
86-
for (LogsWebSessionVO webhook : webhooksAndCount.first()) {
87-
LogsWebSessionResponse response = createLogsWebSessionResponse(webhook);
88-
responsesList.add(response);
93+
Pair<List<LogsWebSessionVO>, Integer> logsWebSessionsAndCount = logsWebSessionDao.searchAndCount(sc, searchFilter);
94+
for (LogsWebSessionVO session : logsWebSessionsAndCount.first()) {
95+
try {
96+
LogsWebSessionResponse response = createLogsWebSessionResponse(session);
97+
responsesList.add(response);
98+
} catch (InternalErrorException exception) {
99+
logger.error("Failed to create response for {}", session, exception);
100+
}
89101
}
90102
ListResponse<LogsWebSessionResponse> response = new ListResponse<>();
91-
response.setResponses(responsesList, webhooksAndCount.second());
103+
response.setResponses(responsesList, logsWebSessionsAndCount.second());
92104
return response;
93105
}
94106

95107
@Override
96108
public LogsWebSessionResponse createLogsWebSession(CreateLogsWebSessionCmd cmd) throws CloudRuntimeException {
109+
final Account caller = CallContext.current().getCallingAccount();
97110
final List<String> filters = cmd.getFilters();
111+
final Map<String, String> params = cmd.getFullUrlParams();
98112
final String extraSecurityToken = cmd.getExtraSecurityToken();
99-
String clientAddress = null;
100-
Map<String, String> params = cmd.getFullUrlParams();
113+
String clientAddress;
101114
if (MapUtils.isNotEmpty(params)) {
102115
clientAddress = params.get(ApiServlet.CLIENT_INET_ADDRESS_KEY);
116+
} else {
117+
clientAddress = null;
118+
}
119+
if (!accountService.isRootAdmin(caller.getAccountId())) {
120+
throw new PermissionDeniedException("Invalid request");
103121
}
104122
for (String filter : filters) {
105123
if (StringUtils.isBlank(filter)) {
@@ -108,18 +126,26 @@ public LogsWebSessionResponse createLogsWebSession(CreateLogsWebSessionCmd cmd)
108126
}
109127
}
110128
if (!logsWSManager.canCreateNewLogsWebSession()) {
111-
throw new CloudRuntimeException("Max Logs Web Session limit reached");
129+
throw new CloudRuntimeException("Failed to create logs web session as max session limit reached");
130+
}
131+
try {
132+
return Transaction.execute((TransactionCallbackWithException<LogsWebSessionResponse, InternalErrorException>) status -> {
133+
LogsWebSessionVO logsWebSessionVO = new LogsWebSessionVO(filters, caller.getDomainId(), caller.getAccountId(),
134+
clientAddress);
135+
logsWebSessionVO = logsWebSessionDao.persist(logsWebSessionVO);
136+
return createLogsWebSessionResponse(logsWebSessionVO);
137+
});
138+
} catch (InternalErrorException e) {
139+
throw new CloudRuntimeException("Failed to create logs web session as unable to prepare response", e);
112140
}
113-
final Account account = CallContext.current().getCallingAccount();
114-
LogsWebSessionVO logsWebSessionVO = new LogsWebSessionVO(filters, account.getDomainId(), account.getAccountId(),
115-
clientAddress);
116-
logsWebSessionVO = logsWebSessionDao.persist(logsWebSessionVO);
117-
return createLogsWebSessionResponse(logsWebSessionVO);
118141
}
119142

120143
@Override
121144
public boolean deleteLogsWebSession(DeleteLogsWebSession cmd) throws CloudRuntimeException {
122145
final long id = cmd.getId();
146+
if (!accountService.isRootAdmin(CallContext.current().getCallingAccountId())) {
147+
throw new PermissionDeniedException("Invalid request");
148+
}
123149
return logsWebSessionDao.remove(id);
124150
}
125151

@@ -142,7 +168,7 @@ protected String getRealIp4Address() {
142168
}
143169

144170
protected Set<LogsWebSessionWebSocketResponse> getLogsWebSessionWebSocketResponses(
145-
final LogsWebSessionVO logsWebSessionVO) {
171+
final LogsWebSessionVO logsWebSessionVO) throws InternalErrorException {
146172
Set<LogsWebSessionWebSocketResponse> responses = new HashSet<>();
147173
List<LogsWebSessionWebSocket> webSockets = logsWSManager.getLogsWebSessionWebSockets(logsWebSessionVO);
148174
for (LogsWebSessionWebSocket socket : webSockets) {
@@ -164,7 +190,7 @@ protected Set<LogsWebSessionWebSocketResponse> getLogsWebSessionWebSocketRespons
164190
return responses;
165191
}
166192

167-
protected LogsWebSessionResponse createLogsWebSessionResponse(final LogsWebSessionVO logsWebSessionVO) {
193+
protected LogsWebSessionResponse createLogsWebSessionResponse(final LogsWebSessionVO logsWebSessionVO) throws InternalErrorException {
168194
LogsWebSessionResponse response = new LogsWebSessionResponse();
169195
response.setObjectName("logswebsession");
170196
response.setId(logsWebSessionVO.getUuid());
@@ -183,15 +209,6 @@ protected LogsWebSessionResponse createLogsWebSessionResponse(final LogsWebSessi
183209
return response;
184210
}
185211

186-
@Override
187-
public LogsWebSessionResponse createLogsWebSessionResponse(long logsEndpointId) {
188-
LogsWebSessionVO logsWebSessionVO = logsWebSessionDao.findById(logsEndpointId);
189-
if (logsWebSessionVO == null) {
190-
return null;
191-
}
192-
return createLogsWebSessionResponse(logsWebSessionVO);
193-
}
194-
195212
@Override
196213
public List<Class<?>> getCommands() {
197214
if (!LogsWebSessionManager.LogsWebServerEnabled.value()) {

plugins/logs-web-server/src/main/java/org/apache/cloudstack/logsws/LogsWebSessionManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
import org.apache.cloudstack.framework.config.ConfigKey;
2323
import org.apache.cloudstack.framework.config.Configurable;
2424

25+
import com.cloud.exception.InternalErrorException;
2526
import com.cloud.utils.component.PluggableService;
2627

2728
public interface LogsWebSessionManager extends PluggableService, Configurable {
2829
String WS_PATH = "/logger";
30+
String ENCRYPTION_PASSPHRASE = "LogsWebSessionEncryptionPassphrase";
2931

3032
ConfigKey<Boolean> LogsWebServerEnabled = new ConfigKey<>("Advanced", Boolean.class,
3133
"logs.web.server.enabled", "false",
@@ -65,6 +67,6 @@ public interface LogsWebSessionManager extends PluggableService, Configurable {
6567
true,
6668
ConfigKey.Scope.ManagementServer);
6769

68-
List<LogsWebSessionWebSocket> getLogsWebSessionWebSockets(final LogsWebSession logsWebSession);
70+
List<LogsWebSessionWebSocket> getLogsWebSessionWebSockets(final LogsWebSession logsWebSession) throws InternalErrorException;
6971
boolean canCreateNewLogsWebSession();
7072
}

plugins/logs-web-server/src/main/java/org/apache/cloudstack/logsws/LogsWebSessionManagerImpl.java

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.cloudstack.logsws;
1919

20+
import java.security.GeneralSecurityException;
2021
import java.util.ArrayList;
2122
import java.util.Date;
2223
import java.util.List;
@@ -43,6 +44,7 @@
4344

4445
import com.cloud.cluster.ManagementServerHostVO;
4546
import com.cloud.cluster.dao.ManagementServerHostDao;
47+
import com.cloud.exception.InternalErrorException;
4648
import com.cloud.utils.DateUtil;
4749
import com.cloud.utils.component.ManagerBase;
4850
import com.cloud.utils.concurrency.NamedThreadFactory;
@@ -60,18 +62,23 @@ public class LogsWebSessionManagerImpl extends ManagerBase implements LogsWebSes
6062
private String serverPath;
6163
private int idleTimeoutSeconds;
6264
private ScheduledExecutorService staleLogsWebSessionCleanupExecutor;
63-
private Long managementServerId = null;
65+
private ManagementServerHost managementServer = null;
6466
private LogsWebSocketRouteManager logsWebSocketRouteManager;
6567

66-
protected Long getManagementServerId() {
67-
if (managementServerId != null) {
68-
ManagementServerHostVO managementServerHostVO =
68+
protected ManagementServerHost getCurrentManagementServer() {
69+
if (managementServer == null) {
70+
managementServer =
6971
managementServerHostDao.findByMsid(ManagementServerNode.getManagementServerId());
70-
if (managementServerHostVO != null) {
71-
managementServerId = managementServerHostVO.getId();
72-
}
7372
}
74-
return managementServerId;
73+
return managementServer;
74+
}
75+
76+
protected Long getManagementServerId() {
77+
return getCurrentManagementServer().getId();
78+
}
79+
80+
protected Long getManagementServerRunId() {
81+
return getCurrentManagementServer().getId();
7582
}
7683

7784
protected void registerLogsWebSocketServerRoute() {
@@ -118,18 +125,30 @@ public boolean stop() {
118125
return true;
119126
}
120127

121-
private String getLogsWebSessionWebSocketPathUsingVO(long msId, LogsWebSession session) {
128+
protected LogsWebSessionTokenPayload getLogsWebSessionWebSocketTokenPayloadUsingVO(LogsWebSession session) {
122129
LogsWebSessionVO sessionVO = null;
123130
if (session instanceof LogsWebSessionVO) {
124-
sessionVO = (LogsWebSessionVO)session;
131+
sessionVO = (LogsWebSessionVO) session;
125132
} else {
126133
sessionVO = logsWebSessionDao.findById(session.getId());
127134
}
135+
LogsWebSessionTokenPayload tokenPayload = new LogsWebSessionTokenPayload(sessionVO.getUuid(),
136+
sessionVO.getCreatorAddress());
137+
return tokenPayload;
138+
}
139+
140+
protected String getLogsWebSessionWebSocketPathForManagementServer(ManagementServerHostVO managementServerHostVO,
141+
LogsWebSessionTokenPayload payload) throws InternalErrorException {
128142
String path = serverPath;
129-
if (!Objects.equals(msId, getManagementServerId())) {
130-
serverPath = LogsWebServerPath.valueIn(msId);
143+
if (!Objects.equals(managementServerHostVO.getId(), getManagementServerId())) {
144+
path = LogsWebServerPath.valueIn(managementServerHostVO.getId());
145+
}
146+
try {
147+
return String.format("%s/%s", path, LogsWebSessionTokenCryptoUtil.encrypt(payload,
148+
String.valueOf(managementServerHostVO.getRunid())));
149+
} catch (GeneralSecurityException e) {
150+
throw new InternalErrorException("Failed to encrypt token payload: " + payload, e);
131151
}
132-
return String.format("%s/%s", path, sessionVO.getUuid());
133152
}
134153

135154
@Override
@@ -165,6 +184,16 @@ public int getMaxReadExistingLines() {
165184
return LogsWebServerSessionTailExistingLines.valueIn(getManagementServerId());
166185
}
167186

187+
@Override
188+
public LogsWebSessionTokenPayload parseToken(String token) {
189+
try {
190+
return LogsWebSessionTokenCryptoUtil.decrypt(token, String.valueOf(getManagementServerRunId()));
191+
} catch (GeneralSecurityException e) {
192+
logger.error("Failed to decrypt route token: {}", token, e);
193+
}
194+
return null;
195+
}
196+
168197
@Override
169198
public LogsWebSession getSession(String route) {
170199
if (StringUtils.isBlank(route)) {
@@ -198,14 +227,15 @@ public void updateSessionConnection(long sessionId, String clientAddress) {
198227
}
199228

200229
@Override
201-
public List<LogsWebSessionWebSocket> getLogsWebSessionWebSockets(final LogsWebSession logsWebSession) {
230+
public List<LogsWebSessionWebSocket> getLogsWebSessionWebSockets(final LogsWebSession logsWebSession) throws InternalErrorException {
202231
List<LogsWebSessionWebSocket> webSockets = new ArrayList<>();
203232
final List<ManagementServerHostVO> activeMsList =
204233
managementServerHostDao.listBy(ManagementServerHost.State.Up);
234+
LogsWebSessionTokenPayload payload = getLogsWebSessionWebSocketTokenPayloadUsingVO(logsWebSession);
205235
for (ManagementServerHostVO managementServerHostVO : activeMsList) {
206236
LogsWebSessionWebSocket logsWebSessionWebSocket = new LogsWebSessionWebSocket(managementServerHostVO,
207237
webSocketServerManager.getServerPort(),
208-
getLogsWebSessionWebSocketPathUsingVO(managementServerHostVO.getId(), logsWebSession));
238+
getLogsWebSessionWebSocketPathForManagementServer(managementServerHostVO, payload));
209239
webSockets.add(logsWebSessionWebSocket);
210240
}
211241
return webSockets;

plugins/logs-web-server/src/main/java/org/apache/cloudstack/logsws/server/LogsWebSocketRoutingHandler.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.nio.charset.StandardCharsets;
2121

2222
import org.apache.cloudstack.logsws.LogsWebSession;
23+
import org.apache.cloudstack.logsws.LogsWebSessionTokenPayload;
24+
import org.apache.commons.lang3.StringUtils;
2325
import org.apache.logging.log4j.LogManager;
2426
import org.apache.logging.log4j.Logger;
2527

@@ -58,8 +60,33 @@ protected void closeChannelWithErrorResponse(ChannelHandlerContext ctx, FullHttp
5860
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
5961
}
6062

63+
protected LogsWebSession getValidSession(String route, ChannelHandlerContext ctx) {
64+
LogsWebSessionTokenPayload tokenPayload = serverHelper.parseToken(route);
65+
if (tokenPayload == null) {
66+
LOGGER.error("Decrypted token payload is null for route: {}", route);
67+
return null;
68+
}
69+
String sessionUuid = tokenPayload.getSessionUuid();
70+
if (StringUtils.isBlank(sessionUuid)) {
71+
LOGGER.error("Session UUID is blank in token payload for route: {}", route);
72+
return null;
73+
}
74+
String creatorAddress = tokenPayload.getCreatorAddress();
75+
if (StringUtils.isBlank(creatorAddress)) {
76+
LOGGER.error("Creator address is blank in token payload for route: {}", route);
77+
return null;
78+
}
79+
String requestAddress = ctx.channel().remoteAddress().toString();
80+
if (!requestAddress.contains(creatorAddress)) {
81+
LOGGER.error("Request address: {} does not match creator address: {} for session: {}",
82+
requestAddress, creatorAddress, sessionUuid);
83+
return null;
84+
}
85+
return serverHelper.getSession(sessionUuid);
86+
}
87+
6188
@Override
62-
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
89+
public void channelRead(ChannelHandlerContext ctx, Object msg) {
6390
if (!(msg instanceof FullHttpRequest)) {
6491
ctx.fireChannelRead(msg);
6592
return;
@@ -80,7 +107,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
80107
closeChannelWithErrorResponse(ctx, req, String.format("Empty route in request URI: %s", uri));
81108
return;
82109
}
83-
LogsWebSession session = serverHelper.getSession(route);
110+
LogsWebSession session = getValidSession(route, ctx);
84111
if (session == null) {
85112
closeChannelWithErrorResponse(ctx, req,
86113
String.format("Unauthorized connection attempt for route: %s", route));
@@ -105,7 +132,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
105132

106133
// Rewrite the URI so that the handshake matches the expected sever path
107134
if (req instanceof DefaultFullHttpRequest) {
108-
((DefaultFullHttpRequest) req).setUri(serverPath);
135+
req.setUri(serverPath);
109136
} else {
110137
DefaultFullHttpRequest newReq = new DefaultFullHttpRequest(
111138
req.protocolVersion(), req.method(), serverPath, req.content().retain());

plugins/logs-web-server/src/main/java/org/apache/cloudstack/logsws/server/LogsWebSocketServerHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
package org.apache.cloudstack.logsws.server;
1919

2020
import org.apache.cloudstack.logsws.LogsWebSession;
21+
import org.apache.cloudstack.logsws.LogsWebSessionTokenPayload;
2122

2223
public interface LogsWebSocketServerHelper {
2324
String getServerPath();
2425
String getLogFile();
2526
int getMaxReadExistingLines();
27+
LogsWebSessionTokenPayload parseToken(String token);
2628
LogsWebSession getSession(String route);
2729
void updateSessionConnection(long sessionId, String clientAddress);
2830
}

0 commit comments

Comments
 (0)