From 3b23d5af740b57acd7745d4475fb2a5877bbf043 Mon Sep 17 00:00:00 2001 From: Frank Maximus Date: Tue, 23 Jan 2018 17:20:03 +0100 Subject: [PATCH 1/4] CLOUDSTACK-10245: Fix password server regression (#2419) In case of isolated, both self.config.is_vpc() and self.config.is_router() are false, but self.config.is_dhcp() is true. Moved the password server logic to the `if has_metadata` block, as this is valid for all 3 systemvm types. --- systemvm/debian/opt/cloud/bin/cs/CsAddress.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py index 8b68f538fa4..42992b55123 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py @@ -556,14 +556,6 @@ class CsIP: if self.config.is_vpc() or self.config.is_router(): CsDevice(self.dev, self.config).configure_rp() - # If redundant then this is dealt with - # by the master backup functions - if not cmdline.is_redundant(): - if method == "add": - CsPasswdSvc(self.address['public_ip']).start() - elif method == "delete": - CsPasswdSvc(self.address['public_ip']).stop() - logging.error( "Not able to setup source-nat for a regular router yet") @@ -575,6 +567,14 @@ class CsIP: app = CsApache(self) app.setup() + # If redundant then this is dealt with + # by the master backup functions + if not cmdline.is_redundant(): + if method == "add": + CsPasswdSvc(self.address['public_ip']).start() + elif method == "delete": + CsPasswdSvc(self.address['public_ip']).stop() + if self.get_type() == "public" and self.config.is_vpc() and method == "add": if self.address["source_nat"]: vpccidr = cmdline.get_vpccidr() From f80f00ebcb6c626a004c2b528b4026710f01e794 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Tue, 23 Jan 2018 20:21:49 +0100 Subject: [PATCH 2/4] CLOUDSTACK-10242: Properly parse incoming rules to Sec Group (#2418) With merge of PR #2028 the separator for lines to the Security Group Python script changed from : to ; to support IPv6 addresses. This broke certain situations where rules were parsed improperly. This commit fixes the issue. Signed-off-by: Wido den Hollander Signed-off-by: Rohit Yadav --- scripts/vm/network/security_group.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py index 9b8ac642413..6a11057b237 100755 --- a/scripts/vm/network/security_group.py +++ b/scripts/vm/network/security_group.py @@ -951,16 +951,15 @@ def parse_network_rules(rules): if rules is None or len(rules) == 0: return ret - lines = rules.split(';')[:-1] + lines = rules.split('NEXT;')[:-1] for line in lines: - tokens = line.split(':', 4) - if len(tokens) != 5: + tokens = line.split(';', 3) + if len(tokens) != 4: continue - ruletype = tokens[0] - protocol = tokens[1] - start = int(tokens[2]) - end = int(tokens[3]) + ruletype, protocol = tokens[0].split(':') + start = int(tokens[1]) + end = int(tokens[2]) cidrs = tokens.pop(); ipv4 = [] From 61a5a297058ce4478766b36d640f76684e31b9c8 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 24 Jan 2018 11:09:45 +0100 Subject: [PATCH 3/4] CLOUDSTACK-10252: Delete dnsmasq leases file on restart (#2427) Delete dnsmasq's leases file when dnsmasq is restarted to avoid it use old ip-mac-address-vm mapping leases. Signed-off-by: Rohit Yadav --- systemvm/debian/opt/cloud/bin/cs/CsDhcp.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py index 7aac837cf1b..30fb38e016a 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py @@ -49,15 +49,21 @@ class CsDhcp(CsDataBag): self.add(self.dbag[item]) self.write_hosts() - if self.cloud.is_changed(): - self.delete_leases() - self.configure_server() - restart_dnsmasq = self.conf.commit() - self.cloud.commit() + restart_dnsmasq = False + + if self.conf.commit(): + restart_dnsmasq = True + + if self.cloud.commit(): + restart_dnsmasq = True + self.dhcp_opts.commit() + if restart_dnsmasq: + self.delete_leases() + if not self.cl.is_redundant() or self.cl.is_master(): if restart_dnsmasq: CsHelper.service("dnsmasq", "restart") From 170b6ce20dd4fc2f1fd3ad84833f440d955d2987 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 24 Jan 2018 13:11:08 +0100 Subject: [PATCH 4/4] CLOUDSTACK-10236: Enable dynamic roles for missing props file (#2426) Automate dynamic roles migration for missing props file - In case commands.properties file is missing, enables dynamic roles. - Adds a new -D or --default flag to migrate-dynamicroles.py script to simply update the global setting and use the default role-rule permissions. - Add warning message, ask admins to move to dynamic roles during upgrade Signed-off-by: Rohit Yadav --- .../upgrade/dao/Upgrade41000to41100.java | 18 +++++++++++++ .../acl/StaticRoleBasedAPIAccessChecker.java | 1 + scripts/util/migrate-dynamicroles.py | 27 ++++++++++++------- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade41000to41100.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade41000to41100.java index 53c2340665f..20294d16518 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade41000to41100.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade41000to41100.java @@ -31,6 +31,7 @@ import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import com.cloud.hypervisor.Hypervisor; +import com.cloud.utils.PropertiesUtil; import com.cloud.utils.exception.CloudRuntimeException; public class Upgrade41000to41100 implements DbUpgrade { @@ -65,10 +66,27 @@ public class Upgrade41000to41100 implements DbUpgrade { @Override public void performDataMigration(Connection conn) { + checkAndEnableDynamicRoles(conn); validateUserDataInBase64(conn); updateSystemVmTemplates(conn); } + private void checkAndEnableDynamicRoles(final Connection conn) { + final Map apiMap = PropertiesUtil.processConfigFile(new String[] { "commands.properties" }); + if (apiMap == null || apiMap.isEmpty()) { + if (LOG.isDebugEnabled()) { + LOG.debug("No commands.properties file was found, enabling dynamic roles by setting dynamic.apichecker.enabled to true if not already enabled."); + } + try (final PreparedStatement updateStatement = conn.prepareStatement("INSERT INTO cloud.configuration (category, instance, name, default_value, value) VALUES ('Advanced', 'DEFAULT', 'dynamic.apichecker.enabled', 'false', 'true') ON DUPLICATE KEY UPDATE value='true'")) { + updateStatement.executeUpdate(); + } catch (SQLException e) { + LOG.error("Failed to set dynamic.apichecker.enabled to true, please run migrate-dynamicroles.py script to manually migrate to dynamic roles.", e); + } + } else { + LOG.warn("Old commands.properties static checker is deprecated, please use migrate-dynamicroles.py to migrate to dynamic roles. Refer http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/accounts.html#using-dynamic-roles"); + } + } + private void validateUserDataInBase64(Connection conn) { try (final PreparedStatement selectStatement = conn.prepareStatement("SELECT `id`, `user_data` FROM `cloud`.`user_vm` WHERE `user_data` IS NOT NULL;"); final ResultSet selectResultSet = selectStatement.executeQuery()) { diff --git a/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java b/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java index fc78268fc62..f3dc3a3b8d7 100644 --- a/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java +++ b/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java @@ -39,6 +39,7 @@ import com.cloud.utils.component.PluggableService; // This is the default API access checker that grab's the user's account // based on the account type, access is granted +@Deprecated public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIChecker { protected static final Logger LOGGER = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class); diff --git a/scripts/util/migrate-dynamicroles.py b/scripts/util/migrate-dynamicroles.py index cbb83f91783..35dfe662513 100755 --- a/scripts/util/migrate-dynamicroles.py +++ b/scripts/util/migrate-dynamicroles.py @@ -55,6 +55,14 @@ def migrateApiRolePermissions(apis, conn): if (octetKey[role] & int(apis[api])) > 0: runSql(conn, "INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), %d, '%s', 'ALLOW', %d);" % (role, api, sortOrder)) sortOrder += 1 + print("Static role permissions from commands.properties have been migrated into the db") + + +def enableDynamicApiChecker(conn): + runSql(conn, "UPDATE `cloud`.`configuration` SET value='true' where name='dynamic.apichecker.enabled'") + conn.commit() + conn.close() + print("Dynamic role based API checker has been enabled!") def main(): @@ -71,6 +79,8 @@ def main(): help="Host or IP of the MySQL server") parser.add_option("-f", "--properties-file", action="store", type="string", dest="commandsfile", default="/etc/cloudstack/management/commands.properties", help="The commands.properties file") + parser.add_option("-D", "--default", action="store_true", dest="defaultRules", default=False, + help="") parser.add_option("-d", "--dryrun", action="store_true", dest="dryrun", default=False, help="Dry run and debug operations this tool will perform") (options, args) = parser.parse_args() @@ -89,8 +99,14 @@ def main(): port=int(options.port), db=options.db) + if options.defaultRules: + print("Applying the default role permissions, ignoring any provided properties files(s).") + enableDynamicApiChecker(conn) + sys.exit(0) + if not os.path.isfile(options.commandsfile): - print("Provided commands.properties cannot be accessed or does not exist, please check check permissions") + print("Provided commands.properties cannot be accessed or does not exist.") + print("Please check passed options, or run only with --default option to use the default role permissions.") sys.exit(1) while True: @@ -122,15 +138,8 @@ def main(): # Migrate rules from commands.properties to cloud.role_permissions migrateApiRolePermissions(apiMap, conn) - print("Static role permissions from commands.properties have been migrated into the db") - - # Enable dynamic role based API checker - runSql(conn, "UPDATE `cloud`.`configuration` SET value='true' where name='dynamic.apichecker.enabled'") - conn.commit() - conn.close() - - print("Dynamic role based API checker has been enabled!") + enableDynamicApiChecker(conn) if __name__ == '__main__': main()