# Harbor

The S3 bucket where Harbor will store container images (registry and ChartMuseum) was already created using CloudFormation.

The ServiceAccount for Harbor's registry and ChartMuseum to access S3 without additional secrets was created by eksctl.

Install harbor helm chart (opens new window) and modify the default values (opens new window).

helm repo add --force-update harbor https://helm.goharbor.io
helm upgrade --install --version 1.7.2 --namespace harbor --wait --values - harbor harbor/harbor << EOF
expose:
  tls:
    certSource: secret
    secret:
      secretName: ingress-cert-${LETSENCRYPT_ENVIRONMENT}
      notarySecretName: ingress-cert-${LETSENCRYPT_ENVIRONMENT}
  ingress:
    hosts:
      core: harbor.${CLUSTER_FQDN}
      notary: notary.${CLUSTER_FQDN}
externalURL: https://harbor.${CLUSTER_FQDN}
persistence:
  enabled: false
  imageChartStorage:
    type: s3
    s3:
      region: ${AWS_DEFAULT_REGION}
      bucket: ${CLUSTER_FQDN}
      # This should be replaced by IRSA once these bugs will be fixed:
      # https://github.com/goharbor/harbor-helm/issues/725
      accesskey: ${AWS_ACCESS_KEY_ID}
      secretkey: ${AWS_SECRET_ACCESS_KEY}
      rootdirectory: /harbor
      storageclass: REDUCED_REDUNDANCY
harborAdminPassword: ${MY_PASSWORD}
metrics:
  enabled: true
  serviceMonitor:
    enabled: true
EOF

Create ServiceMonitor to allow Prometheus to get metric data:

kubectl apply -f - << EOF
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: harbor
  namespace: harbor
  labels:
    app: harbor
spec:
  selector:
    matchLabels:
      app: harbor
  namespaceSelector:
    matchNames:
    - harbor
  endpoints:
  - port: metrics
EOF

Wait for Harbor DNS to be ready:

while [[ -z "$(dig +nocmd +noall +answer +ttlid a "harbor.${CLUSTER_FQDN}")" ]]; do
  date
  sleep 5
done

Configure OIDC for Harbor:

curl -sk -u "admin:${MY_PASSWORD}" -X PUT "https://harbor.${CLUSTER_FQDN}/api/v2.0/configurations" -H "Content-Type: application/json" -d \
  "{
    \"auth_mode\": \"oidc_auth\",
    \"email_from\": \"harbor@${CLUSTER_FQDN}\",
    \"email_host\": \"mailhog.mailhog.svc.cluster.local\",
    \"email_port\": 1025,
    \"self_registration\": false,
    \"oidc_name\": \"Dex\",
    \"oidc_endpoint\": \"https://dex.${CLUSTER_FQDN}\",
    \"oidc_client_id\": \"harbor.${CLUSTER_FQDN}\",
    \"oidc_client_secret\": \"${MY_PASSWORD}\",
    \"oidc_verify_cert\": false,
    \"oidc_scope\": \"openid,profile,email\",
    \"oidc_auto_onboard\": true
  }"

Enable automated vulnerability scan after each "image push" to the project: library:

PROJECT_ID=$(curl -sk -u "admin:${MY_PASSWORD}" -X GET "https://harbor.${CLUSTER_FQDN}/api/v2.0/projects?name=library" | jq ".[].project_id")
curl -sk -u "admin:${MY_PASSWORD}" -X PUT "https://harbor.${CLUSTER_FQDN}/api/v2.0/projects/${PROJECT_ID}" -H "Content-Type: application/json" -d \
  "{
    \"metadata\": {
      \"auto_scan\": \"true\"
    }
  }"

Create new Registry Endpoint:

curl -sk -X POST -H "Content-Type: application/json" -u "admin:${MY_PASSWORD}" "https://harbor.${CLUSTER_FQDN}/api/v2.0/registries" -d \
  "{
    \"name\": \"Docker Hub\",
    \"type\": \"docker-hub\",
    \"url\": \"https://hub.docker.com\",
    \"description\": \"Docker Hub Registry Endpoint\"
  }"

Create new Replication Rule:

I'm going to replicate the "bookinfo" application used for testing Istio: https://istio.io/docs/examples/bookinfo/ (opens new window) When the replication completes all images should be automatically scanned because I'm going to replicate everything into library project which has "Automatically scan images on push" feature enabled.

Create new Replication Rule and initiate replication:

COUNTER=0
for DOCKER_HUB_REPOSITORY in istio/examples-bookinfo-details-v1 istio/examples-bookinfo-ratings-v1; do
  COUNTER=$((COUNTER + 1))
  echo "Replicating (${COUNTER}): ${DOCKER_HUB_REPOSITORY}"
  curl -sk -X POST -H "Content-Type: application/json" -u "admin:${MY_PASSWORD}" "https://harbor.${CLUSTER_FQDN}/api/v2.0/replication/policies" -d \
    "{
      \"name\": \"Replication of ${DOCKER_HUB_REPOSITORY}\",
      \"type\": \"docker-hub\",
      \"url\": \"https://hub.docker.com\",
      \"description\": \"Replication Rule for ${DOCKER_HUB_REPOSITORY}\",
      \"enabled\": true,
      \"src_registry\": {
        \"id\": 1
      },
      \"dest_namespace\": \"library\",
      \"filters\": [{
        \"type\": \"name\",
        \"value\": \"${DOCKER_HUB_REPOSITORY}\"
      },
      {
        \"type\": \"tag\",
        \"value\": \"1.1*\"
      }],
      \"trigger\": {
        \"type\": \"manual\"
      }
    }"
  POLICY_ID=$(curl -sk -H "Content-Type: application/json" -u "admin:${MY_PASSWORD}" "https://harbor.${CLUSTER_FQDN}/api/v2.0/replication/policies" | jq ".[] | select (.filters[].value==\"${DOCKER_HUB_REPOSITORY}\") .id")
  curl -sk -X POST -H "Content-Type: application/json" -u "admin:${MY_PASSWORD}" "https://harbor.${CLUSTER_FQDN}/api/v2.0/replication/executions" -d "{ \"policy_id\": ${POLICY_ID} }"
done

After a while all images used by "bookinfo" application should be replicated into library project and all should be automatically scanned.

# CloudWatch retention

Set retention for all log groups which belongs to the cluster to 1 day:

for LOG_GROUP in $(aws logs describe-log-groups | jq -r ".logGroups[] | select(.logGroupName|test(\"/${CLUSTER_NAME}\")) .logGroupName"); do
  aws logs put-retention-policy --log-group-name "${LOG_GROUP}" --retention-in-days 1
done