public static bool IsSessionBrokerNode(WindowsIdentity identity, string jobId) { // BUG 8419 : Cache BN SecurityIdentiy objects to improve latency if (!CacheBrokerNodeIdentities(jobId)) { return(false); } if (!brokerNodeIdentityCache.Contains(identity.User)) { // dump SID list string strSIDList = string.Empty; foreach (SecurityIdentifier sid in brokerNodeIdentityCache) { strSIDList += sid.Value; strSIDList += ","; } RuntimeTraceHelper.TraceEvent( jobId, TraceEventType.Error, "[HpcServiceHost]: SessionBrokerNodes.IsSessionBrokerNode: {0} is not in cached node list: {1}", identity.User.Value, strSIDList); return(false); } return(true); }
/// <summary> /// Set the trace level according to the env var /// </summary> public static void SetTraceSwitchLevel() { SourceLevels level = Utility.GetTraceSwitchLevel(); ServiceContext.Logger.Switch.Level = level; if (level != SourceLevels.Off) { RuntimeTraceHelper.IsDiagTraceEnabled = x => true; RuntimeTraceHelper.TraceEvent( TraceEventType.Information, SoaHelper.CreateTraceMessage( "Utility", "SetTraceSwitchLevel", "Add the SoaDiagTraceListener.")); ServiceContext.Logger.Listeners.Add(new SoaDiagTraceListener(Utility.GetJobId())); } else { RuntimeTraceHelper.TraceEvent( TraceEventType.Information, SoaHelper.CreateTraceMessage( "Utility", "SetTraceSwitchLevel", "SoaDiagTrace is disabled.")); } }
/// <summary> /// Initializes a new instance of the BrokerNodeAuthManager class. /// </summary> /// <param name="jobId">job id of the soa session</param> internal BrokerNodeAuthManager(string jobId) { this.jobId = jobId; if (SoaHelper.IsCurrentUserLocal() || WindowsIdentity.GetCurrent().IsSystem) { this.enable = false; RuntimeTraceHelper.TraceEvent(this.jobId, TraceEventType.Information, "[HpcServiceHost]: BrokerNodeAuthManager is disabled in non-domain joined compute node."); } else { this.enable = true; this.allowedUser = WindowsIdentity.GetCurrent(); // If this environment does not exist, the jobOwnerUserName will remain null. // Thus the comparision will always fail so that HpcServiceHost will not authenticate job owner. this.jobOwnerUserName = Environment.GetEnvironmentVariable(Constant.JobOwnerNameEnvVar, EnvironmentVariableTarget.Process); RuntimeTraceHelper.TraceEvent( this.jobId, TraceEventType.Information, "[HpcServiceHost]: BrokerNodeAuthManager initialized. AllowerUser = {0}, JobOwner = {1}", this.allowedUser.Name, this.jobOwnerUserName); } }
/// <summary> /// Get the full path of the local cache for the service config file, /// which is specified by Constant.ServiceConfigFileNameEnvVar. /// </summary> /// <returns>local cache folder path</returns> public static string GetServiceLocalCacheFullPath() { string path = SoaHelper.GetCcpPackageRoot(); if (string.IsNullOrEmpty(path)) { RuntimeTraceHelper.TraceEvent( TraceEventType.Error, SoaHelper.CreateTraceMessage( "Utility", "GetServiceLocalCacheFullPath", "The env var CCP_PACKAGE_ROOT has no value")); } else { if (Directory.Exists(path)) { string configFileName = Environment.GetEnvironmentVariable(Constant.ServiceConfigFileNameEnvVar); if (string.IsNullOrEmpty(configFileName)) { RuntimeTraceHelper.TraceEvent( TraceEventType.Error, SoaHelper.CreateTraceMessage( "Utility", "GetServiceLocalCacheFullPath", "The env var CCP_SERVICE_CONFIG_FILENAME has no value")); } else { string subFolderName = Path.GetFileNameWithoutExtension(configFileName); string result = FindLatestSubDirectory(Path.Combine(path, subFolderName)); if (!string.IsNullOrEmpty(result) && Directory.Exists(result)) { return(result); } } } } // fall back to the home folder, which stores the build-in CcpEchoSvc and HpcSeviceHost.exe string home = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); RuntimeTraceHelper.TraceEvent( TraceEventType.Information, SoaHelper.CreateTraceMessage( "Utility", "GetServiceLocalCacheFullPath", string.Format(CultureInfo.InvariantCulture, "Fall back to the home folder {0}.", home))); return(home); }
/// <summary> /// Shutdowns down service host /// </summary> /// <param name="state"></param> public void Exit() { try { lock (this.hostWrapper.SyncObjOnExitingCalled) { // No need to call OnExiting again if it is already called when Ctrl-B signal is received. if (!this.hostWrapper.IsOnExitingCalled) { // Invoke user's Exiting event async with a timeout specified by TaskCancelGracePeriod cluster parameter. Action <object> a = this.InvokeFireExitingEvent; IAsyncResult ar = a.BeginInvoke(null, null, null); if (ar.AsyncWaitHandle.WaitOne(this._cancelTaskGracePeriod, false)) { a.EndInvoke(ar); } this.hostWrapper.IsOnExitingCalled = true; } } } catch (Exception e) { RuntimeTraceHelper.TraceEvent( this._sessionId, TraceEventType.Warning, "[HpcServiceHost]: Exception calling Exiting - {0}", e); } finally { // Keep the exit code the same as before. If the host is cancelled by user or scheduler, // it exits with -1. If the host is closed by graceful shrink, it exits with 0. if (this.hostWrapper.ReceivedCancelEvent) { Console.Out.WriteLine(StringTable.TaskCanceledOrPreempted); Console.Out.Flush(); Environment.Exit(-1); } else { Console.Out.WriteLine(StringTable.ServiceShutdownFromBrokerShrink); Console.Out.Flush(); Environment.Exit(ErrorCode.Success); } } }
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext) { Guid messageId; request.Headers.MessageId.TryGetGuid(out messageId); this.hostWrapper.AllMessageIds.Add(messageId); this.hostWrapper.SerivceHostIdleTimer?.Change(Timeout.Infinite, Timeout.Infinite); this.hostWrapper.ServiceHangTimer?.Change(this.hostWrapper.ServiceHangTimeout, Timeout.Infinite); if (this.propagateActivity) { Trace.CorrelationManager.ActivityId = messageId; } // This trace is included in the user trace. ServiceContext.Logger.TraceEvent( TraceEventType.Verbose, 0, "[HpcServiceHost]: Request is received."); RuntimeTraceHelper.TraceEvent( TraceEventType.Verbose, "[HpcServiceHost]: Request {0} is received.", messageId); if (this.enableSoaDebugger) { int machineNameIndex = request.Headers.FindHeader(SoaDebuggerMachineName, string.Empty); int portIndex = request.Headers.FindHeader(SoaDebuggerPort, string.Empty); if (0 <= machineNameIndex && machineNameIndex < request.Headers.Count && 0 <= portIndex && portIndex < request.Headers.Count) { string ideMachine = request.Headers.GetHeader <string>(machineNameIndex); int port = request.Headers.GetHeader <int>(portIndex); string localMachine = Environment.MachineName; int pid = Process.GetCurrentProcess().Id; // send back debug info "machinename|pid|jobid" to the VS IDE SendByTcp(ideMachine, port, string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}", localMachine, pid, this.sessionId)); ServiceContext.Logger.TraceInformation(string.Format(CultureInfo.InvariantCulture, "[HpcServiceHost]: Send debug info to the VS IDE machine {0}:{1}.", ideMachine, port)); } } return(messageId); }
/// <summary> /// Get switch level from job env var. /// </summary> /// <returns>trace level</returns> private static SourceLevels GetTraceSwitchLevel() { string values = Environment.GetEnvironmentVariable(Constant.TraceSwitchValue); RuntimeTraceHelper.TraceEvent( TraceEventType.Information, "{0}={1}", Constant.TraceSwitchValue, values); SourceLevels level = SourceLevels.Off; if (!string.IsNullOrEmpty(values)) { string[] levelStrings = values.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string value in levelStrings) { try { level |= (SourceLevels)Enum.Parse(typeof(SourceLevels), value, true); } catch (ArgumentException) { RuntimeTraceHelper.TraceEvent( TraceEventType.Error, SoaHelper.CreateTraceMessage( "Utility", "GetTraceSwitchLevel", string.Format(CultureInfo.CurrentCulture, "{0} is not a correct value of SourceLevels.", value))); return(SourceLevels.Off); } } RuntimeTraceHelper.TraceEvent( TraceEventType.Information, SoaHelper.CreateTraceMessage( "Utility", "GetTraceSwitchLevel", string.Format(CultureInfo.InvariantCulture, "The trace switch level is {0}.", level))); } return(level); }
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { Guid guid = Guid.Empty; if (correlationState is Guid) { guid = (Guid)correlationState; } // This trace is included in the user trace. ServiceContext.Logger.TraceEvent( TraceEventType.Verbose, 0, "[HpcServiceHost]: Response is sent back. IsFault = {0}", reply.IsFault); RuntimeTraceHelper.TraceEvent( TraceEventType.Verbose, "[HpcServiceHost]: Response {0} is sent back. IsFault = {1}", guid, reply.IsFault); if (this.propagateActivity) { System.Diagnostics.Trace.CorrelationManager.ActivityId = Guid.Empty; } if (this.hostWrapper.EnableMessageLevelPreemption) { // If the message is skipped, reply a fault message to the broker. if (this.hostWrapper.SkippedMessageIds.Contains(guid)) { this.hostWrapper.SkippedMessageIds.Remove(guid); // For Service_Preempted error, reuse SessionFault.reason property to pass "processing message count" to the broker. int messageCount = this.hostWrapper.ProcessingMessageIds.Count; SessionFault fault = new SessionFault(SOAFaultCode.Service_Preempted, messageCount.ToString()); FaultException faultException = new FaultException <SessionFault>(fault, string.Empty, null, SessionFault.Action); reply = GenerateFaultMessage(guid, reply.Headers.MessageVersion, faultException); } else if (this.hostWrapper.ProcessingMessageIds.Contains(guid)) { this.hostWrapper.ProcessingMessageIds.Remove(guid); // The service host receives the cancel event when the request is being processed, so add a header to notice the broker. if (this.hostWrapper.ReceivedCancelEvent) { // Use the header to pass "processing message count" to the broker. int messageCount = this.hostWrapper.ProcessingMessageIds.Count; reply.Headers.Add(MessageHeader.CreateHeader(Constant.MessageHeaderPreemption, Constant.HpcHeaderNS, messageCount)); } } else { // If the message is not in above two lists, the message doesn't come into the invoker. No need to change its response. } } if (this.hostWrapper.AllMessageIds.Contains(guid)) { this.hostWrapper.AllMessageIds.Remove(guid); lock (this.hostWrapper.AllMessageIds.SyncRoot) { if (this.hostWrapper.AllMessageIds.Count == 0) { this.hostWrapper.SerivceHostIdleTimer?.Change(this.hostWrapper.ServiceHostIdleTimeout, Timeout.Infinite); this.hostWrapper.ServiceHangTimer?.Change(Timeout.Infinite, Timeout.Infinite); } else { this.hostWrapper.ServiceHangTimer?.Change(this.hostWrapper.ServiceHangTimeout, Timeout.Infinite); } } } }
/// <summary> /// Returned broker node identity cache /// </summary> /// <returns></returns> private static bool CacheBrokerNodeIdentities(string jobId) { bool ret = true; if (brokerNodeIdentityCache == null) { lock (brokerNodeIdentityCacheLock) { if (brokerNodeIdentityCache == null) { try { IDictionary envVars = Environment.GetEnvironmentVariables(); // Extract the number of BNs string nodeCountStr = (string)envVars[BrokerCountEnvVarName]; int nodeCount = 0; if (string.IsNullOrEmpty(nodeCountStr) || !int.TryParse(nodeCountStr, out nodeCount)) { RuntimeTraceHelper.TraceEvent( jobId, TraceEventType.Error, "[HpcServiceHost]: SessionBrokerNodes.CacheBrokerNodeIdentities: return false because nodeCountStr={0}", nodeCountStr); return(false); } if (nodeCount <= 0) { RuntimeTraceHelper.TraceEvent( jobId, TraceEventType.Error, "[HpcServiceHost]: SessionBrokerNodes.CacheBrokerNodeIdentities: return false because nodeCount={0}", nodeCount); return(false); } // Enum each BN and create a SecurityIdentifier for it brokerNodeIdentityCache = new List <SecurityIdentifier>(); for (int i = 0; i < nodeCount; i++) { string nodeSSDL = (string)envVars[string.Format(BrokerNameEnvVarNameTemplate, i + 1)]; // Create BN identity using SecurityIdentifier. This ensures correct comparison (including same domain - Bug 6787). SecurityIdentifier brokerNodeIdentity = new SecurityIdentifier(nodeSSDL); brokerNodeIdentityCache.Add(brokerNodeIdentity); RuntimeTraceHelper.TraceEvent( jobId, TraceEventType.Verbose, "[HpcServiceHost]: SessionBrokerNodes.CacheBrokerNodeIdentities: cached node: node SSDL={0}, SID={1}", nodeSSDL, brokerNodeIdentity.Value); } } catch (Exception e) { RuntimeTraceHelper.TraceEvent( jobId, TraceEventType.Error, "[HpcServiceHost]: Fail to cache broker node identities for broker node authentication. - {0}", e); ret = false; } } } } return(ret); }
/// <summary> /// Get service assembly file /// </summary> /// <param name="serviceConfigName">indicating service configuration file</param> /// <param name="onAzure">on azure or in on-premise cluster</param> /// <param name="registration">output service registration instance</param> /// <param name="serviceAssemblyFullPath">full path of the soa service assembly</param> /// <returns>returns return code</returns> public static int GetServiceRegistration(string serviceConfigName, bool onAzure, out ServiceRegistration registration, out string serviceAssemblyFullPath) { // Open the service registration configuration section ExeConfigurationFileMap map = new ExeConfigurationFileMap(); map.ExeConfigFilename = serviceConfigName; Configuration config = null; RetryManager.RetryOnceAsync( () => config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None), TimeSpan.FromSeconds(1), ex => ex is ConfigurationErrorsException) .GetAwaiter() .GetResult(); Debug.Assert(config != null, "Configuration is not opened properly."); registration = ServiceRegistration.GetSectionGroup(config); serviceAssemblyFullPath = registration.Service.AssemblyPath; if (!string.IsNullOrEmpty(serviceAssemblyFullPath)) { serviceAssemblyFullPath = Environment.ExpandEnvironmentVariables(serviceAssemblyFullPath); } if (onAzure) { bool exists = false; try { exists = File.Exists(serviceAssemblyFullPath); } catch (Exception e) { RuntimeTraceHelper.TraceEvent( TraceEventType.Error, SoaHelper.CreateTraceMessage( "Utility", "GetServiceLocalCacheFullPath", string.Format("Exception happens when check the file existence. {0}", e))); } if (!exists) { // If we find the service registration file under the specified path, use it. // Otherwise, fall back to the package root folder. The service assembly is under the same folder as the registration file. serviceAssemblyFullPath = Path.Combine(Path.GetDirectoryName(serviceConfigName), Path.GetFileName(serviceAssemblyFullPath)); } } if (string.IsNullOrEmpty(serviceAssemblyFullPath)) { string message = string.Format(CultureInfo.CurrentCulture, StringTable.AssemblyFileNotRegistered, serviceConfigName); Console.Error.WriteLine(message); RuntimeTraceHelper.TraceEvent(TraceEventType.Error, message); return(ErrorCode.ServiceHost_AssemblyFileNameNullOrEmpty); } if (!File.Exists(serviceAssemblyFullPath)) { // If the obtained service assembly path is not valid or not existing string message = string.Format(CultureInfo.CurrentCulture, StringTable.AssemblyFileCantFind, serviceAssemblyFullPath); Console.Error.WriteLine(message); RuntimeTraceHelper.TraceEvent(TraceEventType.Error, message); return(ErrorCode.ServiceHost_AssemblyFileNotFound); } return(ErrorCode.Success); }
/// <summary> /// Called to auth each request /// </summary> /// <param name="operationContext">Operation's context</param> /// <returns>pass validation or not</returns> protected override bool CheckAccessCore(OperationContext operationContext) { if (this.enable == false) { RuntimeTraceHelper.TraceEvent( this.jobId, TraceEventType.Verbose, "[HpcServiceHost]: BrokerNodeAuthManager is disabled."); return(true); } WindowsIdentity callerIdentity = null; bool result = SoaHelper.CheckWindowsIdentity(operationContext, out callerIdentity); if (result && callerIdentity == null) { // this code path is for Azure. return(true); } if (callerIdentity == null || !result || operationContext.ServiceSecurityContext.IsAnonymous) { RuntimeTraceHelper.TraceEvent(this.jobId, TraceEventType.Warning, "[HpcServiceHost]: Access denied by BrokerNodeAuthManager. WindowsIdeneity is not recognized."); return(false); } RuntimeTraceHelper.TraceEvent(this.jobId, TraceEventType.Verbose, "[HpcServiceHost]: received request from {0}", callerIdentity.Name); // if this is calling from local if (callerIdentity.IsSystem) { return(true); } // Bug 11378: Authenticate job owner also for inprocess broker if (callerIdentity.Name.Equals(this.jobOwnerUserName, StringComparison.InvariantCultureIgnoreCase)) { RuntimeTraceHelper.TraceEvent( this.jobId, TraceEventType.Verbose, "[HpcServiceHost]: Authenticate job owner {0} for inprocess broker.", this.jobOwnerUserName); return(true); } // is this call from a BN if (SessionBrokerNodes.IsSessionBrokerNode(callerIdentity, this.jobId)) { return(true); } RuntimeTraceHelper.TraceEvent( this.jobId, TraceEventType.Warning, "[HpcServiceHost]: {0}/SID={1} is not a broker node", callerIdentity.Name, callerIdentity.User.Value); // Last see if the caller is the 'run as' user for the process. This is mainly needed for diag tests if (callerIdentity.User == this.allowedUser.User) { return(true); } else { RuntimeTraceHelper.TraceEvent( this.jobId, TraceEventType.Warning, "[HpcServiceHost]: Access denied by BrokerNodeAuthManager. {0} is not allowed.", callerIdentity.User); return(false); } }