[release] v0.8.3
This commit is contained in:
parent
e874e53cdb
commit
a4c7eded55
10
changelog.md
10
changelog.md
|
@ -1,7 +1,11 @@
|
|||
## version 0.8.1 -> 0.8.2
|
||||
## version 0.8.1 -> 0.8.3
|
||||
- Added new automatic Docker mapping feature (for people not using (sub)domains)
|
||||
- App store image size issue
|
||||
- Add installer hostname prefix/suffix
|
||||
- Fix issue with inconsistent password when installing from the market
|
||||
- Add installer option for hostname prefix/suffix
|
||||
- Fix minor issue with inconsistent password on market installer
|
||||
- Fixed issue where home page was https:// links on http only servers
|
||||
- Improved setup flow for setting up hostname and HTTPS
|
||||
- Added a new port range system for people not using a domain name
|
||||
|
||||
## Version 0.8.0
|
||||
- Custmizable homepage / theme colors
|
||||
|
|
|
@ -337,7 +337,7 @@ const ConfigManagement = () => {
|
|||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Stack spacing={1}>
|
||||
<InputLabel htmlFor="Hostname-login">Hostname: This will be used to restrict access to your Cosmos Server (Default: 0.0.0.0)</InputLabel>
|
||||
<InputLabel htmlFor="Hostname-login">Hostname: This will be used to restrict access to your Cosmos Server (Your IP, or your domain name)</InputLabel>
|
||||
<OutlinedInput
|
||||
id="Hostname-login"
|
||||
type="text"
|
||||
|
|
|
@ -240,7 +240,7 @@ const HomePage = () => {
|
|||
|
||||
{coStatus && coStatus.domain && (
|
||||
<Alert severity="error">
|
||||
You are using localhost or 0.0.0.0 as a hostname in the configuration. It is recommended that you use a domain name instead.
|
||||
You are using localhost or 0.0.0.0 as a hostname in the configuration. It is recommended that you use a domain name or an IP instead.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ const debounce = (func, wait) => {
|
|||
}
|
||||
}, 500)
|
||||
|
||||
const hostnameIsDomainReg = /^((?!localhost|\d+\.\d+\.\d+\.\d+)[a-zA-Z0-9\-]{1,63}\.)+[a-zA-Z]{2,63}$/
|
||||
|
||||
const NewInstall = () => {
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const [status, setStatus] = useState(null);
|
||||
|
@ -83,6 +85,29 @@ const NewInstall = () => {
|
|||
}
|
||||
}, [activeStep, status]);
|
||||
|
||||
const getHTTPSOptions = (hostname) => {
|
||||
if(!hostname) {
|
||||
return [["", "Set your hostname first"]];
|
||||
}
|
||||
|
||||
if(hostname.match(hostnameIsDomainReg)) {
|
||||
return [
|
||||
["", "Select an option"],
|
||||
["LETSENCRYPT", "Use Let's Encrypt automatic HTTPS (recommended)"],
|
||||
["SELFSIGNED", "Generate self-signed certificate"],
|
||||
["PROVIDED", "Supply my own HTTPS certificate"],
|
||||
["DISABLED", "Use HTTP only (not recommended)"],
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
["", "Select an option"],
|
||||
["SELFSIGNED", "Generate self-signed certificate (recommended)"],
|
||||
["PROVIDED", "Supply my own HTTPS certificate"],
|
||||
["DISABLED", "Use HTTP only (not recommended)"],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
label: 'Welcome! 💖',
|
||||
|
@ -252,26 +277,17 @@ const NewInstall = () => {
|
|||
component: (<Stack item xs={12} spacing={2}>
|
||||
<div>
|
||||
<QuestionCircleOutlined /> It is recommended to use Let's Encrypt to automatically provide HTTPS Certificates.
|
||||
This requires a valid domain name pointing to this server. If you don't have one, you can use a self-signed certificate.
|
||||
This requires a valid domain name pointing to this server. If you don't have one, <strong>you can select "Generate self-signed certificate" in the dropdown. </strong>
|
||||
If you enable HTTPS, it will be effective after the next restart.
|
||||
</div>
|
||||
<div>
|
||||
{status && <>
|
||||
<div>
|
||||
HTTPS Certificate Mode is currently: <b>{status.HTTPSCertificateMode}</b>
|
||||
</div>
|
||||
<div>
|
||||
Hostname is currently: <b>{status.hostname}</b>
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
<div>
|
||||
<Formik
|
||||
initialValues={{
|
||||
HTTPSCertificateMode: "LETSENCRYPT",
|
||||
HTTPSCertificateMode: "",
|
||||
UseWildcardCertificate: false,
|
||||
DNSChallengeProvider: '',
|
||||
DNSChallengeConfig: {},
|
||||
__success: false,
|
||||
}}
|
||||
validationSchema={Yup.object().shape({
|
||||
SSLEmail: Yup.string().when('HTTPSCertificateMode', {
|
||||
|
@ -289,7 +305,7 @@ const NewInstall = () => {
|
|||
}),
|
||||
Hostname: Yup.string().when('HTTPSCertificateMode', {
|
||||
is: "LETSENCRYPT",
|
||||
then: Yup.string().required().matches(/^((?!localhost|\d+\.\d+\.\d+\.\d+)[a-zA-Z0-9\-]{1,63}\.)+[a-zA-Z]{2,63}$/, 'Let\'s Encrypt only accepts domain names'),
|
||||
then: Yup.string().required().matches(hostnameIsDomainReg, 'Let\'s Encrypt only accepts domain names'),
|
||||
otherwise: Yup.string().required()
|
||||
}),
|
||||
})}
|
||||
|
@ -310,7 +326,9 @@ const NewInstall = () => {
|
|||
if(res.status == "OK") {
|
||||
setStatus({ success: true });
|
||||
setHostname((values.HTTPSCertificateMode == "DISABLED" ? "http://" : "https://") + values.Hostname);
|
||||
setActiveStep(4);
|
||||
}
|
||||
return res;
|
||||
} catch (error) {
|
||||
setStatus({ success: false });
|
||||
setErrors({ submit: "Please check you have filled all the inputs properly" });
|
||||
|
@ -320,16 +338,31 @@ const NewInstall = () => {
|
|||
{(formik) => (
|
||||
<form noValidate onSubmit={formik.handleSubmit}>
|
||||
<Stack item xs={12} spacing={2}>
|
||||
<CosmosInputText
|
||||
name="Hostname"
|
||||
label="Hostname (Domain required for Let's Encrypt)"
|
||||
placeholder="yourdomain.com, your ip, or localhost"
|
||||
formik={formik}
|
||||
onChange={(e) => {
|
||||
checkHost(e.target.value, setHostError, setHostIp);
|
||||
}}
|
||||
/>
|
||||
{formik.values.Hostname && (formik.values.Hostname.match(hostnameIsDomainReg) ?
|
||||
<Alert severity="info">
|
||||
You seem to be using a domain name. <br />
|
||||
Let's Encrypt can automatically generate a certificate for you.
|
||||
</Alert>
|
||||
:
|
||||
<Alert severity="info">
|
||||
You seem to be using an IP address or local domain. <br />
|
||||
You can use automatic Self-Signed certificates.
|
||||
</Alert>)
|
||||
}
|
||||
<CosmosSelect
|
||||
name="HTTPSCertificateMode"
|
||||
label="Select your choice"
|
||||
formik={formik}
|
||||
options={[
|
||||
["LETSENCRYPT", "Use Let's Encrypt automatic HTTPS (recommended)"],
|
||||
["PROVIDED", "Supply my own HTTPS certificate"],
|
||||
["SELFSIGNED", "Generate a self-signed certificate"],
|
||||
["DISABLED", "Use HTTP only (not recommended)"],
|
||||
]}
|
||||
options={getHTTPSOptions(formik.values.Hostname && formik.values.Hostname)}
|
||||
/>
|
||||
{formik.values.HTTPSCertificateMode === "LETSENCRYPT" && (
|
||||
<>
|
||||
|
@ -378,15 +411,6 @@ const NewInstall = () => {
|
|||
</>
|
||||
)}
|
||||
|
||||
<CosmosInputText
|
||||
name="Hostname"
|
||||
label="Hostname (Domain required for Let's Encrypt)"
|
||||
placeholder="yourdomain.com, your ip, or localhost"
|
||||
formik={formik}
|
||||
onChange={(e) => {
|
||||
checkHost(e.target.value, setHostError, setHostIp);
|
||||
}}
|
||||
/>
|
||||
{hostError && <Grid item xs={12}>
|
||||
<Alert color='error'>{hostError}</Alert>
|
||||
</Grid>}
|
||||
|
@ -401,11 +425,12 @@ const NewInstall = () => {
|
|||
</Alert>
|
||||
)}
|
||||
|
||||
{(formik.values.HTTPSCertificateMode === "LETSENCRYPT" || formik.values.HTTPSCertificateMode === "SELFSIGNED") && formik.values.Hostname && formik.values.Hostname.match(hostnameIsDomainReg) && (
|
||||
<CosmosCheckbox
|
||||
label={"Use Wildcard Certificate for *." + (formik.values.Hostname || "")}
|
||||
name="UseWildcardCertificate"
|
||||
formik={formik}
|
||||
/>
|
||||
/>)}
|
||||
|
||||
{formik.errors.submit && (
|
||||
<Grid item xs={12}>
|
||||
|
@ -432,7 +457,7 @@ const NewInstall = () => {
|
|||
</div>
|
||||
</Stack>),
|
||||
nextButtonLabel: () => {
|
||||
return status ? 'Next' : 'Skip';
|
||||
return (status && status.hostname != '0.0.0.0') ? 'Next' : '';
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -32,7 +32,7 @@ import DockerContainerSetup from './setup';
|
|||
import whiskers from 'whiskers';
|
||||
import {version} from '../../../../../package.json';
|
||||
import cmp from 'semver-compare';
|
||||
import { HostnameChecker } from '../../../utils/routes';
|
||||
import { HostnameChecker, getHostnameFromName } from '../../../utils/routes';
|
||||
import { CosmosContainerPicker } from '../../config/users/containerPicker';
|
||||
import { randomString } from '../../../utils/indexs';
|
||||
|
||||
|
@ -76,10 +76,6 @@ const isNewerVersion = (minver) => {
|
|||
return cmp(version, minver) === -1;
|
||||
}
|
||||
|
||||
const getHostnameFromName = (name) => {
|
||||
return name.replace('/', '').replace(/_/g, '-').replace(/[^a-zA-Z0-9-]/g, '').toLowerCase().replace(/\s/g, '-') + '.' + window.location.origin.split('://')[1]
|
||||
}
|
||||
|
||||
const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaultName }) => {
|
||||
const cleanDefaultName = defaultName && defaultName.replace(/\s/g, '-').replace(/[^a-zA-Z0-9-]/g, '');
|
||||
const [step, setStep] = useState(0);
|
||||
|
@ -94,6 +90,19 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
const [context, setContext] = useState({});
|
||||
const [installer, setInstaller] = useState(installerInit);
|
||||
const [config, setConfig] = useState({});
|
||||
|
||||
let hostnameErrors = () => {
|
||||
let broken = false;
|
||||
Object.values(hostnames).forEach((service) => {
|
||||
Object.values(service).forEach((route) => {
|
||||
if(!route.host || route.host.match(/\s/g)) {
|
||||
broken = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
return broken;
|
||||
}
|
||||
|
||||
const [passwords, setPasswords] = useState([
|
||||
randomString(24),
|
||||
randomString(24),
|
||||
|
@ -369,7 +378,7 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
if (route.useHost) {
|
||||
let newRoute = Object.assign({}, route);
|
||||
if (route.useHost === true) {
|
||||
newRoute.host = (newRoute.hostPrefix || '') + getHostnameFromName(key + (routeId > 0 ? '-' + routeId : '')) + (newRoute.hostSuffix || '');
|
||||
newRoute.host = getHostnameFromName(key + (routeId > 0 ? '-' + routeId : ''), newRoute, config);
|
||||
}
|
||||
|
||||
if(!newHostnames[key]) newHostnames[key] = {};
|
||||
|
@ -722,7 +731,7 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
setHostnames({});
|
||||
setOverrides({});
|
||||
}}>Close</Button>
|
||||
<Button disabled={!dockerCompose || ymlError} onClick={() => {
|
||||
<Button disabled={!dockerCompose || ymlError || hostnameErrors()} onClick={() => {
|
||||
if (step === 0) {
|
||||
setStep(1);
|
||||
} else {
|
||||
|
|
|
@ -4,7 +4,7 @@ import RestartModal from '../../config/users/restart';
|
|||
import { Alert, Button, Checkbox, Chip, Divider, Stack, useMediaQuery } from '@mui/material';
|
||||
import HostChip from '../../../components/hostChip';
|
||||
import { RouteMode, RouteSecurity } from '../../../components/routeComponents';
|
||||
import { getFaviconURL } from '../../../utils/routes';
|
||||
import { getFaviconURL, getHostnameFromName } from '../../../utils/routes';
|
||||
import * as API from '../../../api';
|
||||
import { ArrowLeftOutlined, ArrowRightOutlined, CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons";
|
||||
import IsLoggedIn from '../../../isLoggedIn';
|
||||
|
@ -20,13 +20,10 @@ import DockerTerminal from './terminal';
|
|||
import NewDockerService from './newService';
|
||||
import RouteManagement from '../../config/routes/routeman';
|
||||
|
||||
const getHostnameFromName = (name) => {
|
||||
return name.replace('/', '').replace(/_/g, '-').replace(/[^a-zA-Z0-9-]/g, '').toLowerCase().replace(/\s/g, '-') + '.' + window.location.origin.split('://')[1]
|
||||
}
|
||||
|
||||
const NewDockerServiceForm = () => {
|
||||
const [currentTab, setCurrentTab] = React.useState(0);
|
||||
const [maxTab, setMaxTab] = React.useState(0);
|
||||
const [config, setConfig] = React.useState(null);
|
||||
const [containerInfo, setContainerInfo] = React.useState({
|
||||
Name: '',
|
||||
Names: [],
|
||||
|
@ -55,6 +52,16 @@ const NewDockerServiceForm = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const refreshConfig = () => {
|
||||
API.config.get().then((res) => {
|
||||
setConfig(res.data);
|
||||
});
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
refreshConfig();
|
||||
}, []);
|
||||
|
||||
let service = {
|
||||
services: {
|
||||
container_name : {
|
||||
|
@ -182,7 +189,7 @@ const NewDockerServiceForm = () => {
|
|||
Name: containerInfo.Name.replace('/', ''),
|
||||
Description: "Expose " + containerInfo.Name.replace('/', '') + " to the internet",
|
||||
UseHost: true,
|
||||
Host: getHostnameFromName(containerInfo.Name),
|
||||
Host: getHostnameFromName(containerInfo.Name, null, config),
|
||||
UsePathPrefix: false,
|
||||
PathPrefix: '',
|
||||
CORSOrigin: '',
|
||||
|
|
|
@ -2,13 +2,9 @@ import React, { useState } from 'react';
|
|||
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Stack } from '@mui/material';
|
||||
import { Alert } from '@mui/material';
|
||||
import RouteManagement from '../config/routes/routeman';
|
||||
import { ValidateRoute, getFaviconURL, sanitizeRoute, getContainersRoutes } from '../../utils/routes';
|
||||
import { ValidateRoute, getFaviconURL, sanitizeRoute, getContainersRoutes, getHostnameFromName } from '../../utils/routes';
|
||||
import * as API from '../../api';
|
||||
|
||||
const getHostnameFromName = (name) => {
|
||||
return name.replace('/', '').replace(/_/g, '-').replace(/[^a-zA-Z0-9-]/g, '').toLowerCase().replace(/\s/g, '-') + '.' + window.location.origin.split('://')[1]
|
||||
}
|
||||
|
||||
const ExposeModal = ({ openModal, setOpenModal, config, updateRoutes, container }) => {
|
||||
const [submitErrors, setSubmitErrors] = useState([]);
|
||||
const [newRoute, setNewRoute] = useState(null);
|
||||
|
@ -36,7 +32,7 @@ const ExposeModal = ({ openModal, setOpenModal, config, updateRoutes, container
|
|||
Name: containerName.replace('/', ''),
|
||||
Description: "Expose " + containerName.replace('/', '') + " to the internet",
|
||||
UseHost: true,
|
||||
Host: getHostnameFromName(containerName),
|
||||
Host: getHostnameFromName(containerName, null, config),
|
||||
UsePathPrefix: false,
|
||||
PathPrefix: '',
|
||||
CORSOrigin: '',
|
||||
|
|
|
@ -37,6 +37,10 @@ const addProtocol = (url) => {
|
|||
if (url.indexOf("http://") === 0 || url.indexOf("https://") === 0) {
|
||||
return url;
|
||||
}
|
||||
// get current protocol
|
||||
if (window.location.protocol === "http:") {
|
||||
return "http://" + url;
|
||||
}
|
||||
return "https://" + url;
|
||||
}
|
||||
|
||||
|
@ -154,3 +158,35 @@ export const HostnameChecker = ({hostname}) => {
|
|||
{hostIp && <Alert color='info'>This hostname is pointing to <strong>{hostIp}</strong>, make sure it is your server IP!</Alert>}
|
||||
</>
|
||||
};
|
||||
|
||||
const hostnameIsDomainReg = /^((?!localhost|\d+\.\d+\.\d+\.\d+)[a-zA-Z0-9\-]{1,63}\.)+[a-zA-Z]{2,63}$/
|
||||
|
||||
export const getHostnameFromName = (name, route, config) => {
|
||||
let origin = window.location.origin.split('://')[1];
|
||||
let protocol = window.location.origin.split('://')[0];
|
||||
let res;
|
||||
|
||||
if(origin.match(hostnameIsDomainReg)) {
|
||||
res = name.replace('/', '').replace(/_/g, '-').replace(/[^a-zA-Z0-9-]/g, '').toLowerCase().replace(/\s/g, '-') + '.' + origin
|
||||
if(route)
|
||||
res = (route.hostPrefix || '') + res + (route.hostSuffix || '');
|
||||
} else {
|
||||
const existingRoutes = Object.values(config.HTTPConfig.ProxyConfig.Routes);
|
||||
origin = origin.split(':')[0];
|
||||
|
||||
// find first port available in range 7200-7400
|
||||
let port = protocol == "https" ? 7200 : 7351;
|
||||
let endPort = protocol == "https" ? 7350 : 7500;
|
||||
while(port < endPort) {
|
||||
if(!existingRoutes.find((exiroute) => exiroute.Host == (origin + ":" + port))) {
|
||||
res = origin + ":" + port;
|
||||
return res;
|
||||
}
|
||||
else
|
||||
port++;
|
||||
}
|
||||
|
||||
return "NO MORE PORT AVAILABLE. PLEASE CLEAN YOUR URLS!";
|
||||
}
|
||||
return res;
|
||||
}
|
169
src/docker/checkPorts.go
Normal file
169
src/docker/checkPorts.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
|
||||
"os"
|
||||
"net"
|
||||
"strings"
|
||||
"errors"
|
||||
"runtime"
|
||||
mountType "github.com/docker/docker/api/types/mount"
|
||||
)
|
||||
|
||||
func isPortAvailable(port string) bool {
|
||||
ln, err := net.Listen("tcp", ":"+port)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ln.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
func CheckPorts() error {
|
||||
utils.Log("Setup: Checking Docker port mapping ")
|
||||
expectedPorts := []string{}
|
||||
isHTTPS := utils.IsHTTPS
|
||||
config := utils.GetMainConfig()
|
||||
HTTPPort := config.HTTPConfig.HTTPPort
|
||||
HTTPSPort := config.HTTPConfig.HTTPSPort
|
||||
routes := config.HTTPConfig.ProxyConfig.Routes
|
||||
expectedPort := HTTPPort
|
||||
if isHTTPS {
|
||||
expectedPort = HTTPSPort
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
if route.UseHost && strings.Contains(route.Host, ":") {
|
||||
hostname := route.Host
|
||||
port := strings.Split(hostname, ":")[1]
|
||||
expectedPorts = append(expectedPorts, port)
|
||||
}
|
||||
}
|
||||
|
||||
// append hostname port
|
||||
hostname := config.HTTPConfig.Hostname
|
||||
if strings.Contains(hostname, ":") {
|
||||
hostnameport := strings.Split(hostname, ":")[1]
|
||||
expectedPorts = append(expectedPorts, hostnameport)
|
||||
}
|
||||
|
||||
errD := Connect()
|
||||
if errD != nil {
|
||||
return errD
|
||||
}
|
||||
|
||||
// Get container ID from HOSTNAME environment variable
|
||||
containerID := os.Getenv("HOSTNAME")
|
||||
if containerID == "" {
|
||||
utils.Warn("Not in docker. Skipping port auto-mapping")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Inspect the container
|
||||
inspect, err := DockerClient.ContainerInspect(DockerContext, containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the ports
|
||||
ports := map[string]struct{}{}
|
||||
for containerPort, hostConfig := range inspect.NetworkSettings.Ports {
|
||||
utils.Debug("Container port: " + containerPort.Port())
|
||||
|
||||
for _, hostPort := range hostConfig {
|
||||
utils.Debug("Host port: " + hostPort.HostPort)
|
||||
ports[hostPort.HostPort] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
finalPorts := []string{}
|
||||
|
||||
hasChanged := false
|
||||
|
||||
utils.Debug("Expected ports: " + strings.Join(expectedPorts, ", "))
|
||||
|
||||
for _, port := range expectedPorts {
|
||||
utils.Debug("Checking port : " + string(port))
|
||||
if _, ok := ports[port]; !ok {
|
||||
if !isPortAvailable(port) {
|
||||
utils.Error("Port "+port+" is added to a URL but it is not available. Skipping for now", nil)
|
||||
} else {
|
||||
utils.Debug("Port "+port+" is not mapped. Adding it.")
|
||||
finalPorts = append(finalPorts, port + ":" + expectedPort)
|
||||
hasChanged = true
|
||||
}
|
||||
} else {
|
||||
finalPorts = append(finalPorts, port + ":" + expectedPort)
|
||||
}
|
||||
}
|
||||
|
||||
if hasChanged {
|
||||
finalPorts = append(finalPorts, config.HTTPConfig.HTTPPort + ":" + config.HTTPConfig.HTTPPort)
|
||||
finalPorts = append(finalPorts, config.HTTPConfig.HTTPSPort + ":" + config.HTTPConfig.HTTPSPort)
|
||||
|
||||
utils.Log("Port mapping changed. Needs update.")
|
||||
utils.Log("New ports: " + strings.Join(finalPorts, ", "))
|
||||
|
||||
UpdatePorts(finalPorts)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func UpdatePorts(finalPorts []string) error {
|
||||
utils.Log("SelUpdatePorts - Starting...")
|
||||
|
||||
if os.Getenv("HOSTNAME") == "" {
|
||||
utils.Error("SelUpdatePorts - not using Docker", nil)
|
||||
return errors.New("SelUpdatePorts - not using Docker")
|
||||
}
|
||||
|
||||
// make sure to remove resiude of old self updater
|
||||
RemoveSelfUpdater()
|
||||
|
||||
containerName := os.Getenv("HOSTNAME")
|
||||
|
||||
version := "latest"
|
||||
|
||||
// if arm
|
||||
if runtime.GOARCH == "arm" {
|
||||
version = "latest-arm64"
|
||||
}
|
||||
|
||||
service := DockerServiceCreateRequest{
|
||||
Services: map[string]ContainerCreateRequestContainer {},
|
||||
}
|
||||
|
||||
service.Services["cosmos-self-updater-agent"] = ContainerCreateRequestContainer{
|
||||
Name: "cosmos-self-updater-agent",
|
||||
Image: "azukaar/docker-self-updater:" + version,
|
||||
RestartPolicy: "no",
|
||||
SecurityOpt: []string{
|
||||
"label:disable",
|
||||
},
|
||||
Environment: []string{
|
||||
"CONTAINER_NAME=" + containerName,
|
||||
"ACTION=ports",
|
||||
"DOCKER_HOST=" + os.Getenv("DOCKER_HOST"),
|
||||
"PORTS=" + strings.Join(finalPorts, ","),
|
||||
},
|
||||
Volumes: []mountType.Mount{
|
||||
{
|
||||
Type: mountType.TypeBind,
|
||||
Source: "/var/run/docker.sock",
|
||||
Target: "/var/run/docker.sock",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
err := CreateService(service, func (msg string) {})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -28,6 +28,8 @@ var serverPortHTTPS = ""
|
|||
func startHTTPServer(router *mux.Router) {
|
||||
utils.Log("Listening to HTTP on : 0.0.0.0:" + serverPortHTTP)
|
||||
|
||||
docker.CheckPorts()
|
||||
|
||||
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, router)
|
||||
|
||||
if err != nil {
|
||||
|
@ -40,7 +42,7 @@ func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
|
|||
|
||||
cfg := simplecert.Default
|
||||
|
||||
cfg.Domains = utils.GetAllHostnames(true, false)
|
||||
if config.HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["LETSENCRYPT"] {
|
||||
cfg.CacheDir = "/config/certificates"
|
||||
cfg.SSLEmail = config.HTTPConfig.SSLEmail
|
||||
cfg.HTTPAddress = "0.0.0.0:"+serverPortHTTP
|
||||
|
@ -49,6 +51,10 @@ func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
|
|||
if config.HTTPConfig.DNSChallengeProvider != "" {
|
||||
utils.Log("Using DNS Challenge with Provider: " + config.HTTPConfig.DNSChallengeProvider)
|
||||
cfg.DNSProvider = config.HTTPConfig.DNSChallengeProvider
|
||||
cfg.Domains = utils.GetAllHostnames(true, false)
|
||||
} else {
|
||||
cfg.Domains = utils.LetsEncryptValidOnly(utils.GetAllHostnames(true, false))
|
||||
}
|
||||
}
|
||||
|
||||
cfg.FailedToRenewCertificate = func(err error) {
|
||||
|
@ -74,6 +80,10 @@ func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
|
|||
}
|
||||
}
|
||||
|
||||
utils.IsHTTPS = true
|
||||
// Redirect ports
|
||||
docker.CheckPorts()
|
||||
|
||||
// redirect http to https
|
||||
go (func () {
|
||||
httpRouter := mux.NewRouter()
|
||||
|
@ -107,8 +117,6 @@ func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
|
|||
utils.Log("Listening to HTTP on :" + serverPortHTTP)
|
||||
utils.Log("Listening to HTTPS on :" + serverPortHTTPS)
|
||||
|
||||
utils.IsHTTPS = true
|
||||
|
||||
tlsConf := tlsconfig.NewServerTLSConfig(tlsconfig.TLSModeServerStrict)
|
||||
|
||||
if(config.HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["LETSENCRYPT"]) {
|
||||
|
@ -133,6 +141,7 @@ func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
|
|||
DisableGeneralOptionsHandler: true,
|
||||
}
|
||||
|
||||
|
||||
// start https server
|
||||
errServ := server.ListenAndServeTLS("", "")
|
||||
|
||||
|
@ -197,13 +206,13 @@ func StartServer() {
|
|||
var tlsCert = HTTPConfig.TLSCert
|
||||
var tlsKey= HTTPConfig.TLSKey
|
||||
|
||||
domains := utils.GetAllHostnames(true, true)
|
||||
domains := utils.GetAllHostnames(true, false)
|
||||
oldDomains := baseMainConfig.HTTPConfig.TLSKeyHostsCached
|
||||
|
||||
NeedsRefresh := (tlsCert == "" || tlsKey == "") || !utils.StringArrayEquals(domains, oldDomains)
|
||||
|
||||
if(NeedsRefresh && HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["SELFSIGNED"]) {
|
||||
utils.Log("Generating new TLS certificate")
|
||||
utils.Log("Generating new TLS certificate for domains: " + strings.Join(domains, ", "))
|
||||
pub, priv := utils.GenerateRSAWebCertificates(domains)
|
||||
|
||||
baseMainConfig.HTTPConfig.TLSCert = pub
|
||||
|
|
|
@ -307,6 +307,22 @@ func RestartServer() {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
func LetsEncryptValidOnly(hostnames []string) []string {
|
||||
wrongPattern := `^(localhost|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|.*\.local)$`
|
||||
re, _ := regexp.Compile(wrongPattern)
|
||||
|
||||
var validDomains []string
|
||||
for _, domain := range hostnames {
|
||||
if !re.MatchString(domain) && !strings.Contains(domain, "*") && !strings.Contains(domain, " ") && !strings.Contains(domain, ",") {
|
||||
validDomains = append(validDomains, domain)
|
||||
} else {
|
||||
Error("Invalid domain found in URLs: " + domain + " it was removed from the certificate to not break Let's Encrypt", nil)
|
||||
}
|
||||
}
|
||||
|
||||
return validDomains
|
||||
}
|
||||
|
||||
func GetAllHostnames(applyWildCard bool, removePorts bool) []string {
|
||||
mainHostname := GetMainConfig().HTTPConfig.Hostname
|
||||
hostnames := []string{
|
||||
|
@ -315,7 +331,7 @@ func GetAllHostnames(applyWildCard bool, removePorts bool) []string {
|
|||
|
||||
proxies := GetMainConfig().HTTPConfig.ProxyConfig.Routes
|
||||
for _, proxy := range proxies {
|
||||
if proxy.UseHost && proxy.Host != "" && strings.Contains(proxy.Host, ".") && !strings.Contains(proxy.Host, ",") && !strings.Contains(proxy.Host, " ") {
|
||||
if proxy.UseHost && proxy.Host != "" && !strings.Contains(proxy.Host, ",") && !strings.Contains(proxy.Host, " ") {
|
||||
if removePorts {
|
||||
hostnames = append(hostnames, strings.Split(proxy.Host, ":")[0])
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue