From a087ba02e37eb29c88ce3c3e05a27908ebe8e4b0 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Wed, 25 Dec 2019 14:59:47 +0800 Subject: [PATCH] Populate basic auth information for registry This commit updates `prepare` and templates to populate the credential for registry for basic authentication. A temporary flag `registry_use_basic_auth` was added to avoid breakage. It MUST be removed before the release. Signed-off-by: Daniel Jiang --- make/harbor.yml.tmpl | 3 +++ make/photon/prepare/Dockerfile.base | 7 ++++--- make/photon/prepare/main.py | 2 +- make/photon/prepare/templates/core/env.jinja | 2 ++ .../templates/registry/config.yml.jinja | 6 ++++++ make/photon/prepare/utils/cert.py | 19 +++--------------- make/photon/prepare/utils/configs.py | 10 ++++++++++ make/photon/prepare/utils/misc.py | 20 ++++++++++++++++--- make/photon/prepare/utils/registry.py | 11 +++++++++- src/core/config/config.go | 5 +++++ 10 files changed, 61 insertions(+), 24 deletions(-) diff --git a/make/harbor.yml.tmpl b/make/harbor.yml.tmpl index b5df0bac9..2d6655c65 100644 --- a/make/harbor.yml.tmpl +++ b/make/harbor.yml.tmpl @@ -26,6 +26,9 @@ https: # Remember Change the admin password from UI after launching Harbor. harbor_admin_password: Harbor12345 +#TODO: remove this temporary flag before ships v1.11/v2, this should always be true +registry_use_basic_auth: false + # Harbor DB configuration database: # The password for the root user of Harbor DB. Change this before any production use. diff --git a/make/photon/prepare/Dockerfile.base b/make/photon/prepare/Dockerfile.base index 7ebe37c73..5c6840768 100644 --- a/make/photon/prepare/Dockerfile.base +++ b/make/photon/prepare/Dockerfile.base @@ -1,5 +1,6 @@ FROM photon:2.0 -RUN tdnf install -y python3 \ - && tdnf install -y python3-pip -RUN pip3 install pipenv==2018.11.26 \ No newline at end of file +RUN tdnf install -y python3 python3-pip httpd +RUN pip3 install pipenv==2018.11.26 + + diff --git a/make/photon/prepare/main.py b/make/photon/prepare/main.py index 626511fca..2439e6f33 100644 --- a/make/photon/prepare/main.py +++ b/make/photon/prepare/main.py @@ -35,7 +35,7 @@ def main(conf, with_notary, with_clair, with_chartmuseum): try: validate(config_dict, notary_mode=with_notary) except Exception as e: - logging.info('Error happend in config validation...') + logging.info('Error happened in config validation...') logging.error(e) sys.exit(-1) diff --git a/make/photon/prepare/templates/core/env.jinja b/make/photon/prepare/templates/core/env.jinja index c056a3f2b..c965a9dbb 100644 --- a/make/photon/prepare/templates/core/env.jinja +++ b/make/photon/prepare/templates/core/env.jinja @@ -44,6 +44,8 @@ RELOAD_KEY={{reload_key}} CHART_REPOSITORY_URL={{chart_repository_url}} REGISTRY_CONTROLLER_URL={{registry_controller_url}} WITH_CHARTMUSEUM={{with_chartmuseum}} +REGISTRY_CREDENTIAL_USERNAME={{registry_username}} +REGISTRY_CREDENTIAL_PASSWORD={{registry_password}} HTTP_PROXY={{core_http_proxy}} HTTPS_PROXY={{core_https_proxy}} diff --git a/make/photon/prepare/templates/registry/config.yml.jinja b/make/photon/prepare/templates/registry/config.yml.jinja index 54592063c..e99a27b5c 100644 --- a/make/photon/prepare/templates/registry/config.yml.jinja +++ b/make/photon/prepare/templates/registry/config.yml.jinja @@ -26,11 +26,17 @@ http: debug: addr: localhost:5001 auth: +{% if registry_use_basic_auth %} + htpasswd: + realm: harbor-registry-basic-realm + path: /etc/registry/passwd +{% else %} token: issuer: harbor-token-issuer realm: {{public_url}}/service/token rootcertbundle: /etc/registry/root.crt service: harbor-registry +{% endif %} validation: disabled: true notifications: diff --git a/make/photon/prepare/utils/cert.py b/make/photon/prepare/utils/cert.py index 3ba42861a..d7ed07c7c 100644 --- a/make/photon/prepare/utils/cert.py +++ b/make/photon/prepare/utils/cert.py @@ -1,14 +1,14 @@ # Get or generate private key -import os, sys, subprocess, shutil +import os, subprocess, shutil from pathlib import Path from subprocess import DEVNULL -from functools import wraps from g import DEFAULT_GID, DEFAULT_UID from .misc import ( mark_file, generate_random_string, - check_permission) + check_permission, + stat_decorator) SSL_CERT_PATH = os.path.join("/etc/cert", "server.crt") SSL_CERT_KEY_PATH = os.path.join("/etc/cert", "server.key") @@ -44,19 +44,6 @@ def get_alias(path): alias = _get_secret(path, "defaultalias", length=8) return alias -## decorator actions -def stat_decorator(func): - @wraps(func) - def check_wrapper(*args, **kw): - stat = func(*args, **kw) - if stat == 0: - print("Generated certificate, key file: {key_path}, cert file: {cert_path}".format(**kw)) - else: - print("Fail to generate key file: {key_path}, cert file: {cert_path}".format(**kw)) - sys.exit(1) - return check_wrapper - - @stat_decorator def create_root_cert(subj, key_path="./k.key", cert_path="./cert.crt"): rc = subprocess.call(["/usr/bin/openssl", "genrsa", "-out", key_path, "4096"], stdout=DEVNULL, stderr=subprocess.STDOUT) diff --git a/make/photon/prepare/utils/configs.py b/make/photon/prepare/utils/configs.py index d50132d19..e6e684da6 100644 --- a/make/photon/prepare/utils/configs.py +++ b/make/photon/prepare/utils/configs.py @@ -9,6 +9,8 @@ default_db_max_open_conns = 0 # NOTE: https://golang.org/pkg/database/sql/#DB.S default_https_cert_path = '/your/certificate/path' default_https_key_path = '/your/certificate/path' +REGISTRY_USER_NAME = 'harbor_registry_user' + def validate(conf: dict, **kwargs): # hostname validate @@ -83,6 +85,7 @@ def validate(conf: dict, **kwargs): # TODO: # If user enable trust cert dir, need check if the files in this dir is readable. + def parse_versions(): if not versions_file_path.is_file(): return {} @@ -90,6 +93,7 @@ def parse_versions(): versions = yaml.load(f) return versions + def parse_yaml_config(config_file_path, with_notary, with_clair, with_chartmuseum): ''' :param configs: config_parser object @@ -321,6 +325,12 @@ def parse_yaml_config(config_file_path, with_notary, with_clair, with_chartmuseu # UAA configs config_dict['uaa'] = configs.get('uaa') or {} + config_dict['registry_username'] = REGISTRY_USER_NAME + config_dict['registry_password'] = generate_random_string(32) + + # TODO: remove the flag before release + config_dict['registry_use_basic_auth'] = configs['registry_use_basic_auth'] + return config_dict diff --git a/make/photon/prepare/utils/misc.py b/make/photon/prepare/utils/misc.py index 0b4d0e66d..6280450ee 100644 --- a/make/photon/prepare/utils/misc.py +++ b/make/photon/prepare/utils/misc.py @@ -1,11 +1,10 @@ -import os -import string +import os, string, sys import secrets from pathlib import Path +from functools import wraps from g import DEFAULT_UID, DEFAULT_GID - # To meet security requirement # By default it will change file mode to 0600, and make the owner of the file to 10000:10000 def mark_file(path, mode=0o600, uid=DEFAULT_UID, gid=DEFAULT_GID): @@ -154,3 +153,18 @@ def other_can_read(st_mode: int) -> bool: Check if other user have the read permission of this st_mode """ return True if st_mode & 0o004 else False + + +# decorator actions +def stat_decorator(func): + @wraps(func) + def check_wrapper(*args, **kw): + stat = func(*args, **kw) + if stat == 0: + print("Successfully called func: %s" % func.__name__) + else: + print("Failed to call func: %s" % func.__name__) + sys.exit(1) + return check_wrapper + + diff --git a/make/photon/prepare/utils/registry.py b/make/photon/prepare/utils/registry.py index 564a39111..932ef563b 100644 --- a/make/photon/prepare/utils/registry.py +++ b/make/photon/prepare/utils/registry.py @@ -1,4 +1,4 @@ -import os, copy +import os, copy, subprocess from g import config_dir, templates_dir, DEFAULT_GID, DEFAULT_UID, data_dir from utils.misc import prepare_dir @@ -8,6 +8,7 @@ from utils.jinja import render_jinja registry_config_dir = os.path.join(config_dir, "registry") registry_config_template_path = os.path.join(templates_dir, "registry", "config.yml.jinja") registry_conf = os.path.join(config_dir, "registry", "config.yml") +registry_passwd_path = os.path.join(config_dir, "registry", "passwd") registry_data_dir = os.path.join(data_dir, 'registry') levels_map = { @@ -18,10 +19,13 @@ levels_map = { 'fatal': 'fatal' } + def prepare_registry(config_dict): prepare_dir(registry_data_dir, uid=DEFAULT_UID, gid=DEFAULT_GID) prepare_dir(registry_config_dir) + if config_dict['registry_use_basic_auth']: + gen_passwd_file(config_dict) storage_provider_info = get_storage_provider_info( config_dict['storage_provider_name'], config_dict['storage_provider_config']) @@ -55,3 +59,8 @@ def get_storage_provider_info(provider_name, provider_config): storage_provider_conf_list.append('{}: {}'.format(config[0], value)) storage_provider_info = ('\n' + ' ' * 4).join(storage_provider_conf_list) return storage_provider_info + + +def gen_passwd_file(config_dict): + return subprocess.call(["/usr/bin/htpasswd", "-bcB", registry_passwd_path, config_dict['registry_username'], + config_dict['registry_password']], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) diff --git a/src/core/config/config.go b/src/core/config/config.go index b95b84f73..2b5f945c3 100755 --- a/src/core/config/config.go +++ b/src/core/config/config.go @@ -299,6 +299,11 @@ func CoreSecret() string { return os.Getenv("CORE_SECRET") } +// RegistryCredential returns the username and password the core uses to access registry +func RegistryCredential() (string, string) { + return os.Getenv("REGISTRY_CREDENTIAL_USERNAME"), os.Getenv("REGISTRY_CREDENTIAL_PASSWORD") +} + // JobserviceSecret returns a secret to mark Jobservice when communicate with // other component // TODO replace it with method of SecretStore