2

I have a Go app that does basic CRUD operations and uses MongoDB for data persistence.

I am using Mongo Atlas connection string to connect the app to MongoDB and things work fine when I run the application on my local dev machine. But when I push everything to a docker container, in a docker-compose workflow, I start getting this error:

server selection error: server selection timeout, current topology: { Type: ReplicaSetNoPrimary, Servers: [{ Addr: app-test-shard-00-00-zfzs6.mongodb.net:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : x509: certificate signed by unknown authority }, { Addr: app-test-shard-00-01-zfzs6.mongodb.net:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : x509: certificate signed by unknown authority }, { Addr: app-test-shard-00-02-zfzs6.mongodb.net:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : x509: certificate signed by unknown authority }, ] }

This is how my docker-compose.yml looks:

services: 
redis:
    image: redis
    restart: always
app:
    image: docker.pkg.github.com/<user>/<project>/<image>:latest
    ports: 
        - 80:8080
    environment: 
        - APP_ENV=docker
        - GIN_MODE=release
    depends_on: 
        - redis
    restart: on-failure

This is how my connection string looks: mongodb://user:password@app-test-shard-00-00-zfzs6.mongodb.net:27017,app-test-shard-00-01-zfzs6.mongodb.net:27017,app-test-shard-00-02-zfzs6.mongodb.net:27017/test?ssl=true&replicaSet=app-test-shard-0&authSource=admin&retryWrites=true&w=majority

I have checked this question and a couple of others, but none seem to fix this.

I also checked docker's networking config, I am using a bridge driver, which should allow access to outside apps (i.e. Mongo Atlas)

Go Version: 1.13

Mongo Driver: mongodb/mongo-go-driver v1.2.0

Dockerfile:

FROM golang:alpine as builder
WORKDIR /build
COPY . .
RUN apk add git openssh build-base
RUN git config --global url."git@github.com:".insteadOf "https://github.com/"
COPY keys/id_rsa /root/.ssh/id_rsa
RUN chmod 600 /root/.ssh/id_rsa
RUN ssh-keyscan -t rsa github.com >> /root/.ssh/known_hosts
ENV GOPRIVATE=github.com/contributor-org
RUN go build

FROM alpine
WORKDIR /org
COPY --from=builder /build/app .
COPY config/*.yml config/
RUN mkdir -p /var/log
CMD ["./app"]

DB Connection code:

type databaseClient struct { // Implements the Database interface
    collection *mongo.Collection
}

func GetNewClient(cfg *config.Config, log *logger.Logger) (Database, error) {
    clientOptions := options.Client().ApplyURI(cfg.Database.ConnectionString)
    client, err := mongo.NewClient(clientOptions)
    if err != nil {
        log.Errorln(err)
    }
    err = client.Connect(context.Background())

    if err != nil {
        log.Fatalln("Failed to connect to Mongo!", err)
        return nil, err
    }

    log.Info("Connected to Mongo!")

    db := client.Database(cfg.Database.Database)
    if db == nil {
        err := fmt.Errorf("DB (%s) not found", cfg.Database)
        log.Error(err)
        return nil, err
    }
    collection := db.Collection(cfg.Database.Collection)
    if collection == nil {
        err := fmt.Errorf("Collection (%s) not found on DB (%s)", cfg.Database.Collection, cfg.Database.Database)
        log.Error(err)
        return nil, err
    }
    return &databaseClient{collection: collection}, nil
}

DB Interface:

type Database interface {
    GetByBookname(context.Context, string) (*model.Book, error)
    GetOneByFilter(context.Context, map[string]interface{}) (*model.Book, error)
    GetAll(context.Context) ([]*model.Book, error)
    GetAllByFilters(context.Context, map[string]interface{}) ([]*model.Book, error)
    AddBook(context.Context, *model.Book) (string, error)
    UpdateBook(context.Context, string, map[string]interface{}) (*model.Book, error)
    DeactivateBook(context.Context, string) error
    OverwriteBook(context.Context, string, *model.Book) (*model.Book, error)
    OnboardBook(*model.Book, string, bool) (string, error)
}
metamemelord
  • 500
  • 1
  • 7
  • 19
  • 1
    Please specify the driver name/version – alessiosavi Jan 01 '20 at 14:04
  • Have you tried to login into the image and send a request to the link? If the request fail from the image, is a network misconfiguration – alessiosavi Jan 01 '20 at 15:54
  • @alessiosavi I installed mongo shell in running image and tried to connect to Mongo, it connects without issues. My issue is likely with the Mongo-Go-Driver. – metamemelord Jan 01 '20 at 16:18
  • You're not getting connection errors. You're getting certificate errors. It is possible that there are some certificates you have on your machine that you use to connect to mongodb, and those certificates are missing in your docker image. – Burak Serdar Jan 01 '20 at 17:31
  • @BurakSerdar That is correct, I got some info from this answer: https://stackoverflow.com/questions/47464161/golang-http-x509-certificate-signed-by-unknown-authority-error?noredirect=1. However, I am not aware how to add/update certificates so that they are x509 compatible/verified. I tried making tls.InsecureSkipVerify=true as well, doesn't seem to work. Could you please help with how can I have proper certs in place for Mongo Atlas. – metamemelord Jan 01 '20 at 17:43
  • What does your dockerfile look like? – Burak Serdar Jan 01 '20 at 18:01
  • @BurakSerdar updated in question. – metamemelord Jan 01 '20 at 18:05
  • The dockers file have an error in the last 3rd line – alessiosavi Jan 01 '20 at 19:15
  • @alessiosavi The dockerfile seems to work fine sir. – metamemelord Jan 02 '20 at 16:38

2 Answers2

3

I quick-fixed this by adding &tlsInsecure=true to the MongoDB URI. Still looking for the correct way to fix it though.

Edit:

Solved it too adding ca-certificates to the Docker image:

# Builder step...

FROM alpine:3
RUN apk update \
    && apk upgrade \
    && apk add --no-cache \
    ca-certificates \
    && update-ca-certificates 2>/dev/null || true
COPY --from=builder /build/main ./
CMD ["/main"]

Another option for a smaller image:

# Builder step...

FROM alpine:3 as certs
RUN apk --no-cache add ca-certificates

FROM scratch as app
COPY --from=builder /build/main ./
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ENTRYPOINT ["/main"]
lewislbr
  • 1,012
  • 14
  • 23
0

The problem seems related to the certificate validation.
You have two options:
- add the self signed certificate to the CA.
- skip the validation.

If you want to skip the self signed certificate validation, you have to modify the ClientOptions struct setting to true the ClientOptions.SSLInsecure variable.

alessiosavi
  • 2,753
  • 2
  • 19
  • 38
  • I think this works same as setting tls.InsecureSkipVerify=true. This does fix that issue, but the app gives this issue now: server selection error: `server selection timeout, current topology: { Type: ReplicaSetNoPrimary, Servers: [{ Addr: app-main-shard-00-00-zfzs6.azure.mongodb.net:27017, Type: Unknown, State: Connected, Average RTT: 0, Last error: connection() : connection(app-main-shard-00-00-zfzs6.azure.mongodb.net:27017[-173]) incomplete read of message header: EOF }] }` – metamemelord Jan 01 '20 at 17:56
  • During connection or during database query? Please share the code related to the db connection – alessiosavi Jan 01 '20 at 19:12
  • The error occurs while querying the DB, NOT while connecting. I am adding the code in question. – metamemelord Jan 02 '20 at 16:39