greenpau / caddy-security

🔐 Authentication, Authorization, and Accounting (AAA) App and Plugin for Caddy v2. 💎 Implements Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0 (Github, Google, Facebook, Okta, etc.), SAML Authentication. MFA/2FA with App Authenticators and Yubico. 💎 Authorization with JWT/PASETO tokens. 🔐
https://authcrunch.com/
Apache License 2.0
1.48k stars 73 forks source link

question: LDAP Authentication for docker registry #118

Open boesr opened 2 years ago

boesr commented 2 years ago

I just setup the ldap authentication. So far it really works well using the browser or curl, but I got stuck trying to use the authentication for a docker registry. It seems like there is no way to execute a docker push / pull against a private repository behind the authentication.

Did somebody already tried it and was successful?

greenpau commented 2 years ago

@boesr , just to clarify… you have a private docker registry protected by this plugin? If so, please share your config.

greenpau commented 2 years ago

Also, you could use “bypass” directive to avoid authentication for docker registry endpoint.

boesr commented 2 years ago

@greenpau exactly. Here is my config:

{
    # debug

    order authenticate before respond
    order authorize before basicauth

    security {

        ldap identity store my.ldap {
            realm my.ldap
            servers {
                ldaps://LDAP_HOST:636 ignore_cert_errors
            }
            attributes {
                name givenName
                surname sn
                username sAMAccountName
                member_of memberOf
                email mail
            }
            username "USER_BIND"
            password "PASSWORD"
            search_base_dn "SEARCH_BASE"
            search_filter "(&(|(sAMAccountName=%s))(objectclass=user))"
            groups {
                "ADMIN_GROUP" admin
            }
        }

        authentication portal myportal {
            crypto default token lifetime 3600
            crypto key sign-verify {env.JWT_SHARED_KEY}
            enable identity store informatics.ldap
            ui {
                logo url "https://caddyserver.com/resources/images/caddy-circle-lock.svg"
                logo description "Caddy"
                links {
                    "My Identity" "/whoami" icon "las la-user"
                }
            }
        }

        authorization policy mypolicy {
            # disable auth redirect
            set auth url https://localhost
            allow roles authp/admin authp/user
            crypto key verify {env.JWT_SHARED_KEY}
            allow roles anonymous guest admin
            allow roles authp/admin authp/guest
            allow roles admin editor viewer
            allow roles AzureAD_Administrator AzureAD_Editor AzureAD_Viewer
            allow roles everyone Everyone
        }
    }
}

localhost {
    # Set this path to your site's directory.
    # root * /srv/http

    # Enable the static file server.
    # file_server

    # Enable logging for URL root to log file
   authenticate with myportal

    log {
        output file /var/log/caddy/url-root-access.log
    }

    rewrite /myapp /myapp/
    handle /myapp/* {
        authorize with mypolicy
        reverse_proxy http://testweb:8080
    }

    rewrite /v2 /v2/
    handle /v2/* {
        authorize with mypolicy
        reverse_proxy http://registry:5000
    }
}
boesr commented 2 years ago

Also, you could use “bypass” directive to avoid authentication for docker registry endpoint.

The reason I am using the plugin is trying to secure the docker registry. So bypassing it isn't an option for me. But yes with the bypass directive I can use the private registry, but it's open again.

greenpau commented 2 years ago

@boesr , please enable caddy-trace plugin, run “docker login” and paste the logs here. Chances are that this is unsupported. At the same time, it great issue to work on.

greenpau commented 2 years ago

@boesr , actually this might work out of the box with “with basic auth” directive.

boesr commented 2 years ago

@greenpau I just enabled the caddy-trace plugin. The logs of caddy now contain the following:

{"level":"debug","ts":1654063710.033493,"logger":"security","msg":"token validation error","session_id":"","request_id":"34f45517-c1c0-42e3-b3fa-ea137b792025","error":"no token found"}
{"level":"debug","ts":1654063710.033529,"logger":"security","msg":"redirecting unauthorized user","session_id":"","request_id":"34f45517-c1c0-42e3-b3fa-ea137b792025","method":"location"}
{"level":"error","ts":1654063710.0336215,"logger":"http.handlers.authentication","msg":"auth provider returned error","provider":"authorizer","error":"user authorization failed: src_ip=172.20.0.1, src_conn_ip=172.20.0.1, reason: no token found"}
{"level":"debug","ts":1654063710.033696,"logger":"http.log.error.log0","msg":"not authenticated","request":{"remote_ip":"172.20.0.1","remote_port":"58138","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/v2/","headers":{"User-Agent":["docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))"],"Accept-Encoding":["gzip"],"Connection":["close"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"localhost"}},"duration":0.000248291,"status":401,"err_id":"8pxd0c6vr","err_trace":"caddyauth.Authentication.ServeHTTP (caddyauth.go:88)"}
{"level":"debug","ts":1654063710.0355916,"logger":"tls.handshake","msg":"choosing certificate","identifier":"localhost","num_choices":1}
{"level":"debug","ts":1654063710.035638,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"localhost","subjects":["localhost"],"managed":true,"issuer_key":"local","hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.035649,"logger":"tls.handshake","msg":"matched certificate in cache","subjects":["localhost"],"managed":true,"expiration":1654105430,"hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.0366776,"logger":"security","msg":"redirect recorded","session_id":"GqWef6Fu5q2seWwy5jCRX6Vo1s63VJpH2AdtBIG4Ey","request_id":"6852ac50-9368-44ac-8d82-4951577e6c09","redirect_url":"AUTHP_REDIRECT_URL=https://localhost/v2/; Path=/; Secure; HttpOnly;"}
{"level":"debug","ts":1654063710.036737,"logger":"security","msg":"Redirect served","session_id":"GqWef6Fu5q2seWwy5jCRX6Vo1s63VJpH2AdtBIG4Ey","request_id":"6852ac50-9368-44ac-8d82-4951577e6c09","redirect_url":"https://localhost/login","status_code":302}
{"level":"debug","ts":1654063710.0383554,"logger":"tls.handshake","msg":"choosing certificate","identifier":"localhost","num_choices":1}
{"level":"debug","ts":1654063710.0383818,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"localhost","subjects":["localhost"],"managed":true,"issuer_key":"local","hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.038386,"logger":"tls.handshake","msg":"matched certificate in cache","subjects":["localhost"],"managed":true,"expiration":1654105430,"hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.0417101,"logger":"tls.handshake","msg":"choosing certificate","identifier":"localhost","num_choices":1}
{"level":"debug","ts":1654063710.0417376,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"localhost","subjects":["localhost"],"managed":true,"issuer_key":"local","hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.0417418,"logger":"tls.handshake","msg":"matched certificate in cache","subjects":["localhost"],"managed":true,"expiration":1654105430,"hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.0427096,"logger":"security","msg":"token validation error","session_id":"","request_id":"5be11404-a38f-4181-9a2a-f90b2de8625d","error":"no token found"}
{"level":"debug","ts":1654063710.0427334,"logger":"security","msg":"redirecting unauthorized user","session_id":"","request_id":"5be11404-a38f-4181-9a2a-f90b2de8625d","method":"location"}
{"level":"error","ts":1654063710.0427492,"logger":"http.handlers.authentication","msg":"auth provider returned error","provider":"authorizer","error":"user authorization failed: src_ip=172.20.0.1, src_conn_ip=172.20.0.1, reason: no token found"}
{"level":"debug","ts":1654063710.0427673,"logger":"http.log.error.log0","msg":"not authenticated","request":{"remote_ip":"172.20.0.1","remote_port":"58150","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/v2/","headers":{"User-Agent":["docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))"],"Accept-Encoding":["gzip"],"Connection":["close"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"localhost"}},"duration":0.000075209,"status":401,"err_id":"t6b48v98j","err_trace":"caddyauth.Authentication.ServeHTTP (caddyauth.go:88)"}
{"level":"debug","ts":1654063710.0449662,"logger":"tls.handshake","msg":"choosing certificate","identifier":"localhost","num_choices":1}
{"level":"debug","ts":1654063710.0449939,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"localhost","subjects":["localhost"],"managed":true,"issuer_key":"local","hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.044999,"logger":"tls.handshake","msg":"matched certificate in cache","subjects":["localhost"],"managed":true,"expiration":1654105430,"hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.0460958,"logger":"security","msg":"redirect recorded","session_id":"5KwjjpRhcWxlTfKg0kRJdHQQMfjmGMBPZ91sXc","request_id":"0b2dd3f6-4798-46a9-9215-5e82f72f98d2","redirect_url":"AUTHP_REDIRECT_URL=https://localhost/v2/; Path=/; Secure; HttpOnly;"}
{"level":"debug","ts":1654063710.0462625,"logger":"security","msg":"Redirect served","session_id":"5KwjjpRhcWxlTfKg0kRJdHQQMfjmGMBPZ91sXc","request_id":"0b2dd3f6-4798-46a9-9215-5e82f72f98d2","redirect_url":"https://localhost/login","status_code":302}
{"level":"debug","ts":1654063710.0475667,"logger":"tls.handshake","msg":"choosing certificate","identifier":"localhost","num_choices":1}
{"level":"debug","ts":1654063710.0475912,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"localhost","subjects":["localhost"],"managed":true,"issuer_key":"local","hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}
{"level":"debug","ts":1654063710.0475957,"logger":"tls.handshake","msg":"matched certificate in cache","subjects":["localhost"],"managed":true,"expiration":1654105430,"hash":"0bb13b4385217774ce35ddd04945e6b9c4b40089d2bafd7cde9c2ef55f457dde"}

Yes basic auth should work. We already have a registry behind a basic auth. Not the one I am testing atm, I am testing a test registry, that is not secured in another way than via the caddy-security plugin. Basic auth isn't an alternative to the one already existing since afaik it requires new accounts. We would like to reuse our LDAP accounts. Please correct me if I am wrong regarding the new accounts.

EDIT: Seems like I did not correctly enable the trace. After enabling it, the following output is added to the log:

{"level":"debug","time":"2022-06-01T07:44:31.462Z","msg":"debugging request","request_id":"cb32b60e-74aa-44ae-955d-629b09419150","direction":"incoming","tag":"registry","method":"GET","proto":"HTTP/1.1","host":"localhost","uri":"/v2/","remote_addr_port":"192.168.48.1:58856","remote_addr":"192.168.48.1","remote_port":58856,"content_length":0,"cookie_count":0,"user_agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))","referer":"","cookies":[],"query_params":{},"headers":{"Accept-Encoding":"gzip","Connection":"close","User-Agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))"},"form":{}}

{"level":"debug","time":"2022-06-01T07:44:31.467Z","msg":"debugging request","request_id":"ca7bf4f7-a25a-4ca8-af2f-1a5014e55931","direction":"incoming","tag":"registry","method":"GET","proto":"HTTP/1.1","host":"localhost","uri":"/v2/","remote_addr_port":"192.168.48.1:58860","remote_addr":"192.168.48.1","remote_port":58860,"content_length":0,"cookie_count":0,"user_agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))","referer":"","cookies":[],"query_params":{},"headers":{"Accept-Encoding":"gzip","Connection":"close","User-Agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))"},"form":{}}

{"level":"debug","time":"2022-06-01T07:44:31.472Z","msg":"debugging request","request_id":"ea591576-ed5e-4f9a-b22a-88f57d9be94c","direction":"incoming","tag":"registry","method":"GET","proto":"HTTP/1.1","host":"localhost","uri":"/v2/","remote_addr_port":"192.168.48.1:58868","remote_addr":"192.168.48.1","remote_port":58868,"content_length":0,"cookie_count":0,"user_agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))","referer":"http://localhost/v2/","cookies":[],"query_params":{},"headers":{"Accept-Encoding":"gzip","Connection":"close","Referer":"http://localhost/v2/","User-Agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))"},"form":{}}

{"level":"debug","time":"2022-06-01T07:44:31.476Z","msg":"debugging request","request_id":"93858b51-ab79-4497-81cd-0200148ea6ec","direction":"incoming","tag":"registry","method":"GET","proto":"HTTP/1.1","host":"localhost","uri":"/v2/","remote_addr_port":"192.168.48.1:58876","remote_addr":"192.168.48.1","remote_port":58876,"content_length":0,"cookie_count":0,"user_agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))","referer":"http://localhost/v2/","cookies":[],"query_params":{},"headers":{"Accept-Encoding":"gzip","Connection":"close","Referer":"http://localhost/v2/","User-Agent":"docker/20.10.14 go/go1.16.15 git-commit/87a90dc kernel/5.10.104-linuxkit os/linux arch/arm64 UpstreamClient(Docker-Client/20.10.14 \\(darwin\\))"},"form":{}}
boesr commented 2 years ago

Just an information for everyone facing the same problem, it is possible to reimplement push and pull via the registry htttp api. With that you can attach the cookie to authenticate against the security plugin. Just a walkaround till it works natively.