[Support] Linuxserver.io - SWAG - Secure Web Application Gateway (Nginx/PHP/Certbot/Fail2ban)


Recommended Posts

Are there any guides or tutorials around on how to have Letsencrypt interact with my other dockers on unraid?

 

I understand the general concept behind Letsencrypt, but I'm not sure what files need to be modified, and how to modify these files.

 

My current setup is your standard dynamic IP address provided by my ISP.  I have this tracked by duckdns so I can associated the IP with the static name.  I'd like to be able to attach to all of my different dockers through https:

 

https://insertname.duckdns.org:2020 - Docker 1c

https://insertname.duckdns.org:3030 - Docker 2

https://insertname.duckdns.org:4040 - Docker 3

 

A few of the dockers I run now are:

 

crashplan

owncloud

plex

plexpy

plexrequests

couchpotato

sonarr

 

Any fingers to point me in the right direction would be greatly appreciated :)

 

You'll have to sort out the SSL on each of the containers you want behind the reverse proxy.  If all the services are on the same Unraid box using https between them is unnecessary imo.  Searching this thread....

 

Thanks for your help!

 

Sorry for being thick :)

 

Managed to get CouchPotato working using the default one and muximux as the homepage but can't seem to get other apps working

 

The apps I wanted were

 

Sonarr

PlexPy

PlexRequests

NetData

Deluge

 

I've tried the PlexPy command and getting a 404 error, I think this might be down to my plexpy settings though

 

# Sonar
# https://github.com/linuxserver/docker-sonarr
#
# Edit the settings and set 
# Url Base to /sonarr

location ^~ /sonarr {
    proxy_pass http://192.168.1.28:8989/sonarr;
    include /config/nginx/proxy.conf;
}

 

# PlexyPy
# https://github.com/linuxserver/docker-plexpy
#
# Settings => Web Interface
# Change http root to /plexpy
#

location ^~ /plexpy/ {
    proxy_pass http://192.168.1.28:8181;
    include /config/nginx/proxy.conf;
    proxy_bind $server_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_set_header X-Forwarded-Ssl     on;
}

 

# PlexRequests
# https://github.com/linuxserver/docker-plexrequests
#
# Run container with -e "URL_BASE"="/plexrequests"
#

location ^~ /requests {
    proxy_pass http://192.168.1.28:3000/requests;
    include /config/nginx/proxy.conf;
}

 

# Deluge
# https://github.com/linuxserver/docker-deluge
#
# No extra settings required
#

location ^~ /deluge {
    proxy_pass http://192.168.1.28:8112/;
    proxy_set_header  X-Deluge-Base "/deluge/";
    include /config/nginx/proxy.conf;
}

 

Netdata i've never heard of, you'll have to figure that one out yourself I'm afraid.

 

and looking up a few posts.

 

Just got this docker setup for my domain, real simple thanks guys. 

 

However, I have no experience with nginx (coming from Apache docker).  Can someone point me to a good reference for how to configure this docker to redirect say my requests.domain.com to my PlexRequests docker?

 

Save this as requests in the same folder as default.

 

server {
       listen         80;
       server_name    requests.server.com;
       return         301 https://$server_name$request_uri;
}

server {
listen 443 ssl;
server_name requests.server.com;

root /config/www;
index index.html index.htm index.php;

###SSL Certificates
ssl_certificate /config/keys/letsencrypt/fullchain.pem;
ssl_certificate_key /config/keys/letsencrypt/privkey.pem;

###Diffie–Hellman key exchange ###
ssl_dhparam /config/nginx/dhparams.pem;

###SSL Ciphers
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

###Extra Settings###
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

        ### Add HTTP Strict Transport Security ###
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header Front-End-Https on;

client_max_body_size 0;

location / {
    	proxy_pass https://192.168.0.1:3000/;
  }
}

 

Alternatively, paste this into default to access plexrequests at server.com/requests (You will need to set the URLBASE to /requests)

 

	location /requests {
	proxy_pass http://192.168.0.1:3000/requests;
	include /config/nginx/proxy.conf;
}

 

Obviously for both you'll need to change the IP address +/- port

 

We don't have an Owncloud container, but in our nextcloud support thread there are some links in the first post.

 

For Plex

 

	location /plex {
	proxy_pass http://192.168.0.1:32400/web;
	include /config/nginx/proxy.conf;
}

location /web {
	proxy_pass http://192.168.0.1:32400/web;
	include /config/nginx/proxy.conf;
}

Link to comment

Thanks for the reply CHBMB.

 

I did a quick search earlier and saw those lines of code.  The post was looking to see if there was more of an all encompassing type of guide on how to do this. 

 

What file am I modifying with those lines of code?  Is it some sort of .conf file within Letsencrypt?

 

And I'm not sure I follow what you're saying about it being unnecessary.  All of the dockers are on the same unraid box.  Why would using https be unnecessary if I'm trying to reach my unraid box from outside of my network?  Maybe I'm misunderstanding some basic security concepts here...

Link to comment

The file to edit is default in the /mnt/cache/nginx folder.

 

All the SSL to handle the outside connections is handled by this container, the communication between this container and those you want to proxy happens, in your case, all on your Unraid box.

 

You may want to create a .htpasswd file to password protect some of those apps.

 

Sent from my LG-H815 using Tapatalk

 

 

Link to comment

Place the .htpasswd file where it states in the configs I posted.  It adds a user & password to whatever you proxy, so this is done at the level of nginx rather than whatever app you're proxying.  Some apps, like Nextcloud and Plex I would still use the default app username and password with, as it integrates better and is implemented well.  But for things like Couch or Sonarr, personally I would trust SSL and nginx security way more than I would whatever they implement.

 

Sent from my LG-H815 using Tapatalk

 

 

Link to comment

I certainly have a very rudimentary understanding of how these dockers interact as I'm still quite confused as to what needs to be modified, and how.

 

For instance, if I simply want Plexypy to run through https I would:

 

1. Modify the nginx.conf by copy and pasting the following:

 

# PlexyPy

# https://github.com/linuxserver/docker-plexpy

#

# Settings => Web Interface

# Change http root to /plexpy

#

 

location ^~ /plexpy/ {

    proxy_pass http://192.168.1.28:8181;

    include /config/nginx/proxy.conf;

    proxy_bind $server_addr;

    proxy_set_header X-Forwarded-Host $server_name;

    proxy_set_header X-Forwarded-Ssl    on;

}

 

Then I'd also have to place an .htpasswd file in ~/plexpy/.  But what are the contents of this .htpasswd file?

 

It looks like letsencrypt is a bit more involved then some of your more basic plug and play apps.

 

My goal is to be able to interact with these dockers securely from outside of my network.  So to do that, I've opened each ones respective port and assumed that as long as I have https running on each open port, I should be good to go? 

 

Maybe if there's some basic primer on how this stuff works, if someone could point me in the right direction I could do some of my own readings rather than having to ask all of these random questions and re ask them when they're answered at a level beyond my understanding  :-[

 

Link to comment

I certainly have a very rudimentary understanding of how these dockers interact as I'm still quite confused as to what needs to be modified, and how.

 

For instance, if I simply want Plexypy to run through https I would:

 

1. Modify the nginx.conf by copy and pasting the following:

 

# PlexyPy

# https://github.com/linuxserver/docker-plexpy

#

# Settings => Web Interface

# Change http root to /plexpy

#

 

location ^~ /plexpy/ {

    proxy_pass http://192.168.1.28:8181;

    include /config/nginx/proxy.conf;

    proxy_bind $server_addr;

    proxy_set_header X-Forwarded-Host $server_name;

    proxy_set_header X-Forwarded-Ssl    on;

}

 

Then I'd also have to place an .htpasswd file in ~/plexpy/.  But what are the contents of this .htpasswd file?

 

It looks like letsencrypt is a bit more involved then some of your more basic plug and play apps.

 

My goal is to be able to interact with these dockers securely from outside of my network.  So to do that, I've opened each ones respective port and assumed that as long as I have https running on each open port, I should be good to go? 

 

Maybe if there's some basic primer on how this stuff works, if someone could point me in the right direction I could do some of my own readings rather than having to ask all of these random questions and re ask them when they're answered at a level beyond my understanding  :-[

See the description at the following link for htpasswd, you just run a single command: https://hub.docker.com/r/linuxserver/letsencrypt/

Link to comment

I certainly have a very rudimentary understanding of how these dockers interact as I'm still quite confused as to what needs to be modified, and how.

 

For instance, if I simply want Plexypy to run through https I would:

 

1. Modify the nginx.conf by copy and pasting the following:

 

# PlexyPy

# https://github.com/linuxserver/docker-plexpy

#

# Settings => Web Interface

# Change http root to /plexpy

#

 

location ^~ /plexpy/ {

    proxy_pass http://192.168.1.28:8181;

    include /config/nginx/proxy.conf;

    proxy_bind $server_addr;

    proxy_set_header X-Forwarded-Host $server_name;

    proxy_set_header X-Forwarded-Ssl    on;

}

 

Let's look at the above as an example.

 

location ^~ /plexpy/

Tells the webserver that this will be located at the server.com/plexpy address

 

 proxy_pass http://192.168.1.28:8181;
    include /config/nginx/proxy.conf;

Tells nginx where to find the plexpy app and to use the proxy settings in the proxy.conf file

 

 proxy_bind $server_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_set_header X-Forwarded-Ssl     on;

Tells plexpy to use some extra settings necessary to get this working.  Don't worry about these, app specific settings can often be found with a bit of googling though.

 

auth_basic "Restricted";
auth_basic_user_file /config/.htpasswd;

Now to add password protection, you need to add the above lines to that config but you also need to create a .htpasswd file.  There are instructions here in the readme or if you prefer you can use an online tool like this one.

 

RTnDohS.png

 

Copying the last line of chbmb:osQXeEdll4XFk to a file called .htpasswd and placing it in the /config folder (/mnt/user/appdata/nginx or something similar)  Note whatever text editor you use (NOT NOTEPAD ON WINDOWS) must use linux line endings.  If you want to use multiple users then just add a new line for each user.  So the linuxserver .htpasswd may end up looking like this.

 

username:hashedpassword  Obviously this example below doesn't have real hashed passwords in...  ;)

 

aptalca:weneedatokenyank
asshopo:notanotheryankwhokeepswaving
chbmb:theonlysaneone
danioj:britabroaddownunder
ironicbadger:gladbadgerbaitingisillegal
j0nnymoe:toocoolforschool
kode:phpninjamaster
saarg:tokenswedishguy
smdion:wethinkhesstillalivebutnotreallysure
sparklyballs:grumpyoldman
stark:anyexcusetowearleather
squid:yumyumcouldbedinner
xe:themannoonereallyknowsexists
stark:anyexcusetowearleather

 

So final "block" looks like this:

 

location ^~ /plexpy/ {
    proxy_pass http://192.168.1.28:8181;
    include /config/nginx/proxy.conf;
    proxy_bind $server_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_set_header X-Forwarded-Ssl     on;
    auth_basic "Restricted";
    auth_basic_user_file /config/.htpasswd;
}

 

 

Then I'd also have to place an .htpasswd file in ~/plexpy/.  But what are the contents of this .htpasswd file?

 

No, you'd place .htpasswd in the /config folder.  As shown in the example above.

 

It looks like letsencrypt is a bit more involved then some of your more basic plug and play apps.

 

Yes it is, has more functionality, so is more complicated, essentially it's a pretty much fully blown webserver, rather than a single app that runs on a webserver.

 

My goal is to be able to interact with these dockers securely from outside of my network.  So to do that, I've opened each ones respective port and assumed that as long as I have https running on each open port, I should be good to go? 

 

That's achievable and what this docker is designed for.  Once you've done one or two apps, it kind of click.  However you've got the above bit a little wrong.  The whole point of this is so you DON'T open a ton of ports on your router.  Just 80 & 443 both forwarded to your Unraid box.  Then all communication with any app is proxied through the nginx container.  The config out of the box will automatically forward all port 80 requests to 443, so all communication actually takes place over https and is encrypted.  Now go and close those ports again!  ;)

 

Maybe if there's some basic primer on how this stuff works, if someone could point me in the right direction I could do some of my own readings rather than having to ask all of these random questions and re ask them when they're answered at a level beyond my understanding  :-[

 

It's difficult to point at one single resource.  We're working on something to try and make this a bit easier but, don't worry, we'll get you going.  Just bear with it.

  • Upvote 1
Link to comment

aptalca:weneedatokenyank
asshopo:notanotheryankwhokeepswaving
chbmb:theonlyinsaneone
danioj:britabroaddownunder
ironicbadger:gladbadgerbaitingisillegal
j0nnymoe:toocoolforschool
kode:phpninjamaster
saarg:tokenswedishguy
smdion:wethinkhesstillalivebutnotreallysure
sparklyballs:grumpyoldman
stark:anyexcusetowearleather
squid:yumyumcouldbedinner
xe:themannoonereallyknowsexists
stark:anyexcusetowearleather

;D ;D ;D

I'm dyin' here!

;D ;D ;D

 

Serious question, which I'm sure the internet knows but I'm too lazy to ask, what happens if you have 2 identical user names with different hashes? Do they both work? Neither? Only 1st listed? Only last?

Link to comment

;D

 

Dunno, lemme check....  :-\

 

EDIT: Only my first entry in .htpasswd worked.

Thanks! Teh more you know...  :)

 

Cheeky bugger! Just spotted what you've done!  ;D

 

chbmb:theonlyinsaneone

;D ;D ;D

I'm dyin' here!

;D ;D ;D

 

Serious question, which I'm sure the internet knows but I'm too lazy to ask, what happens if you have 2 identical user names with different hashes? Do they both work? Neither? Only 1st listed? Only last?

Link to comment

Holy smokes.  Thanks CHBMB for taking the time to write that out.  I'll have time later this evening to thoroughly read through and attempt to apply what you've said.  I think I have a better understanding from a brief glance.  This is supposed to make the system more secure by limiting the amount of ports I'm opening.

 

Post #521373...

https://lime-technology.com/forum/index.php?topic=53542.msg521373#msg521373

...should be a go-to post for super n00b questions like my own.

 

  • Like 1
Link to comment

Holy smokes.  Thanks CHBMB for taking the time to write that out.  I'll have time later this evening to thoroughly read through and attempt to apply what you've said.  I think I have a better understanding from a brief glance.  This is supposed to make the system more secure by limiting the amount of ports I'm opening.

 

Post #521373...

https://lime-technology.com/forum/index.php?topic=53542.msg521373#msg521373

...should be a go-to post for super n00b questions like my own.

 

No problem, like I said, we'll get you there.

Link to comment

aptalca:weneedatokenyank
asshopo:notanotheryankwhokeepswaving
chbmb:theonlyinsaneone
danioj:britabroaddownunder
ironicbadger:gladbadgerbaitingisillegal
j0nnymoe:toocoolforschool
kode:phpninjamaster
saarg:tokenswedishguy
smdion:wethinkhesstillalivebutnotreallysure
sparklyballs:grumpyoldman
stark:anyexcusetowearleather
squid:yumyumcouldbedinner
xe:themannoonereallyknowsexists
stark:anyexcusetowearleather

;D ;D ;D

I'm dyin' here!

;D ;D ;D

 

Serious question, which I'm sure the internet knows but I'm too lazy to ask, what happens if you have 2 identical user names with different hashes? Do they both work? Neither? Only 1st listed? Only last?

 

I AM NOT SWEDISH!! >:( >:( :'(

Link to comment

aptalca:weneedatokenyank
asshopo:notanotheryankwhokeepswaving
chbmb:theonlyinsaneone
danioj:britabroaddownunder
ironicbadger:gladbadgerbaitingisillegal
j0nnymoe:toocoolforschool
kode:phpninjamaster
saarg:tokenswedishguy
smdion:wethinkhesstillalivebutnotreallysure
sparklyballs:grumpyoldman
stark:anyexcusetowearleather
squid:yumyumcouldbedinner
xe:themannoonereallyknowsexists
stark:anyexcusetowearleather

;D ;D ;D

I'm dyin' here!

;D ;D ;D

 

Serious question, which I'm sure the internet knows but I'm too lazy to ask, what happens if you have 2 identical user names with different hashes? Do they both work? Neither? Only 1st listed? Only last?

 

I AM NOT SWEDISH!! >:( >:( :'(

 

hurdy gurdy

Link to comment

aptalca:weneedatokenyank
asshopo:notanotheryankwhokeepswaving
chbmb:theonlyinsaneone
danioj:britabroaddownunder
ironicbadger:gladbadgerbaitingisillegal
j0nnymoe:toocoolforschool
kode:phpninjamaster
saarg:tokenswedishguy
smdion:wethinkhesstillalivebutnotreallysure
sparklyballs:grumpyoldman
stark:anyexcusetowearleather
squid:yumyumcouldbedinner
xe:themannoonereallyknowsexists
stark:anyexcusetowearleather

;D ;D ;D

I'm dyin' here!

;D ;D ;D

 

Serious question, which I'm sure the internet knows but I'm too lazy to ask, what happens if you have 2 identical user names with different hashes? Do they both work? Neither? Only 1st listed? Only last?

 

I AM NOT SWEDISH!! >:( >:( :'(

 

hurdy gurdy

 

Watch your backs guys... You know I'm in Liverpool this weekend....  8)

Link to comment

aptalca:weneedatokenyank
asshopo:notanotheryankwhokeepswaving
chbmb:theonlyinsaneone
danioj:britabroaddownunder
ironicbadger:gladbadgerbaitingisillegal
j0nnymoe:toocoolforschool
kode:phpninjamaster
saarg:tokenswedishguy
smdion:wethinkhesstillalivebutnotreallysure
sparklyballs:grumpyoldman
stark:anyexcusetowearleather
squid:yumyumcouldbedinner
xe:themannoonereallyknowsexists
stark:anyexcusetowearleather

;D ;D ;D

I'm dyin' here!

;D ;D ;D

 

Serious question, which I'm sure the internet knows but I'm too lazy to ask, what happens if you have 2 identical user names with different hashes? Do they both work? Neither? Only 1st listed? Only last?

 

I AM NOT SWEDISH!! >:( >:( :'(

 

hurdy gurdy

 

Watch your backs guys... You know I'm in Liverpool this weekend....  8)

 

cool, i was in liverpool last weekend, one of the christmas market stalls was selling swedish fish...

Link to comment

aptalca:weneedatokenyank
asshopo:notanotheryankwhokeepswaving
chbmb:theonlyinsaneone
danioj:britabroaddownunder
ironicbadger:gladbadgerbaitingisillegal
j0nnymoe:toocoolforschool
kode:phpninjamaster
saarg:tokenswedishguy
smdion:wethinkhesstillalivebutnotreallysure
sparklyballs:grumpyoldman
stark:anyexcusetowearleather
squid:yumyumcouldbedinner
xe:themannoonereallyknowsexists
stark:anyexcusetowearleather

;D ;D ;D

I'm dyin' here!

;D ;D ;D

 

Serious question, which I'm sure the internet knows but I'm too lazy to ask, what happens if you have 2 identical user names with different hashes? Do they both work? Neither? Only 1st listed? Only last?

 

I AM NOT SWEDISH!! >:( >:( :'(

 

hurdy gurdy

 

Watch your backs guys... You know I'm in Liverpool this weekend....  8)

 

cool, i was in liverpool last weekend, one of the christmas market stalls was selling swedish fish...

 

I'm so disappointed now....  :-\ No Christmas cards for you guys! No pøpcørn for you either  ;D

Link to comment

So I think I have everything right on the Letsencrypt end, as myserver.com/plexpy is getting a the green lock on chrome for https acceptance, but it's taking me to a 404 not found screen.

 

When my plexpy settings are this:

 

2fb36a3552cc4b959bd5096a06e490df.png

 

I get this:

 

2bbb4206571c43f39ae5894b726e398d.png

 

Inside my nginix/site-confs/default my code is:

location ^~ /plexpy {
	proxy_pass http://192.168.1.3:8181;
	include /config/nginx/proxy.conf;
	proxy_bind $server_addr;
	proxy_set_header X-Forwarded-Host $server_name;
	proxy_set_header X-Forwarded-Ssl     on;
	auth_basic "Restricted";
	auth_basic_user_file /config/.htpasswd;

 

And on check, if I go to http://192.168.1.3:8181 I get taken directly to the plexpy  home.

 

Any ideas where I'm going wrong?  I think I'm close!

Link to comment

So I think I have everything right on the Letsencrypt end, as myserver.com/plexpy is getting a the green lock on chrome for https acceptance, but it's taking me to a 404 not found screen.

 

When my plexpy settings are this:

 

2fb36a3552cc4b959bd5096a06e490df.png

 

I get this:

 

2bbb4206571c43f39ae5894b726e398d.png

 

Inside my nginix/site-confs/default my code is:

location ^~ /plexpy {
	proxy_pass http://192.168.1.3:8181;
	include /config/nginx/proxy.conf;
	proxy_bind $server_addr;
	proxy_set_header X-Forwarded-Host $server_name;
	proxy_set_header X-Forwarded-Ssl     on;
	auth_basic "Restricted";
	auth_basic_user_file /config/.htpasswd;

 

And on check, if I go to http://192.168.1.3:8181 I get taken directly to the plexpy  home.

 

Any ideas where I'm going wrong?  I think I'm close!

 

Didn't follow the instructions  ;)

 

Change http root to /plexpy

 

# PlexyPy
# https://github.com/linuxserver/docker-plexpy
#
# Settings => Web Interface
# Change http root to /plexpy
#

location ^~ /plexpy/ {
    proxy_pass http://192.168.1.28:8181;
    include /config/nginx/proxy.conf;
    proxy_bind $server_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_set_header X-Forwarded-Ssl     on;
}

Link to comment

Ahh, the comments!  Always miss something.

 

However, now I have a new error:

 

when I type in https://myserver.com/plexpy it redirects me to https://_/plexpy/auth/logout and I get this message.

 

 

Looking at the config you posted you're missing a trailing slash.

 

location ^~ /plexpy {

 

vs

 

location ^~ /plexpy/ {

 

Also, can you actually hit https://www.server.com as it looks like there's no DNS record for it.

 

Link to comment

Going to https://server.com brings me to the index.html page created by letsencrypt:

 

cc1659be923047b787c9f40b06983782.png

 

I added the trailing slash:

 

location ^~ /plexpy/ {
	proxy_pass http://192.168.1.3:8181;
	include /config/nginx/proxy.conf;
	proxy_bind $server_addr;
	proxy_set_header X-Forwarded-Host $server_name;
	proxy_set_header X-Forwarded-Ssl     on;
	auth_basic "Restricted";
	auth_basic_user_file /config/.htpasswd;

 

I'm still brought to:

5eb6dc2c63074348ad88047e62c3a507.png

 

I've also noticed, once I make those two changes to the settings, to enable the proxy and add /plexpy, I can no longer access Plexpy through the web GUI.

 

EDIT: Just ran a quick test.  The checkbox for "Enable for HTTP Proxy" works.  I can check this and restart without an issue.  It's once I add /plexpy and restart that I can no longer access the GUI.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.