From e3f88c8cb819b2ea6312f9026ec94d6df6b1a537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= <valtri@civ.zcu.cz> Date: Fri, 1 Jan 2021 23:51:00 +0100 Subject: [PATCH] Kerberos support in Hadoop + handle persistent variables with secrets --- .gitignore | 1 + deploy.tf | 6 ++ deployments/hadoop/ctx.yaml | 2 + deployments/hadoop/plugin.py | 7 +- deployments/hadoop/site.pp.tmpl | 164 +++++++++++++++++++++++++++++++- image/Puppetfile | 3 + launch.sh | 23 ++++- orchestrate.py | 1 + secrets.auto.tfvars.example | 5 + 9 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 secrets.auto.tfvars.example diff --git a/.gitignore b/.gitignore index 1d88998..7e8eeb4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ config.json inventory hosts public_hosts +secrets.auto.tfvars site.pp site2.pp terraform diff --git a/deploy.tf b/deploy.tf index 4cf61ab..946b044 100644 --- a/deploy.tf +++ b/deploy.tf @@ -53,6 +53,11 @@ EOF } } +variable "secrets" { + type = map(string) + # sensitive = true # terraform >= 0.14 +} + output "config" { value = { n = var.n, @@ -61,6 +66,7 @@ output "config" { master_hostname = var.master_hostname, node_hostname = var.node_hostname, type = var.type, + secrets = var.secrets, } } diff --git a/deployments/hadoop/ctx.yaml b/deployments/hadoop/ctx.yaml index f947825..600ab54 100644 --- a/deployments/hadoop/ctx.yaml +++ b/deployments/hadoop/ctx.yaml @@ -20,6 +20,8 @@ write_files: #!/usr/bin/env ruby #^syntax detection forge "https://forgeapi.puppetlabs.com" + mod 'cesnet-kerberos', + :git => 'https://github.com/MetaCenterCloudPuppet/cesnet-kerberos/' mod 'cesnet-site_hadoop', :git => 'https://github.com/MetaCenterCloudPuppet/cesnet-site_hadoop/' mod 'cesnet-hadoop', diff --git a/deployments/hadoop/plugin.py b/deployments/hadoop/plugin.py index b86c171..3a53892 100644 --- a/deployments/hadoop/plugin.py +++ b/deployments/hadoop/plugin.py @@ -1,3 +1,4 @@ +import os import string DEFAULT_DISTRIBUTION = 'bigtop' @@ -16,7 +17,9 @@ class ComponentHadoop: 'master_hostname': config['master_hostname'], 'node_hostname': config['node_hostname'], 'nodes': list([h for h in hosts.keys() if h != config['master_hostname']]), - 'realm': '', + 'realm': 'HADOOP', + 'kerberos_admin_password': config['secrets']['kerberos_admin_password'], + 'kerberos_master_password': config['secrets']['kerberos_master_password'], } def action(self, action): @@ -29,11 +32,13 @@ class ComponentHadoop: print('-> site.pp') site = template.substitute(self.params) with open('site.pp', 'w') as f: + os.chmod('site.pp', 0o600) f.write(site) self.params['hdfs_deployed'] = 'true' site = template.substitute(self.params) print('-> site2.pp') with open('site2.pp', 'w') as f: + os.chmod('site2.pp', 0o600) f.write(site) def commands(self, action): diff --git a/deployments/hadoop/site.pp.tmpl b/deployments/hadoop/site.pp.tmpl index cedeff9..f739d88 100644 --- a/deployments/hadoop/site.pp.tmpl +++ b/deployments/hadoop/site.pp.tmpl @@ -34,6 +34,40 @@ $$db_type = "$${operatingsystem}-$${operatingsystemmajrelease}" ? { default => 'mariadb', } +$$principals = suffix(concat( + prefix(concat([$$master], $$nodes), 'host/'), + prefix(concat([$$master], $$nodes), 'HTTP/'), + ["httpfs/$$master"], + prefix(concat([$$master], $$nodes), 'hbase/'), + ["hive/$$master"], + prefix($$nodes, 'dn/'), + ["jhs/$$master"], + ["nfs/$$master"], + prefix($$nodes, 'nm/'), + ["nn/$$master"], + ["oozie/$$master"], + ["rm/$$master"], + ["spark/$$master"], + ["zookeeper/$$master"] +), "@$${realm}") + +stage { 'kerberos': + before => Stage['main'], +} + +class{"kerberos": + kadmin_hostname => $$master, + admin_principal => "puppet/admin@$${realm}", + admin_password => '$kerberos_admin_password', + master_password => '$kerberos_master_password', + realm => $$realm, + default_attributes => { + 'requires_preauth' => true, + }, + default_policy => 'default_host', + stage => 'kerberos', +} + class{'hadoop': acl => true, hdfs_hostname => $$master, @@ -63,7 +97,8 @@ class{'hadoop': }, properties => { 'dfs.replication' => 2, - 'hadoop.proxyuser.hive.groups' => "*", + 'hadoop.proxyuser.hive.groups' => "hive,impala,oozie,users", + #'hadoop.proxyuser.hive.groups' => "*", 'hadoop.proxyuser.hive.hosts' => "*", }, version => $$hadoop_version, @@ -133,6 +168,9 @@ class{'site_hadoop': 'example', 'hawking', ], + user_realms => [ + '$$realm', + ], accounting_enable => false, hbase_enable => true, nfs_frontend_enable => false, @@ -142,12 +180,126 @@ class{'site_hadoop': } # site_hadoop::users hasn't shell on the nodes, we need exception for '${image_user}' +$$touchfile = 'hdfs-user-${image_user}-created' hadoop::user{'${image_user}': shell => true, hdfs => $$hadoop::hdfs_hostname == $$::fqdn, groups => 'users', realms => $$site_hadoop::user_realms, - touchfile => 'hdfs-user-${image_user}-created', + touchfile => $$touchfile, +} +if $$hadoop::hdfs_hostname == $$::fqdn { + hadoop::kinit{$$touchfile: + } + -> + Hadoop::User <| touchfile == $$touchfile |> + -> + hadoop::kdestroy{$$touchfile: + touch => true, + } +} + +class local_kerberos { + file{'/etc/security/keytab': + ensure => 'directory', + owner => 'root', + group => 'root', + mode => '0755', + } + + File['/etc/security/keytab'] -> Kerberos::Keytab <| |> +} + +class local_kerberos_master { + include local_kerberos + + kerberos::policy{'default': + ensure => 'present', + minlength => 6, + history => 2, + } + + kerberos::policy{'default_host': + ensure => 'present', + minlength => 6, + } + + kerberos::principal{$$::kerberos::admin_principal: + ensure => 'present', + password => $$::kerberos::admin_password, + } + + kerberos::principal{$$principals:} + + kerberos::keytab{'/etc/krb5.keytab': + principals => ["host/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/hive.service.keytab': + principals => ["hive/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/hbase.service.keytab': + principals => ["hbase/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/http.service.keytab': + principals => ["HTTP/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/httpfs.service.keytab': + principals => ["httpfs/$${::fqdn}@$${realm}"], + } + # works only locally on Kerberos admin server! + kerberos::keytab{'/etc/security/keytab/httpfs-http.service.keytab': + principals => [ + "httpfs/$${::fqdn}@$${realm}", + "HTTP/$${::fqdn}@$${realm}", + ], + } + kerberos::keytab{'/etc/security/keytab/jhs.service.keytab': + principals => ["jhs/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/nfs.service.keytab': + principals => ["nfs/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/nn.service.keytab': + principals => ["nn/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/oozie.service.keytab': + principals => ["oozie/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/rm.service.keytab': + principals => ["rm/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/spark.service.keytab': + principals => ["spark/$${::fqdn}@$${realm}"], + } + kerberos::keytab{'/etc/security/keytab/zookeeper.service.keytab': + principals => ["zookeeper/$${::fqdn}@$${realm}"], + } +} + +class local_kerberos_node { + include local_kerberos + + # this will use kerberos::admin_principal and kerberos::admin_password parameters + kerberos::keytab{'/etc/krb5.keytab': + principals => ["host/$${::fqdn}@$${realm}"], + wait => 600, + } + kerberos::keytab{'/etc/security/keytab/dn.service.keytab': + principals => ["dn/$${::fqdn}@$${realm}"], + wait => 600, + } + kerberos::keytab{'/etc/security/keytab/hbase.service.keytab': + principals => ["hbase/$${::fqdn}@$${realm}"], + wait => 600, + } + kerberos::keytab{'/etc/security/keytab/http.service.keytab': + principals => ["HTTP/$${::fqdn}@$${realm}"], + wait => 600, + } + kerberos::keytab{'/etc/security/keytab/nm.service.keytab': + principals => ["nm/$${::fqdn}@$${realm}"], + wait => 600, + } } node /${master_hostname}\..*/ { @@ -163,8 +315,16 @@ node /${master_hostname}\..*/ { root_password => 'root', } #include ::oozie::client + + class{'local_kerberos_master': + stage => 'kerberos', + } } node /${node_hostname}\d*\..*/ { include ::site_hadoop::role::slave + + class{'local_kerberos_node': + stage => 'kerberos', + } } diff --git a/image/Puppetfile b/image/Puppetfile index e9ddf23..2f71a37 100644 --- a/image/Puppetfile +++ b/image/Puppetfile @@ -3,6 +3,9 @@ forge "https://forgeapi.puppetlabs.com" +mod 'cesnet-kerberos', + :git => 'https://github.com/MetaCenterCloudPuppet/cesnet-kerberos/' + mod 'cesnet-site_hadoop', :git => 'https://github.com/MetaCenterCloudPuppet/cesnet-site_hadoop/' diff --git a/launch.sh b/launch.sh index 71b6b81..7d63f19 100755 --- a/launch.sh +++ b/launch.sh @@ -1,4 +1,25 @@ #! /bin/sh -xe + +if [ ! -s ./secrets.auto.tfvars ]; then + touch ./secrets.auto.tfvars + chmod 0600 ./secrets.auto.tfvars + { + echo 'secrets = {' + for k in kerberos_master_password kerberos_admin_password http_signature_secret; do + echo " $k = \"`dd if=/dev/random bs=27 count=1 2>/dev/null | base64 -`\"" + done + echo "}" + } >> ./secrets.auto.tfvars +fi + ./terraform apply -auto-approve "$@" + +touch config.json; chmod 0600 config.json ./terraform output -json > config.json -./orchestrate.py + +if [ -z "$NO_DEPLOYMENT" ]; then + ./orchestrate.py +else + ./orchestrate.py files ping init wait + ./orchestrate.py -n deployment +fi diff --git a/orchestrate.py b/orchestrate.py index 06c7d67..4f83be6 100755 --- a/orchestrate.py +++ b/orchestrate.py @@ -52,6 +52,7 @@ public_hosts = j['public_hosts']['value'] master_hostname = config['master_hostname'] master_ip = public_hosts[master_hostname] user = config['image_user'] +secrets = config['secrets'] t = config.get('type', None) print('== plugin ==') diff --git a/secrets.auto.tfvars.example b/secrets.auto.tfvars.example new file mode 100644 index 0000000..6a513a7 --- /dev/null +++ b/secrets.auto.tfvars.example @@ -0,0 +1,5 @@ +secrets = { + kerberos_master_password = "SECRET" + kerberos_admin_password = "SECRET" + http_signature_secret = "SECRET" +} -- GitLab