Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Meng Wei 2016-04-19 11:54:07 +08:00
commit 836eb16f5c
25 changed files with 436 additions and 110 deletions

View File

@ -1,5 +1,6 @@
# This file lists all individuals having contributed content to the repository. # This file lists all individuals having contributed content to the repository.
Alexander Zeitler <alexander.zeitler at pdmlab.com>
Amanda Zhang <amzhang at vmware.com> Amanda Zhang <amzhang at vmware.com>
Benniu Ji <benniuji at gmail.com> Benniu Ji <benniuji at gmail.com>
Bobby Zhang <junzhang at vmware.com> Bobby Zhang <junzhang at vmware.com>
@ -9,8 +10,8 @@ Haining Henry Zhang <henryzhang at vmware.com>
Hao Xia <haox at vmware.com> Hao Xia <haox at vmware.com>
Jack Liu <ljack at vmware.com> Jack Liu <ljack at vmware.com>
Kun Wang <kunw at vmware.com> Kun Wang <kunw at vmware.com>
Peng Zhao <zhaopeng1988 at gmail.com>
Shan Zhu <zhus at vmware.com> Shan Zhu <zhus at vmware.com>
Victoria Zheng <vzheng at vmware.com> Victoria Zheng <vzheng at vmware.com>
Wenkai Yin <yinw at vmware.com> Wenkai Yin <yinw at vmware.com>
Yan Wang <wangyan at vmware.com> Yan Wang <wangyan at vmware.com>

View File

@ -57,6 +57,7 @@ services:
- ./config/nginx:/etc/nginx - ./config/nginx:/etc/nginx
ports: ports:
- 80:80 - 80:80
- 443:443
depends_on: depends_on:
- mysql - mysql
- registry - registry

View File

@ -0,0 +1,3 @@
FROM library/nginx:1.9
ADD ./config/nginx /etc/nginx

View File

@ -0,0 +1,33 @@
version: 0.1
log:
level: debug
fields:
service: registry
storage:
cache:
layerinfo: inmemory
filesystem:
rootdirectory: /storage
maintenance:
uploadpurging:
enabled: false
http:
addr: :5000
secret: placeholder
debug:
addr: localhost:5001
auth:
token:
issuer: registry-token-issuer
realm: http://harbor.caicloud.io/service/token
rootcertbundle: /etc/registry/root.crt
service: token-service
notifications:
endpoints:
- name: harbor
disabled: false
url: http://harbor.caicloud.io/service/notifications
timeout: 500
threshold: 5
backoff: 1000

View File

@ -0,0 +1,6 @@
FROM library/registry:2.3.0
ADD ./config/registry/ /etc/registry/
ADD ./kubernetes/dockerfiles/registry-config.yml /etc/registry/config.yml
CMD ["/etc/registry/config.yml"]

View File

@ -0,0 +1,4 @@
FROM deploy_ui
ADD ./config/ui/app.conf /etc/ui/app.conf
ADD ./config/ui/private_key.pem /etc/ui/private_key.pem

View File

@ -0,0 +1,30 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: mysql
labels:
name: mysql
spec:
replicas: 1
selector:
name: mysql
template:
metadata:
labels:
name: mysql
spec:
containers:
- name: mysql
image: caicloud/harbor_deploy_mysql:latest
imagePullPolicy: Always
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: root123
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
emptyDir: {}

View File

@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
name: mysql
spec:
ports:
- port: 3306
selector:
name: mysql

View File

@ -0,0 +1,22 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: proxy
labels:
name: proxy
spec:
replicas: 1
selector:
name: proxy
template:
metadata:
labels:
name: proxy
spec:
containers:
- name: proxy
image: caicloud/harbor_proxy:latest
imagePullPolicy: Always
ports:
- containerPort: 80
- containerPort: 443

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: proxy
labels:
name: proxy
spec:
type: LoadBalancer
ports:
- name: http
port: 80
- name: https
port: 443
selector:
name: proxy

View File

@ -0,0 +1,28 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: registry
labels:
name: registry
spec:
replicas: 1
selector:
name: registry
template:
metadata:
labels:
name: registry
spec:
containers:
- name: registry
image: caicloud/harbor_registry:2.3.0
imagePullPolicy: Always
ports:
- containerPort: 5000
- containerPort: 5001
volumeMounts:
- name: storage
mountPath: /storage
volumes:
- name: storage
emptyDir: {}

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: registry
labels:
name: registry
spec:
ports:
- name: internal
port: 5000
- name: external
port: 5001
selector:
name: registry

View File

@ -0,0 +1,49 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: ui
labels:
name: ui
spec:
replicas: 1
selector:
name: ui
template:
metadata:
labels:
name: ui
spec:
containers:
- name: ui
image: caicloud/harbor_deploy_ui:latest
imagePullPolicy: Always
env:
- name: MYSQL_HOST
value: mysql
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_USR
value: root
- name: MYSQL_PWD
value: root123
- name: REGISTRY_URL
value: http://registry:5000
- name: CONFIG_PATH
value: /etc/ui/app.conf
- name: HARBOR_REG_URL
value: localhost
- name: HARBOR_ADMIN_PASSWORD
value: Harbor12345
- name: HARBOR_URL
value: http://localhost
- name: AUTH_MODE
value: db_auth
- name: LDAP_URL
value: ldaps://ldap.mydomain.com
- name: LDAP_BASE_DN
value: uid=%s,ou=people,dc=mydomain,dc=com
- name: LOG_LEVEL
value: debug
ports:
- containerPort: 80

View File

@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
name: ui
spec:
ports:
- port: 80
selector:
name: ui

View File

@ -1,34 +1,43 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*-
import ConfigParser from __future__ import print_function, unicode_literals # We require Python 2.6 or later
import StringIO
import os
from string import Template from string import Template
import os
import sys
from io import open
if sys.version_info[:3][0] == 2:
import ConfigParser as ConfigParser
import StringIO as StringIO
if sys.version_info[:3][0] == 3:
import configparser as ConfigParser
import io as StringIO
#Read configurations #Read configurations
conf = StringIO.StringIO() conf = StringIO.StringIO()
conf.write("[configuration]\n") conf.write("[configuration]\n")
conf.write(open("harbor.cfg").read()) conf.write(open("harbor.cfg").read())
conf.seek(0, os.SEEK_SET) conf.seek(0, os.SEEK_SET)
cp = ConfigParser.RawConfigParser() rcp = ConfigParser.RawConfigParser()
cp.readfp(conf) rcp.readfp(conf)
hostname = cp.get("configuration", "hostname") hostname = rcp.get("configuration", "hostname")
ui_url = cp.get("configuration", "ui_url_protocol") + "://" + hostname ui_url = rcp.get("configuration", "ui_url_protocol") + "://" + hostname
email_server = cp.get("configuration", "email_server") email_server = rcp.get("configuration", "email_server")
email_server_port = cp.get("configuration", "email_server_port") email_server_port = rcp.get("configuration", "email_server_port")
email_username = cp.get("configuration", "email_username") email_username = rcp.get("configuration", "email_username")
email_password = cp.get("configuration", "email_password") email_password = rcp.get("configuration", "email_password")
email_from = cp.get("configuration", "email_from") email_from = rcp.get("configuration", "email_from")
harbor_admin_password = cp.get("configuration", "harbor_admin_password") harbor_admin_password = rcp.get("configuration", "harbor_admin_password")
auth_mode = cp.get("configuration", "auth_mode") auth_mode = rcp.get("configuration", "auth_mode")
ldap_url = cp.get("configuration", "ldap_url") ldap_url = rcp.get("configuration", "ldap_url")
ldap_basedn = cp.get("configuration", "ldap_basedn") ldap_basedn = rcp.get("configuration", "ldap_basedn")
db_password = cp.get("configuration", "db_password") db_password = rcp.get("configuration", "db_password")
self_registration = cp.get("configuration", "self_registration") self_registration = rcp.get("configuration", "self_registration")
######## ########
base_dir = os.path.dirname(__file__) base_dir = os.path.dirname(__file__)
config_dir = os.path.join(base_dir, "config") config_dir = os.path.join(base_dir, "config")
templates_dir = os.path.join(base_dir, "templates") templates_dir = os.path.join(base_dir, "templates")
@ -45,17 +54,17 @@ def render(src, dest, **kw):
t = Template(open(src, 'r').read()) t = Template(open(src, 'r').read())
with open(dest, 'w') as f: with open(dest, 'w') as f:
f.write(t.substitute(**kw)) f.write(t.substitute(**kw))
print "Generated configuration file: %s" % dest print("Generated configuration file: %s" % dest)
ui_conf_env = os.path.join(config_dir, "ui", "env") ui_conf_env = os.path.join(config_dir, "ui", "env")
ui_conf = os.path.join(config_dir, "ui", "app.conf") ui_conf = os.path.join(config_dir, "ui", "app.conf")
registry_conf = os.path.join(config_dir, "registry", "config.yml") registry_conf = os.path.join(config_dir, "registry", "config.yml")
db_conf_env = os.path.join(config_dir, "db", "env") db_conf_env = os.path.join(config_dir, "db", "env")
conf_files = [ ui_conf, ui_conf_env, registry_conf, db_conf_env ] conf_files = [ ui_conf, ui_conf_env, registry_conf, db_conf_env ]
for f in conf_files: for f in conf_files:
if os.path.exists(f): if os.path.exists(f):
print "Clearing the configuration file: %s" % f print("Clearing the configuration file: %s" % f)
os.remove(f) os.remove(f)
render(os.path.join(templates_dir, "ui", "env"), render(os.path.join(templates_dir, "ui", "env"),
@ -86,4 +95,4 @@ render(os.path.join(templates_dir, "db", "env"),
db_conf_env, db_conf_env,
db_password=db_password) db_password=db_password)
print "The configuration files are ready, please use docker-compose to start the service." print("The configuration files are ready, please use docker-compose to start the service.")

View File

@ -46,7 +46,7 @@ The host must be connected to the Internet.
If everything works fine, you can open a browser to visit the admin portal at http://reg.yourdomain.com . The default administrator username and password are admin/Harbor12345 . If everything works fine, you can open a browser to visit the admin portal at http://reg.yourdomain.com . The default administrator username and password are admin/Harbor12345 .
Create a new project, e.g. myproject, in the admin portal. You can then use docker commands to login and push images. The default port of Harbor registry server is 80: Log in to the admin portal and create a new project, e.g. myproject. You can then use docker commands to login and push images. The default port of Harbor registry server is 80:
```sh ```sh
$ docker login reg.yourdomain.com $ docker login reg.yourdomain.com
$ docker push reg.yourdomain.com/myproject/myrepo $ docker push reg.yourdomain.com/myproject/myrepo
@ -57,6 +57,9 @@ To simplify the installation process, a pre-built installation package of Harbor
For information on how to use Harbor, please see [User Guide](docs/user_guide.md) . For information on how to use Harbor, please see [User Guide](docs/user_guide.md) .
### Deploy harbor on Kubernetes
Detailed instruction about deploying harbor on Kubernetes is described [here](https://github.com/vmware/harbor/blob/master/kubernetes_deployment.md).
### Contribution ### Contribution
We welcome contributions from the community. If you wish to contribute code and you have not signed our contributor license agreement (CLA), our bot will update the issue when you open a pull request. For any questions about the CLA process, please refer to our [FAQ](https://cla.vmware.com/faq). We welcome contributions from the community. If you wish to contribute code and you have not signed our contributor license agreement (CLA), our bot will update the issue when you open a pull request. For any questions about the CLA process, please refer to our [FAQ](https://cla.vmware.com/faq).
@ -68,3 +71,6 @@ Harbor is available under the [Apache 2 license](LICENSE).
### Users ### Users
<a href="https://www.madailicai.com/" border="0" target="_blank"><img alt="MaDaiLiCai" src="docs/img/UserMaDai.jpg"></a> <a href="https://www.madailicai.com/" border="0" target="_blank"><img alt="MaDaiLiCai" src="docs/img/UserMaDai.jpg"></a>
### Supporting Technologies
<img alt="beego" src="docs/img/beegoLogo.png"> Harbor is powered by <a href="http://beego.me/">Beego</a>, an open source framework to build and develop applications in the Go way.

View File

@ -17,7 +17,9 @@ package api
import ( import (
"net/http" "net/http"
"os"
"strconv" "strconv"
"strings"
"github.com/vmware/harbor/dao" "github.com/vmware/harbor/dao"
"github.com/vmware/harbor/models" "github.com/vmware/harbor/models"
@ -27,13 +29,35 @@ import (
// UserAPI handles request to /api/users/{} // UserAPI handles request to /api/users/{}
type UserAPI struct { type UserAPI struct {
BaseAPI BaseAPI
currentUserID int currentUserID int
userID int userID int
SelfRegistration bool
IsAdmin bool
AuthMode string
} }
// Prepare validates the URL and parms // Prepare validates the URL and parms
func (ua *UserAPI) Prepare() { func (ua *UserAPI) Prepare() {
authMode := strings.ToLower(os.Getenv("AUTH_MODE"))
if authMode == "" {
authMode = "db_auth"
}
ua.AuthMode = authMode
selfRegistration := strings.ToLower(os.Getenv("SELF_REGISTRATION"))
if selfRegistration == "on" {
ua.SelfRegistration = true
}
if ua.Ctx.Input.IsPost() {
sessionUserID := ua.GetSession("userId")
_, _, ok := ua.Ctx.Request.BasicAuth()
if sessionUserID == nil && !ok {
return
}
}
ua.currentUserID = ua.ValidateUser() ua.currentUserID = ua.ValidateUser()
id := ua.Ctx.Input.Param(":id") id := ua.Ctx.Input.Param(":id")
if id == "current" { if id == "current" {
@ -56,18 +80,20 @@ func (ua *UserAPI) Prepare() {
ua.CustomAbort(http.StatusNotFound, "") ua.CustomAbort(http.StatusNotFound, "")
} }
} }
var err error
ua.IsAdmin, err = dao.IsAdminRole(ua.currentUserID)
if err != nil {
log.Errorf("Error occurred in IsAdminRole:%v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
} }
// Get ... // Get ...
func (ua *UserAPI) Get() { func (ua *UserAPI) Get() {
exist, err := dao.IsAdminRole(ua.currentUserID)
if err != nil {
log.Errorf("Error occurred in IsAdminRole, error: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
if ua.userID == 0 { //list users if ua.userID == 0 { //list users
if !exist { if !ua.IsAdmin {
log.Errorf("Current user, id: %d does not have admin role, can not list users", ua.currentUserID) log.Errorf("Current user, id: %d does not have admin role, can not list users", ua.currentUserID)
ua.RenderError(http.StatusForbidden, "User does not have admin role") ua.RenderError(http.StatusForbidden, "User does not have admin role")
return return
@ -85,7 +111,7 @@ func (ua *UserAPI) Get() {
} }
ua.Data["json"] = userList ua.Data["json"] = userList
} else if ua.userID == ua.currentUserID || exist { } else if ua.userID == ua.currentUserID || ua.IsAdmin {
userQuery := models.User{UserID: ua.userID} userQuery := models.User{UserID: ua.userID}
u, err := dao.GetUser(userQuery) u, err := dao.GetUser(userQuery)
if err != nil { if err != nil {
@ -103,12 +129,7 @@ func (ua *UserAPI) Get() {
// Put ... // Put ...
func (ua *UserAPI) Put() { //currently only for toggle admin, so no request body func (ua *UserAPI) Put() { //currently only for toggle admin, so no request body
exist, err := dao.IsAdminRole(ua.currentUserID) if !ua.IsAdmin {
if err != nil {
log.Errorf("Error occurred in IsAdminRole, error: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
if !exist {
log.Warningf("current user, id: %d does not have admin role, can not update other user's role", ua.currentUserID) log.Warningf("current user, id: %d does not have admin role, can not update other user's role", ua.currentUserID)
ua.RenderError(http.StatusForbidden, "User does not have admin role") ua.RenderError(http.StatusForbidden, "User does not have admin role")
return return
@ -117,18 +138,38 @@ func (ua *UserAPI) Put() { //currently only for toggle admin, so no request body
dao.ToggleUserAdminRole(userQuery) dao.ToggleUserAdminRole(userQuery)
} }
// Post ...
func (ua *UserAPI) Post() {
if !(ua.AuthMode == "db_auth") {
ua.CustomAbort(http.StatusForbidden, "")
}
if !(ua.SelfRegistration || ua.IsAdmin) {
log.Warning("Registration can only be used by admin role user when self-registration is off.")
ua.CustomAbort(http.StatusForbidden, "")
}
user := models.User{}
ua.DecodeJSONReq(&user)
_, err := dao.Register(user)
if err != nil {
log.Errorf("Error occurred in Register: %v", err)
ua.RenderError(http.StatusInternalServerError, "Internal error.")
return
}
}
// Delete ... // Delete ...
func (ua *UserAPI) Delete() { func (ua *UserAPI) Delete() {
exist, err := dao.IsAdminRole(ua.currentUserID) if !ua.IsAdmin {
if err != nil {
log.Errorf("Error occurred in IsAdminRole, error: %v", err)
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
if !exist {
log.Warningf("current user, id: %d does not have admin role, can not remove user", ua.currentUserID) log.Warningf("current user, id: %d does not have admin role, can not remove user", ua.currentUserID)
ua.RenderError(http.StatusForbidden, "User does not have admin role") ua.RenderError(http.StatusForbidden, "User does not have admin role")
return return
} }
var err error
err = dao.DeleteUser(ua.userID) err = dao.DeleteUser(ua.userID)
if err != nil { if err != nil {
log.Errorf("Failed to delete data from database, error: %v", err) log.Errorf("Failed to delete data from database, error: %v", err)

View File

@ -17,7 +17,6 @@ package controllers
import ( import (
"net/http" "net/http"
"strings"
"github.com/vmware/harbor/dao" "github.com/vmware/harbor/dao"
"github.com/vmware/harbor/models" "github.com/vmware/harbor/models"
@ -65,33 +64,6 @@ func (ac *AddUserController) Get() {
} }
} }
// SignUp insert data into DB based on data in form.
func (cc *CommonController) SignUp() {
if !(cc.AuthMode == "db_auth") {
cc.CustomAbort(http.StatusForbidden, "")
}
if !(cc.SelfRegistration || cc.IsAdmin) {
log.Warning("Registration can only be used by admin role user when self-registration is off.")
cc.CustomAbort(http.StatusForbidden, "")
}
username := strings.TrimSpace(cc.GetString("username"))
email := strings.TrimSpace(cc.GetString("email"))
realname := strings.TrimSpace(cc.GetString("realname"))
password := strings.TrimSpace(cc.GetString("password"))
comment := strings.TrimSpace(cc.GetString("comment"))
user := models.User{Username: username, Email: email, Realname: realname, Password: password, Comment: comment}
_, err := dao.Register(user)
if err != nil {
log.Errorf("Error occurred in Register: %v", err)
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
}
}
// UserExists checks if user exists when user input value in sign in form. // UserExists checks if user exists when user input value in sign in form.
func (cc *CommonController) UserExists() { func (cc *CommonController) UserExists() {
target := cc.GetString("target") target := cc.GetString("target")

View File

@ -1,8 +1,8 @@
#Configure Harbor with HTTPS Access #Configuring Harbor with HTTPS Access
Because Harbor does not ship with any certificates, it uses HTTP by default to serve registry requests. This makes it relatively simple to configure. However, it is highly recommended that security be enabled for any production environment. Harbor has an Nginx instance as a reverse proxy for all services, you can configure Nginx to enable https. Because Harbor does not ship with any certificates, it uses HTTP by default to serve registry requests. This makes it relatively simple to configure. However, it is highly recommended that security be enabled for any production environment. Harbor has an Nginx instance as a reverse proxy for all services, you can configure Nginx to enable https.
##Get a certificate ##Getting a certificate
Assuming that your registry's **hostname** is **reg.yourdomain.com**, and that its DNS record points to the host where you are running Harbor. You first should get a certificate from a CA. The certificate usually contains a .crt file and a .key file, for example, **yourdomain.com.crt** and **yourdomain.com.key**. Assuming that your registry's **hostname** is **reg.yourdomain.com**, and that its DNS record points to the host where you are running Harbor. You first should get a certificate from a CA. The certificate usually contains a .crt file and a .key file, for example, **yourdomain.com.crt** and **yourdomain.com.key**.
@ -22,7 +22,7 @@ In a test or development environment, you may choose to use a self-signed certif
``` ```
3) Generate the certificate of your registry host: 3) Generate the certificate of your registry host:
You need to configure openssl first. On Ubuntu, the config file locates at /etc/ssl/openssl.cnf. Refer to openssl document for more information. The default CA directory of openssl is called demoCA. Let's create necessary directories and files: You need to configure openssl first. On Ubuntu, the config file locates at **/etc/ssl/openssl.cnf**. Refer to openssl document for more information. The default CA directory of openssl is called demoCA. Let's create necessary directories and files:
``` ```
mkdir demoCA mkdir demoCA
cd demoCA cd demoCA
@ -40,7 +40,11 @@ After obtaining the **yourdomain.com.crt** and **yourdomain.com.key** files, cha
``` ```
cd Deploy/config/nginx cd Deploy/config/nginx
``` ```
Create a new directory cert/, if it does not exist. Then copy **yourdomain.com.crt** and **yourdomain.com.key** to cert/. Create a new directory cert/, if it does not exist. Then copy **yourdomain.com.crt** and **yourdomain.com.key** to cert/, e.g. :
```
cp yourdomain.com.crt cert/
cp yourdomain.com.key cert/
```
Rename the existing configuration file of Nginx: Rename the existing configuration file of Nginx:
``` ```

BIN
docs/img/beegoLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -28,7 +28,7 @@ Before installing Harbor, you should configure the parameters in the file **harb
At minimum, you need to change the **hostname** attribute in **harbor.cfg**. The description of each attribute is as follows: At minimum, you need to change the **hostname** attribute in **harbor.cfg**. The description of each attribute is as follows:
**hostname**: The hostname for a user to access the user interface and the registry service. It should be the IP address or the fully qualified domain name (FQDN) of your target machine, for example 192.168.1.10 or reg.yourdomain.com . Do NOT use localhost or 127.0.0.1 for the hostname because the registry service needs to be accessed by external clients. **hostname**: The hostname for a user to access the user interface and the registry service. It should be the IP address or the fully qualified domain name (FQDN) of your target machine, for example 192.168.1.10 or reg.yourdomain.com . Do NOT use localhost or 127.0.0.1 for the hostname because the registry service needs to be accessed by external clients.
**ui_url_protocol**: The protocol for accessing the user interface and the token/notification service, by default it is http. **ui_url_protocol**: The protocol for accessing the user interface and the token/notification service, by default it is http. To set up the https protocol, refer to [Configuring Harbor with HTTPS Access](configure_https.md).
**Email settings**: the following 5 attributes are used to send an email to reset a user's password, they are not mandatory unless the password reset function is needed in Harbor. **Email settings**: the following 5 attributes are used to send an email to reset a user's password, they are not mandatory unless the password reset function is needed in Harbor.
* email_server = smtp.mydomain.com * email_server = smtp.mydomain.com
* email_server_port = 25 * email_server_port = 25
@ -40,8 +40,9 @@ At minimum, you need to change the **hostname** attribute in **harbor.cfg**. The
**auth_mode**: The authentication mode of Harbor. By default it is *db_auth*, i.e. the credentials are stored in a database. Please set it to *ldap_auth* if you want to verify user's credentials against an LDAP server. **auth_mode**: The authentication mode of Harbor. By default it is *db_auth*, i.e. the credentials are stored in a database. Please set it to *ldap_auth* if you want to verify user's credentials against an LDAP server.
**ldap_url**: The URL for LDAP endpoint, for example ldaps://ldap.mydomain.com. It is only used when **auth_mode** is set to *ldap_auth*. **ldap_url**: The URL for LDAP endpoint, for example ldaps://ldap.mydomain.com. It is only used when **auth_mode** is set to *ldap_auth*.
**ldap_basedn**: The basedn template for verifying the user's credentials against LDAP, for example uid=%s,ou=people,dc=mydomain,dc=com. It is only used when **auth_mode** is set to *ldap_auth*. **ldap_basedn**: The basedn template for verifying the user's credentials against LDAP, for example uid=%s,ou=people,dc=mydomain,dc=com. It is only used when **auth_mode** is set to *ldap_auth*.
**db_password**: The password of root user of mySQL database. **db_password**: The password of root user of mySQL database. Change this password for any production use.
**self_registration**: The flag to turn on or off the user self-registration function. If this flag is turned off, only an admin user can create new users in Harbor. The default value is on. **self_registration**: The flag to turn on or off the user self-registration function. If this flag is turned off, only an admin user can create new users in Harbor. The default value is on.
NOTE: When **auth_mode** is *ldap_auth*, the self-registration feature is always disabled, therefore, this flag is ignored.
#### Building and starting Harbor #### Building and starting Harbor
After configuring harbor.cfg, build and start Harbor by the following commands. Because it requires downloading necessary files from the Internet, it may take a while for the docker-compose process to finish. After configuring harbor.cfg, build and start Harbor by the following commands. Because it requires downloading necessary files from the Internet, it may take a while for the docker-compose process to finish.
@ -61,7 +62,7 @@ After configuring harbor.cfg, build and start Harbor by the following commands.
If everything works fine, you can open a browser to visit the admin portal at http://reg.yourdomain.com . The default administrator username and password are admin/Harbor12345 . If everything works fine, you can open a browser to visit the admin portal at http://reg.yourdomain.com . The default administrator username and password are admin/Harbor12345 .
Create a new project, e.g. myproject, in the admin portal. You can then use docker commands to login and push images. The default port of Harbor registry server is 80: Log in to the admin portal and create a new project, e.g. myproject. You can then use docker commands to login and push images. The default port of Harbor registry server is 80:
```sh ```sh
$ docker login reg.yourdomain.com $ docker login reg.yourdomain.com
$ docker push reg.yourdomain.com/myproject/myrepo $ docker push reg.yourdomain.com/myproject/myrepo
@ -96,7 +97,7 @@ $ sudo docker-compose up -d
...... ......
``` ```
### Deploying Harbor to a target machine that does not have Internet access ### Deploying Harbor to a host which does not have Internet access
When you run *docker-compose up* to start Harbor, it will pull base images from Docker Hub and build new images for the containers. This process requires accessing the Internet. If you want to deploy Harbor to a host that is not connected to the Internet, you need to prepare Harbor on a machine that has access to the Internet. After that, you export the images as tgz files and transfer them to the target machine. Then load the tgz file into Docker's local image repo. When you run *docker-compose up* to start Harbor, it will pull base images from Docker Hub and build new images for the containers. This process requires accessing the Internet. If you want to deploy Harbor to a host that is not connected to the Internet, you need to prepare Harbor on a machine that has access to the Internet. After that, you export the images as tgz files and transfer them to the target machine. Then load the tgz file into Docker's local image repo.
#### Building and saving images for offline installation #### Building and saving images for offline installation
@ -121,8 +122,10 @@ $ cd ../
$ tar -cvzf harbor_offline-0.1.1.tgz harbor $ tar -cvzf harbor_offline-0.1.1.tgz harbor
``` ```
The file **harbor_offline-0.1.1.tgz** contains the images saved by previously steps and the files required to start Harbor. The file **harbor_offline-0.1.1.tgz** contains the images saved by previous steps and the other files required to start Harbor.
You can use tools such as scp to transfer the file **harbor_offline-0.1.1.tgz** to the target machine that does not have Internet connection. On the target machine, you can execute the following commands to start Harbor. Again, before running the **prepare** script, be sure to update **harbor.cfg** to reflect the right configuration of the target machine. (Refer to Section [Configure Harbor](#configuring-harbor) .) You can use tools such as scp to transfer the file **harbor_offline-0.1.1.tgz** to the target machine that does not have Internet connection.
On the target machine, you can execute the following commands to start Harbor. Again, before running the **prepare** script,
be sure to update **harbor.cfg** to reflect the right configuration of the target machine. (Refer to Section [Configuring Harbor](#configuring-harbor) .)
``` ```
$ tar -xzvf harbor_offline-0.1.1.tgz $ tar -xzvf harbor_offline-0.1.1.tgz
$ cd harbor $ cd harbor

65
kubernetes_deployment.md Normal file
View File

@ -0,0 +1,65 @@
## Deploy harbor on kubernetes.
For now, it's a little tricky to start harbor on kubernetes because
1. Registry uses https, so we need cert or workaround to avoid errors like this:
```
Error response from daemon: invalid registry endpoint https://{HOST}/v0/: unable to ping registry endpoint https://{HOST}/v0/
v2 ping attempt failed with error: Get https://{HOST}/v2/: EOF
v1 ping attempt failed with error: Get https://{HOST}/v1/_ping: EOF. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry {HOST}` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/{HOST}/ca.crt
```
There is a workaround if you don't have a cert. The workaround is to add the host into the list of insecure registry by editting the ```/etc/default/docker``` file:
```
sudo vi /etc/default/docker
```
add the line at the end of file:
```
DOCKER_OPTS="$DOCKER_OPTS --insecure-registry={HOST}"
```
restart docker service
```
sudo service docker restart
```
2. The registry config file need to know the IP (or DNS name) of the registry, but on kubernetes, you won't know the IP before the service is created. There are several workarounds to solve this problem for now:
- Use DNS name and link th DNS name with the IP after the service is created.
- Rebuild the registry image with the service IP after the service is created and use ```kubectl rolling-update``` to update to the new image.
To start harbor on kubernetes, you first need to build the docker images. The docker images for deploying Harbor on Kubernetes depends on the docker images to deploy Harbor with docker-compose. So the first step is to build docker images with docker-compose. Before actually building the images, you need to first adjust the [configuration](https://github.com/vmware/harbor/blob/master/Deploy/harbor.cfg):
- Change the [hostname](https://github.com/vmware/harbor/blob/master/Deploy/harbor.cfg#L5) to ```localhost```
- Adjust the [email settings](https://github.com/vmware/harbor/blob/master/Deploy/harbor.cfg#L11) according to your needs.
Then you can run the following commends to build docker images:
```
cd Deploy
./prepare
docker-compose build
docker build -f kubernetes/dockerfiles/proxy-dockerfile -t {your_account}/proxy .
docker build -f kubernetes/dockerfiles/registry-dockerfile -t {your_account}/registry .
docker build -f kubernetes/dockerfiles/ui-dockerfile -t {your_account}/deploy_ui .
docker tag deploy_mysql {your_account}/deploy_mysql
docker push {your_account}/proxy
docker push {your_account}/registry
docker push {your_account}/deploy_ui
docker push {your_account}/deploy_mysql
```
where "your_account" is your own registry. Then you need to update the "image" field in the ```*-rc.yaml``` files at:
```
Deploy/kubernetes/mysql-rc.yaml
Deploy/kubernetes/proxy-rc.yaml
Deploy/kubernetes/registry-rc.yaml
Deploy/kubernetes/ui-rc.yaml
```
Further more, the following configuration could be changed according to your need:
- **harbor_admin_password**: The password for the administrator of Harbor, by default the password is Harbor12345. You can changed it [here](https://github.com/vmware/harbor/blob/master/Deploy/kubernetes/ui-rc.yaml#L36).
- **auth_mode**: The authentication mode of Harbor. By default it is *db_auth*, i.e. the credentials are stored in a database. Please set it to *ldap_auth* if you want to verify user's credentials against an LDAP server. You can change the configuration [here](https://github.com/vmware/harbor/blob/master/Deploy/kubernetes/ui-rc.yaml#L40).
- **ldap_url**: The URL for LDAP endpoint, for example ldaps://ldap.mydomain.com. It is only used when **auth_mode** is set to *ldap_auth*. It could be changed [here](https://github.com/vmware/harbor/blob/master/Deploy/kubernetes/ui-rc.yaml#L42).
- **ldap_basedn**: The basedn template for verifying the user's credentials against LDAP, for example uid=%s,ou=people,dc=mydomain,dc=com. It is only used when **auth_mode** is set to *ldap_auth*. It could be changed [here](https://github.com/vmware/harbor/blob/master/Deploy/kubernetes/ui-rc.yaml#L44).
- **db_password**: The password of root user of mySQL database. Change this password for any production use. You need to change both [here](https://github.com/vmware/harbor/blob/master/Deploy/kubernetes/ui-rc.yaml#L28) and [here](https://github.com/vmware/harbor/blob/master/Deploy/harbor.cfg#L32) to make the change. Please note, you need to change the ```harbor.cfg``` before building the docker images.
Finally you can start the jobs by running:
```
kubectl create -f Deploy/kubernetes
```

View File

@ -22,11 +22,11 @@ import (
// User holds the details of a user. // User holds the details of a user.
type User struct { type User struct {
UserID int `orm:"column(user_id)" json:"UserId"` UserID int `orm:"column(user_id)" json:"UserId"`
Username string `orm:"column(username)"` Username string `orm:"column(username)" json:"username"`
Email string `orm:"column(email)"` Email string `orm:"column(email)" json:"email"`
Password string `orm:"column(password)"` Password string `orm:"column(password)" json:"password"`
Realname string `orm:"column(realname)"` Realname string `orm:"column(realname)" json:"realname"`
Comment string `orm:"column(comment)"` Comment string `orm:"column(comment)" json:"comment"`
Deleted int `orm:"column(deleted)"` Deleted int `orm:"column(deleted)"`
Rolename string Rolename string
RoleID int `json:"RoleId"` RoleID int `json:"RoleId"`

View File

@ -38,14 +38,25 @@ jQuery(function(){
var comment = $.trim($("#Comment").val()); var comment = $.trim($("#Comment").val());
var isAdmin = $("#isAdmin").val(); var isAdmin = $("#isAdmin").val();
$.ajax({ new AjaxUtil({
url : '/signUp', url : "/api/users",
data:{username: username, password: password, realname: realname, comment: comment, email: email}, data: {"username": username, "password": password, "realname": realname, "comment": comment, "email": email},
type: "POST", type: "POST",
beforeSend: function(e){ beforeSend: function(e){
$("#btnPageSignUp").prop("disabled", true); $("#btnPageSignUp").prop("disabled", true);
}, },
success: function(data, status, xhr){ error:function(jqxhr, status, error){
$("#dlgModal")
.dialogModal({
"title": i18n.getMessage("title_sign_up"),
"content": i18n.getMessage("internal_error"),
"callback": function(){
return;
}
});
},
complete: function(xhr, status){
$("#btnPageSignUp").prop("disabled", false);
if(xhr && xhr.status == 200){ if(xhr && xhr.status == 200){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
@ -60,21 +71,8 @@ jQuery(function(){
} }
}); });
} }
},
error:function(jqxhr, status, error){
$("#dlgModal")
.dialogModal({
"title": i18n.getMessage("title_sign_up"),
"content": i18n.getMessage("internal_error"),
"callback": function(){
return;
}
});
},
complete: function(){
$("#btnPageSignUp").prop("disabled", false);
} }
}); }).exec();
}); });
}); });
}); });

View File

@ -32,7 +32,6 @@ func initRouters() {
beego.Router("/login", &controllers.CommonController{}, "post:Login") beego.Router("/login", &controllers.CommonController{}, "post:Login")
beego.Router("/logout", &controllers.CommonController{}, "get:Logout") beego.Router("/logout", &controllers.CommonController{}, "get:Logout")
beego.Router("/language", &controllers.CommonController{}, "get:SwitchLanguage") beego.Router("/language", &controllers.CommonController{}, "get:SwitchLanguage")
beego.Router("/signUp", &controllers.CommonController{}, "post:SignUp")
beego.Router("/userExists", &controllers.CommonController{}, "post:UserExists") beego.Router("/userExists", &controllers.CommonController{}, "post:UserExists")
beego.Router("/reset", &controllers.CommonController{}, "post:ResetPassword") beego.Router("/reset", &controllers.CommonController{}, "post:ResetPassword")
beego.Router("/sendEmail", &controllers.CommonController{}, "get:SendEmail") beego.Router("/sendEmail", &controllers.CommonController{}, "get:SendEmail")
@ -56,6 +55,7 @@ func initRouters() {
beego.Router("/api/projects/:pid/members/?:mid", &api.ProjectMemberAPI{}) beego.Router("/api/projects/:pid/members/?:mid", &api.ProjectMemberAPI{})
beego.Router("/api/projects/?:id", &api.ProjectAPI{}) beego.Router("/api/projects/?:id", &api.ProjectAPI{})
beego.Router("/api/projects/:id/logs/filter", &api.ProjectAPI{}, "post:FilterAccessLog") beego.Router("/api/projects/:id/logs/filter", &api.ProjectAPI{}, "post:FilterAccessLog")
beego.Router("/api/users", &api.UserAPI{})
beego.Router("/api/users/?:id", &api.UserAPI{}) beego.Router("/api/users/?:id", &api.UserAPI{})
beego.Router("/api/repositories", &api.RepositoryAPI{}) beego.Router("/api/repositories", &api.RepositoryAPI{})
beego.Router("/api/repositories/tags", &api.RepositoryAPI{}, "get:GetTags") beego.Router("/api/repositories/tags", &api.RepositoryAPI{}, "get:GetTags")