Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Artifact download with basic authentication #8853

Closed
orcuny opened this issue Sep 9, 2020 · 13 comments
Closed

Artifact download with basic authentication #8853

orcuny opened this issue Sep 9, 2020 · 13 comments
Assignees
Labels

Comments

@orcuny
Copy link

orcuny commented Sep 9, 2020

Hi,

I suppose root cause is something related to go-getter library, but in my case, as I can't make Nomad download artifacts, I'm posting the issue in Nomad list.

Summary: When remote file server needs basic authentication, artifact source as user:pass@url does not work as expected.

Nomad version:

0.11.4

Operating system and Environment details

Environment: Linux amd
File server: Nexus3 3.24.0

Issue

Without basic authentication, artifacts can be downloaded successfully.
When basic authentication is taken into action, artifact download fails with 401 Unauthorized.
I tested Nexus api with curl and when user:pass is given with --user option, it starts to download with no problem.

Reproduction steps

Run a file server with no anonymous user support.
Run nomad with go-getter's user:pass@url format.

Job file (if appropriate)

job "job" {
  datacenters = ["dc1"]

  type = "service"

  constraint {
    attribute = "${node.class}"
    value = "service"
  }

  constraint {
    operator = "distinct_hosts"
    value = "true"
  }

  priority = "100"

  group "group" {
    count = "1"

    ephemeral_disk {
      size = "2048"
    }

    task "task" {
      driver = "raw_exec"

      config {
        command = "java"
        args = [
          "-Xms512m",
          "-Xmx1024m",
          "-Dspring.profiles.active=${TARGET_PLATFORM}",
          "-Dserver.port=${NOMAD_PORT_http}",
          "-jar",
          "local/job.jar"
        ]
      }

      artifact {
        source = "https://downloader:download1@fabio.service.dc1.consul/nexus/service/rest/v1/search/assets/download"
        options {
          group = "com.dc1.job"
          name = "job-app"
          maven.baseVersion = "${NOMAD_META_APP_VERSION}"
          maven.classifier = "spring-boot"
          maven.extension = "jar"
          sort = "version"
        }
        mode = "file"
        destination = "local/job.jar"
      }

      service {
        name = "job"
		tags = ["urlprefix-/job-app proto=https tlsskipverify=true"]
        port = "http"

        check {
          type = "http"
          protocol = "https"
          path = "/job-app/actuator/health"
          interval = "10s"
          timeout = "5s"
          tls_skip_verify = true

          check_restart {
            limit = "3"
            grace = "2m"
            ignore_warnings = false
          }
        }
      }

      resources {
        cpu = "1024"
        memory = "1024"

        network {
          mbits = "10"
          port "http" {}
        }
      }

      template {
        change_mode = "restart"
        destination = "local/env"
        env = true
        data = <<EOH
TARGET_PLATFORM="{{ key "target_platform" }}"
EOH
      }

      meta {
        APP_VERSION = "0.0.1-SNAPSHOT"
      }
    }
  }
}

Nomad Client logs (if appropriate)

2020-09-09T11:14:58.369+0300 [DEBUG] client.alloc_runner.task_runner.task_hook.artifacts: downloading artifact: alloc_id=1298bb82-2d4a-6af8-36c3-26d2e175a5b1 task=task artifact=https://downloader:download1@fabio.service.dc1.consul/nexus/service/rest/v1/search/assets/download
2020-09-09T11:14:58.550+0300 [DEBUG] client: updated allocations: index=21985 total=7 pulled=5 filtered=2
2020-09-09T11:14:58.550+0300 [DEBUG] client: allocation updates: added=0 removed=0 updated=5 ignored=2
2020-09-09T11:14:58.558+0300 [ERROR] client.alloc_runner.task_runner: prestart failed: alloc_id=1298bb82-2d4a-6af8-36c3-26d2e175a5b1 task=task error="prestart hook "artifacts" failed: failed to download artifact "https://downloader:download1@fabio.service.dc1.consul/nexus/service/rest/v1/search/assets/download": bad response code: 401"

Nomad Server logs (if appropriate)

@tgross
Copy link
Member

tgross commented Sep 9, 2020

Hi @orcuny! What you've got here should be working for sure. Can you confirm that you're URL encoding the auth string (see https://github.com/hashicorp/go-getter#basic-authentication)

@orcuny
Copy link
Author

orcuny commented Sep 9, 2020

Hi @tgross. Actually, my example includes the values I used for my tests. Username is downloader and password is download1. So, I suppose it should be about something different.

A couple of minutes ago, I came across a similar case with ansible get_url module. Ansible get_url also got exception with the url and the same username as url_username and the same password as url_password. Then, I noticed the force_basic_auth parameter which resolved the issue with get_url.

Description of that parameter is: Force the sending of the Basic authentication header upon initial request. httplib2, the library used by the uri module only sends authentication information when a webservice responds to an initial request with a 401 status. Since some basic auth services do not properly send a 401, logins will fail.

I don't know if it helps but can it be some kind of hint or sth?

@tgross
Copy link
Member

tgross commented Sep 9, 2020

Ok, thanks for that extra info. Let me see if I can make a minimal reproduction for investigation. I'm hoping it's not a bug in go-getter itself but if so, we'll just have to fix that in our upstream as well.

@tgross tgross self-assigned this Sep 9, 2020
@tgross
Copy link
Member

tgross commented Sep 10, 2020

Hi @orcuny I tried to make a minimal reproduction and it seems to work as I'd expect. So there might be some element we're missing here. Is there a particular artifact server implementation (ex. is this Nginx? Artifactory?) you've got that I might be able to try?

bottle.py artifact server with HTTP Basic Auth
from bottle import auth_basic, route, run, static_file

def check(user, pw):
    if user != "user":
        return False
    if pw != "password1":
        return False
    return True

@route('/')
@auth_basic(check)
def index():
    return static_file("text.txt", root="./")


run(host='localhost', port=8080)
jobspec

Jobfile:

job "example" {
  datacenters = ["dc1"]

  group "box" {
    task "box" {
      driver = "docker"

      config {
        image = "busybox:1"
        command = "/bin/sh"
        args = ["-c", "cat /local/result.txt; sleep 300"]
      }

      artifact {
        source      = "http://user:password1@localhost:8080"
        destination = "local/result.txt"
      }

      resources {
        cpu    = 256
        memory = 128

      }
    }
  }
}

Run nomad agent -dev in one terminal, and python authserver.py in the other. Then run the job and check its logs:

$ nomad job run ./example.nomad
==> Monitoring evaluation "24636fae"
    Evaluation triggered by job "example"
    Evaluation within deployment: "1132e32b"
    Allocation "20356526" created: node "c37c9b6b", group "box"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "24636fae" finished with status "complete"
$ nomad alloc logs 203
Hello world

Artifact server logs:

$ python authserver.py
Bottle v0.13-dev server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [10/Sep/2020 13:29:01] "HEAD / HTTP/1.1" 200 0
127.0.0.1 - - [10/Sep/2020 13:29:01] "GET / HTTP/1.1" 206 12
^C

@tgross
Copy link
Member

tgross commented Sep 10, 2020

Oh this is interesting. In 0.11.3 we shipped a fix for #7854, which is for redirect following in the artifact stanza. I wonder if your artifact server is generating a redirect?

@orcuny
Copy link
Author

orcuny commented Sep 10, 2020

Hi @tgross. Environment I'm working on is Linux amd with sonatype/nexus3:3.24.0 docker image as file server and Fabio LB.

In the mean time, I tried with Nomad 0.10.5 and nexus3:3.26.1. Sadly, the problem persists.

On the other hand, you might be on the correct path. There seems to be a 302 response followed by a redirection to the artifact.

curl "https://10.10.10.215/nexus/service/rest/v1/search/assets/download?group=com.dc1.job&name=job-app&maven.extension=jar&maven.classifier=spring-boot&sort=version" -v --insecure --user downloader:download1 -o output.jar --silent
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 3128 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to 10.10.10.215:443
* Server auth using Basic with user 'downloader'
> CONNECT 10.10.10.215:443 HTTP/1.1
> Host: 10.10.10.215:443
> User-Agent: curl/7.55.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied OK to CONNECT request
* CONNECT phase completed!
* schannel: SSL/TLS connection with 10.10.10.215 port 443 (step 1/3)
* schannel: disabled server certificate revocation checks
* schannel: verifyhost setting prevents Schannel from comparing the supplied target name with the subject names in server certificates.
* schannel: using IP address, SNI is not supported by OS.
* schannel: sending initial handshake data: sending 153 bytes...
* schannel: sent initial handshake data: sent 153 bytes
* schannel: SSL/TLS connection with 10.10.10.215 port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* CONNECT phase completed!
* CONNECT phase completed!
* schannel: SSL/TLS connection with 10.10.10.215 port 443 (step 2/3)
* schannel: encrypted data got 2196
* schannel: encrypted data buffer: offset 2196 length 4096
* schannel: sending next handshake data: sending 93 bytes...
* schannel: SSL/TLS connection with 10.10.10.215 port 443 (step 2/3)
* schannel: encrypted data got 186
* schannel: encrypted data buffer: offset 186 length 4096
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with 10.10.10.215 port 443 (step 3/3)
* schannel: stored credential handle in session cache
* Server auth using Basic with user 'downloader'
> GET /nexus/service/rest/v1/search/assets/download?group=com.dc1.job&name=job-app&maven.extension=jar&maven.classifier=spring-boot&sort=version HTTP/1.1
> Host: 10.10.10.215
> Authorization: Basic ZG93bmxvYWRlcjpkb3dubG9hZDE=
> User-Agent: curl/7.55.1
> Accept: */*
>
* schannel: client wants to read 102400 bytes
* schannel: encdata_buffer resized 103424
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 391
* schannel: encrypted data buffer: offset 391 length 103424
* schannel: decrypted data length: 362
* schannel: decrypted data added: 362
* schannel: decrypted data cached: offset 362 length 102400
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: decrypted data buffer: offset 362 length 102400
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 362
* schannel: decrypted data buffer: offset 0 length 102400
< **HTTP/1.1 302 Found**
< Content-Length: 0
< Date: Thu, 10 Sep 2020 20:08:29 GMT
< Location: https://10.10.10.215/nexus/repository/maven-snapshots/com/dc1/job/job-app/0.0.1-SNAPSHOT/job-app-0.0.1-20200910.181203-1-spring-boot.jar
< Server: Nexus/3.24.0-02 (OSS)
< Strict-Transport-Security: max-age=7776000
< X-Content-Type-Options: nosniff
<
* Connection #0 to host 127.0.0.1 left intact

@tgross
Copy link
Member

tgross commented Sep 14, 2020

@orcuny I'm not at all familiar with sonatype and in trying to reproduce this I just crashed Sonatype a lot (?). Do you have a working Nomad job that can run sonatype (ideally with your fabio setup as well)?

@orcuny
Copy link
Author

orcuny commented Sep 15, 2020

@tgross I’ll try to make you reproduce the issue.

First, create the directory structure /opt/nexus/data/etc/ and place attached nexus.properties under /opt/nexus/data/etc/ with nexus host ip address corrected. Then, change ownership of /opt/nexus for 200:200 recursively.

Volume definition in my client config is as follows:

host_volume "nexus_data" {
  path = "/opt/nexus/data"
}

Execute attached nexus.nomad and it should run. You can find initial admin password in /opt/nexus/data/admin.password after some time.

Login to admin console and disable anonymous access when prompted.
Create a new role and give nx-repository-view---browse, nx-repository-view---read, nx-search-read privileges.
Create a new user with defined role. In my example, user id: downloader and pass: download1

Upload a jar file to maven-releases repo. I also attached an example maven project. Coordinates requested should match pom.xml definitions. You can upload the jar with spring-boot as classifier. So, classifier is spring-boot and extension is jar.
group id: org.example artifact id: sleeper version: 1.0

Finally, execute test.nomad and it will fail with 401.

My fabio config is a little complicated with TLS. I attached the skeleton parts, but I suppose any fabio config will work.

Thanks in advance.

nexus.properties

application-host=10.10.10.223
nexus-context-path=/nexus3/

nexus.nomad

job "nexus3" {
  datacenters = ["dc1"]

  type = "service"

  priority = "100"

  group "nexus" {
    count = "1"

    ephemeral_disk {
      size = "4096"
      sticky = true
      migrate = true
    }

    volume "nexus_volume" {
      type = "host"
      source = "nexus_data"
    }

    task "nexus" {
      driver = "docker"

      config {
        image = "sonatype/nexus3:3.24.0"
        network_mode = "host"
        port_map {
          http = 8081
        }
      }

      volume_mount {
        volume = "nexus_volume"
        destination = "/nexus-data"
      }

      service {
        name = "nexus3"
        tags = ["urlprefix-/nexus3"]
        port = "http"

        check {
          type = "http"
          protocol = "http"
          path = "/nexus3/service/rest/v1/status"
          interval = "10s"
          timeout = "5s"

          check_restart {
            limit = "3"
            grace = "5m"
            ignore_warnings = false
          }
        }
      }

      resources {
        cpu = "2048"
        memory = "2048"

        network {
          mbits = "10"
          port "http" {
            static = 8081
          }
        }
      }
    }
  }
}

test.nomad

job "test" {
  datacenters = ["dc1"]

  type = "batch"

  priority = "100"

  group "test" {
    count = "1"

    ephemeral_disk {
      size = "2048"
    }

    task "test" {
      driver = "raw_exec"

      config {
        command = "java"
        args = [
          "-Xms512m",
          "-Xmx512m",
          "-jar",
          "local/job.jar"
        ]
      }

      artifact {
        source = "http://downloader:download1@10.10.10.223:8081/nexus3/service/rest/v1/search/assets/download"
        options {
          group = "org.example"
          name = "sleeper"
          maven.baseVersion = "1.0"
          maven.classifier = "spring-boot"
          maven.extension = "jar"
          sort = "version"
        }
        mode = "file"
        destination = "local/job.jar"
      }

      resources {
        cpu = "512"
        memory = "512"

        network {
          mbits = "10"
        }
      }
    }
  }
}

fabio.nomad

job "fabio" {
  datacenters = ["dc1"]

  type = "service"

  priority = "100"

  group "fabio" {
    count = "1"

    ephemeral_disk {
      size = "256"
    }

    task "fabio" {
      driver = "docker"

      config {
        image = "${meta.ent_docker_registry}/pluton/fabio:1.5.13-go1.13.4"
        auth {
          username = "${meta.ent_docker_username}"
          password = "${meta.ent_docker_password}"
        }
        volumes = [
          "${meta.app_root}/volumes/fabio/config/fabio.properties:/etc/fabio/fabio.properties"
        ]
        network_mode = "host"
      }

      resources {
        cpu = "1024"
        memory = "128"

        network {
          mbits = "10"
          port "lb" {
            static = 443
          }
          port "ui" {
            static = 9998
          }
        }
      }
    }
  }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
    </parent>

    <groupId>org.example</groupId>
    <artifactId>sleeper</artifactId>
    <version>1.0</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                        <configuration>
                            <classifier>spring-boot</classifier>
                            <mainClass>org.example.sleeper.SleeperApplication</mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

SleeperApplication.java

package org.example.sleeper;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SleeperApplication implements CommandLineRunner {

    public static void main(String... args) {
        SpringApplication.run(SleeperApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        for (int i = 0; i < 10; ++i) {
            Thread.sleep(1000);
            System.out.print(".");
        }
    }

}

curl
curl -u downloader:download1 "http://10.10.10.223:8081/nexus3/service/rest/v1/search/assets/download?group=org.example&name=sleeper&maven.baseVersion=1.0&maven.classifier=spring-boot&maven.extension=jar&sort=version" -o job.jar -L

@tgross
Copy link
Member

tgross commented Sep 15, 2020

Hello again @orcuny. It took me a bit to get the sonatype container working because it has some weird requirements around volume permissions. But I wanted to make sure we were solving the right problem here, so I modified your setup to remove Fabio from the mix. I had to make a few modifications to get this all working, but I was finally able to reproduce the error.

sonatype jobspec
job "nexus3" {
  datacenters = ["dc1"]

  type = "service"

  group "nexus" {

    network {
      port "http" {
        static = 8081
        to     = 8081
      }
    }

    task "nexus" {
      driver = "docker"

      config {
        image = "sonatype/nexus3:3.24.0"
        ports = ["http"]

        # note: we have to set permissions manually on the host because
        # sonatype doesn't understand how users work in Docker
        #  chown -R 200 /srv/nexus
        volumes = [
          "/srv/nexus:/nexus-data"
        ]
      }

      resources {
        cpu    = "1048"
        memory = "2048"
      }
    }
  }
}
java job that needs the artifact
job "example" {

  datacenters = ["dc1"]

  type = "batch"

  group "test" {
    task "test" {
      driver = "java"

      config {
        jar_path    = "/local/helloworld.jar"
        jvm_options = ["-Xmx256m"]
      }

      # derived from:
      # curl -L -u downloader:download1 \
      #      -o helloworld.jar \
      #      "http://10.0.2.15:8081/service/rest/v1/search/assets/download?group=org.example&name=helloworld&maven.baseVersion=1.0&sort=version"

      artifact {
        source = "http://downloader:download1@10.0.2.15:8081/service/rest/v1/search/assets/download"

        options {
          group             = "org.example"
          name              = "helloworld"
          maven.baseVersion = "1.0"
          sort              = "version"
        }
      }

      resources {
        cpu    = 256
        memory = 300

      }
    }
  }
}

I packet-captured the communication between Nomad and the Sonatype container (with sudo tcpdump -i docker0 port 8081 -w dump.pcap) and then was able to extract this HTTP conversation, wherein we see that the artifact fetcher is not sending the username and password after the redirect.

HEAD /service/rest/v1/search/assets/download?group=org.example&maven.baseVersion=1.0&name=helloworld&sort=version HTTP/1.1
Host: 10.0.2.15:8081
User-Agent: Go-http-client/1.1
Authorization: Basic ZG93bmxvYWRlcjpkb3dubG9hZDE=
Connection: close

HTTP/1.1 302 Found
Connection: close
Date: Tue, 15 Sep 2020 19:06:42 GMT
Server: Nexus/3.24.0-02 (OSS)
X-Content-Type-Options: nosniff
Location: http://10.0.2.15:8081/repository/maven-releases/org/example/helloworld/1.0/helloworld-1.0.jar

HEAD /repository/maven-releases/org/example/helloworld/1.0/helloworld-1.0.jar HTTP/1.1
Host: 10.0.2.15:8081
User-Agent: Go-http-client/1.1
Referer: http://10.0.2.15:8081/service/rest/v1/search/assets/download?group=org.example&maven.baseVersion=1.0&name=helloworld&sort=version
Connection: close

HTTP/1.1 401 Unauthorized
Connection: close
Date: Tue, 15 Sep 2020 19:06:42 GMT
Server: Nexus/3.24.0-02 (OSS)
X-Content-Type-Options: nosniff
Content-Security-Policy: sandbox allow-forms allow-modals allow-popups allow-presentation allow-scripts allow-top-navigation
X-XSS-Protection: 1; mode=block
WWW-Authenticate: BASIC realm="Sonatype Nexus Repository Manager"

This doesn't look like it's some hidden default behavior of golang. The following program returns the length in bytes of my artifact, as I'd expect.

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {

	client := &http.Client{}
	req, err := http.NewRequest("GET", "http://10.0.2.15:8081/service/rest/v1/search/assets/download?group=org.example&maven.baseVersion=1.0&name=helloworld&sort=version", nil)

	req.SetBasicAuth("downloader", "download1")
	resp, err := client.Do(req)

	if err != nil {
		panic(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	fmt.Println(len(body))
}

But using go-getter as vendored in Nomad results in the 401:

package main

import (
	"github.com/hashicorp/go-getter"
)

func main() {

	src := "http://downloader:download1@10.0.2.15:8081/service/rest/v1/search/assets/download?group=org.example&maven.baseVersion=1.0&name=helloworld&sort=version"

	dst := "./hello.jar"

	client := &getter.Client{
		Src: src,
		Dst: dst,
	}

	err := client.Get()
	if err != nil {
		panic(err)
	}
}

A more recent version of go-getter fails in the same way.

So as far as I can tell, the problem is in go-getter: we follow the redirect but for some reason don't pass along the authorization headers we'd expect to see there. I'll open an issue in the upstream repository and see what we can do to get that fixed soon.

In the meantime, if it's at all possible I would recommend not using the Sonatype search interface, but get the well-known artifact path out of Sonatype and use that directly.

@tgross tgross added stage/accepted Confirmed, and intend to work on. No timeline committment though. theme/dependencies Pull requests that update a dependency file and removed stage/waiting-reply labels Sep 15, 2020
@orcuny
Copy link
Author

orcuny commented Sep 15, 2020

Hi @tgross Glad to see you reproduce the issue.

Last comment is unfortunately not possible with Sonatype as it appends suffixes to snapshot artifacts in order not to override previous ones. Because of those suffixes, one cannot know the exact artifact paths. Search api is Sonatype's only way to find and return the most recent snapshot to client with that sort=version parameter.

Looking forward to fix :-)

@tgross
Copy link
Member

tgross commented Sep 16, 2020

Ok, after a bit more investigation and comparing the exact responses I captured, I've discovered this is actually an issue between Sonatype's redirect and golang's redirect handling behavior. When Nexus returns the redirect, it's returning the full URL, but with the authentication string stripped off. Which means that when the HTTP client follows the redirect, it no longer has the auth information. This is all happening "below" the go-getter library... and as far as I can tell this is the correct behavior in http.Client:

When following redirects, the Client will forward all headers set on the initial Request except:

• when forwarding sensitive headers like "Authorization", "WWW-Authenticate", and "Cookie" to untrusted targets. These headers will be ignored when following a redirect to a domain that is not a subdomain match or exact match of the initial domain. For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" will forward the sensitive headers, but a redirect to "bar.com" will not.

(The relevant bit of the go stdlib: https://golang.org/src/net/http/client.go?s=1950:3998#L981)

@orcuny this might provide a workaround option for you as well if you can configure the Nexus to provide a relative URL rather than a full URL in its redirects. Or if you can verify that the Nexus is providing the redirect to its DNS name and not the IP address.

The golang program below exercises this in detail:

package main

import (
	"fmt"
	"net/http"

	getter "github.com/hashicorp/go-getter"
)

var redirect = ""

func search() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		username, password, ok := r.BasicAuth()
		if username != "test" || password != "test" || !ok {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		http.Redirect(w, r, redirect, http.StatusFound)
		return
	})
}

func artifact() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		username, password, ok := r.BasicAuth()
		if username != "test" || password != "test" || !ok {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		fmt.Fprintln(w, "Hello, World!")
	})
}

func runServer() *http.Server {

	router := http.NewServeMux()
	router.Handle("/search", search())
	router.Handle("/artifact", artifact())

	srv := &http.Server{
		Addr:    ":8000",
		Handler: router,
	}

	go srv.ListenAndServe()
	return srv
}

func runTest() {
	fmt.Printf("test with redirect: %q\n", redirect)
	src := "http://test:test@localhost:8000/search"

	client := &http.Client{}
	req, err := http.NewRequest("GET", src, nil)
	resp, err := client.Do(req)

	if err != nil {
		panic(err)
	}
	fmt.Printf("  golang HTTP client got HTTP status code: %v\n", resp.StatusCode)

	getter := &getter.Client{
		Src: src,
		Dst: "./hello.jar",
	}

	err = getter.Get()
	if err != nil {
		fmt.Printf("  go-getter client got: %v\n", err)
	} else {
		fmt.Println("  go-getter worked!")
	}

}

func main() {

	srv := runServer()
	defer srv.Close()

	redirect = "http://localhost:8000/artifact"
	runTest()

	redirect = "/artifact"
	runTest()
}
$ go run ./main.go
test with redirect: "http://localhost:8000/artifact"
  golang HTTP client got HTTP status code: 401
  go-getter client got: bad response code: 401
test with redirect: "/artifact"
  golang HTTP client got HTTP status code: 200
  go-getter worked!

@tgross tgross added stage/not-a-bug and removed stage/accepted Confirmed, and intend to work on. No timeline committment though. labels Sep 16, 2020
@tgross
Copy link
Member

tgross commented Sep 16, 2020

@orcuny at this point I'm going to close this issue, because the problem is with the Nexus configuration and its redirects. If you provide a packet capture of the unencrypted HTTP traffic between Fabio and Nexus I might be able to help you poke at the specific configuration problem, but this isn't really a Nomad issue at this point.

@github-actions
Copy link

github-actions bot commented Nov 2, 2022

I'm going to lock this issue because it has been closed for 120 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants