6464 ArcACloudName : "armmanagement.autonomous.cloud.private"
6565}
6666
67+ MeCMv2RuntimeDirectory = "/var/run/azuremetricsext"
68+ MeCMv2ServiceUser = "azuremetricsext"
69+ MeCMv2ServiceGroup = "azuremonitoragent"
70+ HimdsGroupName = "himds"
71+
6772
6873def is_running (is_lad ):
6974 """
@@ -354,7 +359,7 @@ def get_ArcA_MSI_token(resource = "https://monitoring.azs"):
354359 return True , token_string , log_messages
355360
356361
357- def setup_me_service (is_lad , configFolder , monitoringAccount , metrics_ext_bin , me_influx_port , managed_identity = "sai" , HUtilObj = None ):
362+ def setup_me_service (is_lad , configFolder , monitoringAccount , metrics_ext_bin , me_influx_port , managed_identity = "sai" , HUtilObj = None , supplementary_grps = None ):
358363 """
359364 Setup the metrics service if VM is using systemd
360365 :param configFolder: Path for the config folder for metrics extension
@@ -382,6 +387,10 @@ def setup_me_service(is_lad, configFolder, monitoringAccount, metrics_ext_bin, m
382387 os .system (r"sed -i 's+%ME_MONITORING_ACCOUNT%+{1}+' {0}" .format (me_service_path , monitoringAccount ))
383388 os .system (r"sed -i 's+%ME_MANAGED_IDENTITY%+{1}+' {0}" .format (me_service_path , managed_identity ))
384389 os .system (r"sed -i 's+%ME_INFLUX_SOCKET_FILE_PATH%+{1}+' {0}" .format (me_service_path , me_influx_socket_path ))
390+ supplementary_groups_line = ""
391+ if supplementary_grps :
392+ supplementary_groups_line = "SupplementaryGroups={0}" .format (supplementary_grps )
393+ os .system (r"sed -i 's+%SUPPLEMENTARY_GROUPS_LINE%+{1}+' {0}" .format (me_service_path , supplementary_groups_line ))
385394 daemon_reload_status = os .system ("systemctl daemon-reload" )
386395 if daemon_reload_status != 0 :
387396 message = "Unable to reload systemd after ME service file change. Failed to set up ME service. Check system for hardening. Exit code:" + str (daemon_reload_status )
@@ -833,31 +842,21 @@ def get_metrics_extension_service_name(is_lad):
833842 return metrics_constants .metrics_extension_service_name
834843
835844
836- def setup_me (is_lad , managed_identity = "sai" , HUtilObj = None , is_local_control_channel = True , user = None , group = None ):
845+ def setup_me (is_lad , managed_identity = "sai" , HUtilObj = None , is_local_control_channel = True ):
837846 """
838847 The main method for creating and writing MetricsExtension configuration as well as service setup
839848 :param is_lad: Boolean value for whether the extension is Lad or not (AMA)
840849 :param is_local_control_channel: Boolean value for whether MetricsExtension needs to be run in `-LocalControlChannel` mode (CMv1 only)
841- :param user: User that would own MetricsExtension process. If not specified, would default to the caller, in this case being root
842- :param group: Group that would own MetricsExtension process. If not specified, would default to the caller, in this case being root
843850 """
844851 _ , config_folder = get_handler_vars ()
845852 me_config_dir = config_folder + "/metrics_configs/"
846- create_empty_data_directory (me_config_dir )
853+ setup_data_directory (me_config_dir )
854+ supplementary_grps = ""
847855
848856 if not is_local_control_channel :
849857 # CMv2 and related modes
850858 me_monitoring_account = ""
851- if user and group :
852- # Remove any previous user setup for MetricsExtension if it exists
853- remove_user (user , HUtilObj = HUtilObj )
854- # Create user/group for metrics-extension.service if it is requested
855- ensure_user_and_group (user , group , create_if_missing = True , HUtilObj = HUtilObj )
856- # For ARC, add user to himds group if it exists
857- ensure_user_and_group (user , "himds" , create_if_missing = False , HUtilObj = HUtilObj )
858- # In CMv2 with user and group specified, create directory for MetricsExtension config caching
859- me_config_dir = "/var/run/azuremetricsext"
860- create_empty_data_directory (me_config_dir , user , group , HUtilObj = HUtilObj )
859+ me_config_dir , supplementary_grps = prepare_cmv2_service_runtime (HUtilObj = HUtilObj )
861860 else :
862861 # query imds to get the required information
863862 az_resource_id , subscription_id , location , az_environment , data = get_imds_values (is_lad )
@@ -953,16 +952,14 @@ def setup_me(is_lad, managed_identity="sai", HUtilObj=None, is_local_control_cha
953952 # setup metrics extension service
954953 # If the VM has systemd, then we use that to start/stop
955954 if metrics_utils .is_systemd ():
956- setup_me_service (is_lad , me_config_dir , me_monitoring_account , metrics_ext_bin , me_influx_port , managed_identity , HUtilObj )
955+ setup_me_service (is_lad , me_config_dir , me_monitoring_account , metrics_ext_bin , me_influx_port , managed_identity , HUtilObj , supplementary_grps = supplementary_grps )
957956
958957 return True
959958
960959
961- def remove_user (user , HUtilObj = None ):
960+ def remove_user (user = MeCMv2ServiceUser , HUtilObj = None ):
962961 """
963962 Removes existing user.
964- Note: This is important as the older MetricsExtension might have created the user which needs to be removed.
965- This mechanism can be removed in the future, if the user and group are maintained from MetricsExtension package.
966963 :param user: linux user
967964 :param HUtilObj: utility object for logging
968965 """
@@ -978,7 +975,7 @@ def remove_user(user, HUtilObj=None):
978975 return
979976
980977 try :
981- process = subprocess .Popen (['userdel' , "-r" , user ], stdout = subprocess .PIPE , stderr = subprocess .PIPE )
978+ process = subprocess .Popen (['userdel' , '-r' , user ], stdout = subprocess .PIPE , stderr = subprocess .PIPE )
982979 out , err = process .communicate ()
983980 if process .returncode != 0 :
984981 if HUtilObj :
@@ -988,21 +985,17 @@ def remove_user(user, HUtilObj=None):
988985 HUtilObj .log ('Error while deleting user {0}: {1}' .format (user , e ))
989986
990987
991- def ensure_user_and_group ( user , group , create_if_missing = False , HUtilObj = None ):
988+ def ensure_group ( group , create_if_missing = False , HUtilObj = None ):
992989 """
993- Ensures if the user and group exists, optionally creating them if it does not exist.
994- Group is checked, user is checked and then user is added to the group.
995- Returns True if all of them are available (or created), else returns False.
996- :param user: linux user
990+ Ensure the group exists, optionally creating it if it does not exist.
997991 :param group: linux group
998- :param create_if_missing: boolean if true, create the requested user and group, where user belongs to the group
999- :param HUtilObj: utility object for logging
992+ :param create_if_missing: boolean if true, create the requested group if it does not exist
1000993 """
1001- # Check/Create group if missing
1002994 try :
1003995 grp .getgrnam (group )
1004996 if HUtilObj :
1005997 HUtilObj .log ('Group {0} exists.' .format (group ))
998+ return True
1006999 except KeyError :
10071000 if create_if_missing :
10081001 try :
@@ -1014,6 +1007,7 @@ def ensure_user_and_group(user, group, create_if_missing=False, HUtilObj=None):
10141007 return False
10151008 if HUtilObj :
10161009 HUtilObj .log ('Group {0} created.' .format (group ))
1010+ return True
10171011 except Exception as e :
10181012 if HUtilObj :
10191013 HUtilObj .log ('Error while creating group {0}: {1}' .format (group , e ))
@@ -1027,16 +1021,24 @@ def ensure_user_and_group(user, group, create_if_missing=False, HUtilObj=None):
10271021 HUtilObj .log ('Error while checking group {0}: {1}' .format (group , e ))
10281022 return False
10291023
1030- # Check/Create user if missing
1024+
1025+ def ensure_user_primary_group (user , group , create_if_missing = False , HUtilObj = None ):
1026+ """
1027+ Ensure the user exists and uses the provided group as its primary group.
1028+ If the user is missing and creation is allowed, create it with that primary group.
1029+ :param user: linux user
1030+ :param group: linux group
1031+ :param create_if_missing: boolean if true, create the requested user if it does not exist
1032+ """
10311033 try :
1032- pwd .getpwnam (user )
1034+ user_info = pwd .getpwnam (user )
10331035 if HUtilObj :
10341036 HUtilObj .log ('User {0} exists.' .format (user ))
10351037 except KeyError :
10361038 if create_if_missing :
10371039 try :
10381040 process = subprocess .Popen ([
1039- 'useradd' , '--no-create-home' , '--system' , '--shell' , '/usr/sbin/nologin' , user
1041+ 'useradd' , '--no-create-home' , '--system' , '--shell' , '/usr/sbin/nologin' , '-g' , group , user
10401042 ], stdout = subprocess .PIPE , stderr = subprocess .PIPE )
10411043 out , err = process .communicate ()
10421044 if process .returncode != 0 :
@@ -1045,57 +1047,94 @@ def ensure_user_and_group(user, group, create_if_missing=False, HUtilObj=None):
10451047 return False
10461048 if HUtilObj :
10471049 HUtilObj .log ('User {0} created.' .format (user ))
1050+ HUtilObj .log ('User {0} created with primary group {1}.' .format (user , group ))
1051+ return True
10481052 except Exception as e :
10491053 if HUtilObj :
10501054 HUtilObj .log ('Error while creating user {0}: {1}' .format (user , e ))
10511055 return False
1052- else :
1053- if HUtilObj :
1054- HUtilObj .log ('User {0} does not exist.' .format (user ))
1055- return False
1056+ if HUtilObj :
1057+ HUtilObj .log ('User {0} does not exist.' .format (user ))
1058+ return False
10561059 except Exception as e :
10571060 if HUtilObj :
10581061 HUtilObj .log ('Error while checking user {0}: {1}' .format (user , e ))
10591062 return False
10601063
1061- # Add user to group
10621064 try :
1063- process = subprocess .Popen (['usermod' , '-aG' , group , user ], stdout = subprocess .PIPE , stderr = subprocess .PIPE )
1065+ target_group = grp .getgrnam (group )
1066+ except Exception as e :
1067+ if HUtilObj :
1068+ HUtilObj .log ('Error while checking group {0}: {1}' .format (group , e ))
1069+ return False
1070+
1071+ if user_info .pw_gid == target_group .gr_gid :
1072+ if HUtilObj :
1073+ HUtilObj .log ('User {0} already has primary group {1}.' .format (user , group ))
1074+ return True
1075+
1076+ try :
1077+ process = subprocess .Popen (['usermod' , '-g' , group , user ], stdout = subprocess .PIPE , stderr = subprocess .PIPE )
10641078 out , err = process .communicate ()
10651079 if process .returncode != 0 :
10661080 if HUtilObj :
1067- HUtilObj .log ('Failed to add user {0} to group {1}. stderr: {2}' .format (user , group , err ))
1081+ HUtilObj .log ('Failed to set primary group {0} for user {1}. stderr: {2}' .format (group , user , err ))
10681082 return False
10691083 if HUtilObj :
1070- HUtilObj .log ('User {0} added to group {1}.' .format (user , group ))
1084+ HUtilObj .log ('User {0} primary group set to {1}.' .format (user , group ))
1085+ return True
10711086 except Exception as e :
10721087 if HUtilObj :
1073- HUtilObj .log ('Error while adding user {0} to group {1}: {2}' .format (user , group , e ))
1088+ HUtilObj .log ('Error while setting primary group {0} for user {1}: {2}' .format (group , user , e ))
10741089 return False
10751090
1076- if HUtilObj :
1077- HUtilObj .log ('User {0} added to group {1} (or already a member).' .format (user , group ))
1078- return True
10791091
1092+ def ensure_service_user_and_group (user , group , create_if_missing = False , HUtilObj = None ):
1093+ """
1094+ Ensure the service group exists and the service user uses it as the primary group.
1095+ :param user: linux user
1096+ :param group: linux group
1097+ :param create_if_missing: boolean if true, create the requested user and group if they do not exist
1098+ """
1099+ if not ensure_group (group , create_if_missing = create_if_missing , HUtilObj = HUtilObj ):
1100+ return False
1101+
1102+ return ensure_user_primary_group (user , group , create_if_missing = create_if_missing , HUtilObj = HUtilObj )
1103+
1104+
1105+ def prepare_cmv2_service_runtime (user = MeCMv2ServiceUser , group = MeCMv2ServiceGroup , HUtilObj = None ):
1106+ """
1107+ Prepare local runtime prerequisites for the CMv2 MetricsExtension service.
1108+ Returns the service data directory and any supplementary groups the unit should use at runtime.
1109+ """
1110+ if not ensure_service_user_and_group (user , group , create_if_missing = True , HUtilObj = HUtilObj ):
1111+ raise Exception ("Failed to ensure user {0} with primary group {1}." .format (user , group ))
1112+
1113+ supplementary_grps = []
1114+ if ensure_group (HimdsGroupName , HUtilObj = HUtilObj ):
1115+ supplementary_grps .append (HimdsGroupName )
10801116
1081- def create_empty_data_directory (me_config_dir , user = None , group = None , mode = 0o755 , HUtilObj = None ):
1117+ setup_data_directory (MeCMv2RuntimeDirectory , user , group , clear_existing = False , HUtilObj = HUtilObj )
1118+ return MeCMv2RuntimeDirectory , " " .join (supplementary_grps )
1119+
1120+
1121+ def setup_data_directory (me_config_dir , user = None , group = None , mode = 0o755 , clear_existing = True , HUtilObj = None ):
10821122 '''
1083- Creates an empty data directory where MetricsExtension can store cached configurations.
1123+ Creates a data directory where MetricsExtension can store cached configurations.
10841124 For CMv1, MetricsExtension requires mdsd to provide all configurations on disk.
1085- For CMv2, MetricsExtension requires an empty data directory where it can cache its configurations.
1125+ For CMv2, MetricsExtension requires a runtime data directory where it can cache its configurations.
10861126 '''
10871127 try :
1088- # Clear older config directory if exists.
1089- if os .path .exists (me_config_dir ):
1128+ if clear_existing and os .path .exists (me_config_dir ):
10901129 rmtree (me_config_dir )
1091- os .makedirs (me_config_dir , mode = mode )
1130+ if not os .path .exists (me_config_dir ):
1131+ os .makedirs (me_config_dir , mode = mode )
1132+ else :
1133+ os .chmod (me_config_dir , mode )
10921134
10931135 if user and group :
1094- # Get UID and GID from user and group names
10951136 uid = pwd .getpwnam (user ).pw_uid
10961137 gid = grp .getgrnam (group ).gr_gid
1097-
1098- # Set the ownership
10991138 os .chown (me_config_dir , uid , gid )
11001139
11011140 if HUtilObj :
0 commit comments