mirror of https://github.com/portainer/portainer
fix(docker/tls): update tls certs for Docker API env [EE-4286] (#9112)
parent
f1f46f4da1
commit
f02ede00b3
|
@ -4,6 +4,7 @@ import (
|
|||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/libhttp/request"
|
||||
|
@ -246,7 +247,10 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
}
|
||||
}
|
||||
|
||||
if (payload.URL != nil && *payload.URL != endpoint.URL) || (payload.TLS != nil && endpoint.TLSConfig.TLS != *payload.TLS) || endpoint.Type == portainer.AzureEnvironment {
|
||||
if (payload.URL != nil && *payload.URL != endpoint.URL) ||
|
||||
(payload.TLS != nil && endpoint.TLSConfig.TLS != *payload.TLS) ||
|
||||
endpoint.Type == portainer.AzureEnvironment ||
|
||||
shouldReloadTLSConfiguration(endpoint, &payload) {
|
||||
handler.ProxyManager.DeleteEndpointProxy(endpoint.ID)
|
||||
_, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint)
|
||||
if err != nil {
|
||||
|
@ -285,3 +289,22 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
|
||||
return response.JSON(w, endpoint)
|
||||
}
|
||||
|
||||
func shouldReloadTLSConfiguration(endpoint *portainer.Endpoint, payload *endpointUpdatePayload) bool {
|
||||
// When updating Docker API environment, as long as TLS is true and TLSSkipVerify is false,
|
||||
// we assume that new TLS files have been uploaded and we need to reload the TLS configuration.
|
||||
if endpoint.Type != portainer.DockerEnvironment ||
|
||||
!strings.HasPrefix(*payload.URL, "tcp://") ||
|
||||
payload.TLS == nil || !*payload.TLS {
|
||||
return false
|
||||
}
|
||||
|
||||
if payload.TLSSkipVerify != nil && !*payload.TLSSkipVerify {
|
||||
return true
|
||||
}
|
||||
|
||||
if payload.TLSSkipClientVerify != nil && !*payload.TLSSkipClientVerify {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -8,8 +8,9 @@ import { boxSelectorModule } from './BoxSelector';
|
|||
import { beFeatureIndicator } from './BEFeatureIndicator';
|
||||
import { InformationPanelAngular } from './InformationPanel';
|
||||
import { gitFormModule } from './forms/git-form';
|
||||
import { tlsFieldsetModule } from './tls-fieldset';
|
||||
|
||||
export default angular
|
||||
.module('portainer.app.components', [boxSelectorModule, widgetModule, gitFormModule, porAccessManagementModule, formComponentsModule])
|
||||
.module('portainer.app.components', [boxSelectorModule, widgetModule, gitFormModule, porAccessManagementModule, formComponentsModule, tlsFieldsetModule])
|
||||
.component('informationPanel', InformationPanelAngular)
|
||||
.component('beFeatureIndicator', beFeatureIndicator).name;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import {
|
||||
TLSFieldset,
|
||||
tlsConfigValidation,
|
||||
} from '@/react/components/TLSFieldset';
|
||||
import { withFormValidation } from '@/react-tools/withFormValidation';
|
||||
|
||||
export const ngModule = angular.module(
|
||||
'portainer.app.components.tls-fieldset',
|
||||
[]
|
||||
);
|
||||
|
||||
export const tlsFieldsetModule = ngModule.name;
|
||||
|
||||
withFormValidation(
|
||||
ngModule,
|
||||
TLSFieldset,
|
||||
'tlsFieldset',
|
||||
[],
|
||||
tlsConfigValidation
|
||||
);
|
|
@ -72,7 +72,7 @@
|
|||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<form class="form-horizontal" name="$ctrl.endpointForm">
|
||||
<div class="col-sm-12 form-section-title"> Configuration </div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
|
@ -124,6 +124,14 @@
|
|||
</div>
|
||||
|
||||
<!-- !endpoint-public-url-input -->
|
||||
|
||||
<tls-fieldset
|
||||
ng-if="!state.edgeEndpoint && endpoint.Status !== 4 && state.showTLSConfig"
|
||||
values="formValues.tlsConfig"
|
||||
on-change="(onChangeTLSConfigFormValues)"
|
||||
validation-data="{optionalCert: true}"
|
||||
></tls-fieldset>
|
||||
|
||||
<azure-endpoint-config
|
||||
ng-if="state.azureEndpoint"
|
||||
application-id="endpoint.AzureCredentials.ApplicationID"
|
||||
|
@ -142,12 +150,6 @@
|
|||
|
||||
<tag-selector ng-if="endpoint" value="endpoint.TagIds" allow-create="state.allowCreate" on-change="(onChangeTags)"></tag-selector>
|
||||
|
||||
<!-- endpoint-security -->
|
||||
<div ng-if="endpointType === 'remote' && !state.azureEndpoint && !state.kubernetesEndpoint && !state.edgeEndpoint && endpoint.Type !== 6">
|
||||
<div class="col-sm-12 form-section-title"> Security </div>
|
||||
<por-endpoint-security form-data="formValues.SecurityFormData" endpoint="endpoint"></por-endpoint-security>
|
||||
</div>
|
||||
<!-- !endpoint-security -->
|
||||
<!-- open-amt info -->
|
||||
<div ng-if="state.showAMTInfo">
|
||||
<div class="col-sm-12 form-section-title"> Open Active Management Technology </div>
|
||||
|
@ -219,7 +221,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm !ml-0"
|
||||
ng-disabled="state.actionInProgress || !endpoint.Name || !endpoint.URL || (endpoint.TLS && ((endpoint.TLSVerify && !formValues.TLSCACert) || (endpoint.TLSClientCert && (!formValues.TLSCert || !formValues.TLSKey))))"
|
||||
ng-disabled="state.actionInProgress || !endpoint.Name || !endpoint.URL || !$ctrl.endpointForm.$valid"
|
||||
ng-click="updateEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
|
|
|
@ -2,11 +2,10 @@ import _ from 'lodash-es';
|
|||
import uuidv4 from 'uuid/v4';
|
||||
|
||||
import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
|
||||
import { EndpointSecurityFormData } from '@/portainer/components/endpointSecurity/porEndpointSecurityModel';
|
||||
import EndpointHelper from '@/portainer/helpers/endpointHelper';
|
||||
import { getAMTInfo } from 'Portainer/hostmanagement/open-amt/open-amt.service';
|
||||
import { confirmDestructive } from '@@/modals/confirm';
|
||||
import { isEdgeEnvironment } from '@/react/portainer/environments/utils';
|
||||
import { isEdgeEnvironment, isDockerAPIEnvironment } from '@/react/portainer/environments/utils';
|
||||
|
||||
import { commandsTabs } from '@/react/edge/components/EdgeScriptForm/scripts';
|
||||
import { confirmDisassociate } from '@/react/portainer/environments/ItemView/ConfirmDisassociateModel';
|
||||
|
@ -33,6 +32,8 @@ function EndpointController(
|
|||
$scope.onChangeCheckInInterval = onChangeCheckInInterval;
|
||||
$scope.setFieldValue = setFieldValue;
|
||||
$scope.onChangeTags = onChangeTags;
|
||||
$scope.onChangeTLSConfigFormValues = onChangeTLSConfigFormValues;
|
||||
|
||||
const isBE = process.env.PORTAINER_EDITION === 'BE';
|
||||
|
||||
$scope.state = {
|
||||
|
@ -53,6 +54,7 @@ function EndpointController(
|
|||
allowSelfSignedCerts: true,
|
||||
showAMTInfo: false,
|
||||
showNomad: isBE,
|
||||
showTLSConfig: false,
|
||||
edgeScriptCommands: {
|
||||
linux: _.compact([commandsTabs.k8sLinux, commandsTabs.swarmLinux, commandsTabs.standaloneLinux, isBE && commandsTabs.nomadLinux]),
|
||||
win: [commandsTabs.swarmWindows, commandsTabs.standaloneWindow],
|
||||
|
@ -100,7 +102,14 @@ function EndpointController(
|
|||
};
|
||||
|
||||
$scope.formValues = {
|
||||
SecurityFormData: new EndpointSecurityFormData(),
|
||||
tlsConfig: {
|
||||
tls: false,
|
||||
skipVerify: false,
|
||||
skipClientVerify: false,
|
||||
caCertFile: null,
|
||||
certFile: null,
|
||||
keyFile: null,
|
||||
},
|
||||
};
|
||||
|
||||
$scope.onDisassociateEndpoint = async function () {
|
||||
|
@ -134,6 +143,15 @@ function EndpointController(
|
|||
setFieldValue('TagIds', value);
|
||||
}
|
||||
|
||||
function onChangeTLSConfigFormValues(newValues) {
|
||||
return this.$async(async () => {
|
||||
$scope.formValues.tlsConfig = {
|
||||
...$scope.formValues.tlsConfig,
|
||||
...newValues,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function setFieldValue(name, value) {
|
||||
return $scope.$evalAsync(() => {
|
||||
$scope.endpoint = {
|
||||
|
@ -158,11 +176,6 @@ function EndpointController(
|
|||
|
||||
$scope.updateEndpoint = async 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');
|
||||
|
||||
if (isEdgeEnvironment(endpoint.Type) && _.difference($scope.initialTagIds, endpoint.TagIds).length > 0) {
|
||||
let confirmed = await confirmDestructive({
|
||||
|
@ -182,12 +195,6 @@ function EndpointController(
|
|||
Gpus: endpoint.Gpus,
|
||||
GroupID: endpoint.GroupId,
|
||||
TagIds: endpoint.TagIds,
|
||||
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,
|
||||
|
@ -201,6 +208,18 @@ function EndpointController(
|
|||
endpoint.Type !== PortainerEndpointTypes.AgentOnKubernetesEnvironment
|
||||
) {
|
||||
payload.URL = 'tcp://' + endpoint.URL;
|
||||
|
||||
if (endpoint.Type === PortainerEndpointTypes.DockerEnvironment) {
|
||||
var tlsConfig = $scope.formValues.tlsConfig;
|
||||
payload.TLS = tlsConfig.tls;
|
||||
payload.TLSSkipVerify = tlsConfig.skipVerify;
|
||||
if (tlsConfig.tls && !tlsConfig.skipVerify) {
|
||||
payload.TLSSkipClientVerify = tlsConfig.skipClientVerify;
|
||||
payload.TLSCACert = tlsConfig.caCertFile;
|
||||
payload.TLSCert = tlsConfig.certFile;
|
||||
payload.TLSKey = tlsConfig.keyFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment) {
|
||||
|
@ -267,11 +286,25 @@ function EndpointController(
|
|||
}
|
||||
}
|
||||
|
||||
function configureTLS(endpoint) {
|
||||
$scope.formValues = {
|
||||
tlsConfig: {
|
||||
tls: endpoint.TLSConfig.TLS || false,
|
||||
skipVerify: endpoint.TLSConfig.TLSSkipVerify || false,
|
||||
skipClientVerify: endpoint.TLSConfig.TLSSkipClientVerify || false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function initView() {
|
||||
return $async(async () => {
|
||||
try {
|
||||
const [endpoint, groups, settings] = await Promise.all([EndpointService.endpoint($transition$.params().id), GroupService.groups(), SettingsService.settings()]);
|
||||
|
||||
if (isDockerAPIEnvironment(endpoint)) {
|
||||
$scope.state.showTLSConfig = true;
|
||||
}
|
||||
|
||||
// Check if the environment is docker standalone, to decide whether to show the GPU insights box
|
||||
const isDockerEnvironment = endpoint.Type === PortainerEndpointTypes.DockerEnvironment;
|
||||
if (isDockerEnvironment) {
|
||||
|
@ -305,6 +338,8 @@ function EndpointController(
|
|||
|
||||
configureState();
|
||||
|
||||
configureTLS(endpoint);
|
||||
|
||||
if (EndpointHelper.isDockerEndpoint(endpoint) && $scope.state.edgeAssociated) {
|
||||
$scope.state.showAMTInfo = settings && settings.openAMTConfiguration && settings.openAMTConfiguration.enabled;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
import { useFormikContext } from 'formik';
|
||||
import { FormikErrors } from 'formik';
|
||||
import { SchemaOf, boolean, object } from 'yup';
|
||||
|
||||
import { file, withFileSize } from '@@/form-components/yup-file-validation';
|
||||
import { FileUploadField } from '@@/form-components/FileUpload';
|
||||
import { SwitchField } from '@@/form-components/SwitchField';
|
||||
import { FormControl } from '@@/form-components/FormControl';
|
||||
|
||||
import { FormValues } from './types';
|
||||
import { TLSConfig } from './types';
|
||||
|
||||
export function TLSFieldset() {
|
||||
const { values, setFieldValue, errors } = useFormikContext<FormValues>();
|
||||
interface Props {
|
||||
values: TLSConfig;
|
||||
onChange: (value: Partial<TLSConfig>) => void;
|
||||
errors?: FormikErrors<TLSConfig>;
|
||||
}
|
||||
|
||||
export function TLSFieldset({ values, onChange, errors }: Props) {
|
||||
return (
|
||||
<>
|
||||
<div className="form-group">
|
||||
|
@ -18,7 +23,7 @@ export function TLSFieldset() {
|
|||
label="TLS"
|
||||
labelClass="col-sm-3 col-lg-2"
|
||||
checked={values.tls}
|
||||
onChange={(checked) => setFieldValue('tls', checked)}
|
||||
onChange={(checked) => handleChange({ tls: checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,7 +35,8 @@ export function TLSFieldset() {
|
|||
<SwitchField
|
||||
label="Skip Certification Verification"
|
||||
checked={!!values.skipVerify}
|
||||
onChange={(checked) => setFieldValue('skipVerify', checked)}
|
||||
onChange={(checked) => handleChange({ skipVerify: checked })}
|
||||
labelClass="col-sm-3 col-lg-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -40,33 +46,33 @@ export function TLSFieldset() {
|
|||
<FormControl
|
||||
label="TLS CA certificate"
|
||||
inputId="ca-cert-field"
|
||||
errors={errors.caCertFile}
|
||||
errors={errors?.caCertFile}
|
||||
>
|
||||
<FileUploadField
|
||||
inputId="ca-cert-field"
|
||||
onChange={(file) => setFieldValue('caCertFile', file)}
|
||||
onChange={(file) => handleChange({ caCertFile: file })}
|
||||
value={values.caCertFile}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
label="TLS certificate"
|
||||
inputId="cert-field"
|
||||
errors={errors.certFile}
|
||||
errors={errors?.certFile}
|
||||
>
|
||||
<FileUploadField
|
||||
inputId="cert-field"
|
||||
onChange={(file) => setFieldValue('certFile', file)}
|
||||
onChange={(file) => handleChange({ certFile: file })}
|
||||
value={values.certFile}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
label="TLS key"
|
||||
inputId="tls-key-field"
|
||||
errors={errors.keyFile}
|
||||
errors={errors?.keyFile}
|
||||
>
|
||||
<FileUploadField
|
||||
inputId="tls-key-field"
|
||||
onChange={(file) => setFieldValue('keyFile', file)}
|
||||
onChange={(file) => handleChange({ keyFile: file })}
|
||||
value={values.keyFile}
|
||||
/>
|
||||
</FormControl>
|
||||
|
@ -76,21 +82,29 @@ export function TLSFieldset() {
|
|||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
function handleChange(partialValue: Partial<TLSConfig>) {
|
||||
onChange(partialValue);
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_FILE_SIZE = 5_242_880; // 5MB
|
||||
|
||||
function certValidation() {
|
||||
function certValidation(optional?: boolean) {
|
||||
return withFileSize(file(), MAX_FILE_SIZE).when(['tls', 'skipVerify'], {
|
||||
is: (tls: boolean, skipVerify: boolean) => tls && !skipVerify,
|
||||
is: (tls: boolean, skipVerify: boolean) => tls && !skipVerify && !optional,
|
||||
then: (schema) => schema.required('File is required'),
|
||||
});
|
||||
}
|
||||
|
||||
export function validation() {
|
||||
return {
|
||||
caCertFile: certValidation(),
|
||||
certFile: certValidation(),
|
||||
keyFile: certValidation(),
|
||||
};
|
||||
export function tlsConfigValidation({
|
||||
optionalCert,
|
||||
}: { optionalCert?: boolean } = {}): SchemaOf<TLSConfig> {
|
||||
return object({
|
||||
tls: boolean().default(false),
|
||||
skipVerify: boolean().default(false),
|
||||
caCertFile: certValidation(optionalCert),
|
||||
certFile: certValidation(optionalCert),
|
||||
keyFile: certValidation(optionalCert),
|
||||
});
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { TLSFieldset, tlsConfigValidation } from './TLSFieldset';
|
|
@ -0,0 +1,7 @@
|
|||
export interface TLSConfig {
|
||||
tls: boolean;
|
||||
skipVerify?: boolean;
|
||||
caCertFile?: File;
|
||||
certFile?: File;
|
||||
keyFile?: File;
|
||||
}
|
|
@ -67,6 +67,13 @@ export function isLocalEnvironment(environment: Environment) {
|
|||
);
|
||||
}
|
||||
|
||||
export function isDockerAPIEnvironment(environment: Environment) {
|
||||
return (
|
||||
environment.URL.startsWith('tcp://') &&
|
||||
environment.Type === EnvironmentType.Docker
|
||||
);
|
||||
}
|
||||
|
||||
export function getDashboardRoute(environment: Environment) {
|
||||
if (isEdgeEnvironment(environment.Type)) {
|
||||
if (!environment.EdgeID) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
Environment,
|
||||
EnvironmentCreationTypes,
|
||||
} from '@/react/portainer/environments/types';
|
||||
import { TLSFieldset } from '@/react/components/TLSFieldset/TLSFieldset';
|
||||
|
||||
import { LoadingButton } from '@@/buttons/LoadingButton';
|
||||
import { FormControl } from '@@/form-components/FormControl';
|
||||
|
@ -19,7 +20,6 @@ import { MoreSettingsSection } from '../../shared/MoreSettingsSection';
|
|||
|
||||
import { useValidation } from './APIForm.validation';
|
||||
import { FormValues } from './types';
|
||||
import { TLSFieldset } from './TLSFieldset';
|
||||
|
||||
interface Props {
|
||||
onCreate(environment: Environment): void;
|
||||
|
@ -31,7 +31,10 @@ export function APIForm({ onCreate, isDockerStandalone }: Props) {
|
|||
const initialValues: FormValues = {
|
||||
url: '',
|
||||
name: '',
|
||||
tls: false,
|
||||
tlsConfig: {
|
||||
tls: false,
|
||||
skipVerify: false,
|
||||
},
|
||||
meta: {
|
||||
groupId: 1,
|
||||
tagIds: [],
|
||||
|
@ -52,7 +55,7 @@ export function APIForm({ onCreate, isDockerStandalone }: Props) {
|
|||
validateOnMount
|
||||
key={formKey}
|
||||
>
|
||||
{({ isValid, dirty }) => (
|
||||
{({ values, errors, setFieldValue, isValid, dirty }) => (
|
||||
<Form>
|
||||
<NameField />
|
||||
|
||||
|
@ -70,7 +73,15 @@ export function APIForm({ onCreate, isDockerStandalone }: Props) {
|
|||
/>
|
||||
</FormControl>
|
||||
|
||||
<TLSFieldset />
|
||||
<TLSFieldset
|
||||
values={values.tlsConfig}
|
||||
onChange={(value) =>
|
||||
Object.entries(value).forEach(([key, value]) =>
|
||||
setFieldValue(`tlsConfig.${key}`, value)
|
||||
)
|
||||
}
|
||||
errors={errors.tlsConfig}
|
||||
/>
|
||||
|
||||
<MoreSettingsSection>
|
||||
{isDockerStandalone && (
|
||||
|
@ -141,24 +152,24 @@ export function APIForm({ onCreate, isDockerStandalone }: Props) {
|
|||
}
|
||||
);
|
||||
function getTlsValues() {
|
||||
if (!values.tls) {
|
||||
if (!values.tlsConfig.tls) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
skipVerify: values.skipVerify,
|
||||
skipVerify: values.tlsConfig.skipVerify,
|
||||
...getCertFiles(),
|
||||
};
|
||||
|
||||
function getCertFiles() {
|
||||
if (values.skipVerify) {
|
||||
if (values.tlsConfig.skipVerify) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
caCertFile: values.caCertFile,
|
||||
certFile: values.certFile,
|
||||
keyFile: values.keyFile,
|
||||
caCertFile: values.tlsConfig.caCertFile,
|
||||
certFile: values.tlsConfig.certFile,
|
||||
keyFile: values.tlsConfig.keyFile,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
import { boolean, object, SchemaOf, string } from 'yup';
|
||||
import { object, SchemaOf, string } from 'yup';
|
||||
|
||||
import { tlsConfigValidation } from '@/react/components/TLSFieldset/TLSFieldset';
|
||||
|
||||
import { metadataValidation } from '../../shared/MetadataFieldset/validation';
|
||||
import { useNameValidation } from '../../shared/NameField';
|
||||
|
||||
import { validation as certsValidation } from './TLSFieldset';
|
||||
import { FormValues } from './types';
|
||||
|
||||
export function useValidation(): SchemaOf<FormValues> {
|
||||
return object({
|
||||
name: useNameValidation(),
|
||||
url: string().required('This field is required.'),
|
||||
tls: boolean().default(false),
|
||||
skipVerify: boolean(),
|
||||
tlsConfig: tlsConfigValidation(),
|
||||
meta: metadataValidation(),
|
||||
...certsValidation(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import { TLSConfig } from '@/react/components/TLSFieldset/types';
|
||||
import { EnvironmentMetadata } from '@/react/portainer/environments/environment.service/create';
|
||||
|
||||
export interface FormValues {
|
||||
name: string;
|
||||
url: string;
|
||||
tls: boolean;
|
||||
skipVerify?: boolean;
|
||||
caCertFile?: File;
|
||||
certFile?: File;
|
||||
keyFile?: File;
|
||||
tlsConfig: TLSConfig;
|
||||
meta: EnvironmentMetadata;
|
||||
}
|
||||
|
|
|
@ -126,6 +126,7 @@ function OverrideSocketFieldset() {
|
|||
checked={values.overridePath}
|
||||
onChange={(checked) => setFieldValue('overridePath', checked)}
|
||||
label="Override default socket path"
|
||||
labelClass="col-sm-3 col-lg-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue