# Managing multiple htpasswd users with the Ingress-Nginx's auth-map annotation

I was asked to add multiple [htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) users to a temporary demo service in Kubernetes. It took a little time to find the relevant documentation, so I decided to write this article (at least for my future self to remember).

The [Kubernetes Nginx Ingress](https://github.com/kubernetes/ingress-nginx) has an **auth-file** annotation which is excellent for single-user credentials, but the **auth-map** annotation was designed for easily adding multiple users.

> Disclaimer: I'm on MacOS. So the commands may not work the same on a different operating system. Hopefully, the process can be replicated.

In this article, I'll share:

* A way to generate the htpasswd usernames and hashed passwords
    
* How to define a multi-user htpasswd manifest yaml for the ingress nginx
    
* How to configure the Ingress Nginx auth-map annotation.
    

## Pre-requisites

* You need a K8s cluster setup with an Ingress Nginx
    
* You need a deployment and service to hide behind an htpasswd.
    

## Step 1. Creating a list of usernames and passwords

You could autogenerate user names in any way you like. I used a website to generate passwords and a spreadsheet to collate them.

Nonetheless, we're good to go as long as you have a simple CSV file with the following columns.

```yaml
num,username,password
1,john,random01
2,smith,random02
```

**Note, in our version, the password should be 8 characters or less due to using the md5 hash.**

## Step 2. Generating the hashed passwords

Create a bash file.

```bash
touch hash.sh;
chmod +x ./hash.sh
```

Add the following code:

```bash
#!/bin/bash
ed -s $1 <<< w

exec < $1
read header
while IFS="," read -r num username password
do
    hashed=`openssl passwd -quiet $password`
    echo "$username: $hashed"
done
```

To explain how it works:

* `exec < $1` allows us to pass in a file path as an argument.
    
* `ed -s $1 <<< w` adds an extra line to the end of the CSV if one doesn't exist. This is to ensure when we loop over the CSV rows, we loop over every row. Otherwise, it will miss the final row in the spreadsheet.
    
* `openssl passwd -quiet $password` creates a simple md5 hashed password. The `-quiet` is because of a truncation error warning that's irrelevant if the password is 8 characters or less.
    
* `$username: $hashed` is just so we have something we can copy-paste into our Kubernetes secret file
    

Now, we can run the `hash.sh` script to generate the passwords.

```bash
./hash.sh ./passwords.csv
```

The output will look something like this.

```bash
john: XQIeryMOkCNpk
smith: gB1FNtHU3drCA
```

## Step 3. Creating the Kubernetes secret file

We can take the output of the `hash.sh` command and add the key-value pairs under the `stringData` property.

```yaml
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: secret-file
stringData:
  john: XQIeryMOkCNpk
  smith: gB1FNtHU3drCA
```

You want to apply the secret file to the cluster.

```yaml
kubectl apply -f <secret-file-path>
```

## Step 4. Configuring the ingress

Now we have our usernames and passwords configured in a Kubernetes secret file, we need to attach them to the ingress. Add the following annotations to the ingress.

```yaml
kind: Ingress
metadata:
  name: project-ingress
  annotations:    
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: secret-file
    nginx.ingress.kubernetes.io/auth-secret-type: auth-map
    nginx.ingress.kubernetes.io/auth-realm: "Progress Authentication"
    #...etc
```

* `nginx.ingress.kubernetes.io/auth-type: basic` tells the ingress to use Basic authentication versus Digest auth.
    
* `nginx.ingress.kubernetes.io/auth-secret: secret-file` is the name of our secrets file which we defined earlier.
    
* `nginx.ingress.kubernetes.io/auth-secret-type: auth-map` tells the ingress to interpret the secrets a list of usernames and hashed passwords versus a single htpasswd file.
    
* `nginx.ingress.kubernetes.io/auth-realm: "Progress Authentication"` returns a `WWW-Authenticate"` header with whatever value you define.
    

Finally, you want to apply your ingress.

```yaml
kubectl apply -f <ingress-file-path>
```

## Step 5. Testing the login

Now, when you visit the page, you'll be presented with a sign-in form, of which, you can test the credentials.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1679079003107/0cc99cb0-bbb7-4168-8893-33bf68a9326f.png align="center")

If you're testing via the GUI, you may want to use Roland Toth's advice to [log out:](https://blog.rolandtoth.hu/logout-from-htpasswd-protected-site/)

```bash
http://logout@example.com/
```

Alternatively, you can test authentication through curl or [httpie](https://httpie.io/):

```bash
http https://example.com

http https://example.com --auth username:password
```

## Signing out

If you're looking for a robust sign-in solution, perhaps `htpasswd` isn't as ideal as implementing or using an existing email-based username and password system. But for hiding functionality in testing environments that only a few people need access to, it's a handy and quick way to add protection to your website.

## Additional resources

[https://github.com/kubernetes/ingress-nginx/issues/5858](https://github.com/kubernetes/ingress-nginx/issues/5858)

[https://github.com/kubernetes/ingress-nginx/pull/4560](https://github.com/kubernetes/ingress-nginx/pull/4560)
