How to configure Traefik as a reverse proxy to Artifactory with Docker sub-domain

Hello everyone,

I am using Traefik 2.4.5:

Version:      2.4.5
Codename:     livarot
Go version:   go1.15.8
Built:        2021-02-18T17:21:25Z
OS/Arch:      linux/amd64

I'm trying to convert the below Nginx conf to a Traefik setup, but I'm not sure how to extract the subdomain from the server name (in this case, the ״repo״ template) and rewrite it to the appropriate URL.
The following is the Nginx conf snippet that allows that:

server_name ~(?<repo>.+)\.artifactory.jfrog artifactory.jfrog;
rewrite ^/(v1|v2)/(.*) /artifactory/api/docker/$repo/$1/$2;

I'm using file as a provider, and my current dynamic traefik configuration is as follows:

http:
  routers:
    artifactoryRouterHTTP:
      rule: "Host(`artifactory.jfrog`)"
      service: artifactory
      entryPoints:
        - web
      middlewares:
        - dockerSubDomainRedirect
        - uiRedirect
        - uiLongRedirect
        - limit
    artifactoryApiRouter:
      rule: "Host(`artifactory.jfrog`) && PathPrefix(`/artifactory{regex:|/.+}`)"
      service: artifactoryApi
      entryPoints:
        - web
    artifactoryDockerRouter:
      rule: "HostRegexp(`{repo:(.+)}\\.artifactory.jfrog`)"
      service: artifactory
      entryPoints
        - web
      middlewares:
        - dockerSubDomainRedirect
  services:
    artifactory:
      loadbalancer:
        servers:
          - url: 'http://127.0.0.1:8082'
    artifactoryApi:
      loadbalancer:
        servers:
          - url: 'http://127.0.0.1:8081'

  middlewares:
    limit:
      buffering:
        maxRequestBodyBytes: 0
    dockerSubDomainRedirect:
      replacePathRegex:
        regex: "^/(v1|v2)/(.*)"
        replacement: "/artifactory/api/docker/$repo/$1/$2"
    uiRedirect:
      replacePathRegex:
        regex: "^/$"
        replacement: "/ui/"
    uiLongRedirect:
      replacePathRegex:
        regex: "^/ui$"
        replacement: "/ui/"

As a reverse proxy, it appears to work as expected, but still, I am unable to get the Docker sub-domain method to work.

Any help would be appreciated.

Hello @vasily-sk,

In golang, the backticks are used to denote a string literal. Your rule here:

rule: "HostRegexp(`{repo:(.+)}\\.artifactory.jfrog`)"

Is the equivalent of:

HostRegexp("{repo:(.+)}\\\\.artifactory.jfrog")

Which I doubt is going to match your requests.

I would instead try reformatting your rule to look like this:

rule: "HostRegexp(`{repo:(.+)}.artifactory.jfrog`)"

Similar to the documentation rules in Routers - Traefik

You can also confirm the loaded rule in the dashboard, and you may have to modify the rule formatting slightly depending on the nesting of your strings inside strings (escaping quotes as required).

@vasily-sk Did you manage to get it to work, i am trying to do make subdomain to work to no avail

Thank you for your response; I have made the necessary changes to the setup, unfortunately, I am still unable to find a solution that fits my use case, perhaps a screenshot would help:


My goal here is to extract the subdomain from the Traefik router rule and use it in the middleware, for example:
docker-registry.artifactory.jfrog

According to my current configuration, the above should be redirected to:
artifactory.jfrog/artifactory/api/docker/docker-registry/v2/alpine:latest

This, I believe, will not function because the router and middleware are considered two separate "blocks.".
Any suggestions will be appreciated.

Hello @vasily-sk,

You are correct in that the capture groups from one section of Traefik are not capable of being reused in another.

One question: are you looking to replace a path on the backend request? or are you trying to redirect the client to a new URI?

replacePathRegex is used for the former, and only can operate on paths. The latter you can use RedirectRegex - Traefik which allows capture groups and replacements across the request.

Note that redirectRegex will return a 30X, and force a client redirect, so the URI will not be hidden from the user, however this may not be an issue for you.

Hello @daniel.tomcej,

Thank you for your help.
I am trying to replace a path on the backend request, is there a possible workaround?
In Nginx, the capture group is shared across the entire configuration file, thus making it easily achievable.

Thank you in advance.

This is well beyond a year old. Were you ever able to get this working? I'm having the same issue and my Googling is turning up lots of users asking the same question.

version: "3.9"

services:
  artifactory:
    image: releases-docker.jfrog.io/jfrog/artifactory-oss:latest
    container_name: artifactory
    volumes:
      - ./apps/artifactory/var/etc/system.yaml:/var/opt/jfrog/artifactory/etc/system.yaml
    restart: unless-stopped
    labels:
      # Web Ui Rules
      - "traefik.http.routers.artifactory.Rule=HostRegexp(`artifactory.{anydomain:.*}`)"
      - "traefik.http.routers.artifactory.service=artifactory-svc"
      - "traefik.http.services.artifactory-svc.loadbalancer.server.port=8082"
      # API Rules
      - "traefik.http.routers.artifactory-api.Rule=HostRegexp(`artifactory.{anydomain:.*}`) && PathPrefix(`/artifactory/api/`)"
      - "traefik.http.routers.artifactory-api.service=artifactory-api-svc"
      - "traefik.http.services.artifactory-api-svc.loadbalancer.server.port=8081"

Confirm the API:

curl -u admin:YourPassword123 http://artifactory.docker.localhost/artifactory/api/system