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 <jiangd@vmware.com>
This commit is contained in:
Daniel Jiang 2019-12-25 14:59:47 +08:00
parent 79f93af471
commit a087ba02e3
10 changed files with 61 additions and 24 deletions

View File

@ -26,6 +26,9 @@ https:
# Remember Change the admin password from UI after launching Harbor. # Remember Change the admin password from UI after launching Harbor.
harbor_admin_password: Harbor12345 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 # Harbor DB configuration
database: database:
# The password for the root user of Harbor DB. Change this before any production use. # The password for the root user of Harbor DB. Change this before any production use.

View File

@ -1,5 +1,6 @@
FROM photon:2.0 FROM photon:2.0
RUN tdnf install -y python3 \ RUN tdnf install -y python3 python3-pip httpd
&& tdnf install -y python3-pip RUN pip3 install pipenv==2018.11.26
RUN pip3 install pipenv==2018.11.26

View File

@ -35,7 +35,7 @@ def main(conf, with_notary, with_clair, with_chartmuseum):
try: try:
validate(config_dict, notary_mode=with_notary) validate(config_dict, notary_mode=with_notary)
except Exception as e: except Exception as e:
logging.info('Error happend in config validation...') logging.info('Error happened in config validation...')
logging.error(e) logging.error(e)
sys.exit(-1) sys.exit(-1)

View File

@ -44,6 +44,8 @@ RELOAD_KEY={{reload_key}}
CHART_REPOSITORY_URL={{chart_repository_url}} CHART_REPOSITORY_URL={{chart_repository_url}}
REGISTRY_CONTROLLER_URL={{registry_controller_url}} REGISTRY_CONTROLLER_URL={{registry_controller_url}}
WITH_CHARTMUSEUM={{with_chartmuseum}} WITH_CHARTMUSEUM={{with_chartmuseum}}
REGISTRY_CREDENTIAL_USERNAME={{registry_username}}
REGISTRY_CREDENTIAL_PASSWORD={{registry_password}}
HTTP_PROXY={{core_http_proxy}} HTTP_PROXY={{core_http_proxy}}
HTTPS_PROXY={{core_https_proxy}} HTTPS_PROXY={{core_https_proxy}}

View File

@ -26,11 +26,17 @@ http:
debug: debug:
addr: localhost:5001 addr: localhost:5001
auth: auth:
{% if registry_use_basic_auth %}
htpasswd:
realm: harbor-registry-basic-realm
path: /etc/registry/passwd
{% else %}
token: token:
issuer: harbor-token-issuer issuer: harbor-token-issuer
realm: {{public_url}}/service/token realm: {{public_url}}/service/token
rootcertbundle: /etc/registry/root.crt rootcertbundle: /etc/registry/root.crt
service: harbor-registry service: harbor-registry
{% endif %}
validation: validation:
disabled: true disabled: true
notifications: notifications:

View File

@ -1,14 +1,14 @@
# Get or generate private key # Get or generate private key
import os, sys, subprocess, shutil import os, subprocess, shutil
from pathlib import Path from pathlib import Path
from subprocess import DEVNULL from subprocess import DEVNULL
from functools import wraps
from g import DEFAULT_GID, DEFAULT_UID from g import DEFAULT_GID, DEFAULT_UID
from .misc import ( from .misc import (
mark_file, mark_file,
generate_random_string, generate_random_string,
check_permission) check_permission,
stat_decorator)
SSL_CERT_PATH = os.path.join("/etc/cert", "server.crt") SSL_CERT_PATH = os.path.join("/etc/cert", "server.crt")
SSL_CERT_KEY_PATH = os.path.join("/etc/cert", "server.key") 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) alias = _get_secret(path, "defaultalias", length=8)
return alias 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 @stat_decorator
def create_root_cert(subj, key_path="./k.key", cert_path="./cert.crt"): 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) rc = subprocess.call(["/usr/bin/openssl", "genrsa", "-out", key_path, "4096"], stdout=DEVNULL, stderr=subprocess.STDOUT)

View File

@ -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_cert_path = '/your/certificate/path'
default_https_key_path = '/your/certificate/path' default_https_key_path = '/your/certificate/path'
REGISTRY_USER_NAME = 'harbor_registry_user'
def validate(conf: dict, **kwargs): def validate(conf: dict, **kwargs):
# hostname validate # hostname validate
@ -83,6 +85,7 @@ def validate(conf: dict, **kwargs):
# TODO: # TODO:
# If user enable trust cert dir, need check if the files in this dir is readable. # If user enable trust cert dir, need check if the files in this dir is readable.
def parse_versions(): def parse_versions():
if not versions_file_path.is_file(): if not versions_file_path.is_file():
return {} return {}
@ -90,6 +93,7 @@ def parse_versions():
versions = yaml.load(f) versions = yaml.load(f)
return versions return versions
def parse_yaml_config(config_file_path, with_notary, with_clair, with_chartmuseum): def parse_yaml_config(config_file_path, with_notary, with_clair, with_chartmuseum):
''' '''
:param configs: config_parser object :param configs: config_parser object
@ -321,6 +325,12 @@ def parse_yaml_config(config_file_path, with_notary, with_clair, with_chartmuseu
# UAA configs # UAA configs
config_dict['uaa'] = configs.get('uaa') or {} 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 return config_dict

View File

@ -1,11 +1,10 @@
import os import os, string, sys
import string
import secrets import secrets
from pathlib import Path from pathlib import Path
from functools import wraps
from g import DEFAULT_UID, DEFAULT_GID from g import DEFAULT_UID, DEFAULT_GID
# To meet security requirement # To meet security requirement
# By default it will change file mode to 0600, and make the owner of the file to 10000:10000 # 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): 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 Check if other user have the read permission of this st_mode
""" """
return True if st_mode & 0o004 else False 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

View File

@ -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 g import config_dir, templates_dir, DEFAULT_GID, DEFAULT_UID, data_dir
from utils.misc import prepare_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_dir = os.path.join(config_dir, "registry")
registry_config_template_path = os.path.join(templates_dir, "registry", "config.yml.jinja") registry_config_template_path = os.path.join(templates_dir, "registry", "config.yml.jinja")
registry_conf = os.path.join(config_dir, "registry", "config.yml") 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') registry_data_dir = os.path.join(data_dir, 'registry')
levels_map = { levels_map = {
@ -18,10 +19,13 @@ levels_map = {
'fatal': 'fatal' 'fatal': 'fatal'
} }
def prepare_registry(config_dict): def prepare_registry(config_dict):
prepare_dir(registry_data_dir, uid=DEFAULT_UID, gid=DEFAULT_GID) prepare_dir(registry_data_dir, uid=DEFAULT_UID, gid=DEFAULT_GID)
prepare_dir(registry_config_dir) prepare_dir(registry_config_dir)
if config_dict['registry_use_basic_auth']:
gen_passwd_file(config_dict)
storage_provider_info = get_storage_provider_info( storage_provider_info = get_storage_provider_info(
config_dict['storage_provider_name'], config_dict['storage_provider_name'],
config_dict['storage_provider_config']) 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_conf_list.append('{}: {}'.format(config[0], value))
storage_provider_info = ('\n' + ' ' * 4).join(storage_provider_conf_list) storage_provider_info = ('\n' + ' ' * 4).join(storage_provider_conf_list)
return storage_provider_info 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)

View File

@ -299,6 +299,11 @@ func CoreSecret() string {
return os.Getenv("CORE_SECRET") 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 // JobserviceSecret returns a secret to mark Jobservice when communicate with
// other component // other component
// TODO replace it with method of SecretStore // TODO replace it with method of SecretStore