2022-07-19

Running static sites from minio

One aspect of S3-like stores that I have found useful is the ability to serve static websites. Unlike S3 and Ceph RGW, doing this in minio requires some additional tooling. This website is hosted on my minio cluster and these are the steps that I took to make that happen.

  1. Create the bucket in minio
  2. Create a user that only has permissions to this bucket. (This isn't strictly required but, I like having a user per bucket).

    • Create a policy for the user, I give full permissions on the bucket (you can simply replace gnuherder.how with the name of your bucket):

      {
         "Version": "2012-10-17",
         "Statement": [
             {
                 "Sid": "VisualEditor0",
                 "Effect": "Allow",
                 "Action": [
                     "s3:GetBucketPolicyStatus",
                     "s3:GetEncryptionConfiguration",
                     "s3:GetLifecycleConfiguration",
                     "s3:PutObject",
                     "s3:DeleteObjectVersion",
                     "s3:GetBucketLocation",
                     "s3:GetBucketNotification",
                     "s3:GetObjectVersion",
                     "s3:GetObjectVersionTagging",
                     "s3:ListBucket",
                     "s3:ListBucketVersions",
                     "s3:ReplicateObject",
                     "s3:GetBucketObjectLockConfiguration",
                     "s3:GetBucketPolicy",
                     "s3:GetBucketVersioning",
                     "s3:ListBucketMultipartUploads",
                     "s3:DeleteObject",
                     "s3:GetObjectRetention",
                     "s3:GetObjectTagging",
                     "s3:GetObjectVersionForReplication",
                     "s3:ListMultipartUploadParts",
                     "s3:PutObjectRetention",
                     "s3:AbortMultipartUpload",
                     "s3:GetBucketTagging",
                     "s3:GetObject"
                 ],
                 "Resource": [
                     "arn:aws:s3:::gnuherder.how",
                     "arn:aws:s3:::gnuherder.how/*"
                 ]
             }
         ]
      }
      
    • Create the user and assign them to the group with this policy

  3. Install the mc utility

  4. Add an alias for your minio cluster mc alias set minio {{url to minio cluster}} {{username}} '{{password}}'
  5. Set the bucket policy to download to allow public downloads mc policy set download minio/gnuherder.how (again, replacing gnuherder.how with the name of your bucket)
  6. Upload the content to the bucket. I use the the aws cli to do so but, I'm not going to cover how to configure. I run AWS_PROFILE=rgw aws --endpoint-url http://minio.tobolaski.lan:9000 s3 sync --delete public/ s3://gnuherder.how/ where the AWS_PROFILE is the name of the profile I have configured with the minio credentials, --endpoint-url is the host where my minio cluster is running, public/ is the source directory of the content and s3://gnuherder.how/ is the bucket to put the content into.
  7. Now you'll need to have a reverse proxy in order to serve the content at the public dns address, i.e. gnuherder.how. I use nginx for this, this is my configuration:

    server {
        listen 0.0.0.0:443 http2 ssl ;
        listen [::0]:443 http2 ssl ;
        server_name gnuherder.how ;
        # Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx)
        # We use ^~ here, so that we don't check any regexes (which could
        # otherwise easily override this intended match accidentally).
        location ^~ /.well-known/acme-challenge/ {
            root /var/lib/acme/acme-challenge;
            auth_basic off;
        }
        ssl_certificate /var/lib/acme/gnuherder.how/fullchain.pem;
        ssl_certificate_key /var/lib/acme/gnuherder.how/key.pem;
        ssl_trusted_certificate /var/lib/acme/gnuherder.how/chain.pem;
        location / {
            rewrite ^/$ /index.html last;
            rewrite ^(.*)/$ $1/index.html last;
            proxy_set_header HOST object-store.net.tobolaski.com;
            proxy_set_header CONNECTION '';
            proxy_set_header AUTHORIZATION '';
            proxy_hide_header Set-Cookie;
            proxy_hide_header 'Access-Control-Allow-Origin';
            proxy_hide_header 'Access-Control-Allow-Methods';
            proxy_hide_header 'Access-Control-Allow-Headers';
            proxy_hide_header x-amz-id-2;
            proxy_hide_header x-amz-request-id;
            proxy_hide_header x-amz-meta-server-side-encryption;
            proxy_hide_header x-amz-server-side-encryption;
            proxy_hide_header x-amz-bucket-region;
            proxy_hide_header x-amzn-requestid;
            proxy_ignore_headers Set-Cookie;
            proxy_pass https://object-store.net.tobolaski.com:9001/gnuherder.how/;
        }
        proxy_buffering off;
    }
    

    The rewrite line is to handle going from url that end with / to /index.html which is how the files are stored in minio. It also removes a number of cookies that minio will add to the response which are not required for people to use the site. Be sure to do an nginx -t in order to test the configuration and then a systemd reload nginx to make the server active.

    One gotcha that I ran into was that nginx does not use the system's dns settings. In order to resolve a hostname to proxy to, you need to have resolver x.x.x.x; (where x.x.x.x is an ip address) set in a block. I have it set in the http block as I want it to be used for everything.

Note 2023-06-12

This site is no longer a static site running out of Minio. This was the last configuration I used before switching. It will likely work for quite a while but, eventually, some configuration option will change and it will stop working.

Discussion

Return to blog