# Securing Docker Registry with LDAP Authentication Using Goma Gateway

## Introduction

Securing a self-hosted Docker Registry is crucial, especially in production environments. LDAP (Lightweight Directory Access Protocol) is a widely adopted standard for managing user authentication and access control across services.

In this tutorial, we’ll use **Goma Gateway** to secure a Docker Registry using **LLDAP** for authentication. Goma makes it easy to protect routes with built-in middleware and declarative configuration.

---

## Prerequisites

* Docker and Docker Compose installed
    
* Basic understanding of:
    
    * Docker networking
        
    * Reverse proxies
        
    * LDAP concepts
        

### **Architecture Overview**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754370336699/66c0b61c-2774-43f8-9c55-e269b1791360.png align="center")

---

## 1\. What Is Goma Gateway?

**Goma Gateway** is a lightweight, high-performance, and security-first API Gateway built for modern cloud-native infrastructure. With a developer-friendly configuration, Goma supports powerful middleware, observability, and advanced protocols out of the box.

### Key Features

* Built-in authentication: Basic, JWT, OAuth2, LDAP, and ForwardAuth
    
* Rate limiting, HTTP caching, and bot detection
    
* Observability: Prometheus metrics, route health checks
    
* Protocol support: REST, gRPC, TCP, UDP
    
* TLS support: Let’s Encrypt and custom certificates
    

GitHub: [jkaninda/goma-gateway](https://github.com/jkaninda/goma-gateway)

## 2\. What Is LLDAP?

**LLDAP** is a lightweight, opinionated LDAP server designed to simplify authentication. It’s easy to deploy and comes with a minimal web UI for managing users and groups.

GitHub: [lldap/lldap](https://github.com/lldap/lldap)

## 3\. Configure Local Hosts

Edit your `/etc/hosts` file to define virtual domains:

```bash
127.0.0.1 lldap.example.com registry.example.com okapi-api.example.com
```

Replace [`example.com`](http://example.com) With your domain, if needed.

## 4\. Create a Docker Network

Create a shared Docker network for all services:

```bash
docker network create web
```

## 5\. Docker Compose Setup

Create a `compose.yaml` file with the following services:

* `gateway`: Goma Gateway
    
* `lldap`: LDAP server
    
* `registry`: Docker Registry
    
* `okapi-api`: A test API service
    

```yaml
services:
  gateway:
    image: jkaninda/goma-gateway
    container_name: gateway
    restart: always
    volumes:
      - ./goma.yml:/etc/goma/goma.yml
      - ./extra:/etc/goma/extra
      - ./certs:/etc/goma/certs
    ports:
      - 80:80
      - 443:443
    networks: [web]

  lldap:
    image: lldap/lldap:stable
    container_name: lldap
    restart: always
    environment:
      - UID=1
      - GID=1
      - TZ=Europe/Paris
      - LLDAP_JWT_SECRET=REPLACE_WITH_RANDOM
      - LLDAP_KEY_SEED=REPLACE_WITH_RANDOM
      - LLDAP_LDAP_BASE_DN=dc=example,dc=com
      - LLDAP_LDAP_USER_PASS=adminPasword
    ports:
      - "3890:3890"
    volumes:
      - data:/data
    networks: [web]

  registry:
    image: registry:3.0.0
    container_name: registry
    restart: always
    volumes:
      - registry:/var/lib/registry
    networks: [web]

  okapi-api:
    image: jkaninda/okapi-example
    container_name: okapi-api
    restart: always
    networks: [web]

volumes:
  data: {}
  registry: {}

networks:
  web:
    external: true
```

**Important Security Notes**:

1. Replace `LLDAP_JWT_SECRET` and `LLDAP_KEY_SEED` with strong random values
    
2. Change default `LLDAP_LDAP_USER_PASS`
    

## 6\. Goma Gateway Configuration

Create a file named `goma.yml` in the project root:

```yaml
version: 2
gateway:
  entryPoints:
    web:
      address: "[::]:80"
    webSecure:
      address: "[::]:443"
  tls:
    keys: []
    # Uncomment for custom certs:
    # - cert: /etc/goma/certs/cert.crt
    #   key: /etc/goma/certs/server.key.pem
  log:
    level: info
  networking:
    transport:
      insecureSkipVerify: true
  monitoring:
    enableMetrics: true
    metricsPath: /metrics
    enableLiveness: true
    enableRouteHealthCheck: true
    includeRouteHealthErrors: true
  extraConfig:
    directory: /etc/goma/extra
    watch: true

certManager:
  provider: acme
  acme:
    # Uncomment to enable Let’s Encrypt
    # email: admin@example.com
    storageFile: /etc/letsencrypt/acme.json
```

## 7\. Define Routes and Middlewares

Create a folder named `extra` and add two files: `routes.yml` and `middlewares.yml`.

> `extra/routes.yml`

```yaml
routes:
  - name: lldap
    path: /
    hosts: [lldap.example.com]
    target: http://lldap:17170
    middlewares: [enforceHttps]

  - name: registry
    path: /
    hosts: [registry.example.com]
    rewrite: ''
    target: http://registry:5000
    middlewares: [enforceHttps]

  - name: okapi-api
    path: /
    hosts: [okapi-api.example.com]
    rewrite: ''
    target: http://okapi-api:8080
    middlewares: [enforceHttps]
```

> `extra/middlewares.yml`

```yaml
middlewares:
  - name: enforceHttps
    type: redirectScheme
    rule:
      scheme: https
      permanent: true
```

## 8\. Deployment

Launch all services:

```bash
docker compose up -d
```

## 9\. Insecure Registry Configuration

If you haven't configured TLS yet, Docker needs to allow connections to the insecure registry (development only):

Create or edit `/etc/docker/daemon.json`:

```json
{
  "insecure-registries": ["registry.example.com"]
}
```

Restart Docker:

```bash
sudo systemctl restart docker
```

**Tip:** Use Let’s Encrypt or a custom certificate for production environments.

## 10\. Access Your Services

* **LDAP UI**: [https://lldap.example.com](https://lldap.example.com) Login with `admin / adminPasword`. Then:
    
    * Create a group: `registry_users`
        
    * Create a user: `bind_user` (add to `lldap_strict_readonly`)
        
    * Create another user and assign them to `registry_users`
        
* **Okapi API**: [https://okapi-api.example.com/api/books](https://okapi-api.example.com/api/books)
    
* **Docker Registry API**: [https://registry.example.com/v2/\_catalog](https://registry.example.com/v2/_catalog)
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754370611283/f1c43b24-6521-48bc-be5f-294d8a01915e.png align="center")

## 11\. Enable LDAP Authentication

Update `middlewares.yml` with LDAP auth middleware:

```yaml
  - name: ldap-auth
    type: ldap
    paths:
      - /.*
    rule:
      forwardUsername: true
      realm: restricted
      url: ldap://lldap:3890
      baseDN: dc=example,dc=com
      bindDN: uid=bind_user,ou=people,dc=example,dc=com
      bindPass: bind_user_password
      userFilter: "(&(objectclass=person)(memberof=cn=registry_users,ou=groups,dc=example,dc=com)(uid=%s))"
      startTLS: false
      insecureSkipVerify: true
      connPool:
        size: 150
        burst: 100
        ttl: 30s
```

Then apply it in `routes.yml`:

```yaml
  - name: registry
    path: /
    hosts: [registry.example.com]
    rewrite: ''
    target: http://registry:5000
    middlewares:
      - enforceHttps
      - ldap-auth

  - name: okapi-api
    path: /
    hosts: [okapi-api.example.com]
    rewrite: ''
    target: http://okapi-api:8080
    middlewares:
      - enforceHttps
      - ldap-auth
```

---

## 12\. Test the Registry

### Pull the Nginx image:

```bash
docker pull nginx:latest
```

### Tag the image:

```bash
docker tag nginx:latest registry.example.com/nginx:latest
```

### Push to your registry:

```bash
docker push registry.example.com/nginx:latest
```

You should be prompted for credentials, and only LDAP-authorized users will be able to push or pull.

---

## Conclusion

You’ve successfully secured your Docker Registry using **Goma Gateway** and **LLDAP**. With just a few declarative configs and containers, you now have a robust, LDAP-backed authentication layer for your registry and a reusable pattern for protecting any internal API or service.

Feel free to extend this setup to protect other services and APIs with Goma’s flexible middleware system.
