1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

feat(server): use https by default (#5315) [EE-332]

This commit is contained in:
Chaim Lev-Ari 2021-08-10 07:59:47 +03:00 committed by GitHub
parent 3257cb1e28
commit 11d555bbd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 1160 additions and 319 deletions

View file

@ -1,6 +1,7 @@
import _ from 'lodash-es';
import componentsModule from './components';
import settingsModule from './settings';
async function initAuthentication(authManager, Authentication, $rootScope, $state) {
authManager.checkAuthOnRefresh();
@ -17,7 +18,7 @@ async function initAuthentication(authManager, Authentication, $rootScope, $stat
return await Authentication.init();
}
angular.module('portainer.app', ['portainer.oauth', componentsModule]).config([
angular.module('portainer.app', ['portainer.oauth', componentsModule, settingsModule]).config([
'$stateRegistryProvider',
function ($stateRegistryProvider) {
'use strict';

17
app/portainer/rest/ssl.js Normal file
View file

@ -0,0 +1,17 @@
import angular from 'angular';
const API_ENDPOINT_SSL = 'api/ssl';
angular.module('portainer.app').factory('SSL', SSLFactory);
/* @ngInject */
function SSLFactory($resource) {
return $resource(
API_ENDPOINT_SSL,
{},
{
get: { method: 'GET' },
upload: { method: 'PUT' },
}
);
}

View file

@ -0,0 +1,19 @@
import angular from 'angular';
angular.module('portainer.app').service('SSLService', SSLServiceFactory);
/* @ngInject */
function SSLServiceFactory(SSL) {
return {
upload,
get,
};
function get() {
return SSL.get().$promise;
}
function upload(httpEnabled, cert, key) {
return SSL.upload({ httpEnabled, cert, key }).$promise;
}
}

View file

@ -0,0 +1,5 @@
import angular from 'angular';
import { sslCertificate } from './ssl-certificate';
export default angular.module('portainer.settings.general', []).component('sslCertificateSettings', sslCertificate).name;

View file

@ -0,0 +1,6 @@
import controller from './ssl-certificate.controller.js';
export const sslCertificate = {
templateUrl: './ssl-certificate.html',
controller,
};

View file

@ -0,0 +1,68 @@
class SslCertificateController {
/* @ngInject */
constructor($async, $state, SSLService, Notifications) {
Object.assign(this, { $async, $state, SSLService, Notifications });
this.cert = null;
this.originalValues = {
forceHTTPS: false,
certFile: null,
keyFile: null,
};
this.formValues = {
certFile: null,
keyFile: null,
forceHTTPS: false,
};
this.state = {
actionInProgress: false,
reloadingPage: false,
};
const pemPattern = '.pem';
this.certFilePattern = `${pemPattern},.crt,.cer,.cert`;
this.keyFilePattern = `${pemPattern},.key`;
this.save = this.save.bind(this);
}
isFormChanged() {
return Object.entries(this.originalValues).some(([key, value]) => value != this.formValues[key]);
}
async save() {
return this.$async(async () => {
this.state.actionInProgress = true;
try {
const cert = this.formValues.certFile ? await this.formValues.certFile.text() : null;
const key = this.formValues.keyFile ? await this.formValues.keyFile.text() : null;
const httpEnabled = !this.formValues.forceHTTPS;
await this.SSLService.upload(httpEnabled, cert, key);
await new Promise((resolve) => setTimeout(resolve, 2000));
location.reload();
this.state.reloadingPage = true;
} catch (err) {
this.Notifications.error('Failure', err, 'Failed applying changes');
}
this.state.actionInProgress = false;
});
}
async $onInit() {
return this.$async(async () => {
try {
const certInfo = await this.SSLService.get();
this.formValues.forceHTTPS = !certInfo.httpEnabled;
this.originalValues.forceHTTPS = this.formValues.forceHTTPS;
} catch (err) {
this.Notifications.error('Failure', err, 'Failed loading certificate info');
}
});
}
}
export default SslCertificateController;

View file

@ -0,0 +1,95 @@
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header icon="fa-key" title-text="SSL certificate"></rd-widget-header>
<rd-widget-body>
<form class="form-horizontal" name="$ctrl.sslForm">
<span class="small">
<p class="text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Forcing HTTPs only will cause Portainer to stop listening on the HTTP port. Any edge agent endpoint that is using HTTP will no longer be available.
</p>
</span>
<por-switch-field ng-model="$ctrl.formValues.forceHTTPS" label="Force HTTPS only" on-change="($ctrl.onChangeForceHTTPS)"></por-switch-field>
<hr />
<div class="form-group">
<span class="col-sm-12">
Provide a new SSL Certificate to replace the existing one that is used for HTTPS connections.
</span>
</div>
<div class="form-group">
<span class="col-sm-12">
Upload a X.509 certificate, commonly a crt, a cer, or a pem file.
</span>
</div>
<div class="form-group">
<div class="col-sm-12">
<button class="btn btn-sm btn-primary" ngf-select ng-model="$ctrl.formValues.certFile" ngf-pattern="$ctrl.certFilePattern" name="certFile">
Select file
</button>
<span style="margin-left: 5px;">
{{ $ctrl.formValues.certFile.name }}
<i class="fa fa-times red-icon" ng-if="!$ctrl.formValues.certFile" aria-hidden="true"></i>
</span>
</div>
</div>
<div class="form-group col-md-12" ng-show="$ctrl.sslForm.certFile.$invalid">
<div class="small text-warning">
<div ng-messages="$ctrl.sslForm.certFile.$error">
<p ng-message="pattern"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> File type is invalid.</p>
</div>
</div>
</div>
<div class="form-group">
<span class="col-sm-12">
Upload a private key, commonly a key, or a pem file.
</span>
</div>
<div class="form-group">
<div class="col-sm-12">
<button class="btn btn-sm btn-primary" ngf-select ng-model="$ctrl.formValues.keyFile" ngf-pattern="$ctrl.keyFilePattern" name="keyFile">
Select file
</button>
<span style="margin-left: 5px;">
{{ $ctrl.formValues.keyFile.name }}
<i class="fa fa-times red-icon" ng-if="!$ctrl.formValues.keyFile" aria-hidden="true"></i>
</span>
</div>
</div>
<div class="form-group col-md-12" ng-show="$ctrl.sslForm.keyFile.$invalid">
<div class="small text-warning">
<div ng-messages="$ctrl.sslForm.keyFile.$error">
<p ng-message="pattern"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> File type is invalid.</p>
</div>
</div>
</div>
<hr />
<div class="form-group">
<div class="col-sm-12">
<button
type="button"
class="btn btn-primary btn-sm"
ng-disabled="$ctrl.state.actionInProgress || !$ctrl.isFormChanged()"
ng-click="$ctrl.save()"
button-spinner="$ctrl.state.actionInProgress"
>
<span ng-hide="$ctrl.state.actionInProgress || $ctrl.state.reloadingPage">Apply Changes</span>
<span ng-show="$ctrl.state.actionInProgress">Saving in progress...</span>
<span ng-show="$ctrl.state.reloadingPage">Reloading Page...</span>
</button>
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
</div>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>

View file

@ -0,0 +1,5 @@
import angular from 'angular';
import generalModule from './general';
export default angular.module('portainer.settings', [generalModule]).name;

View file

@ -44,20 +44,27 @@
<label class="btn btn-primary" ng-model="state.platformType" uib-btn-radio="'windows'"><i class="fab fa-windows" style="margin-right: 2px;"></i> Windows</label>
</div>
</div>
<por-switch-field
label="Allow self-signed certs"
ng-model="state.allowSelfSignedCerts"
tooltip="When allowing self-signed certificates the edge agent will ignore the domain validation when connecting to Portainer via HTTPS"
></por-switch-field>
<div style="margin-top: 10px;">
<uib-tabset active="state.deploymentTab">
<uib-tab index="0" ng-if="state.platformType === 'linux'" heading="Kubernetes">
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;"
>curl https://downloads.portainer.io/portainer-edge-agent-setup.sh | sudo bash -s -- {{ randomEdgeID }} {{ endpoint.EdgeKey }}</code
>
<uib-tab index="'kubernetes'" heading="Kubernetes" ng-if="state.platformType === 'linux'">
<code style="display: block; white-space: pre-wrap; padding: 16px 45px;">
{{ dockerCommands[state.deploymentTab][state.platformType](randomEdgeID, endpoint.EdgeKey, state.allowSelfSignedCerts) }}
</code>
</uib-tab>
<uib-tab index="1" heading="Docker Swarm">
<code ng-if="state.platformType === 'linux'" style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.linuxSwarm }}</code>
<code ng-if="state.platformType === 'windows'" style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.windowsSwarm }}</code>
<uib-tab index="'swarm'" heading="Docker Swarm">
<code style="display: block; white-space: pre-wrap; padding: 16px 45px;">
{{ dockerCommands[state.deploymentTab][state.platformType](randomEdgeID, endpoint.EdgeKey, state.allowSelfSignedCerts) }}
</code>
</uib-tab>
<uib-tab index="2" heading="Docker Standalone">
<code ng-if="state.platformType === 'linux'" style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.linuxStandalone }}</code>
<code ng-if="state.platformType === 'windows'" style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.windowsStandalone }}</code>
<uib-tab index="'standalone'" heading="Docker Standalone">
<code style="display: block; white-space: pre-wrap; padding: 16px 45px;">
{{ dockerCommands[state.deploymentTab][state.platformType](randomEdgeID, endpoint.EdgeKey, state.allowSelfSignedCerts) }}
</code>
</uib-tab>
</uib-tabset>
<div style="margin-top: 10px;">

View file

@ -1,292 +1,290 @@
import _ from 'lodash-es';
import uuidv4 from 'uuid/v4';
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
import { EndpointSecurityFormData } from '../../../components/endpointSecurity/porEndpointSecurityModel';
angular
.module('portainer.app')
.controller('EndpointController', function EndpointController(
$async,
$q,
$scope,
$state,
$transition$,
$filter,
clipboard,
EndpointService,
GroupService,
TagService,
EndpointProvider,
Notifications,
Authentication,
SettingsService,
ModalService
) {
$scope.state = {
uploadInProgress: false,
actionInProgress: false,
deploymentTab: 0,
azureEndpoint: false,
kubernetesEndpoint: false,
agentEndpoint: false,
edgeEndpoint: false,
platformType: 'linux',
allowCreate: Authentication.isAdmin(),
availableEdgeAgentCheckinOptions: [
{ key: 'Use default interval', value: 0 },
{
key: '5 seconds',
value: 5,
},
{
key: '10 seconds',
value: 10,
},
{
key: '30 seconds',
value: 30,
},
{ key: '5 minutes', value: 300 },
{ key: '1 hour', value: 3600 },
{ key: '1 day', value: 86400 },
],
};
import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
import { EndpointSecurityFormData } from '@/portainer/components/endpointSecurity/porEndpointSecurityModel';
$scope.formValues = {
SecurityFormData: new EndpointSecurityFormData(),
};
angular.module('portainer.app').controller('EndpointController', EndpointController);
$scope.copyEdgeAgentDeploymentCommand = function () {
if ($scope.state.deploymentTab === 2 && $scope.state.platformType === 'linux') {
clipboard.copyText(
'docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes -v /:/host -v portainer_agent_data:/data --restart always -e EDGE=1 -e EDGE_ID=' +
$scope.randomEdgeID +
' -e EDGE_KEY=' +
$scope.endpoint.EdgeKey +
' -e CAP_HOST_MANAGEMENT=1 --name portainer_edge_agent portainer/agent'
);
} else if ($scope.state.deploymentTab === 2 && $scope.state.platformType === 'windows') {
clipboard.copyText(
'docker run -d --mount type=npipe,src=\\\\.\\pipe\\docker_engine,dst=\\\\.\\pipe\\docker_engine --mount type=bind,src=C:\\ProgramData\\docker\\volumes,dst=C:\\ProgramData\\docker\\volumes --mount type=volume,src=portainer_agent_data,dst=C:\\data -e EDGE=1 -e EDGE_ID=' +
$scope.randomEdgeID +
' -e EDGE_KEY=' +
$scope.endpoint.EdgeKey +
' -e CAP_HOST_MANAGEMENT=1 --name portainer_edge_agent portainer/agent'
);
} else if ($scope.state.deploymentTab === 1 && $scope.state.platformType === 'linux') {
clipboard.copyText(
'docker network create --driver overlay portainer_agent_network; docker service create --name portainer_edge_agent --network portainer_agent_network -e AGENT_CLUSTER_ADDR=tasks.portainer_edge_agent -e EDGE=1 -e EDGE_ID=' +
$scope.randomEdgeID +
' -e EDGE_KEY=' +
$scope.endpoint.EdgeKey +
" -e CAP_HOST_MANAGEMENT=1 --mode global --constraint 'node.platform.os == linux' --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock --mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes --mount type=bind,src=//,dst=/host --mount type=volume,src=portainer_agent_data,dst=/data portainer/agent"
);
} else if ($scope.state.deploymentTab === 1 && $scope.state.platformType === 'windows') {
clipboard.copyText(
'docker network create --driver overlay portainer_edge_agent_network && docker service create --name portainer_edge_agent --network portainer_edge_agent_network -e AGENT_CLUSTER_ADDR=tasks.portainer_edge_agent -e EDGE=1 -e EDGE_ID=' +
$scope.randomEdgeID +
' -e EDGE_KEY=' +
$scope.endpoint.EdgeKey +
' -e CAP_HOST_MANAGEMENT=1 --mode global --constraint node.platform.os==windows --mount type=npipe,src=\\\\.\\pipe\\docker_engine,dst=\\\\.\\pipe\\docker_engine --mount type=bind,src=C:\\ProgramData\\docker\\volumes,dst=C:\\ProgramData\\docker\\volumes --mount type=volume,src=portainer_agent_data,dst=C:\\data portainer/agent'
);
} else {
clipboard.copyText('curl https://downloads.portainer.io/portainer-edge-agent-setup.sh | bash -s -- ' + $scope.randomEdgeID + ' ' + $scope.endpoint.EdgeKey);
}
$('#copyNotificationDeploymentCommand').show().fadeOut(2500);
};
/* @ngInject */
function EndpointController(
$async,
$q,
$scope,
$state,
$transition$,
$filter,
clipboard,
EndpointService,
GroupService,
TagService,
EndpointProvider,
Notifications,
Authentication,
SettingsService,
ModalService
) {
const DEPLOYMENT_TABS = {
SWARM: 'swarm',
STANDALONE: 'standalone',
KUBERNETES: 'kubernetes',
};
$scope.copyEdgeAgentKey = function () {
clipboard.copyText($scope.endpoint.EdgeKey);
$('#copyNotificationEdgeKey').show().fadeOut(2500);
};
const PLATFORM_TYPES = {
WINDOWS: 'windows',
LINUX: 'linux',
};
$scope.onCreateTag = function onCreateTag(tagName) {
return $async(onCreateTagAsync, tagName);
};
$scope.state = {
uploadInProgress: false,
actionInProgress: false,
deploymentTab: DEPLOYMENT_TABS.KUBERNETES,
platformType: PLATFORM_TYPES.LINUX,
azureEndpoint: false,
kubernetesEndpoint: false,
agentEndpoint: false,
edgeEndpoint: false,
allowCreate: Authentication.isAdmin(),
availableEdgeAgentCheckinOptions: [
{ key: 'Use default interval', value: 0 },
{
key: '5 seconds',
value: 5,
},
{
key: '10 seconds',
value: 10,
},
{
key: '30 seconds',
value: 30,
},
{ key: '5 minutes', value: 300 },
{ key: '1 hour', value: 3600 },
{ key: '1 day', value: 86400 },
],
allowSelfSignedCerts: true,
};
async function onCreateTagAsync(tagName) {
try {
const tag = await TagService.createTag(tagName);
$scope.availableTags = $scope.availableTags.concat(tag);
$scope.endpoint.TagIds = $scope.endpoint.TagIds.concat(tag.Id);
} catch (err) {
Notifications.error('Failue', err, 'Unable to create tag');
}
$scope.dockerCommands = {
[DEPLOYMENT_TABS.STANDALONE]: {
[PLATFORM_TYPES.LINUX]: buildLinuxStandaloneCommand,
[PLATFORM_TYPES.WINDOWS]: buildWindowsStandaloneCommand,
},
[DEPLOYMENT_TABS.SWARM]: {
[PLATFORM_TYPES.LINUX]: buildLinuxSwarmCommand,
[PLATFORM_TYPES.WINDOWS]: buildWindowsSwarmCommand,
},
[DEPLOYMENT_TABS.KUBERNETES]: {
[PLATFORM_TYPES.LINUX]: buildKubernetesCommand,
[PLATFORM_TYPES.WINDOWS]: () => '',
},
};
$scope.formValues = {
SecurityFormData: new EndpointSecurityFormData(),
};
$scope.copyEdgeAgentDeploymentCommand = copyEdgeAgentDeploymentCommand;
function copyEdgeAgentDeploymentCommand() {
const command = $scope.dockerCommands[$scope.state.deploymentTab][$scope.state.platformType]($scope.randomEdgeID, $scope.endpoint.EdgeKey, $scope.state.allowSelfSignedCerts);
clipboard.copyText(command.trim());
$('#copyNotificationDeploymentCommand').show().fadeOut(2500);
}
$scope.copyEdgeAgentKey = function () {
clipboard.copyText($scope.endpoint.EdgeKey);
$('#copyNotificationEdgeKey').show().fadeOut(2500);
};
$scope.onCreateTag = function onCreateTag(tagName) {
return $async(onCreateTagAsync, tagName);
};
async function onCreateTagAsync(tagName) {
try {
const tag = await TagService.createTag(tagName);
$scope.availableTags = $scope.availableTags.concat(tag);
$scope.endpoint.TagIds = $scope.endpoint.TagIds.concat(tag.Id);
} catch (err) {
Notifications.error('Failue', err, 'Unable to create tag');
}
}
$scope.onDeassociateEndpoint = async function () {
ModalService.confirmDeassociate((confirmed) => {
if (confirmed) {
deassociateEndpoint();
}
});
};
async function deassociateEndpoint() {
var endpoint = $scope.endpoint;
try {
$scope.state.actionInProgress = true;
await EndpointService.deassociateEndpoint(endpoint.Id);
Notifications.success('Endpoint de-associated', $scope.endpoint.Name);
$state.reload();
} catch (err) {
Notifications.error('Failure', err, 'Unable to de-associate endpoint');
} finally {
$scope.state.actionInProgress = false;
$scope.onDeassociateEndpoint = async function () {
ModalService.confirmDeassociate((confirmed) => {
if (confirmed) {
deassociateEndpoint();
}
}
});
};
$scope.updateEndpoint = function () {
var endpoint = $scope.endpoint;
var securityData = $scope.formValues.SecurityFormData;
var TLS = securityData.TLS;
var TLSMode = securityData.TLSMode;
var TLSSkipVerify = TLS && (TLSMode === 'tls_client_noca' || TLSMode === 'tls_only');
var TLSSkipClientVerify = TLS && (TLSMode === 'tls_ca' || TLSMode === 'tls_only');
var payload = {
Name: endpoint.Name,
PublicURL: endpoint.PublicURL,
GroupID: endpoint.GroupId,
TagIds: endpoint.TagIds,
EdgeCheckinInterval: endpoint.EdgeCheckinInterval,
TLS: TLS,
TLSSkipVerify: TLSSkipVerify,
TLSSkipClientVerify: TLSSkipClientVerify,
TLSCACert: TLSSkipVerify || securityData.TLSCACert === endpoint.TLSConfig.TLSCACert ? null : securityData.TLSCACert,
TLSCert: TLSSkipClientVerify || securityData.TLSCert === endpoint.TLSConfig.TLSCert ? null : securityData.TLSCert,
TLSKey: TLSSkipClientVerify || securityData.TLSKey === endpoint.TLSConfig.TLSKey ? null : securityData.TLSKey,
AzureApplicationID: endpoint.AzureCredentials.ApplicationID,
AzureTenantID: endpoint.AzureCredentials.TenantID,
AzureAuthenticationKey: endpoint.AzureCredentials.AuthenticationKey,
};
if (
$scope.endpointType !== 'local' &&
endpoint.Type !== PortainerEndpointTypes.AzureEnvironment &&
endpoint.Type !== PortainerEndpointTypes.KubernetesLocalEnvironment &&
endpoint.Type !== PortainerEndpointTypes.AgentOnKubernetesEnvironment
) {
payload.URL = 'tcp://' + endpoint.URL;
}
if (endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment || endpoint.Type === PortainerEndpointTypes.KubernetesLocalEnvironment) {
payload.URL = endpoint.URL;
}
async function deassociateEndpoint() {
var endpoint = $scope.endpoint;
try {
$scope.state.actionInProgress = true;
EndpointService.updateEndpoint(endpoint.Id, payload).then(
function success() {
Notifications.success('Endpoint updated', $scope.endpoint.Name);
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
$state.go('portainer.endpoints', {}, { reload: true });
},
function error(err) {
Notifications.error('Failure', err, 'Unable to update endpoint');
$scope.state.actionInProgress = false;
},
function update(evt) {
if (evt.upload) {
$scope.state.uploadInProgress = evt.upload;
}
}
);
await EndpointService.deassociateEndpoint(endpoint.Id);
Notifications.success('Endpoint de-associated', $scope.endpoint.Name);
$state.reload();
} catch (err) {
Notifications.error('Failure', err, 'Unable to de-associate endpoint');
} finally {
$scope.state.actionInProgress = false;
}
}
$scope.updateEndpoint = function () {
var endpoint = $scope.endpoint;
var securityData = $scope.formValues.SecurityFormData;
var TLS = securityData.TLS;
var TLSMode = securityData.TLSMode;
var TLSSkipVerify = TLS && (TLSMode === 'tls_client_noca' || TLSMode === 'tls_only');
var TLSSkipClientVerify = TLS && (TLSMode === 'tls_ca' || TLSMode === 'tls_only');
var payload = {
Name: endpoint.Name,
PublicURL: endpoint.PublicURL,
GroupID: endpoint.GroupId,
TagIds: endpoint.TagIds,
EdgeCheckinInterval: endpoint.EdgeCheckinInterval,
TLS: TLS,
TLSSkipVerify: TLSSkipVerify,
TLSSkipClientVerify: TLSSkipClientVerify,
TLSCACert: TLSSkipVerify || securityData.TLSCACert === endpoint.TLSConfig.TLSCACert ? null : securityData.TLSCACert,
TLSCert: TLSSkipClientVerify || securityData.TLSCert === endpoint.TLSConfig.TLSCert ? null : securityData.TLSCert,
TLSKey: TLSSkipClientVerify || securityData.TLSKey === endpoint.TLSConfig.TLSKey ? null : securityData.TLSKey,
AzureApplicationID: endpoint.AzureCredentials.ApplicationID,
AzureTenantID: endpoint.AzureCredentials.TenantID,
AzureAuthenticationKey: endpoint.AzureCredentials.AuthenticationKey,
};
function decodeEdgeKey(key) {
let keyInformation = {};
if (
$scope.endpointType !== 'local' &&
endpoint.Type !== PortainerEndpointTypes.AzureEnvironment &&
endpoint.Type !== PortainerEndpointTypes.KubernetesLocalEnvironment &&
endpoint.Type !== PortainerEndpointTypes.AgentOnKubernetesEnvironment
) {
payload.URL = 'tcp://' + endpoint.URL;
}
if (key === '') {
return keyInformation;
if (endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment || endpoint.Type === PortainerEndpointTypes.KubernetesLocalEnvironment) {
payload.URL = endpoint.URL;
}
$scope.state.actionInProgress = true;
EndpointService.updateEndpoint(endpoint.Id, payload).then(
function success() {
Notifications.success('Endpoint updated', $scope.endpoint.Name);
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
$state.go('portainer.endpoints', {}, { reload: true });
},
function error(err) {
Notifications.error('Failure', err, 'Unable to update endpoint');
$scope.state.actionInProgress = false;
},
function update(evt) {
if (evt.upload) {
$scope.state.uploadInProgress = evt.upload;
}
}
);
};
let decodedKey = _.split(atob(key), '|');
keyInformation.instanceURL = decodedKey[0];
keyInformation.tunnelServerAddr = decodedKey[1];
function decodeEdgeKey(key) {
let keyInformation = {};
if (key === '') {
return keyInformation;
}
function configureState() {
if (
$scope.endpoint.Type === PortainerEndpointTypes.KubernetesLocalEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
) {
$scope.state.kubernetesEndpoint = true;
}
if ($scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || $scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
$scope.state.edgeEndpoint = true;
}
if ($scope.endpoint.Type === PortainerEndpointTypes.AzureEnvironment) {
$scope.state.azureEndpoint = true;
}
if (
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnDockerEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
) {
$scope.state.agentEndpoint = true;
}
let decodedKey = _.split(atob(key), '|');
keyInformation.instanceURL = decodedKey[0];
keyInformation.tunnelServerAddr = decodedKey[1];
return keyInformation;
}
function configureState() {
if (
$scope.endpoint.Type === PortainerEndpointTypes.KubernetesLocalEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
) {
$scope.state.kubernetesEndpoint = true;
}
function initView() {
$q.all({
endpoint: EndpointService.endpoint($transition$.params().id),
groups: GroupService.groups(),
tags: TagService.tags(),
settings: SettingsService.settings(),
})
.then(function success(data) {
var endpoint = data.endpoint;
if (endpoint.URL.indexOf('unix://') === 0 || endpoint.URL.indexOf('npipe://') === 0) {
$scope.endpointType = 'local';
} else {
$scope.endpointType = 'remote';
}
endpoint.URL = $filter('stripprotocol')(endpoint.URL);
if (endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
$scope.edgeKeyDetails = decodeEdgeKey(endpoint.EdgeKey);
$scope.randomEdgeID = uuidv4();
$scope.dockerCommands = {
linuxStandalone: buildLinuxStandaloneCommand($scope.randomEdgeID, endpoint.EdgeKey),
windowsStandalone: buildWindowsStandaloneCommand($scope.randomEdgeID, endpoint.EdgeKey),
linuxSwarm: buildLinuxSwarmCommand($scope.randomEdgeID, endpoint.EdgeKey),
windowsSwarm: buildWindowsSwarmCommand($scope.randomEdgeID, endpoint.EdgeKey),
};
const settings = data.settings;
$scope.state.availableEdgeAgentCheckinOptions[0].key += ` (${settings.EdgeAgentCheckinInterval} seconds)`;
}
$scope.endpoint = endpoint;
$scope.groups = data.groups;
$scope.availableTags = data.tags;
configureState();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve endpoint details');
});
if ($scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || $scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
$scope.state.edgeEndpoint = true;
}
function buildLinuxStandaloneCommand(edgeId, edgeKey) {
return `docker run -d \\
-v /var/run/docker.sock:/var/run/docker.sock \\
-v /var/lib/docker/volumes:/var/lib/docker/volumes \\
-v /:/host \\
-v portainer_agent_data:/data \\
--restart always \\
-e EDGE=1 \\
-e EDGE_ID=${edgeId} \\
-e EDGE_KEY=${edgeKey} \\
-e CAP_HOST_MANAGEMENT=1 \\
--name portainer_edge_agent \\
portainer/agent`;
if ($scope.endpoint.Type === PortainerEndpointTypes.AzureEnvironment) {
$scope.state.azureEndpoint = true;
}
if (
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnDockerEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment ||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
) {
$scope.state.agentEndpoint = true;
}
}
function buildWindowsStandaloneCommand(edgeId, edgeKey) {
return `docker run -d \\
async function initView() {
return $async(async () => {
try {
const [endpoint, groups, tags, settings] = await Promise.all([
EndpointService.endpoint($transition$.params().id),
GroupService.groups(),
TagService.tags(),
SettingsService.settings(),
]);
if (endpoint.URL.indexOf('unix://') === 0 || endpoint.URL.indexOf('npipe://') === 0) {
$scope.endpointType = 'local';
} else {
$scope.endpointType = 'remote';
}
endpoint.URL = $filter('stripprotocol')(endpoint.URL);
if (endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
$scope.edgeKeyDetails = decodeEdgeKey(endpoint.EdgeKey);
$scope.randomEdgeID = uuidv4();
$scope.state.availableEdgeAgentCheckinOptions[0].key += ` (${settings.EdgeAgentCheckinInterval} seconds)`;
}
$scope.endpoint = endpoint;
$scope.groups = groups;
$scope.availableTags = tags;
configureState();
} catch (err) {
Notifications.error('Failure', err, 'Unable to retrieve endpoint details');
}
});
}
function buildLinuxStandaloneCommand(edgeId, edgeKey, allowSelfSignedCerts) {
return `
docker run -d \\
-v /var/run/docker.sock:/var/run/docker.sock \\
-v /var/lib/docker/volumes:/var/lib/docker/volumes \\
-v /:/host \\
-v portainer_agent_data:/data \\
--restart always \\
-e EDGE=1 \\
-e EDGE_ID=${edgeId} \\
-e EDGE_KEY=${edgeKey} \\
-e CAP_HOST_MANAGEMENT=1 \\
-e EDGE_INSECURE_POLL=${allowSelfSignedCerts ? 1 : 0} \\
--name portainer_edge_agent \\
portainer/agent:2.4.0`;
}
function buildWindowsStandaloneCommand(edgeId, edgeKey, allowSelfSignedCerts) {
return `
docker run -d \\
--mount type=npipe,src=\\\\.\\pipe\\docker_engine,dst=\\\\.\\pipe\\docker_engine \\
--mount type=bind,src=C:\\ProgramData\\docker\\volumes,dst=C:\\ProgramData\\docker\\volumes \\
--mount type=volume,src=portainer_agent_data,dst=C:\\data \\
@ -295,12 +293,14 @@ angular
-e EDGE_ID=${edgeId} \\
-e EDGE_KEY=${edgeKey} \\
-e CAP_HOST_MANAGEMENT=1 \\
-e EDGE_INSECURE_POLL=${allowSelfSignedCerts ? 1 : 0} \\
--name portainer_edge_agent \\
portainer/agent`;
}
}
function buildLinuxSwarmCommand(edgeId, edgeKey) {
return `docker network create \\
function buildLinuxSwarmCommand(edgeId, edgeKey, allowSelfSignedCerts) {
return `
docker network create \\
--driver overlay \\
portainer_agent_network;
@ -312,6 +312,7 @@ docker service create \\
-e EDGE_ID=${edgeId} \\
-e EDGE_KEY=${edgeKey} \\
-e CAP_HOST_MANAGEMENT=1 \\
-e EDGE_INSECURE_POLL=${allowSelfSignedCerts ? 1 : 0} \\
--mode global \\
--constraint 'node.platform.os == linux' \\
--mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \\
@ -319,10 +320,11 @@ docker service create \\
--mount type=bind,src=//,dst=/host \\
--mount type=volume,src=portainer_agent_data,dst=/data \\
portainer/agent`;
}
}
function buildWindowsSwarmCommand(edgeId, edgeKey) {
return `docker network create \\
function buildWindowsSwarmCommand(edgeId, edgeKey, allowSelfSignedCerts) {
return `
docker network create \\
--driver overlay \\
portainer_edge_agent_network && \\
docker service create \\
@ -333,13 +335,20 @@ docker service create \\
-e EDGE_ID=${edgeId} \\
-e EDGE_KEY=${edgeKey} \\
-e CAP_HOST_MANAGEMENT=1 \\
-e EDGE_INSECURE_POLL=${allowSelfSignedCerts ? 1 : 0} \\
--mode global \\
--constraint node.platform.os==windows \\
--mount type=npipe,src=\\\\.\\pipe\\docker_engine,dst=\\\\.\\pipe\\docker_engine \\
--mount type=bind,src=C:\\ProgramData\\docker\\volumes,dst=C:\\ProgramData\\docker\\volumes \\
--mount type=volume,src=portainer_agent_data,dst=C:\\data \\
portainer/agent`;
}
}
initView();
});
function buildKubernetesCommand(edgeId, edgeKey, allowSelfSignedCerts) {
return `
curl https://downloads.portainer.io/portainer-ce29-edge-agent-setup.sh | bash -s -- ${edgeId} ${edgeKey} ${allowSelfSignedCerts ? '1' : ''}
`;
}
initView();
}

View file

@ -131,6 +131,8 @@
</div>
</div>
<ssl-certificate-settings></ssl-certificate-settings>
<div class="row">
<div class="col-sm-12">
<rd-widget>

View file

@ -189,10 +189,10 @@
</li>
<li class="sidebar-list" ng-if="isAdmin">
<a ui-sref="portainer.settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs fa-fw"></span></a>
<div class="sidebar-sublist" ng-if="toggle && ($state.current.name === 'portainer.settings' || $state.current.name === 'portainer.settings.authentication') && isAdmin">
<a ui-sref="portainer.settings.authentication" ui-sref-active="active">Authentication</a></div
>
<div class="sidebar-sublist" ng-if="toggle && ($state.current.name === 'portainer.settings' || $state.current.name === 'portainer.settings.authentication')">
<div class="sidebar-sublist" ng-if="toggle && $state.current.name.startsWith('portainer.settings') && isAdmin">
<a ui-sref="portainer.settings.authentication" ui-sref-active="active">Authentication</a>
</div>
<div class="sidebar-sublist" ng-if="toggle && $state.current.name.startsWith('portainer.settings')">
<a href="http://www.portainer.io/help_about" target="_blank">Help / About</a>
</div>
</li>